1 from __future__ import unicode_literals
4 from django.db import models
5 from django.contrib.auth.models import User
6 from django.core import urlresolvers
7 from django.db.models.signals import post_save
8 from django.db import connection
11 logger = logging.getLogger(__name__)
14 class Antibody(models.Model):
15 antigene = models.CharField(max_length=500, db_index=True)
18 # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
19 nickname = models.CharField(
25 catalog = models.CharField(max_length=50, blank=True, null=True)
26 antibodies = models.CharField(max_length=500, db_index=True)
27 source = models.CharField(max_length=500,
28 blank=True, null=True, db_index=True)
29 biology = models.TextField(blank=True, null=True)
30 notes = models.TextField(blank=True, null=True)
33 return '%s - %s' % (self.antigene, self.antibodies)
36 verbose_name_plural = "antibodies"
37 ordering = ["antigene"]
40 class Cellline(models.Model):
41 cellline_name = models.CharField(max_length=100,
42 unique=True, db_index=True)
43 nickname = models.CharField(
49 notes = models.TextField(blank=True)
52 return str(self.cellline_name)
55 ordering = ["cellline_name"]
58 class Condition(models.Model):
59 condition_name = models.CharField(
60 max_length=2000, unique=True, db_index=True)
61 nickname = models.CharField(
66 verbose_name='Short Name')
67 notes = models.TextField(blank=True)
70 return str(self.condition_name)
73 ordering = ["condition_name"]
76 class ExperimentType(models.Model):
77 name = models.CharField(max_length=50, unique=True)
83 class Tag(models.Model):
84 tag_name = models.CharField(max_length=100,
85 db_index=True, blank=False, null=False)
87 # ('Antibody','Antibody'),
88 # ('Cellline', 'Cellline'),
89 # ('Condition', 'Condition'),
90 ('Library', 'Library'),
93 context = models.CharField(
95 choices=TAG_CONTEXT, default='Library')
98 return '%s' % (self.tag_name,)
101 ordering = ["context", "tag_name"]
104 class Species(models.Model):
105 scientific_name = models.CharField(
110 common_name = models.CharField(max_length=256, blank=True)
111 #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
114 return '%s (%s)' % (self.scientific_name, self.common_name)
117 verbose_name_plural = "species"
118 ordering = ["scientific_name"]
121 def get_absolute_url(self):
122 return ('samples.views.species', [str(self.id)])
125 class Affiliation(models.Model):
126 name = models.CharField(max_length=256, db_index=True, verbose_name='Name')
127 contact = models.CharField(max_length=256,
128 null=True, blank=True, verbose_name='Lab Name')
129 email = models.EmailField(null=True, blank=True)
130 users = models.ManyToManyField('HTSUser', null=True, blank=True)
131 users.admin_order_field = "username"
134 name = str(self.name)
135 if self.contact is not None and len(self.contact) > 0:
136 name += ' ('+self.contact+')'
140 users = self.users.all().order_by('username')
141 return ", ".join([str(a) for a in users])
144 ordering = ["name", "contact"]
145 unique_together = (("name", "contact"),)
148 class LibraryType(models.Model):
149 name = models.CharField(max_length=255, unique=True,
150 verbose_name="Adapter Type")
151 is_paired_end = models.BooleanField(
153 help_text="can you do a paired end run with this adapter")
154 can_multiplex = models.BooleanField(
156 help_text="Does this adapter provide multiplexing?")
159 return str(self.name)
165 class MultiplexIndex(models.Model):
166 """Map adapter types to the multiplex sequence"""
167 adapter_type = models.ForeignKey(LibraryType)
168 multiplex_id = models.CharField(max_length=6, null=False)
169 sequence = models.CharField(max_length=12, blank=True, null=True)
172 verbose_name_plural = "multiplex indicies"
173 unique_together = ('adapter_type', 'multiplex_id')
176 class Library(models.Model):
177 id = models.CharField(max_length=10, primary_key=True)
178 library_name = models.CharField(max_length=100, unique=True)
179 library_species = models.ForeignKey(Species)
180 hidden = models.BooleanField(default=False)
181 account_number = models.CharField(max_length=100, null=True, blank=True)
182 cell_line = models.ForeignKey(Cellline, blank=True, null=True,
183 verbose_name="Background")
184 condition = models.ForeignKey(Condition, blank=True, null=True)
185 antibody = models.ForeignKey(Antibody, blank=True, null=True)
186 affiliations = models.ManyToManyField(
187 Affiliation, related_name='library_affiliations', null=True)
188 tags = models.ManyToManyField(Tag, related_name='library_tags',
189 blank=True, null=True)
190 REPLICATE_NUM = [(x, x) for x in range(1, 7)]
191 replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
192 blank=True, null=True)
193 experiment_type = models.ForeignKey(ExperimentType)
194 library_type = models.ForeignKey(LibraryType, blank=True, null=True,
195 verbose_name="Adapter Type")
196 multiplex_id = models.CharField(max_length=128,
197 blank=True, null=True,
198 verbose_name="Index ID")
199 creation_date = models.DateField(blank=True, null=True)
200 made_for = models.CharField(max_length=50, blank=True,
201 verbose_name='ChIP/DNA/RNA Made By')
202 made_by = models.CharField(max_length=50, blank=True, default="Lorian")
204 PROTOCOL_END_POINTS = (
206 ('Sample', 'Raw sample'),
207 ('Progress', 'In progress'),
208 ('1A', 'Ligation, then gel'),
209 ('PCR', 'Ligation, then PCR'),
210 ('1Ab', 'Ligation, PCR, then gel'),
211 ('1Ac', 'Ligation, gel, then 12x PCR'),
212 ('1Aa', 'Ligation, gel, then 18x PCR'),
213 ('2A', 'Ligation, PCR, gel, PCR'),
214 ('Done', 'Completed'),
216 PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
217 stopping_point = models.CharField(max_length=25,
218 choices=PROTOCOL_END_POINTS,
221 amplified_from_sample = models.ForeignKey(
223 related_name='amplified_into_sample',
224 blank=True, null=True)
226 undiluted_concentration = models.DecimalField(
228 max_digits=5, decimal_places=2, blank=True, null=True,
229 help_text="Undiluted concentration (ng/\u00b5l)")
230 # note \u00b5 is the micro symbol in unicode
231 successful_pM = models.DecimalField(
232 max_digits=9, decimal_places=1, blank=True, null=True)
233 ten_nM_dilution = models.BooleanField(default=False)
234 gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
235 insert_size = models.IntegerField(blank=True, null=True)
236 notes = models.TextField(blank=True)
238 bioanalyzer_summary = models.TextField(blank=True, default="")
239 bioanalyzer_concentration = models.DecimalField(
240 max_digits=5, decimal_places=2, blank=True, null=True,
241 help_text="(ng/\u00b5l)")
242 bioanalyzer_image_url = models.URLField(blank=True, default="")
245 return '#%s: %s' % (self.id, self.library_name)
248 verbose_name_plural = "libraries"
249 # ordering = ["-creation_date"]
252 def antibody_name(self):
253 str = '<a target=_self href="/admin/samples/antibody/' + \
254 self.antibody.id.__str__() + \
255 '/" title="' + self.antibody.__str__() + '">' + \
256 self.antibody.label+'</a>'
258 antibody_name.allow_tags = True
261 return self.library_species.common_name
263 def index_sequences(self):
264 """Return a dictionary of multiplex index id to sequence
265 Return None if the library can't multiplex,
267 if self.library_type is None:
269 if not self.library_type.can_multiplex:
271 if self.multiplex_id is None or len(self.multiplex_id) == 0:
272 return 'Err: id empty'
274 multiplex_expressions = self.multiplex_id.split(',')
275 for multiplex_term in multiplex_expressions:
276 pairs = multiplex_term.split('-')
279 seq = self._lookup_index(pairs[0])
280 elif len(pairs) == 2:
281 key = pairs[0] + '-' + pairs[1]
282 seq0 = self._lookup_index(pairs[0])
283 seq1 = self._lookup_index(pairs[1])
284 if seq0 is None or seq1 is None:
287 seq = seq0 + '-' + seq1
289 raise RuntimeError("Too many - seperated sequences")
291 seq = 'Err: index not found'
295 def _lookup_index(self, multiplex_id):
297 multiplex = MultiplexIndex.objects.get(
298 adapter_type=self.library_type.id,
299 multiplex_id=multiplex_id)
300 return multiplex.sequence
301 except MultiplexIndex.DoesNotExist as e:
304 def index_sequence_text(self, seperator=' '):
305 """Return formatted multiplex index sequences"""
306 sequences = self.index_sequences()
307 if sequences is None:
309 if isinstance(sequences, six.string_types):
311 multiplex_ids = sorted(sequences)
312 return seperator.join(
313 ("%s:%s" % (i, sequences[i]) for i in multiplex_ids))
314 index_sequence_text.short_description = "Index"
316 def affiliation(self):
317 affs = self.affiliations.all().order_by('name')
321 ar.append(t.__str__())
322 return '%s' % (", ".join(ar))
324 def is_archived(self):
325 """returns True if archived else False
327 if self.longtermstorage_set.count() > 0:
332 def lanes_sequenced(self):
333 """Count how many lanes of each type were run.
340 counts = [[0, 0, 0], [0, 0, 0]]
342 for lane in self.lane_set.all():
343 if lane.flowcell.paired_end:
348 if lane.flowcell.read_length < 40:
349 read_type = short_read
350 elif lane.flowcell.read_length < 100:
351 read_type = medium_read
353 read_type = long_read
354 counts[lane_type][read_type] += 1
358 def stopping_point_name(self):
359 end_points = Library.PROTOCOL_END_POINTS_DICT
360 name = end_points.get(self.stopping_point, None)
362 name = "Lookup Error"
363 logger.error("protocol stopping point in database"
364 "didn't match names in library model")
368 affs = self.tags.all().order_by('tag_name')
371 ar.append(t.__str__())
372 return '%s' % (", ".join(ar))
375 str = '<a target=_self href="/admin/experiments/datarun/?q=' + \
377 '" title="Check All Data Runs for This Specific Library ..."' \
380 DataRun.allow_tags = True
382 def aligned_m_reads(self):
383 return getLibReads(self.id)
385 def aligned_reads(self):
386 res = getLibReads(self.id)
390 return '<div style="border:solid red 2px">'+res[2]+'</div>'
392 rc = "%1.2f" % (res[1]/1000000.0)
393 # 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
395 bgcolor = '#ff3300' # Red
396 rc_thr = [10000000, 5000000, 3000000]
397 if self.experiment_type == 'RNA-seq':
398 rc_thr = [20000000, 10000000, 6000000]
400 if res[1] > rc_thr[0]:
401 bgcolor = '#66ff66' # Green
403 if res[1] > rc_thr[1]:
404 bgcolor = '#00ccff' # Blue
406 if res[1] > rc_thr[2]:
407 bgcolor = '#ffcc33' # Orange
408 tstr = '<div style="background-color:'+bgcolor+';color:black">'
409 tstr += res[0].__str__()+' Lanes, '+rc+' M Reads'
412 tstr = 'not processed yet'
414 aligned_reads.allow_tags = True
417 summary_url = self.get_absolute_url()
418 return '<a href="%s">S</a>' % (summary_url,)
419 public.allow_tags = True
422 def get_absolute_url(self):
423 return ('samples.views.library_to_flowcells', [str(self.id)])
425 def get_admin_url(self):
426 return urlresolvers.reverse('admin:samples_library_change',
432 Provide some site-specific customization for the django user class
434 # objects = UserManager()
437 ordering = ['first_name', 'last_name', 'username']
440 return '/admin/%s/%s/%d' % (self._meta.app_label,
441 self._meta.module_name, self.id)
444 # return str(self.username) + " (" + str(self.get_full_name()) + u")"
445 return str(self.get_full_name()) + ' (' + str(self.username) + ')'
448 def HTSUserInsertID(sender, instance, **kwargs):
450 Force addition of HTSUsers when someone just modifies the auth_user object
452 u = HTSUser.objects.filter(pk=instance.id)
454 cursor = connection.cursor()
456 'INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' %
460 post_save.connect(HTSUserInsertID, sender=User)