X-Git-Url: http://woldlab.caltech.edu/gitweb/?a=blobdiff_plain;f=htsworkflow%2Ffrontend%2Fsamples%2Fmodels.py;h=d7c70c239c037db3dd4fc77a8c67cc9cb5cb1678;hb=53a5a342ce2b08d948bf6011b120fbc64631b4d9;hp=cce961918138a1e7f57a1f300be3f3078d594159;hpb=e498a9dd4a3bf2e7cbe308e9b50c3210297423cf;p=htsworkflow.git diff --git a/htsworkflow/frontend/samples/models.py b/htsworkflow/frontend/samples/models.py index cce9619..d7c70c2 100644 --- a/htsworkflow/frontend/samples/models.py +++ b/htsworkflow/frontend/samples/models.py @@ -1,32 +1,33 @@ +import types +import logging import urlparse from django.db import models from django.contrib.auth.models import User, UserManager +from django.core import urlresolvers from django.db.models.signals import pre_save, post_save from django.db import connection -from htsworkflow.frontend import settings from htsworkflow.frontend.reports.libinfopar import * -# Create your models here. +logger = logging.getLogger(__name__) class Antibody(models.Model): antigene = models.CharField(max_length=500, db_index=True) # New field Aug/20/08 - # SQL to add column: + # SQL to add column: # alter table fctracker_antibody add column "nickname" varchar(20) NULL; nickname = models.CharField( max_length=20, blank=True, - null=True, - db_index=True, - verbose_name = 'Short Name' + null=True, + db_index=True ) - catalog = models.CharField(max_length=50, unique=True, db_index=True) + catalog = models.CharField(max_length=50, blank=True, null=True) antibodies = models.CharField(max_length=500, db_index=True) - source = models.CharField(max_length=500, blank=True, db_index=True) - biology = models.TextField(blank=True) - notes = models.TextField(blank=True) + source = models.CharField(max_length=500, blank=True, null=True, db_index=True) + biology = models.TextField(blank=True, null=True) + notes = models.TextField(blank=True, null=True) def __unicode__(self): - return u'%s - %s (%s)' % (self.antigene, self.antibodies, self.catalog) + return u'%s - %s' % (self.antigene, self.antibodies) class Meta: verbose_name_plural = "antibodies" ordering = ["antigene"] @@ -35,9 +36,9 @@ class Cellline(models.Model): cellline_name = models.CharField(max_length=100, unique=True, db_index=True) nickname = models.CharField(max_length=20, blank=True, - null=True, - db_index=True, - verbose_name = 'Short Name') + null=True, + db_index=True) + notes = models.TextField(blank=True) def __unicode__(self): return unicode(self.cellline_name) @@ -50,7 +51,7 @@ class Condition(models.Model): max_length=2000, unique=True, db_index=True) nickname = models.CharField(max_length=20, blank=True, - null=True, + null=True, db_index=True, verbose_name = 'Short Name') notes = models.TextField(blank=True) @@ -61,33 +62,34 @@ class Condition(models.Model): class Meta: ordering = ["condition_name"] + class ExperimentType(models.Model): name = models.CharField(max_length=50, unique=True) def __unicode__(self): return unicode(self.name) -class Tag(models.Model): - tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False) - TAG_CONTEXT = ( - #('Antibody','Antibody'), - #('Cellline', 'Cellline'), - #('Condition', 'Condition'), - ('Library', 'Library'), - ('ANY','ANY'), - ) - context = models.CharField(max_length=50, - choices=TAG_CONTEXT, default='Library') - - def __unicode__(self): - return u'%s' % (self.tag_name) - - class Meta: - ordering = ["context","tag_name"] - +class Tag(models.Model): + tag_name = models.CharField(max_length=100, db_index=True,blank=False,null=False) + TAG_CONTEXT = ( + #('Antibody','Antibody'), + #('Cellline', 'Cellline'), + #('Condition', 'Condition'), + ('Library', 'Library'), + ('ANY','ANY'), + ) + context = models.CharField(max_length=50, + choices=TAG_CONTEXT, default='Library') + + def __unicode__(self): + return u'%s' % (self.tag_name) + + class Meta: + ordering = ["context","tag_name"] + class Species(models.Model): - scientific_name = models.CharField(max_length=256, - unique=False, + scientific_name = models.CharField(max_length=256, + unique=False, db_index=True ) common_name = models.CharField(max_length=256, blank=True) @@ -95,22 +97,26 @@ class Species(models.Model): def __unicode__(self): return u'%s (%s)' % (self.scientific_name, self.common_name) - + class Meta: verbose_name_plural = "species" ordering = ["scientific_name"] - + + @models.permalink + def get_absolute_url(self): + return ('htsworkflow.frontend.samples.views.species', [str(self.id)]) + class Affiliation(models.Model): name = models.CharField(max_length=256, db_index=True, verbose_name='Name') - contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name') + contact = models.CharField(max_length=256, null=True, blank=True,verbose_name='Lab Name') email = models.EmailField(null=True,blank=True) users = models.ManyToManyField('HTSUser', null=True, blank=True) users.admin_order_field = "username" - + def __unicode__(self): str = unicode(self.name) if self.contact is not None and len(self.contact) > 0: - str += u' ('+self.contact+u')' + str += u' ('+self.contact+u')' return str def Users(self): @@ -122,38 +128,58 @@ class Affiliation(models.Model): unique_together = (("name", "contact"),) class LibraryType(models.Model): - name = models.CharField(max_length=255, unique=True) + name = models.CharField(max_length=255, unique=True, + verbose_name="Adapter Type") + is_paired_end = models.BooleanField(default=True, + help_text="can you do a paired end run with this adapter") + can_multiplex = models.BooleanField(default=True, + help_text="Does this adapter provide multiplexing?") def __unicode__(self): - return unicode(self.name) + return unicode(self.name) + + class Meta: + ordering = ["-id"] + +class MultiplexIndex(models.Model): + """Map adapter types to the multiplex sequence""" + adapter_type = models.ForeignKey(LibraryType) + multiplex_id = models.CharField(max_length=6, null=False) + sequence = models.CharField(max_length=12, blank=True, null=True) + + class Meta: + verbose_name_plural = "multiplex indicies" + unique_together = ('adapter_type', 'multiplex_id') class Library(models.Model): id = models.CharField(max_length=10, primary_key=True) library_name = models.CharField(max_length=100, unique=True) library_species = models.ForeignKey(Species) - # new field 2008 Mar 5, alter table samples_library add column "hidden" NOT NULL default 0; - hidden = models.BooleanField() - # new field 2009 Oct 6, alter table samples_library add column "account_number" varchar(100) NULL + hidden = models.BooleanField(default=False) account_number = models.CharField(max_length=100, null=True, blank=True) - cell_line = models.ForeignKey(Cellline, blank=True, null=True, verbose_name="Background") + cell_line = models.ForeignKey(Cellline, blank=True, null=True, + verbose_name="Background") condition = models.ForeignKey(Condition, blank=True, null=True) antibody = models.ForeignKey(Antibody,blank=True,null=True) - # New field Aug/25/08. SQL: alter table fctracker_library add column "lib_affiliation" varchar(256) NULL; - affiliations = models.ManyToManyField(Affiliation,related_name='library_affiliations',null=True) - # new field Nov/14/08 - tags = models.ManyToManyField(Tag,related_name='library_tags',blank=True,null=True) - # New field Aug/19/08 - # SQL to add column: alter table fctracker_library add column "replicate" smallint unsigned NULL; - REPLICATE_NUM = ((1,1),(2,2),(3,3),(4,4)) - replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM,default=1) + affiliations = models.ManyToManyField( + Affiliation,related_name='library_affiliations',null=True) + tags = models.ManyToManyField(Tag,related_name='library_tags', + blank=True,null=True) + REPLICATE_NUM = [(x,x) for x in range(1,7)] + replicate = models.PositiveSmallIntegerField(choices=REPLICATE_NUM, + blank=True,null=True) experiment_type = models.ForeignKey(ExperimentType) - library_type = models.ForeignKey(LibraryType, blank=True, null=True) + library_type = models.ForeignKey(LibraryType, blank=True, null=True, + verbose_name="Adapter Type") + multiplex_id = models.CharField(max_length=128, + blank=True, null=True, + verbose_name="Index ID") creation_date = models.DateField(blank=True, null=True) - made_for = models.CharField(max_length=50, blank=True, - verbose_name='ChIP/DNA/RNA Made By') + made_for = models.CharField(max_length=50, blank=True, + verbose_name='ChIP/DNA/RNA Made By') made_by = models.CharField(max_length=50, blank=True, default="Lorian") - + PROTOCOL_END_POINTS = ( ('?', 'Unknown'), ('Sample', 'Raw sample'), @@ -161,38 +187,108 @@ class Library(models.Model): ('1A', 'Ligation, then gel'), ('PCR', 'Ligation, then PCR'), ('1Ab', 'Ligation, PCR, then gel'), - ('1Aa', 'Ligation, gel, then PCR'), + ('1Ac', 'Ligation, gel, then 12x PCR'), + ('1Aa', 'Ligation, gel, then 18x PCR'), ('2A', 'Ligation, PCR, gel, PCR'), ('Done', 'Completed'), ) - stopping_point = models.CharField(max_length=25, choices=PROTOCOL_END_POINTS, default='Done') - amplified_from_sample = models.ForeignKey('self', blank=True, null=True, related_name='amplified_into_sample') - - undiluted_concentration = models.DecimalField("Concentration", + PROTOCOL_END_POINTS_DICT = dict(PROTOCOL_END_POINTS) + stopping_point = models.CharField(max_length=25, + choices=PROTOCOL_END_POINTS, + default='Done') + + amplified_from_sample = models.ForeignKey('self', + related_name='amplified_into_sample', + blank=True, null=True) + + undiluted_concentration = models.DecimalField("Concentration", max_digits=5, decimal_places=2, blank=True, null=True, - help_text=u"Undiluted concentration (ng/\u00b5l)") + help_text=u"Undiluted concentration (ng/\u00b5l)") # note \u00b5 is the micro symbol in unicode - successful_pM = models.DecimalField(max_digits=9, decimal_places=1, blank=True, null=True) - ten_nM_dilution = models.BooleanField() - avg_lib_size = models.IntegerField(default=225, blank=True, null=True) + successful_pM = models.DecimalField(max_digits=9, + decimal_places=1, blank=True, null=True) + ten_nM_dilution = models.BooleanField(default=False) + gel_cut_size = models.IntegerField(default=225, blank=True, null=True) + insert_size = models.IntegerField(blank=True, null=True) notes = models.TextField(blank=True) - + + bioanalyzer_summary = models.TextField(blank=True,default="") + bioanalyzer_concentration = models.DecimalField(max_digits=5, + decimal_places=2, blank=True, null=True, + help_text=u"(ng/\u00b5l)") + bioanalyzer_image_url = models.URLField(blank=True,default="") + def __unicode__(self): return u'#%s: %s' % (self.id, self.library_name) - + class Meta: - verbose_name_plural = "libraries" - #ordering = ["-creation_date"] - ordering = ["-id"] - + verbose_name_plural = "libraries" + #ordering = ["-creation_date"] + ordering = ["-id"] + def antibody_name(self): - str =''+self.antibody.nickname+'' + str =''+self.antibody.label+'' return str antibody_name.allow_tags = True def organism(self): return self.library_species.common_name + def index_sequences(self): + """Return a dictionary of multiplex index id to sequence + Return None if the library can't multiplex, + + """ + if self.library_type is None: + return None + if not self.library_type.can_multiplex: + return None + if self.multiplex_id is None or len(self.multiplex_id) == 0: + return 'Err: id empty' + sequences = {} + multiplex_expressions = self.multiplex_id.split(',') + for multiplex_term in multiplex_expressions: + pairs = multiplex_term.split('-') + if len(pairs) == 1: + key = pairs[0] + seq = self._lookup_index(pairs[0]) + elif len(pairs) == 2: + key = pairs[0] + '-' + pairs[1] + seq0 = self._lookup_index(pairs[0]) + seq1 = self._lookup_index(pairs[1]) + if seq0 is None or seq1 is None: + seq = None + else: + seq = seq0 + '-' + seq1 + else: + raise RuntimeError("Too many - seperated sequences") + if seq is None: + seq = 'Err: index not found' + sequences[key] = seq + return sequences + + def _lookup_index(self, multiplex_id): + try: + multiplex = MultiplexIndex.objects.get( + adapter_type = self.library_type.id, + multiplex_id = multiplex_id) + return multiplex.sequence + except MultiplexIndex.DoesNotExist, e: + return None + + def index_sequence_text(self, seperator=' '): + """Return formatted multiplex index sequences""" + sequences = self.index_sequences() + if sequences is None: + return "" + if type(sequences) in types.StringTypes: + return sequences + multiplex_ids = sequences.keys() + multiplex_ids.sort() + return seperator.join(( "%s:%s" %(i,sequences[i]) for i in multiplex_ids)) + index_sequence_text.short_description = "Index" + + def affiliation(self): affs = self.affiliations.all().order_by('name') tstr = '' @@ -200,7 +296,7 @@ class Library(models.Model): for t in affs: ar.append(t.__unicode__()) return '%s' % (", ".join(ar)) - + def is_archived(self): """ returns True if archived else False @@ -210,6 +306,15 @@ class Library(models.Model): else: return False + def stopping_point_name(self): + end_points = Library.PROTOCOL_END_POINTS_DICT + name = end_points.get(self.stopping_point, None) + if name is None: + name = "Lookup Error" + logger.error("protocol stopping point in database didn't match names in library model") + return name + + def libtags(self): affs = self.tags.all().order_by('tag_name') ar = [] @@ -218,7 +323,7 @@ class Library(models.Model): return u'%s' % ( ", ".join(ar)) def DataRun(self): - str ='Data Run' + str ='Data Run' return str DataRun.allow_tags = True @@ -246,28 +351,28 @@ class Library(models.Model): if res[1] > rc_thr[1]: bgcolor ='#00ccff' # Blue else: - if res[1] > rc_thr[2]: + if res[1] > rc_thr[2]: bgcolor ='#ffcc33' # Orange tstr = '
' tstr += res[0].__unicode__()+' Lanes, '+rc+' M Reads' tstr += '
' - else: tstr = 'not processed yet' + else: tstr = 'not processed yet' return tstr aligned_reads.allow_tags = True - + def public(self): SITE_ROOT = '/' summary_url = self.get_absolute_url() return 'S' % (summary_url,) public.allow_tags = True - + @models.permalink def get_absolute_url(self): return ('htsworkflow.frontend.samples.views.library_to_flowcells', [str(self.id)]) - - - + def get_admin_url(self): + return urlresolvers.reverse('admin:samples_library_change', + args=(self.id,)) class HTSUser(User): """ @@ -284,7 +389,7 @@ class HTSUser(User): def __unicode__(self): #return unicode(self.username) + u" (" + unicode(self.get_full_name()) + u")" return unicode(self.get_full_name()) + u' (' + unicode(self.username) + ')' - + def HTSUserInsertID(sender, instance, **kwargs): """ Force addition of HTSUsers when someone just modifies the auth_user object @@ -294,5 +399,5 @@ def HTSUserInsertID(sender, instance, **kwargs): cursor = connection.cursor() cursor.execute('INSERT INTO samples_htsuser (user_ptr_id) VALUES (%s);' % (instance.id,)) cursor.close() - + post_save.connect(HTSUserInsertID, sender=User)