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