3 from django.db import models
4 from django.contrib.auth.models import User, UserManager
5 from django.core import urlresolvers
6 from django.db.models.signals import pre_save, post_save
7 from django.db import connection
8 from htsworkflow.frontend.reports.libinfopar import *
10 logger = logging.getLogger(__name__)
12 class Antibody(models.Model):
13 antigene = models.CharField(max_length=500, db_index=True)
16 # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
17 nickname = models.CharField(
23 catalog = models.CharField(max_length=50, blank=True, null=True)
24 antibodies = models.CharField(max_length=500, db_index=True)
25 source = models.CharField(max_length=500, blank=True, null=True, db_index=True)
26 biology = models.TextField(blank=True, null=True)
27 notes = models.TextField(blank=True, null=True)
28 def __unicode__(self):
29 return u'%s - %s' % (self.antigene, self.antibodies)
31 verbose_name_plural = "antibodies"
32 ordering = ["antigene"]
34 class Cellline(models.Model):
35 cellline_name = models.CharField(max_length=100, unique=True, db_index=True)
36 nickname = models.CharField(max_length=20,
41 notes = models.TextField(blank=True)
42 def __unicode__(self):
43 return unicode(self.cellline_name)
46 ordering = ["cellline_name"]
48 class Condition(models.Model):
49 condition_name = models.CharField(
50 max_length=2000, unique=True, db_index=True)
51 nickname = models.CharField(max_length=20,
55 verbose_name = 'Short Name')
56 notes = models.TextField(blank=True)
58 def __unicode__(self):
59 return unicode(self.condition_name)
62 ordering = ["condition_name"]
65 class ExperimentType(models.Model):
66 name = models.CharField(max_length=50, unique=True)
68 def __unicode__(self):
69 return unicode(self.name)
71 class Tag(models.Model):
72 tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False)
74 #('Antibody','Antibody'),
75 #('Cellline', 'Cellline'),
76 #('Condition', 'Condition'),
77 ('Library', 'Library'),
80 context = models.CharField(max_length=50,
81 choices=TAG_CONTEXT, default='Library')
83 def __unicode__(self):
84 return u'%s' % (self.tag_name)
87 ordering = ["context","tag_name"]
89 class Species(models.Model):
90 scientific_name = models.CharField(max_length=256,
94 common_name = models.CharField(max_length=256, blank=True)
95 #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
97 def __unicode__(self):
98 return u'%s (%s)' % (self.scientific_name, self.common_name)
101 verbose_name_plural = "species"
102 ordering = ["scientific_name"]
105 def get_absolute_url(self):
106 return ('htsworkflow.frontend.samples.views.species', [str(self.id)])
108 class Affiliation(models.Model):
109 name = models.CharField(max_length=256, db_index=True, verbose_name='Name')
110 contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name')
111 email = models.EmailField(null=True,blank=True)
112 users = models.ManyToManyField('HTSUser', null=True, blank=True)
113 users.admin_order_field = "username"
115 def __unicode__(self):
116 str = unicode(self.name)
117 if self.contact is not None and len(self.contact) > 0:
118 str += u' ('+self.contact+u')'
122 users = self.users.all().order_by('username')
123 return ", ".join([unicode(a) for a in users ])
126 ordering = ["name","contact"]
127 unique_together = (("name", "contact"),)
129 class LibraryType(models.Model):
130 name = models.CharField(max_length=255, unique=True,
132 is_paired_end = models.BooleanField(default=True,
133 help_text="can you do a paired end run with this adapter")
134 can_multiplex = models.BooleanField(default=True,
135 help_text="Does this adapter provide multiplexing?")
137 def __unicode__(self):
138 return unicode(self.name)
144 class MultiplexIndex(models.Model):
145 """Map adapter types to the multiplex sequence"""
146 adapter_type = models.ForeignKey(LibraryType)
147 multiplex_id = models.CharField(max_length=6, null=False)
148 sequence = models.CharField(max_length=12, blank=True, null=True)
151 unique_together = ('adapter_type', 'multiplex_id')
153 class Library(models.Model):
154 id = models.CharField(max_length=10, primary_key=True)
155 library_name = models.CharField(max_length=100, unique=True)
156 library_species = models.ForeignKey(Species)
157 hidden = models.BooleanField()
158 account_number = models.CharField(max_length=100, null=True, blank=True)
159 cell_line = models.ForeignKey(Cellline, blank=True, null=True,
160 verbose_name="Background")
161 condition = models.ForeignKey(Condition, blank=True, null=True)
162 antibody = models.ForeignKey(Antibody,blank=True,null=True)
163 affiliations = models.ManyToManyField(
164 Affiliation,related_name='library_affiliations',null=True)
165 tags = models.ManyToManyField(Tag,related_name='library_tags',
166 blank=True,null=True)
167 REPLICATE_NUM = [(x,x) for x in range(1,7)]
168 replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
169 blank=True,null=True)
170 experiment_type = models.ForeignKey(ExperimentType)
171 library_type = models.ForeignKey(LibraryType, blank=True, null=True,
172 verbose_name="Adapter Type")
173 multiplex_id = models.CharField(max_length=128,
174 blank=True, null=True,
175 verbose_name="Index ID")
176 creation_date = models.DateField(blank=True, null=True)
177 made_for = models.CharField(max_length=50, blank=True,
178 verbose_name='ChIP/DNA/RNA Made By')
179 made_by = models.CharField(max_length=50, blank=True, default="Lorian")
181 PROTOCOL_END_POINTS = (
183 ('Sample', 'Raw sample'),
184 ('Progress', 'In progress'),
185 ('1A', 'Ligation, then gel'),
186 ('PCR', 'Ligation, then PCR'),
187 ('1Ab', 'Ligation, PCR, then gel'),
188 ('1Ac', 'Ligation, gel, then 12x PCR'),
189 ('1Aa', 'Ligation, gel, then 18x PCR'),
190 ('2A', 'Ligation, PCR, gel, PCR'),
191 ('Done', 'Completed'),
193 PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
194 stopping_point = models.CharField(max_length=25,
195 choices=PROTOCOL_END_POINTS,
198 amplified_from_sample = models.ForeignKey('self',
199 related_name='amplified_into_sample',
200 blank=True, null=True)
202 undiluted_concentration = models.DecimalField("Concentration",
203 max_digits=5, decimal_places=2, blank=True, null=True,
204 help_text=u"Undiluted concentration (ng/\u00b5l)")
205 # note \u00b5 is the micro symbol in unicode
206 successful_pM = models.DecimalField(max_digits=9,
207 decimal_places=1, blank=True, null=True)
208 ten_nM_dilution = models.BooleanField()
209 gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
210 insert_size = models.IntegerField(blank=True, null=True)
211 notes = models.TextField(blank=True)
213 bioanalyzer_summary = models.TextField(blank=True,default="")
214 bioanalyzer_concentration = models.DecimalField(max_digits=5,
215 decimal_places=2, blank=True, null=True,
216 help_text=u"(ng/\u00b5l)")
217 bioanalyzer_image_url = models.URLField(blank=True,default="")
219 def __unicode__(self):
220 return u'#%s: %s' % (self.id, self.library_name)
223 verbose_name_plural = "libraries"
224 #ordering = ["-creation_date"]
227 def antibody_name(self):
228 str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>'
230 antibody_name.allow_tags = True
233 return self.library_species.common_name
235 def index_sequences(self):
236 """Return a dictionary of multiplex index id to sequence
237 Return None if the library can't multiplex,
240 if self.library_type is None:
242 if not self.library_type.can_multiplex:
244 if self.multiplex_id is None or len(self.multiplex_id) == 0:
245 return 'Err: id empty'
247 multiplex_expressions = self.multiplex_id.split(',')
248 for multiplex_term in multiplex_expressions:
249 pairs = multiplex_term.split('-')
252 seq = self._lookup_index(pairs[0])
253 elif len(pairs) == 2:
254 key = pairs[0] + '-' + pairs[1]
255 seq0 = self._lookup_index(pairs[0])
256 seq1 = self._lookup_index(pairs[1])
257 if seq0 is None or seq1 is None:
260 seq = seq0 + '-' + seq1
262 raise RuntimeError("Too many - seperated sequences")
264 seq = 'Err: index not found'
268 def _lookup_index(self, multiplex_id):
270 multiplex = MultiplexIndex.objects.get(
271 adapter_type = self.library_type.id,
272 multiplex_id = multiplex_id)
273 return multiplex.sequence
274 except MultiplexIndex.DoesNotExist, e:
277 def index_sequence_text(self, seperator=' '):
278 """Return formatted multiplex index sequences"""
279 sequences = self.index_sequences()
280 if sequences is None:
282 multiplex_ids = sequences.keys()
284 return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids))
285 index_sequence_text.short_description = "Index"
288 def affiliation(self):
289 affs = self.affiliations.all().order_by('name')
293 ar.append(t.__unicode__())
294 return '%s' % (", ".join(ar))
296 def is_archived(self):
298 returns True if archived else False
300 if self.longtermstorage_set.count() > 0:
305 def stopping_point_name(self):
306 end_points = Library.PROTOCOL_END_POINTS_DICT
307 name = end_points.get(self.stopping_point, None)
309 name = "Lookup Error"
310 logger.error("protocol stopping point in database didn't match names in library model")
315 affs = self.tags.all().order_by('tag_name')
318 ar.append(t.__unicode__())
319 return u'%s' % ( ", ".join(ar))
322 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
324 DataRun.allow_tags = True
326 def aligned_m_reads(self):
327 return getLibReads(self.id)
329 def aligned_reads(self):
330 res = getLibReads(self.id)
334 return u'<div style="border:solid red 2px">'+res[2]+'</div>'
336 rc = "%1.2f" % (res[1]/1000000.0)
337 # 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
339 bgcolor = '#ff3300' # Red
340 rc_thr = [10000000,5000000,3000000]
341 if self.experiment_type == 'RNA-seq':
342 rc_thr = [20000000,10000000,6000000]
344 if res[1] > rc_thr[0]:
345 bgcolor = '#66ff66' # Green
347 if res[1] > rc_thr[1]:
348 bgcolor ='#00ccff' # Blue
350 if res[1] > rc_thr[2]:
351 bgcolor ='#ffcc33' # Orange
352 tstr = '<div style="background-color:'+bgcolor+';color:black">'
353 tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads'
355 else: tstr = 'not processed yet'
357 aligned_reads.allow_tags = True
361 summary_url = self.get_absolute_url()
362 return '<a href="%s">S</a>' % (summary_url,)
363 public.allow_tags = True
366 def get_absolute_url(self):
367 return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)])
369 def get_admin_url(self):
370 return urlresolvers.reverse('admin:samples_library_change',
375 Provide some site-specific customization for the django user class
377 #objects = UserManager()
380 ordering = ['first_name', 'last_name', 'username']
383 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
385 def __unicode__(self):
386 #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")"
387 return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')'
389 def HTSUserInsertID(sender, instance, **kwargs):
391 Force addition of HTSUsers when someone just modifies the auth_user object
393 u = HTSUser.objects.filter(pk=instance.id)
395 cursor = connection.cursor()
396 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
399 post_save.connect(HTSUserInsertID, sender=User)