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
9 logger = logging.getLogger(__name__)
11 class Antibody(models.Model):
12 antigene = models.CharField(max_length=500, db_index=True)
15 # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
16 nickname = models.CharField(
22 catalog = models.CharField(max_length=50, blank=True, null=True)
23 antibodies = models.CharField(max_length=500, db_index=True)
24 source = models.CharField(max_length=500, blank=True, null=True, db_index=True)
25 biology = models.TextField(blank=True, null=True)
26 notes = models.TextField(blank=True, null=True)
27 def __unicode__(self):
28 return u'%s - %s' % (self.antigene, self.antibodies)
30 verbose_name_plural = "antibodies"
31 ordering = ["antigene"]
33 class Cellline(models.Model):
34 cellline_name = models.CharField(max_length=100, unique=True, db_index=True)
35 nickname = models.CharField(max_length=20,
40 notes = models.TextField(blank=True)
41 def __unicode__(self):
42 return unicode(self.cellline_name)
45 ordering = ["cellline_name"]
47 class Condition(models.Model):
48 condition_name = models.CharField(
49 max_length=2000, unique=True, db_index=True)
50 nickname = models.CharField(max_length=20,
54 verbose_name = 'Short Name')
55 notes = models.TextField(blank=True)
57 def __unicode__(self):
58 return unicode(self.condition_name)
61 ordering = ["condition_name"]
64 class ExperimentType(models.Model):
65 name = models.CharField(max_length=50, unique=True)
67 def __unicode__(self):
68 return unicode(self.name)
70 class Tag(models.Model):
71 tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False)
73 #('Antibody','Antibody'),
74 #('Cellline', 'Cellline'),
75 #('Condition', 'Condition'),
76 ('Library', 'Library'),
79 context = models.CharField(max_length=50,
80 choices=TAG_CONTEXT, default='Library')
82 def __unicode__(self):
83 return u'%s' % (self.tag_name)
86 ordering = ["context","tag_name"]
88 class Species(models.Model):
89 scientific_name = models.CharField(max_length=256,
93 common_name = models.CharField(max_length=256, blank=True)
94 #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
96 def __unicode__(self):
97 return u'%s (%s)' % (self.scientific_name, self.common_name)
100 verbose_name_plural = "species"
101 ordering = ["scientific_name"]
104 def get_absolute_url(self):
105 return ('samples.views.species', [str(self.id)])
107 class Affiliation(models.Model):
108 name = models.CharField(max_length=256, db_index=True, verbose_name='Name')
109 contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name')
110 email = models.EmailField(null=True,blank=True)
111 users = models.ManyToManyField('HTSUser', null=True, blank=True)
112 users.admin_order_field = "username"
114 def __unicode__(self):
115 str = unicode(self.name)
116 if self.contact is not None and len(self.contact) > 0:
117 str += u' ('+self.contact+u')'
121 users = self.users.all().order_by('username')
122 return ", ".join([unicode(a) for a in users ])
125 ordering = ["name","contact"]
126 unique_together = (("name", "contact"),)
128 class LibraryType(models.Model):
129 name = models.CharField(max_length=255, unique=True,
130 verbose_name="Adapter Type")
131 is_paired_end = models.BooleanField(default=True,
132 help_text="can you do a paired end run with this adapter")
133 can_multiplex = models.BooleanField(default=True,
134 help_text="Does this adapter provide multiplexing?")
136 def __unicode__(self):
137 return unicode(self.name)
143 class MultiplexIndex(models.Model):
144 """Map adapter types to the multiplex sequence"""
145 adapter_type = models.ForeignKey(LibraryType)
146 multiplex_id = models.CharField(max_length=6, null=False)
147 sequence = models.CharField(max_length=12, blank=True, null=True)
150 verbose_name_plural = "multiplex indicies"
151 unique_together = ('adapter_type', 'multiplex_id')
153 class Library(models.Model):
154 id = models.CharField(max_length=10, primary_key=True)
155 library_name = models.CharField(max_length=100, unique=True)
156 library_species = models.ForeignKey(Species)
157 hidden = models.BooleanField(default=False)
158 account_number = models.CharField(max_length=100, null=True, blank=True)
159 cell_line = models.ForeignKey(Cellline, blank=True, null=True,
160 verbose_name="Background")
161 condition = models.ForeignKey(Condition, blank=True, null=True)
162 antibody = models.ForeignKey(Antibody,blank=True,null=True)
163 affiliations = models.ManyToManyField(
164 Affiliation,related_name='library_affiliations',null=True)
165 tags = models.ManyToManyField(Tag,related_name='library_tags',
166 blank=True,null=True)
167 REPLICATE_NUM = [(x,x) for x in range(1,7)]
168 replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
169 blank=True,null=True)
170 experiment_type = models.ForeignKey(ExperimentType)
171 library_type = models.ForeignKey(LibraryType, blank=True, null=True,
172 verbose_name="Adapter Type")
173 multiplex_id = models.CharField(max_length=128,
174 blank=True, null=True,
175 verbose_name="Index ID")
176 creation_date = models.DateField(blank=True, null=True)
177 made_for = models.CharField(max_length=50, blank=True,
178 verbose_name='ChIP/DNA/RNA Made By')
179 made_by = models.CharField(max_length=50, blank=True, default="Lorian")
181 PROTOCOL_END_POINTS = (
183 ('Sample', 'Raw sample'),
184 ('Progress', 'In progress'),
185 ('1A', 'Ligation, then gel'),
186 ('PCR', 'Ligation, then PCR'),
187 ('1Ab', 'Ligation, PCR, then gel'),
188 ('1Ac', 'Ligation, gel, then 12x PCR'),
189 ('1Aa', 'Ligation, gel, then 18x PCR'),
190 ('2A', 'Ligation, PCR, gel, PCR'),
191 ('Done', 'Completed'),
193 PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
194 stopping_point = models.CharField(max_length=25,
195 choices=PROTOCOL_END_POINTS,
198 amplified_from_sample = models.ForeignKey('self',
199 related_name='amplified_into_sample',
200 blank=True, null=True)
202 undiluted_concentration = models.DecimalField("Concentration",
203 max_digits=5, decimal_places=2, blank=True, null=True,
204 help_text=u"Undiluted concentration (ng/\u00b5l)")
205 # note \u00b5 is the micro symbol in unicode
206 successful_pM = models.DecimalField(max_digits=9,
207 decimal_places=1, blank=True, null=True)
208 ten_nM_dilution = models.BooleanField(default=False)
209 gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
210 insert_size = models.IntegerField(blank=True, null=True)
211 notes = models.TextField(blank=True)
213 bioanalyzer_summary = models.TextField(blank=True,default="")
214 bioanalyzer_concentration = models.DecimalField(max_digits=5,
215 decimal_places=2, blank=True, null=True,
216 help_text=u"(ng/\u00b5l)")
217 bioanalyzer_image_url = models.URLField(blank=True,default="")
219 def __unicode__(self):
220 return u'#%s: %s' % (self.id, self.library_name)
223 verbose_name_plural = "libraries"
224 #ordering = ["-creation_date"]
227 def antibody_name(self):
228 str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>'
230 antibody_name.allow_tags = True
233 return self.library_species.common_name
235 def index_sequences(self):
236 """Return a dictionary of multiplex index id to sequence
237 Return None if the library can't multiplex,
240 if self.library_type is None:
242 if not self.library_type.can_multiplex:
244 if self.multiplex_id is None or len(self.multiplex_id) == 0:
245 return 'Err: id empty'
247 multiplex_expressions = self.multiplex_id.split(',')
248 for multiplex_term in multiplex_expressions:
249 pairs = multiplex_term.split('-')
252 seq = self._lookup_index(pairs[0])
253 elif len(pairs) == 2:
254 key = pairs[0] + '-' + pairs[1]
255 seq0 = self._lookup_index(pairs[0])
256 seq1 = self._lookup_index(pairs[1])
257 if seq0 is None or seq1 is None:
260 seq = seq0 + '-' + seq1
262 raise RuntimeError("Too many - seperated sequences")
264 seq = 'Err: index not found'
268 def _lookup_index(self, multiplex_id):
270 multiplex = MultiplexIndex.objects.get(
271 adapter_type = self.library_type.id,
272 multiplex_id = multiplex_id)
273 return multiplex.sequence
274 except MultiplexIndex.DoesNotExist as e:
277 def index_sequence_text(self, seperator=' '):
278 """Return formatted multiplex index sequences"""
279 sequences = self.index_sequences()
280 if sequences is None:
282 if type(sequences) in types.StringTypes:
284 multiplex_ids = sequences.keys()
286 return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids))
287 index_sequence_text.short_description = "Index"
290 def affiliation(self):
291 affs = self.affiliations.all().order_by('name')
295 ar.append(t.__unicode__())
296 return '%s' % (", ".join(ar))
298 def is_archived(self):
300 returns True if archived else False
302 if self.longtermstorage_set.count() > 0:
307 def stopping_point_name(self):
308 end_points = Library.PROTOCOL_END_POINTS_DICT
309 name = end_points.get(self.stopping_point, None)
311 name = "Lookup Error"
312 logger.error("protocol stopping point in database didn't match names in library model")
317 affs = self.tags.all().order_by('tag_name')
320 ar.append(t.__unicode__())
321 return u'%s' % ( ", ".join(ar))
324 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
326 DataRun.allow_tags = True
328 def aligned_m_reads(self):
329 return getLibReads(self.id)
331 def aligned_reads(self):
332 res = getLibReads(self.id)
336 return u'<div style="border:solid red 2px">'+res[2]+'</div>'
338 rc = "%1.2f" % (res[1]/1000000.0)
339 # 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
341 bgcolor = '#ff3300' # Red
342 rc_thr = [10000000,5000000,3000000]
343 if self.experiment_type == 'RNA-seq':
344 rc_thr = [20000000,10000000,6000000]
346 if res[1] > rc_thr[0]:
347 bgcolor = '#66ff66' # Green
349 if res[1] > rc_thr[1]:
350 bgcolor ='#00ccff' # Blue
352 if res[1] > rc_thr[2]:
353 bgcolor ='#ffcc33' # Orange
354 tstr = '<div style="background-color:'+bgcolor+';color:black">'
355 tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads'
357 else: tstr = 'not processed yet'
359 aligned_reads.allow_tags = True
363 summary_url = self.get_absolute_url()
364 return '<a href="%s">S</a>' % (summary_url,)
365 public.allow_tags = True
368 def get_absolute_url(self):
369 return ('samples.views.library_to_flowcells', [str(self.id)])
371 def get_admin_url(self):
372 return urlresolvers.reverse('admin:samples_library_change',
377 Provide some site-specific customization for the django user class
379 #objects = UserManager()
382 ordering = ['first_name', 'last_name', 'username']
385 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
387 def __unicode__(self):
388 #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")"
389 return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')'
391 def HTSUserInsertID(sender, instance, **kwargs):
393 Force addition of HTSUsers when someone just modifies the auth_user object
395 u = HTSUser.objects.filter(pk=instance.id)
397 cursor = connection.cursor()
398 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
401 post_save.connect(HTSUserInsertID, sender=User)