1 from __future__ import unicode_literals
5 from django.db import models
6 from django.contrib.auth.models import User, UserManager
7 from django.core import urlresolvers
8 from django.db.models.signals import pre_save, post_save
9 from django.db import connection
12 logger = logging.getLogger(__name__)
15 class Antibody(models.Model):
16 antigene = models.CharField(max_length=500, db_index=True)
19 # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
20 nickname = models.CharField(
26 catalog = models.CharField(max_length=50, blank=True, null=True)
27 antibodies = models.CharField(max_length=500, db_index=True)
28 source = models.CharField(max_length=500,
29 blank=True, null=True, db_index=True)
30 biology = models.TextField(blank=True, null=True)
31 notes = models.TextField(blank=True, null=True)
34 return '%s - %s' % (self.antigene, self.antibodies)
37 verbose_name_plural = "antibodies"
38 ordering = ["antigene"]
41 class Cellline(models.Model):
42 cellline_name = models.CharField(max_length=100,
43 unique=True, db_index=True)
44 nickname = models.CharField(
50 notes = models.TextField(blank=True)
53 return str(self.cellline_name)
56 ordering = ["cellline_name"]
59 class Condition(models.Model):
60 condition_name = models.CharField(
61 max_length=2000, unique=True, db_index=True)
62 nickname = models.CharField(
67 verbose_name='Short Name')
68 notes = models.TextField(blank=True)
71 return str(self.condition_name)
74 ordering = ["condition_name"]
77 class ExperimentType(models.Model):
78 name = models.CharField(max_length=50, unique=True)
84 class Tag(models.Model):
85 tag_name = models.CharField(max_length=100,
86 db_index=True,blank=False,null=False)
88 #('Antibody','Antibody'),
89 #('Cellline', 'Cellline'),
90 #('Condition', 'Condition'),
91 ('Library', 'Library'),
94 context = models.CharField(
96 choices=TAG_CONTEXT, default='Library')
99 return '%s' % (self.tag_name,)
102 ordering = ["context", "tag_name"]
105 class Species(models.Model):
106 scientific_name = models.CharField(
111 common_name = models.CharField(max_length=256, blank=True)
112 #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
115 return '%s (%s)' % (self.scientific_name, self.common_name)
118 verbose_name_plural = "species"
119 ordering = ["scientific_name"]
122 def get_absolute_url(self):
123 return ('samples.views.species', [str(self.id)])
126 class Affiliation(models.Model):
127 name = models.CharField(max_length=256, db_index=True, verbose_name='Name')
128 contact = models.CharField(max_length=256,
129 null=True, blank=True, verbose_name='Lab Name')
130 email = models.EmailField(null=True, blank=True)
131 users = models.ManyToManyField('HTSUser', null=True, blank=True)
132 users.admin_order_field = "username"
135 name = str(self.name)
136 if self.contact is not None and len(self.contact) > 0:
137 name += ' ('+self.contact+')'
141 users = self.users.all().order_by('username')
142 return ", ".join([str(a) for a in users])
145 ordering = ["name", "contact"]
146 unique_together = (("name", "contact"),)
149 class LibraryType(models.Model):
150 name = models.CharField(max_length=255, unique=True,
151 verbose_name="Adapter Type")
152 is_paired_end = models.BooleanField(
154 help_text="can you do a paired end run with this adapter")
155 can_multiplex = models.BooleanField(
157 help_text="Does this adapter provide multiplexing?")
160 return str(self.name)
166 class MultiplexIndex(models.Model):
167 """Map adapter types to the multiplex sequence"""
168 adapter_type = models.ForeignKey(LibraryType)
169 multiplex_id = models.CharField(max_length=6, null=False)
170 sequence = models.CharField(max_length=12, blank=True, null=True)
173 verbose_name_plural = "multiplex indicies"
174 unique_together = ('adapter_type', 'multiplex_id')
177 class Library(models.Model):
178 id = models.CharField(max_length=10, primary_key=True)
179 library_name = models.CharField(max_length=100, unique=True)
180 library_species = models.ForeignKey(Species)
181 hidden = models.BooleanField(default=False)
182 account_number = models.CharField(max_length=100, null=True, blank=True)
183 cell_line = models.ForeignKey(Cellline, blank=True, null=True,
184 verbose_name="Background")
185 condition = models.ForeignKey(Condition, blank=True, null=True)
186 antibody = models.ForeignKey(Antibody,blank=True,null=True)
187 affiliations = models.ManyToManyField(
188 Affiliation,related_name='library_affiliations',null=True)
189 tags = models.ManyToManyField(Tag,related_name='library_tags',
190 blank=True,null=True)
191 REPLICATE_NUM = [(x,x) for x in range(1,7)]
192 replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
193 blank=True,null=True)
194 experiment_type = models.ForeignKey(ExperimentType)
195 library_type = models.ForeignKey(LibraryType, blank=True, null=True,
196 verbose_name="Adapter Type")
197 multiplex_id = models.CharField(max_length=128,
198 blank=True, null=True,
199 verbose_name="Index ID")
200 creation_date = models.DateField(blank=True, null=True)
201 made_for = models.CharField(max_length=50, blank=True,
202 verbose_name='ChIP/DNA/RNA Made By')
203 made_by = models.CharField(max_length=50, blank=True, default="Lorian")
205 PROTOCOL_END_POINTS = (
207 ('Sample', 'Raw sample'),
208 ('Progress', 'In progress'),
209 ('1A', 'Ligation, then gel'),
210 ('PCR', 'Ligation, then PCR'),
211 ('1Ab', 'Ligation, PCR, then gel'),
212 ('1Ac', 'Ligation, gel, then 12x PCR'),
213 ('1Aa', 'Ligation, gel, then 18x PCR'),
214 ('2A', 'Ligation, PCR, gel, PCR'),
215 ('Done', 'Completed'),
217 PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
218 stopping_point = models.CharField(max_length=25,
219 choices=PROTOCOL_END_POINTS,
222 amplified_from_sample = models.ForeignKey(
224 related_name='amplified_into_sample',
225 blank=True, null=True)
227 undiluted_concentration = models.DecimalField(
229 max_digits=5, decimal_places=2, blank=True, null=True,
230 help_text="Undiluted concentration (ng/\u00b5l)")
231 # note \u00b5 is the micro symbol in unicode
232 successful_pM = models.DecimalField(
233 max_digits=9, decimal_places=1, blank=True, null=True)
234 ten_nM_dilution = models.BooleanField(default=False)
235 gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
236 insert_size = models.IntegerField(blank=True, null=True)
237 notes = models.TextField(blank=True)
239 bioanalyzer_summary = models.TextField(blank=True, default="")
240 bioanalyzer_concentration = models.DecimalField(
241 max_digits=5, decimal_places=2, blank=True, null=True,
242 help_text="(ng/\u00b5l)")
243 bioanalyzer_image_url = models.URLField(blank=True, default="")
246 return '#%s: %s' % (self.id, self.library_name)
249 verbose_name_plural = "libraries"
250 # ordering = ["-creation_date"]
253 def antibody_name(self):
254 str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>'
256 antibody_name.allow_tags = True
259 return self.library_species.common_name
261 def index_sequences(self):
262 """Return a dictionary of multiplex index id to sequence
263 Return None if the library can't multiplex,
265 if self.library_type is None:
267 if not self.library_type.can_multiplex:
269 if self.multiplex_id is None or len(self.multiplex_id) == 0:
270 return 'Err: id empty'
272 multiplex_expressions = self.multiplex_id.split(',')
273 for multiplex_term in multiplex_expressions:
274 pairs = multiplex_term.split('-')
277 seq = self._lookup_index(pairs[0])
278 elif len(pairs) == 2:
279 key = pairs[0] + '-' + pairs[1]
280 seq0 = self._lookup_index(pairs[0])
281 seq1 = self._lookup_index(pairs[1])
282 if seq0 is None or seq1 is None:
285 seq = seq0 + '-' + seq1
287 raise RuntimeError("Too many - seperated sequences")
289 seq = 'Err: index not found'
293 def _lookup_index(self, multiplex_id):
295 multiplex = MultiplexIndex.objects.get(
296 adapter_type=self.library_type.id,
297 multiplex_id=multiplex_id)
298 return multiplex.sequence
299 except MultiplexIndex.DoesNotExist as e:
302 def index_sequence_text(self, seperator=' '):
303 """Return formatted multiplex index sequences"""
304 sequences = self.index_sequences()
305 if sequences is None:
307 if isinstance(sequences, six.string_types):
309 multiplex_ids = sorted(sequences)
310 return seperator.join(("%s:%s" % (i,sequences[i]) for i in multiplex_ids))
311 index_sequence_text.short_description = "Index"
313 def affiliation(self):
314 affs = self.affiliations.all().order_by('name')
318 ar.append(t.__str__())
319 return '%s' % (", ".join(ar))
321 def is_archived(self):
322 """returns True if archived else False
324 if self.longtermstorage_set.count() > 0:
329 def lanes_sequenced(self):
330 """Count how many lanes of each type were run.
337 counts = [[0, 0, 0], [0, 0, 0]]
339 for lane in self.lane_set.all():
340 if lane.flowcell.paired_end:
345 if lane.flowcell.read_length < 40:
346 read_type = short_read
347 elif lane.flowcell.read_length < 100:
348 read_type = medium_read
350 read_type = long_read
351 counts[lane_type][read_type] += 1
355 def stopping_point_name(self):
356 end_points = Library.PROTOCOL_END_POINTS_DICT
357 name = end_points.get(self.stopping_point, None)
359 name = "Lookup Error"
360 logger.error("protocol stopping point in database didn't match names in library model")
364 affs = self.tags.all().order_by('tag_name')
367 ar.append(t.__str__())
368 return '%s' % (", ".join(ar))
371 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
373 DataRun.allow_tags = True
375 def aligned_m_reads(self):
376 return getLibReads(self.id)
378 def aligned_reads(self):
379 res = getLibReads(self.id)
383 return '<div style="border:solid red 2px">'+res[2]+'</div>'
385 rc = "%1.2f" % (res[1]/1000000.0)
386 # Color Scheme: green is more than 10M, blue is more than 5M, orange is more than 3M and red is less. For RNAseq, all those thresholds should be doubled
388 bgcolor = '#ff3300' # Red
389 rc_thr = [10000000, 5000000, 3000000]
390 if self.experiment_type == 'RNA-seq':
391 rc_thr = [20000000, 10000000, 6000000]
393 if res[1] > rc_thr[0]:
394 bgcolor = '#66ff66' # Green
396 if res[1] > rc_thr[1]:
397 bgcolor = '#00ccff' # Blue
399 if res[1] > rc_thr[2]:
400 bgcolor = '#ffcc33' # Orange
401 tstr = '<div style="background-color:'+bgcolor+';color:black">'
402 tstr += res[0].__str__()+' Lanes, '+rc+' M Reads'
405 tstr = 'not processed yet'
407 aligned_reads.allow_tags = True
410 summary_url = self.get_absolute_url()
411 return '<a href="%s">S</a>' % (summary_url,)
412 public.allow_tags = True
415 def get_absolute_url(self):
416 return ('samples.views.library_to_flowcells', [str(self.id)])
418 def get_admin_url(self):
419 return urlresolvers.reverse('admin:samples_library_change',
425 Provide some site-specific customization for the django user class
427 #objects = UserManager()
430 ordering = ['first_name', 'last_name', 'username']
433 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
436 # return str(self.username) + " (" + str(self.get_full_name()) + u")"
437 return str(self.get_full_name()) + ' (' + str(self.username) + ')'
439 def HTSUserInsertID(sender, instance, **kwargs):
441 Force addition of HTSUsers when someone just modifies the auth_user object
443 u = HTSUser.objects.filter(pk=instance.id)
445 cursor = connection.cursor()
446 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
449 post_save.connect(HTSUserInsertID, sender=User)