Make antibody catalog number optional for creating entries
[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.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 *
9
10 logger = logging.getLogger(__name__)
11
12 class Antibody(models.Model):
13     antigene = models.CharField(max_length=500, db_index=True)
14     # New field Aug/20/08
15     # SQL to add column: 
16     # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
17     nickname = models.CharField(
18         max_length=20,
19         blank=True,
20         null=True, 
21         db_index=True
22     )
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)
30     class Meta:
31         verbose_name_plural = "antibodies"
32         ordering = ["antigene"]
33
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,
37         blank=True,
38         null=True, 
39         db_index=True)
40     
41     notes = models.TextField(blank=True)
42     def __unicode__(self):
43         return unicode(self.cellline_name)
44
45     class Meta:
46         ordering = ["cellline_name"]
47
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,
52         blank=True,
53         null=True, 
54         db_index=True,
55         verbose_name = 'Short Name')
56     notes = models.TextField(blank=True)
57
58     def __unicode__(self):
59         return unicode(self.condition_name)
60
61     class Meta:
62         ordering = ["condition_name"]
63
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   hidden = models.BooleanField()
141   account_number = models.CharField(max_length=100, null=True, blank=True)
142   cell_line = models.ForeignKey(Cellline, blank=True, null=True,
143                                 verbose_name="Background")
144   condition = models.ForeignKey(Condition, blank=True, null=True)
145   antibody = models.ForeignKey(Antibody,blank=True,null=True)
146   affiliations = models.ManyToManyField(
147       Affiliation,related_name='library_affiliations',null=True)
148   tags = models.ManyToManyField(Tag,related_name='library_tags',
149                                 blank=True,null=True)
150   REPLICATE_NUM = ((1,1),(2,2),(3,3),(4,4))
151   replicate =  models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
152                                                 blank=True,null=True) 
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   stopping_point = models.CharField(max_length=25,
174                                     choices=PROTOCOL_END_POINTS,
175                                     default='Done')
176   
177   amplified_from_sample = models.ForeignKey('self',
178                             related_name='amplified_into_sample',
179                             blank=True, null=True)
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,
186                                       decimal_places=1, blank=True, null=True)
187   ten_nM_dilution = models.BooleanField()
188   gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
189   insert_size = models.IntegerField(blank=True, null=True)
190   notes = models.TextField(blank=True)
191
192   bioanalyzer_summary = models.TextField(blank=True,default="")
193   bioanalyzer_concentration = models.DecimalField(max_digits=5, 
194                                 decimal_places=2, blank=True, null=True,
195                                 help_text=u"(ng/\u00b5l)")
196   bioanalyzer_image_url = models.URLField(blank=True,default="")
197   
198   def __unicode__(self):
199     return u'#%s: %s' % (self.id, self.library_name)
200   
201   class Meta:
202       verbose_name_plural = "libraries"
203       #ordering = ["-creation_date"] 
204       ordering = ["-id"]
205   
206   def antibody_name(self):
207     str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>' 
208     return str
209   antibody_name.allow_tags = True
210
211   def organism(self):
212     return self.library_species.common_name
213
214   def affiliation(self):
215     affs = self.affiliations.all().order_by('name')
216     tstr = ''
217     ar = []
218     for t in affs:
219         ar.append(t.__unicode__())
220     return '%s' % (", ".join(ar))
221     
222   def is_archived(self):
223     """
224     returns True if archived else False
225     """
226     if self.longtermstorage_set.count() > 0:
227         return True
228     else:
229         return False
230
231   def stopping_point_name(self):
232       end_points = Library.PROTOCOL_END_POINTS_DICT
233       name = end_points.get(self.stopping_point, None)
234       if name is None:
235           name = "Lookup Error"
236           logger.error("protocol stopping point in database didn't match names in library model")
237       return name
238       
239
240   def libtags(self):
241     affs = self.tags.all().order_by('tag_name')
242     ar = []
243     for t in affs:
244       ar.append(t.__unicode__())
245     return u'%s' % ( ", ".join(ar))
246
247   def DataRun(self):
248     str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>' 
249     return str
250   DataRun.allow_tags = True
251
252   def aligned_m_reads(self):
253     return getLibReads(self.id)
254
255   def aligned_reads(self):
256     res = getLibReads(self.id)
257
258     # Check data sanity
259     if res[2] != "OK":
260       return u'<div style="border:solid red 2px">'+res[2]+'</div>'
261
262     rc = "%1.2f" % (res[1]/1000000.0)
263     # 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
264     if res[0] > 0:
265       bgcolor = '#ff3300'  # Red
266       rc_thr = [10000000,5000000,3000000]
267       if self.experiment_type == 'RNA-seq':
268         rc_thr = [20000000,10000000,6000000]
269
270       if res[1] > rc_thr[0]:
271         bgcolor = '#66ff66'  # Green
272       else:
273         if res[1] > rc_thr[1]:
274           bgcolor ='#00ccff'  # Blue
275         else:
276            if res[1] > rc_thr[2]: 
277              bgcolor ='#ffcc33'  # Orange
278       tstr = '<div style="background-color:'+bgcolor+';color:black">'
279       tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads'
280       tstr += '</div>'
281     else: tstr = 'not processed yet' 
282     return tstr
283   aligned_reads.allow_tags = True
284   
285   def public(self):
286     SITE_ROOT = '/'
287     summary_url = self.get_absolute_url()
288     return '<a href="%s">S</a>' % (summary_url,)
289   public.allow_tags = True
290     
291   @models.permalink
292   def get_absolute_url(self):
293     return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)])
294
295   def get_admin_url(self):
296       return urlresolvers.reverse('admin:samples_library_change',
297                                   args=(self.id,))
298
299 class HTSUser(User):
300     """
301     Provide some site-specific customization for the django user class
302     """
303     #objects = UserManager()
304
305     class Meta:
306         ordering = ['first_name', 'last_name', 'username']
307
308     def admin_url(self):
309         return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
310
311     def __unicode__(self):
312         #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")"
313         return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')'
314     
315 def HTSUserInsertID(sender, instance, **kwargs):
316     """
317     Force addition of HTSUsers when someone just modifies the auth_user object
318     """
319     u = HTSUser.objects.filter(pk=instance.id)
320     if len(u) == 0:
321         cursor = connection.cursor()
322         cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
323         cursor.close()
324     
325 post_save.connect(HTSUserInsertID, sender=User)