4 from django.db import models
5 from django.contrib.auth.models import User, UserManager
6 from django.core import urlresolvers
7 from django.db.models.signals import pre_save, post_save
8 from django.db import connection
10 logger = logging.getLogger(__name__)
12 class Antibody(models.Model):
13 antigene = models.CharField(max_length=500, db_index=True)
16 # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
17 nickname = models.CharField(
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)
31 verbose_name_plural = "antibodies"
32 ordering = ["antigene"]
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,
41 notes = models.TextField(blank=True)
42 def __unicode__(self):
43 return unicode(self.cellline_name)
46 ordering = ["cellline_name"]
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,
55 verbose_name = 'Short Name')
56 notes = models.TextField(blank=True)
58 def __unicode__(self):
59 return unicode(self.condition_name)
62 ordering = ["condition_name"]
65 class ExperimentType(models.Model):
66 name = models.CharField(max_length=50, unique=True)
68 def __unicode__(self):
69 return unicode(self.name)
71 class Tag(models.Model):
72 tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False)
74 #('Antibody','Antibody'),
75 #('Cellline', 'Cellline'),
76 #('Condition', 'Condition'),
77 ('Library', 'Library'),
80 context = models.CharField(max_length=50,
81 choices=TAG_CONTEXT, default='Library')
83 def __unicode__(self):
84 return u'%s' % (self.tag_name)
87 ordering = ["context","tag_name"]
89 class Species(models.Model):
90 scientific_name = models.CharField(max_length=256,
94 common_name = models.CharField(max_length=256, blank=True)
95 #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
97 def __unicode__(self):
98 return u'%s (%s)' % (self.scientific_name, self.common_name)
101 verbose_name_plural = "species"
102 ordering = ["scientific_name"]
105 def get_absolute_url(self):
106 return ('samples.views.species', [str(self.id)])
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"
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')'
122 users = self.users.all().order_by('username')
123 return ", ".join([unicode(a) for a in users ])
126 ordering = ["name","contact"]
127 unique_together = (("name", "contact"),)
129 class LibraryType(models.Model):
130 name = models.CharField(max_length=255, unique=True,
131 verbose_name="Adapter Type")
132 is_paired_end = models.BooleanField(default=True,
133 help_text="can you do a paired end run with this adapter")
134 can_multiplex = models.BooleanField(default=True,
135 help_text="Does this adapter provide multiplexing?")
137 def __unicode__(self):
138 return unicode(self.name)
144 class MultiplexIndex(models.Model):
145 """Map adapter types to the multiplex sequence"""
146 adapter_type = models.ForeignKey(LibraryType)
147 multiplex_id = models.CharField(max_length=6, null=False)
148 sequence = models.CharField(max_length=12, blank=True, null=True)
151 verbose_name_plural = "multiplex indicies"
152 unique_together = ('adapter_type', 'multiplex_id')
154 class Library(models.Model):
155 id = models.CharField(max_length=10, primary_key=True)
156 library_name = models.CharField(max_length=100, unique=True)
157 library_species = models.ForeignKey(Species)
158 hidden = models.BooleanField(default=False)
159 account_number = models.CharField(max_length=100, null=True, blank=True)
160 cell_line = models.ForeignKey(Cellline, blank=True, null=True,
161 verbose_name="Background")
162 condition = models.ForeignKey(Condition, blank=True, null=True)
163 antibody = models.ForeignKey(Antibody,blank=True,null=True)
164 affiliations = models.ManyToManyField(
165 Affiliation,related_name='library_affiliations',null=True)
166 tags = models.ManyToManyField(Tag,related_name='library_tags',
167 blank=True,null=True)
168 REPLICATE_NUM = [(x,x) for x in range(1,7)]
169 replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
170 blank=True,null=True)
171 experiment_type = models.ForeignKey(ExperimentType)
172 library_type = models.ForeignKey(LibraryType, blank=True, null=True,
173 verbose_name="Adapter Type")
174 multiplex_id = models.CharField(max_length=128,
175 blank=True, null=True,
176 verbose_name="Index ID")
177 creation_date = models.DateField(blank=True, null=True)
178 made_for = models.CharField(max_length=50, blank=True,
179 verbose_name='ChIP/DNA/RNA Made By')
180 made_by = models.CharField(max_length=50, blank=True, default="Lorian")
182 PROTOCOL_END_POINTS = (
184 ('Sample', 'Raw sample'),
185 ('Progress', 'In progress'),
186 ('1A', 'Ligation, then gel'),
187 ('PCR', 'Ligation, then PCR'),
188 ('1Ab', 'Ligation, PCR, then gel'),
189 ('1Ac', 'Ligation, gel, then 12x PCR'),
190 ('1Aa', 'Ligation, gel, then 18x PCR'),
191 ('2A', 'Ligation, PCR, gel, PCR'),
192 ('Done', 'Completed'),
194 PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
195 stopping_point = models.CharField(max_length=25,
196 choices=PROTOCOL_END_POINTS,
199 amplified_from_sample = models.ForeignKey('self',
200 related_name='amplified_into_sample',
201 blank=True, null=True)
203 undiluted_concentration = models.DecimalField("Concentration",
204 max_digits=5, decimal_places=2, blank=True, null=True,
205 help_text=u"Undiluted concentration (ng/\u00b5l)")
206 # note \u00b5 is the micro symbol in unicode
207 successful_pM = models.DecimalField(max_digits=9,
208 decimal_places=1, blank=True, null=True)
209 ten_nM_dilution = models.BooleanField(default=False)
210 gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
211 insert_size = models.IntegerField(blank=True, null=True)
212 notes = models.TextField(blank=True)
214 bioanalyzer_summary = models.TextField(blank=True,default="")
215 bioanalyzer_concentration = models.DecimalField(max_digits=5,
216 decimal_places=2, blank=True, null=True,
217 help_text=u"(ng/\u00b5l)")
218 bioanalyzer_image_url = models.URLField(blank=True,default="")
220 def __unicode__(self):
221 return u'#%s: %s' % (self.id, self.library_name)
224 verbose_name_plural = "libraries"
225 #ordering = ["-creation_date"]
228 def antibody_name(self):
229 str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>'
231 antibody_name.allow_tags = True
234 return self.library_species.common_name
236 def index_sequences(self):
237 """Return a dictionary of multiplex index id to sequence
238 Return None if the library can't multiplex,
241 if self.library_type is None:
243 if not self.library_type.can_multiplex:
245 if self.multiplex_id is None or len(self.multiplex_id) == 0:
246 return 'Err: id empty'
248 multiplex_expressions = self.multiplex_id.split(',')
249 for multiplex_term in multiplex_expressions:
250 pairs = multiplex_term.split('-')
253 seq = self._lookup_index(pairs[0])
254 elif len(pairs) == 2:
255 key = pairs[0] + '-' + pairs[1]
256 seq0 = self._lookup_index(pairs[0])
257 seq1 = self._lookup_index(pairs[1])
258 if seq0 is None or seq1 is None:
261 seq = seq0 + '-' + seq1
263 raise RuntimeError("Too many - seperated sequences")
265 seq = 'Err: index not found'
269 def _lookup_index(self, multiplex_id):
271 multiplex = MultiplexIndex.objects.get(
272 adapter_type = self.library_type.id,
273 multiplex_id = multiplex_id)
274 return multiplex.sequence
275 except MultiplexIndex.DoesNotExist, e:
278 def index_sequence_text(self, seperator=' '):
279 """Return formatted multiplex index sequences"""
280 sequences = self.index_sequences()
281 if sequences is None:
283 if type(sequences) in types.StringTypes:
285 multiplex_ids = sequences.keys()
287 return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids))
288 index_sequence_text.short_description = "Index"
291 def affiliation(self):
292 affs = self.affiliations.all().order_by('name')
296 ar.append(t.__unicode__())
297 return '%s' % (", ".join(ar))
299 def is_archived(self):
301 returns True if archived else False
303 if self.longtermstorage_set.count() > 0:
308 def stopping_point_name(self):
309 end_points = Library.PROTOCOL_END_POINTS_DICT
310 name = end_points.get(self.stopping_point, None)
312 name = "Lookup Error"
313 logger.error("protocol stopping point in database didn't match names in library model")
318 affs = self.tags.all().order_by('tag_name')
321 ar.append(t.__unicode__())
322 return u'%s' % ( ", ".join(ar))
325 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
327 DataRun.allow_tags = True
329 def aligned_m_reads(self):
330 return getLibReads(self.id)
332 def aligned_reads(self):
333 res = getLibReads(self.id)
337 return u'<div style="border:solid red 2px">'+res[2]+'</div>'
339 rc = "%1.2f" % (res[1]/1000000.0)
340 # 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
342 bgcolor = '#ff3300' # Red
343 rc_thr = [10000000,5000000,3000000]
344 if self.experiment_type == 'RNA-seq':
345 rc_thr = [20000000,10000000,6000000]
347 if res[1] > rc_thr[0]:
348 bgcolor = '#66ff66' # Green
350 if res[1] > rc_thr[1]:
351 bgcolor ='#00ccff' # Blue
353 if res[1] > rc_thr[2]:
354 bgcolor ='#ffcc33' # Orange
355 tstr = '<div style="background-color:'+bgcolor+';color:black">'
356 tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads'
358 else: tstr = 'not processed yet'
360 aligned_reads.allow_tags = True
364 summary_url = self.get_absolute_url()
365 return '<a href="%s">S</a>' % (summary_url,)
366 public.allow_tags = True
369 def get_absolute_url(self):
370 return ('samples.views.library_to_flowcells', [str(self.id)])
372 def get_admin_url(self):
373 return urlresolvers.reverse('admin:samples_library_change',
378 Provide some site-specific customization for the django user class
380 #objects = UserManager()
383 ordering = ['first_name', 'last_name', 'username']
386 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
388 def __unicode__(self):
389 #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")"
390 return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')'
392 def HTSUserInsertID(sender, instance, **kwargs):
394 Force addition of HTSUsers when someone just modifies the auth_user object
396 u = HTSUser.objects.filter(pk=instance.id)
398 cursor = connection.cursor()
399 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
402 post_save.connect(HTSUserInsertID, sender=User)