1 from __future__ import unicode_literals
5 from django.db import models
6 from django.contrib.auth.models import User, UserManager
7 from django.core import urlresolvers
8 from django.db.models.signals import pre_save, post_save
9 from django.db import connection
12 logger = logging.getLogger(__name__)
14 class Antibody(models.Model):
15 antigene = models.CharField(max_length=500, db_index=True)
18 # alter table fctracker_antibody add column "nickname" varchar(20) NULL;
19 nickname = models.CharField(
25 catalog = models.CharField(max_length=50, blank=True, null=True)
26 antibodies = models.CharField(max_length=500, db_index=True)
27 source = models.CharField(max_length=500, blank=True, null=True, db_index=True)
28 biology = models.TextField(blank=True, null=True)
29 notes = models.TextField(blank=True, null=True)
31 return '%s - %s' % (self.antigene, self.antibodies)
33 verbose_name_plural = "antibodies"
34 ordering = ["antigene"]
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,
43 notes = models.TextField(blank=True)
45 return str(self.cellline_name)
48 ordering = ["cellline_name"]
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,
57 verbose_name = 'Short Name')
58 notes = models.TextField(blank=True)
61 return str(self.condition_name)
64 ordering = ["condition_name"]
67 class ExperimentType(models.Model):
68 name = models.CharField(max_length=50, unique=True)
73 class Tag(models.Model):
74 tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False)
76 #('Antibody','Antibody'),
77 #('Cellline', 'Cellline'),
78 #('Condition', 'Condition'),
79 ('Library', 'Library'),
82 context = models.CharField(max_length=50,
83 choices=TAG_CONTEXT, default='Library')
86 return '%s' % (self.tag_name)
89 ordering = ["context","tag_name"]
91 class Species(models.Model):
92 scientific_name = models.CharField(max_length=256,
96 common_name = models.CharField(max_length=256, blank=True)
97 #use_genome_build = models.CharField(max_length=100, blank=False, null=False)
100 return '%s (%s)' % (self.scientific_name, self.common_name)
103 verbose_name_plural = "species"
104 ordering = ["scientific_name"]
107 def get_absolute_url(self):
108 return ('samples.views.species', [str(self.id)])
110 class Affiliation(models.Model):
111 name = models.CharField(max_length=256, db_index=True, verbose_name='Name')
112 contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name')
113 email = models.EmailField(null=True,blank=True)
114 users = models.ManyToManyField('HTSUser', null=True, blank=True)
115 users.admin_order_field = "username"
118 name = str(self.name)
119 if self.contact is not None and len(self.contact) > 0:
120 name += ' ('+self.contact+')'
124 users = self.users.all().order_by('username')
125 return ", ".join([str(a) for a in users ])
128 ordering = ["name","contact"]
129 unique_together = (("name", "contact"),)
131 class LibraryType(models.Model):
132 name = models.CharField(max_length=255, unique=True,
133 verbose_name="Adapter Type")
134 is_paired_end = models.BooleanField(default=True,
135 help_text="can you do a paired end run with this adapter")
136 can_multiplex = models.BooleanField(default=True,
137 help_text="Does this adapter provide multiplexing?")
140 return str(self.name)
146 class MultiplexIndex(models.Model):
147 """Map adapter types to the multiplex sequence"""
148 adapter_type = models.ForeignKey(LibraryType)
149 multiplex_id = models.CharField(max_length=6, null=False)
150 sequence = models.CharField(max_length=12, blank=True, null=True)
153 verbose_name_plural = "multiplex indicies"
154 unique_together = ('adapter_type', 'multiplex_id')
156 class Library(models.Model):
157 id = models.CharField(max_length=10, primary_key=True)
158 library_name = models.CharField(max_length=100, unique=True)
159 library_species = models.ForeignKey(Species)
160 hidden = models.BooleanField(default=False)
161 account_number = models.CharField(max_length=100, null=True, blank=True)
162 cell_line = models.ForeignKey(Cellline, blank=True, null=True,
163 verbose_name="Background")
164 condition = models.ForeignKey(Condition, blank=True, null=True)
165 antibody = models.ForeignKey(Antibody,blank=True,null=True)
166 affiliations = models.ManyToManyField(
167 Affiliation,related_name='library_affiliations',null=True)
168 tags = models.ManyToManyField(Tag,related_name='library_tags',
169 blank=True,null=True)
170 REPLICATE_NUM = [(x,x) for x in range(1,7)]
171 replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,
172 blank=True,null=True)
173 experiment_type = models.ForeignKey(ExperimentType)
174 library_type = models.ForeignKey(LibraryType, blank=True, null=True,
175 verbose_name="Adapter Type")
176 multiplex_id = models.CharField(max_length=128,
177 blank=True, null=True,
178 verbose_name="Index ID")
179 creation_date = models.DateField(blank=True, null=True)
180 made_for = models.CharField(max_length=50, blank=True,
181 verbose_name='ChIP/DNA/RNA Made By')
182 made_by = models.CharField(max_length=50, blank=True, default="Lorian")
184 PROTOCOL_END_POINTS = (
186 ('Sample', 'Raw sample'),
187 ('Progress', 'In progress'),
188 ('1A', 'Ligation, then gel'),
189 ('PCR', 'Ligation, then PCR'),
190 ('1Ab', 'Ligation, PCR, then gel'),
191 ('1Ac', 'Ligation, gel, then 12x PCR'),
192 ('1Aa', 'Ligation, gel, then 18x PCR'),
193 ('2A', 'Ligation, PCR, gel, PCR'),
194 ('Done', 'Completed'),
196 PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS)
197 stopping_point = models.CharField(max_length=25,
198 choices=PROTOCOL_END_POINTS,
201 amplified_from_sample = models.ForeignKey('self',
202 related_name='amplified_into_sample',
203 blank=True, null=True)
205 undiluted_concentration = models.DecimalField("Concentration",
206 max_digits=5, decimal_places=2, blank=True, null=True,
207 help_text = "Undiluted concentration (ng/\u00b5l)")
208 # note \u00b5 is the micro symbol in unicode
209 successful_pM = models.DecimalField(max_digits=9,
210 decimal_places=1, blank=True, null=True)
211 ten_nM_dilution = models.BooleanField(default=False)
212 gel_cut_size = models.IntegerField(default=225, blank=True, null=True)
213 insert_size = models.IntegerField(blank=True, null=True)
214 notes = models.TextField(blank=True)
216 bioanalyzer_summary = models.TextField(blank=True,default="")
217 bioanalyzer_concentration = models.DecimalField(max_digits=5,
218 decimal_places=2, blank=True, null=True,
219 help_text="(ng/\u00b5l)")
220 bioanalyzer_image_url = models.URLField(blank=True,default="")
223 return '#%s: %s' % (self.id, self.library_name)
226 verbose_name_plural = "libraries"
227 #ordering = ["-creation_date"]
230 def antibody_name(self):
231 str ='<a target=_self href="/admin/samples/antibody/'+self.antibody.id.__str__()+'/" title="'+self.antibody.__str__()+'">'+self.antibody.label+'</a>'
233 antibody_name.allow_tags = True
236 return self.library_species.common_name
238 def index_sequences(self):
239 """Return a dictionary of multiplex index id to sequence
240 Return None if the library can't multiplex,
243 if self.library_type is None:
245 if not self.library_type.can_multiplex:
247 if self.multiplex_id is None or len(self.multiplex_id) == 0:
248 return 'Err: id empty'
250 multiplex_expressions = self.multiplex_id.split(',')
251 for multiplex_term in multiplex_expressions:
252 pairs = multiplex_term.split('-')
255 seq = self._lookup_index(pairs[0])
256 elif len(pairs) == 2:
257 key = pairs[0] + '-' + pairs[1]
258 seq0 = self._lookup_index(pairs[0])
259 seq1 = self._lookup_index(pairs[1])
260 if seq0 is None or seq1 is None:
263 seq = seq0 + '-' + seq1
265 raise RuntimeError("Too many - seperated sequences")
267 seq = 'Err: index not found'
271 def _lookup_index(self, multiplex_id):
273 multiplex = MultiplexIndex.objects.get(
274 adapter_type = self.library_type.id,
275 multiplex_id = multiplex_id)
276 return multiplex.sequence
277 except MultiplexIndex.DoesNotExist as e:
280 def index_sequence_text(self, seperator=' '):
281 """Return formatted multiplex index sequences"""
282 sequences = self.index_sequences()
283 if sequences is None:
285 if isinstance(sequences, six.string_types):
287 multiplex_ids = sequences.keys()
289 return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids))
290 index_sequence_text.short_description = "Index"
293 def affiliation(self):
294 affs = self.affiliations.all().order_by('name')
298 ar.append(t.__str__())
299 return '%s' % (", ".join(ar))
301 def is_archived(self):
303 returns True if archived else False
305 if self.longtermstorage_set.count() > 0:
310 def stopping_point_name(self):
311 end_points = Library.PROTOCOL_END_POINTS_DICT
312 name = end_points.get(self.stopping_point, None)
314 name = "Lookup Error"
315 logger.error("protocol stopping point in database didn't match names in library model")
320 affs = self.tags.all().order_by('tag_name')
323 ar.append(t.__str__())
324 return '%s' % ( ", ".join(ar))
327 str ='<a target=_self href="/admin/experiments/datarun/?q='+self.id+'" title="Check All Data Runs for This Specific Library ..." ">Data Run</a>'
329 DataRun.allow_tags = True
331 def aligned_m_reads(self):
332 return getLibReads(self.id)
334 def aligned_reads(self):
335 res = getLibReads(self.id)
339 return '<div style="border:solid red 2px">'+res[2]+'</div>'
341 rc = "%1.2f" % (res[1]/1000000.0)
342 # 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
344 bgcolor = '#ff3300' # Red
345 rc_thr = [10000000,5000000,3000000]
346 if self.experiment_type == 'RNA-seq':
347 rc_thr = [20000000,10000000,6000000]
349 if res[1] > rc_thr[0]:
350 bgcolor = '#66ff66' # Green
352 if res[1] > rc_thr[1]:
353 bgcolor ='#00ccff' # Blue
355 if res[1] > rc_thr[2]:
356 bgcolor ='#ffcc33' # Orange
357 tstr = '<div style="background-color:'+bgcolor+';color:black">'
358 tstr += res[0].__str__()+' Lanes, '+rc+' M Reads'
360 else: tstr = 'not processed yet'
362 aligned_reads.allow_tags = True
366 summary_url = self.get_absolute_url()
367 return '<a href="%s">S</a>' % (summary_url,)
368 public.allow_tags = True
371 def get_absolute_url(self):
372 return ('samples.views.library_to_flowcells', [str(self.id)])
374 def get_admin_url(self):
375 return urlresolvers.reverse('admin:samples_library_change',
380 Provide some site-specific customization for the django user class
382 #objects = UserManager()
385 ordering = ['first_name', 'last_name', 'username']
388 return '/admin/%s/%s/%d' % (self._meta.app_label, self._meta.module_name, self.id)
391 #return str(self.username) + " (" + str(self.get_full_name()) + u")"
392 return str(self.get_full_name()) + ' (' + str(self.username) + ')'
394 def HTSUserInsertID(sender, instance, **kwargs):
396 Force addition of HTSUsers when someone just modifies the auth_user object
398 u = HTSUser.objects.filter(pk=instance.id)
400 cursor = connection.cursor()
401 cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,))
404 post_save.connect(HTSUserInsertID, sender=User)