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=3, 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_ids = self.multiplex_id.split(',')
248 for multiplex_id in multiplex_ids:
250 multiplex = MultiplexIndex.objects.get(
251 adapter_type = self.library_type.id,
252 multiplex_id = multiplex_id)
253 sequences[multiplex_id] = multiplex.sequence
254 except MultiplexIndex.DoesNotExist, e:
255 sequences[multiplex_id] = 'Err: index not found'
258 def index_sequence_text(self, seperator=' '):
259 """Return formatted multiplex index sequences"""
260 sequences = self.index_sequences()
261 if sequences is None:
264 multiplex_ids = sequences.keys()
266 return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids))
267 index_sequence_text.short_description = "Index"
270 def affiliation(self):
271 affs = self.affiliations.all().order_by('name')
275 ar.append(t.__unicode__())
276 return '%s' % (", ".join(ar))
278 def is_archived(self):
280 returns True if archived else False
282 if self.longtermstorage_set.count() > 0:
287 def stopping_point_name(self):
288 end_points = Library.PROTOCOL_END_POINTS_DICT
289 name = end_points.get(self.stopping_point, None)
291 name = "Lookup Error"
292 logger.error("protocol stopping point in database didn't match names in library model")
297 affs = self.tags.all().order_by('tag_name')
300 ar.append(t.__unicode__())
301 return u'%s' % ( ", ".join(ar))
304 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
306 DataRun.allow_tags = True
308 def aligned_m_reads(self):
309 return getLibReads(self.id)
311 def aligned_reads(self):
312 res = getLibReads(self.id)
316 return u'<div style="border:solid red 2px">'+res[2]+'</div>'
318 rc = "%1.2f" % (res[1]/1000000.0)
319 # 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
321 bgcolor = '#ff3300' # Red
322 rc_thr = [10000000,5000000,3000000]
323 if self.experiment_type == 'RNA-seq':
324 rc_thr = [20000000,10000000,6000000]
326 if res[1] > rc_thr[0]:
327 bgcolor = '#66ff66' # Green
329 if res[1] > rc_thr[1]:
330 bgcolor ='#00ccff' # Blue
332 if res[1] > rc_thr[2]:
333 bgcolor ='#ffcc33' # Orange
334 tstr = '<div style="background-color:'+bgcolor+';color:black">'
335 tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads'
337 else: tstr = 'not processed yet'
339 aligned_reads.allow_tags = True
343 summary_url = self.get_absolute_url()
344 return '<a href="%s">S</a>' % (summary_url,)
345 public.allow_tags = True
348 def get_absolute_url(self):
349 return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)])
351 def get_admin_url(self):
352 return urlresolvers.reverse('admin:samples_library_change',
357 Provide some site-specific customization for the django user class
359 #objects = UserManager()
362 ordering = ['first_name', 'last_name', 'username']
365 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
367 def __unicode__(self):
368 #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")"
369 return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')'
371 def HTSUserInsertID(sender, instance, **kwargs):
373 Force addition of HTSUsers when someone just modifies the auth_user object
375 u = HTSUser.objects.filter(pk=instance.id)
377 cursor = connection.cursor()
378 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
381 post_save.connect(HTSUserInsertID, sender=User)