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/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>'
255 antibody_name.allow_tags = True
258 return self.library_species.common_name
260 def index_sequences(self):
261 """Return a dictionary of multiplex index id to sequence
262 Return None if the library can't multiplex,
264 if self.library_type is None:
266 if not self.library_type.can_multiplex:
268 if self.multiplex_id is None or len(self.multiplex_id) == 0:
269 return 'Err: id empty'
271 multiplex_expressions = self.multiplex_id.split(',')
272 for multiplex_term in multiplex_expressions:
273 pairs = multiplex_term.split('-')
276 seq = self._lookup_index(pairs[0])
277 elif len(pairs) == 2:
278 key = pairs[0] + '-' + pairs[1]
279 seq0 = self._lookup_index(pairs[0])
280 seq1 = self._lookup_index(pairs[1])
281 if seq0 is None or seq1 is None:
284 seq = seq0 + '-' + seq1
286 raise RuntimeError("Too many - seperated sequences")
288 seq = 'Err: index not found'
292 def _lookup_index(self, multiplex_id):
294 multiplex = MultiplexIndex.objects.get(
295 adapter_type=self.library_type.id,
296 multiplex_id=multiplex_id)
297 return multiplex.sequence
298 except MultiplexIndex.DoesNotExist as e:
301 def index_sequence_text(self, seperator=' '):
302 """Return formatted multiplex index sequences"""
303 sequences = self.index_sequences()
304 if sequences is None:
306 if isinstance(sequences, six.string_types):
308 multiplex_ids = sorted(sequences)
309 return seperator.join(("%s:%s" % (i,sequences[i]) for i in multiplex_ids))
310 index_sequence_text.short_description = "Index"
312 def affiliation(self):
313 affs = self.affiliations.all().order_by('name')
317 ar.append(t.__str__())
318 return '%s' % (", ".join(ar))
320 def is_archived(self):
321 """returns True if archived else False
323 if self.longtermstorage_set.count() > 0:
328 def lanes_sequenced(self):
329 """Count how many lanes of each type were run.
336 counts = [[0, 0, 0], [0, 0, 0]]
338 for lane in self.lane_set.all():
339 if lane.flowcell.paired_end:
344 if lane.flowcell.read_length < 40:
345 read_type = short_read
346 elif lane.flowcell.read_length < 100:
347 read_type = medium_read
349 read_type = long_read
350 counts[lane_type][read_type] += 1
354 def stopping_point_name(self):
355 end_points = Library.PROTOCOL_END_POINTS_DICT
356 name = end_points.get(self.stopping_point, None)
358 name = "Lookup Error"
359 logger.error("protocol stopping point in database didn't match names in library model")
363 affs = self.tags.all().order_by('tag_name')
366 ar.append(t.__str__())
367 return '%s' % (", ".join(ar))
370 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
372 DataRun.allow_tags = True
374 def aligned_m_reads(self):
375 return getLibReads(self.id)
377 def aligned_reads(self):
378 res = getLibReads(self.id)
382 return '<div style="border:solid red 2px">'+res[2]+'</div>'
384 rc = "%1.2f" % (res[1]/1000000.0)
385 # 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
387 bgcolor = '#ff3300' # Red
388 rc_thr = [10000000, 5000000, 3000000]
389 if self.experiment_type == 'RNA-seq':
390 rc_thr = [20000000, 10000000, 6000000]
392 if res[1] > rc_thr[0]:
393 bgcolor = '#66ff66' # Green
395 if res[1] > rc_thr[1]:
396 bgcolor = '#00ccff' # Blue
398 if res[1] > rc_thr[2]:
399 bgcolor = '#ffcc33' # Orange
400 tstr = '<div style="background-color:'+bgcolor+';color:black">'
401 tstr += res[0].__str__()+' Lanes, '+rc+' M Reads'
404 tstr = 'not processed yet'
406 aligned_reads.allow_tags = True
409 summary_url = self.get_absolute_url()
410 return '<a href="%s">S</a>' % (summary_url,)
411 public.allow_tags = True
414 def get_absolute_url(self):
415 return ('samples.views.library_to_flowcells', [str(self.id)])
417 def get_admin_url(self):
418 return urlresolvers.reverse('admin:samples_library_change',
424 Provide some site-specific customization for the django user class
426 #objects = UserManager()
429 ordering = ['first_name', 'last_name', 'username']
432 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
435 # return str(self.username) + " (" + str(self.get_full_name()) + u")"
436 return str(self.get_full_name()) + ' (' + str(self.username) + ')'
438 def HTSUserInsertID(sender, instance, **kwargs):
440 Force addition of HTSUsers when someone just modifies the auth_user object
442 u = HTSUser.objects.filter(pk=instance.id)
444 cursor = connection.cursor()
445 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
448 post_save.connect(HTSUserInsertID, sender=User)