a8201c9c39068477736ab6fbd845e9c3bc161fd8
[htsworkflow.git] / htsworkflow / frontend / samples / models.py
1 import logging
2 import urlparse
3 from django.db import models
4 from django.contrib.auth.models import User, UserManager
5 from django.db.models.signals import pre_save, post_save
6 from django.db import connection
7 from htsworkflow.frontend import settings
8 from htsworkflow.frontend.reports.libinfopar import *
9
10
11 # Create your models here.
12 logger = logging.getLogger(__name__)
13
14 class Antibody(models.Model):
15     antigene = models.CharField(max_length=500, db_index=True)
16     # New field Aug/20/08
17     # SQL to add column: 
18     # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
19     nickname = models.CharField(
20         max_length=20,
21         blank=True,
22         null=True, 
23         db_index=True,
24         verbose_name = 'Short Name'
25     )
26     catalog = models.CharField(max_length=50, unique=True, db_index=True)
27     antibodies = models.CharField(max_length=500, db_index=True)
28     source = models.CharField(max_length=500, blank=True, db_index=True)
29     biology = models.TextField(blank=True)
30     notes = models.TextField(blank=True)
31     def __unicode__(self):
32         return u'%s - %s (%s)' % (self.antigene, self.antibodies, self.catalog)
33     class Meta:
34         verbose_name_plural = "antibodies"
35         ordering = ["antigene"]
36
37 class Cellline(models.Model):
38     cellline_name = models.CharField(max_length=100, unique=True, db_index=True)
39     nickname = models.CharField(max_length=20,
40         blank=True,
41         null=True, 
42         db_index=True,
43         verbose_name = 'Short Name')
44     notes = models.TextField(blank=True)
45     def __unicode__(self):
46         return unicode(self.cellline_name)
47
48     class Meta:
49         ordering = ["cellline_name"]
50
51 class Condition(models.Model):
52     condition_name = models.CharField(
53         max_length=2000, unique=True, db_index=True)
54     nickname = models.CharField(max_length=20,
55         blank=True,
56         null=True, 
57         db_index=True,
58         verbose_name = 'Short Name')
59     notes = models.TextField(blank=True)
60
61     def __unicode__(self):
62         return unicode(self.condition_name)
63
64     class Meta:
65         ordering = ["condition_name"]
66
67 class ExperimentType(models.Model):
68   name = models.CharField(max_length=50, unique=True)
69
70   def __unicode__(self):
71     return unicode(self.name)
72
73 class Tag(models.Model): 
74   tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False) 
75   TAG_CONTEXT = ( 
76       #('Antibody','Antibody'), 
77       #('Cellline', 'Cellline'), 
78       #('Condition', 'Condition'), 
79       ('Library', 'Library'), 
80       ('ANY','ANY'), 
81   ) 
82   context = models.CharField(max_length=50, 
83       choices=TAG_CONTEXT, default='Library') 
84  
85   def __unicode__(self): 
86     return u'%s' % (self.tag_name) 
87  
88   class Meta: 
89     ordering = ["context","tag_name"] 
90  
91 class Species(models.Model):
92   scientific_name = models.CharField(max_length=256, 
93       unique=False, 
94       db_index=True
95   )
96   common_name = models.CharField(max_length=256, blank=True)
97   #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
98
99   def __unicode__(self):
100     return u'%s (%s)' % (self.scientific_name, self.common_name)
101   
102   class Meta:
103     verbose_name_plural = "species"
104     ordering = ["scientific_name"]
105
106   @models.permalink
107   def get_absolute_url(self):
108     return ('htsworkflow.frontend.samples.views.species', [str(self.id)])
109   
110 class Affiliation(models.Model):
111   name = models.CharField(max_length=256, db_index=True, verbose_name='Name')
112   contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name')  
113   email = models.EmailField(null=True,blank=True)
114   users = models.ManyToManyField('HTSUser', null=True, blank=True)
115   users.admin_order_field = "username"
116   
117   def __unicode__(self):
118     str = unicode(self.name)
119     if self.contact is not None and len(self.contact) > 0:
120       str += u' ('+self.contact+u')' 
121     return str
122
123   def Users(self):
124       users = self.users.all().order_by('username')
125       return ", ".join([unicode(a) for a in users ])
126
127   class Meta:
128     ordering = ["name","contact"]
129     unique_together = (("name", "contact"),)
130
131 class LibraryType(models.Model):
132   name = models.CharField(max_length=255, unique=True)
133
134   def __unicode__(self):
135     return unicode(self.name)
136
137
138 class Library(models.Model):
139   id = models.CharField(max_length=10, primary_key=True)
140   library_name = models.CharField(max_length=100, unique=True)
141   library_species = models.ForeignKey(Species)
142   # new field 2008 Mar 5, alter table samples_library add column "hidden" NOT NULL default 0;
143   hidden = models.BooleanField()
144   # new field 2009 Oct 6, alter table samples_library add column "account_number" varchar(100) NULL
145   account_number = models.CharField(max_length=100, null=True, blank=True)
146   cell_line = models.ForeignKey(Cellline, blank=True, null=True, verbose_name="Background")
147   condition = models.ForeignKey(Condition, blank=True, null=True)
148   antibody = models.ForeignKey(Antibody,blank=True,null=True)
149   # New field Aug/25/08. SQL: alter table fctracker_library add column "lib_affiliation" varchar(256)  NULL;
150   affiliations = models.ManyToManyField(Affiliation,related_name='library_affiliations',null=True)
151   # new field Nov/14/08
152   tags = models.ManyToManyField(Tag,related_name='library_tags',blank=True,null=True)
153   # New field Aug/19/08
154   # SQL to add column: alter table fctracker_library add column "replicate" smallint unsigned NULL;
155   REPLICATE_NUM = ((1,1),(2,2),(3,3),(4,4))
156   replicate =  models.PositiveSmallIntegerField(choices=REPLICATE_NUM,default=1) 
157   experiment_type = models.ForeignKey(ExperimentType)
158   library_type = models.ForeignKey(LibraryType, blank=True, null=True)
159   creation_date = models.DateField(blank=True, null=True)
160   made_for = models.CharField(max_length=50, blank=True, 
161       verbose_name='ChIP/DNA/RNA Made By')
162   made_by = models.CharField(max_length=50, blank=True, default="Lorian")
163   
164   PROTOCOL_END_POINTS = (
165       ('?', 'Unknown'),
166       ('Sample', 'Raw sample'),
167       ('Progress', 'In progress'),
168       ('1A', 'Ligation, then gel'),
169       ('PCR', 'Ligation, then PCR'),
170       ('1Ab', 'Ligation, PCR, then gel'),
171       ('1Ac', 'Ligation, gel, then 12x PCR'),
172       ('1Aa', 'Ligation, gel, then 18x PCR'),
173       ('2A', 'Ligation, PCR, gel, PCR'),
174       ('Done', 'Completed'),
175     )
176   PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
177   
178   stopping_point = models.CharField(max_length=25, choices=PROTOCOL_END_POINTS, default='Done')
179   amplified_from_sample = models.ForeignKey('self', blank=True, null=True, related_name='amplified_into_sample')  
180   
181   undiluted_concentration = models.DecimalField("Concentration", 
182       max_digits=5, decimal_places=2, blank=True, null=True,
183       help_text=u"Undiluted concentration (ng/\u00b5l)") 
184       # note \u00b5 is the micro symbol in unicode
185   successful_pM = models.DecimalField(max_digits=9, decimal_places=1, blank=True, null=True)
186   ten_nM_dilution = models.BooleanField()
187   gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
188   insert_size = models.IntegerField(blank=True, null=True)
189   notes = models.TextField(blank=True)
190
191   bioanalyzer_summary = models.TextField(blank=True,default="")
192   bioanalyzer_concentration = models.DecimalField(max_digits=5, 
193                                 decimal_places=2, blank=True, null=True,
194                                 help_text=u"(ng/\u00b5l)")
195   bioanalyzer_image_url = models.URLField(blank=True,default="")
196   
197   def __unicode__(self):
198     return u'#%s: %s' % (self.id, self.library_name)
199   
200   class Meta:
201       verbose_name_plural = "libraries"
202       #ordering = ["-creation_date"] 
203       ordering = ["-id"]
204   
205   def antibody_name(self):
206     str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.nickname+'</a>' 
207     return str
208   antibody_name.allow_tags = True
209
210   def organism(self):
211     return self.library_species.common_name
212
213   def affiliation(self):
214     affs = self.affiliations.all().order_by('name')
215     tstr = ''
216     ar = []
217     for t in affs:
218         ar.append(t.__unicode__())
219     return '%s' % (", ".join(ar))
220     
221   def is_archived(self):
222     """
223     returns True if archived else False
224     """
225     if self.longtermstorage_set.count() > 0:
226         return True
227     else:
228         return False
229
230   def stopping_point_name(self):
231       end_points = Library.PROTOCOL_END_POINTS_DICT
232       name = end_points.get(self.stopping_point, None)
233       if name is None:
234           name = "Lookup Error"
235           logger.error("protocol stopping point in database didn't match names in library model")
236       return name
237       
238
239   def libtags(self):
240     affs = self.tags.all().order_by('tag_name')
241     ar = []
242     for t in affs:
243       ar.append(t.__unicode__())
244     return u'%s' % ( ", ".join(ar))
245
246   def DataRun(self):
247     str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>' 
248     return str
249   DataRun.allow_tags = True
250
251   def aligned_m_reads(self):
252     return getLibReads(self.id)
253
254   def aligned_reads(self):
255     res = getLibReads(self.id)
256
257     # Check data sanity
258     if res[2] != "OK":
259       return u'<div style="border:solid red 2px">'+res[2]+'</div>'
260
261     rc = "%1.2f" % (res[1]/1000000.0)
262     # 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
263     if res[0] > 0:
264       bgcolor = '#ff3300'  # Red
265       rc_thr = [10000000,5000000,3000000]
266       if self.experiment_type == 'RNA-seq':
267         rc_thr = [20000000,10000000,6000000]
268
269       if res[1] > rc_thr[0]:
270         bgcolor = '#66ff66'  # Green
271       else:
272         if res[1] > rc_thr[1]:
273           bgcolor ='#00ccff'  # Blue
274         else:
275            if res[1] > rc_thr[2]: 
276              bgcolor ='#ffcc33'  # Orange
277       tstr = '<div style="background-color:'+bgcolor+';color:black">'
278       tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads'
279       tstr += '</div>'
280     else: tstr = 'not processed yet' 
281     return tstr
282   aligned_reads.allow_tags = True
283   
284   def public(self):
285     SITE_ROOT = '/'
286     summary_url = self.get_absolute_url()
287     return '<a href="%s">S</a>' % (summary_url,)
288   public.allow_tags = True
289     
290   @models.permalink
291   def get_absolute_url(self):
292     return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)])
293
294
295 class HTSUser(User):
296     """
297     Provide some site-specific customization for the django user class
298     """
299     #objects = UserManager()
300
301     class Meta:
302         ordering = ['first_name', 'last_name', 'username']
303
304     def admin_url(self):
305         return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
306
307     def __unicode__(self):
308         #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")"
309         return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')'
310     
311 def HTSUserInsertID(sender, instance, **kwargs):
312     """
313     Force addition of HTSUsers when someone just modifies the auth_user object
314     """
315     u = HTSUser.objects.filter(pk=instance.id)
316     if len(u) == 0:
317         cursor = connection.cursor()
318         cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
319         cursor.close()
320     
321 post_save.connect(HTSUserInsertID, sender=User)