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