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