Package WholeCellKB :: Package public :: Module models
[hide private]
[frames] | no frames]

Source Code for Module WholeCellKB.public.models

   1  ''' 
   2  Whole-cell data model 
   3   
   4  Author: Jonathan Karr, jkarr@stanford.edu 
   5  Affiliation: Covert Lab, Department of Bioengineering, Stanford University 
   6  Last updated: 2012-07-17 
   7  ''' 
   8   
   9  from __future__ import unicode_literals 
  10  from Bio.Seq import Seq 
  11  from Bio.Alphabet import IUPAC 
  12  from django.contrib.auth.models import User 
  13  from django.core import validators 
  14  from django.core.exceptions import ValidationError, ObjectDoesNotExist 
  15  from django.core.urlresolvers import reverse 
  16  from django.db import models 
  17  from django.db.models import Model, OneToOneField, CharField, IntegerField, URLField, PositiveIntegerField, FloatField, ForeignKey, BooleanField, SlugField, ManyToManyField, TextField, DateTimeField, options, permalink, SET_NULL, Min, Max 
  18  from django.db.models.query import EmptyQuerySet 
  19  from django.utils.http import urlencode 
  20  from itertools import chain 
  21  from public.templatetags.templatetags import set_time_zone 
  22  import math 
  23  import re 
  24  import settings 
  25  import subprocess 
  26   
  27  ''' BEGIN: choices ''' 
  28   
  29  CHOICES_DIRECTION = ( 
  30          ('f', 'Forward'), 
  31          ('r', 'Reverse'), 
  32  ) 
  33   
  34  CHOICES_REFERENCE_TYPE = ( 
  35          ('article', 'Article'), 
  36          ('book', 'Book'), 
  37          ('thesis', 'Thesis'), 
  38          ('misc', 'Miscellaneous'), 
  39  ) 
  40   
  41  CHOICES_STRANDEDNESS = ( 
  42          ('dsDNA', 'dsDNA'), 
  43          ('ssDNA', 'ssDNA'), 
  44          ('xsDNA', 'xsDNA'), 
  45  ) 
  46   
  47  CHOICES_VMAX_UNITS = ( 
  48          ('1/min', '1/min'), 
  49          ('U/mg', 'U/mg'), 
  50  ) 
  51   
  52  CHOICES_HOMOLOG_SPECIES = ( 
  53          ('B. subtilis', 'B. subtilis'), 
  54          ('E. coli', 'E. coli'), 
  55          ('M. hyopneumoniae', 'M. hyopneumoniae'), 
  56          ('M. mobile', 'M. mobile'), 
  57          ('M. pneumoniae', 'M. pneumoniae'), 
  58          ('S. coelicolor', 'S. coelicolor'), 
  59          ('S. oneidensis', 'S. oneidensis'), 
  60  ) 
  61   
  62  HOMOLOG_SPECIES_URLS = { 
  63          "B. subtilis": "http://www.genome.jp/dbget-bin/www_bget?bsu:%s", 
  64          "E. coli": "http://www.genome.jp/dbget-bin/www_bget?eco:%s", 
  65          "M. hyopneumoniae": "http://www.genome.jp/dbget-bin/www_bget?mhj:%s", 
  66          "M. mobile": "http://www.genome.jp/dbget-bin/www_bget?mmo:%s", 
  67          "M. pneumoniae": "http://www.genome.jp/dbget-bin/www_bget?mpn:%s", 
  68          "S. coelicolor": "http://www.genome.jp/dbget-bin/www_bget?sco:%s", 
  69          "S. oneidensis": "http://www.genome.jp/dbget-bin/www_bget?son:%s", 
  70  } 
  71   
  72  CHOICES_CROSS_REFERENCE_SOURCES = ( 
  73          ('ATCC', 'ATCC'), 
  74          ('BiGG', 'BiGG'), 
  75          ('BioCyc', 'BioCyc'), 
  76          ('BioProject', 'BioProject'), #http://www.ncbi.nlm.nih.gov/bioproject/%s 
  77          ('CAS', 'CAS'), 
  78          ('ChEBI', 'ChEBI'), 
  79          ('CMR', 'CMR'), 
  80          ('EC', 'EC'), 
  81          ('GenBank', 'GenBank'), 
  82          ('ISBN', 'ISBN'), 
  83          ('KEGG', 'KEGG'), 
  84          ('KNApSAcK', 'KNApSAcK'), 
  85          ('LipidBank', 'LipidBank'), 
  86          ('LIPIDMAPS', 'LIPIDMAPS'), 
  87          ('PDB', 'PDB'), 
  88          ('PDBCCD', 'PDBCCD'), 
  89          ('PubChem', 'PubChem'), 
  90          ('PubMed', 'PubMed'), 
  91          ('RefSeq', 'RefSeq'), 
  92          ('SABIO-RK', 'SABIO-RK'), 
  93          ('SwissProt', 'SwissProt'), 
  94          ('Taxonomy', 'Taxonomy'), 
  95          ('ThreeDMET', 'ThreeDMET'),      
  96          ('URL', 'URL'), 
  97  ) 
  98   
  99  CROSS_REFERENCE_SOURCE_URLS = { 
 100          "ATCC": "http://www.atcc.org/ATCCAdvancedCatalogSearch/ProductDetails/tabid/452/Default.aspx?Template=bacteria&ATCCNum=%s", 
 101          "BiGG": "http://bigg.ucsd.edu/bigg/postMet.pl?organism=3307911&organism=1461534&organism=222668&organism=3277493&organism=2795088&organism=2423527&organism=1823466&compartment_list=any&pathway_list=any&name_text=%s", 
 102          "BioCyc": "http://biocyc.org/MGEN243273/NEW-IMAGE?type=GENE&object=%s", 
 103          "BioProject": "http://www.ncbi.nlm.nih.gov/bioproject/%s", 
 104          "CAS": "http://www.ncbi.nlm.nih.gov/sites/entrez?db=pccompound&term=%s", 
 105          "ChEBI": "http://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:%s", 
 106          "CMR": "http://cmr.jcvi.org/tigr-scripts/CMR/shared/GenePage.cgi?locus=%s", 
 107          "EC": "http://www.expasy.ch/enzyme/%s", 
 108          "GenBank": "http://www.ncbi.nlm.nih.gov/sites/gquery?term=%s", 
 109          "ISBN": "http://isbndb.com/search-all.html?kw=%s", 
 110          "KEGG": "http://www.genome.jp/dbget-bin/www_bget?cpd:%s", 
 111          "KNApSAcK": "http://kanaya.naist.jp/knapsack_jsp/information.jsp?word=%s", 
 112          "LipidBank": "http://lipidbank.jp/cgi-bin/detail.cgi?id=%s", 
 113          "LIPIDMAPS": "http://www.lipidmaps.org/data/get_lm_lipids_dbgif.php?LM_ID=%s", 
 114          "PDB": "http://www.pdb.org/pdb/explore/explore.do?structureId=%s", 
 115          "PDBCCD": "http://www.ebi.ac.uk/pdbe-srv/pdbechem/chemicalCompound/show/%s", 
 116          "PubChem": "http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?viewopt=PubChem&sid=%s", 
 117          "PubMed": "http://www.ncbi.nlm.nih.gov/pubmed/%s", 
 118          "RefSeq": "http://www.ncbi.nlm.nih.gov/nuccore/%s", 
 119          "SABIO-RK": "http://sabio.villa-bosch.de/kineticLawEntry.jsp?kinlawid=%s&viewData=true", 
 120          "SwissProt": "http://www.uniprot.org/uniprot/%s", 
 121          "Taxonomy": "http://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi?id=%s", 
 122          "ThreeDMET": "http://www.3dmet.dna.affrc.go.jp/bin2/show_data.e?acc=%s",         
 123          "URL": "%s", 
 124  } 
 125   
 126  CHOICES_GENETIC_CODE = ( 
 127          ('1', 'Standard'), 
 128          ('2', 'Vertebrate'), 
 129          ('3', 'Yeast'), 
 130          ('4', 'Mold, protozoa, coelenterate mitochondria, mycoplasma, and spiroplasma'), 
 131          ('5', 'Invertebrate mitochondria'), 
 132          ('6', 'Ciliate, dasycladacean and hexamita'), 
 133          ('9', 'Echinoderm and flatworm mitochondria'), 
 134          ('10', 'Euplotid'), 
 135          ('11', 'Bacteria, archaea and plant plastids'), 
 136          ('12', 'Alternative yeast'), 
 137          ('13', 'Ascidian mitochondria'), 
 138          ('14', 'Alternative flatworm mitochondria'), 
 139          ('15', 'Blepharisma'), 
 140          ('16', 'Chlorophycean mitochondria'), 
 141          ('21', 'Trematode mitochondria'), 
 142          ('22', 'Scenedesmus obliquus mitochondria'), 
 143          ('23', 'Thraustochytrium mitochondria'), 
 144          ('24', 'Pterobranchia mitochondria'), 
 145  ) 
 146   
 147  CHOICES_REACTION_DIRECTION = ( 
 148          ('f', 'Forward'), 
 149          ('b', 'Backward'), 
 150          ('r', 'Reversible'), 
 151  ) 
 152   
 153  CHOICES_SIGNAL_SEQUENCE_LOCATION = ( 
 154          ('N', 'N-terminus'), 
 155          ('C', 'C-terminus'), 
 156  ) 
 157   
 158  CHOICES_SIGNAL_SEQUENCE_TYPE = ( 
 159          ('lipoprotein', 'Lipoprotein'), 
 160          ('secretory', 'Secretory'), 
 161  ) 
 162   
 163  ''' END: CHOICES ''' 
 164   
 165  # add model options 
 166  options.DEFAULT_NAMES = options.DEFAULT_NAMES + ( 
 167          'concrete_entry_model',  
 168          'hide_public', 
 169          'fieldsets',  
 170          'field_list',  
 171          'facet_fields',  
 172          'clean',  
 173          'validate_unique', 
 174          ) 
 175   
 176  ''' BEGIN: validators ''' 
177 -def validate_dna_sequence(seq):
178 validators.RegexValidator(regex=r'^[ACGT]+$', message='Enter a valid DNA sequence consisting of only the letters A, C, G, and T')(seq)
179
180 -def validate_kinetics(reaction, direction):
181 if direction == 'f': 182 prop_name = 'kinetics_forward' 183 else: 184 prop_name = 'kinetics_backward' 185 kinetics = reaction[prop_name] 186 187 wids = [] 188 for s in reaction['stoichiometry']: 189 if (direction == 'f' and s['coefficient'] < 0) or (direction == 'r' and s['coefficient'] > 0): 190 wids.append(s['molecule']) 191 192 #rate law 193 usedKmMax = 0 194 usedVmax = 0 195 for match in re.finditer(r'([a-z][a-z0-9_]*)', kinetics['rate_law'], flags=re.I): 196 if match.group(1) == 'Vmax': 197 usedVmax = 1 198 elif match.group(1)[0:2] == 'Km': 199 if len(match.group(1)) == 2: 200 usedKmMax = max(usedKmMax, 1); 201 elif match.group(1)[2:].isnumeric(): 202 usedKmMax = max(usedKmMax, int(float(match.group(1)[2:]))) 203 else: 204 raise ValidationError({prop_name: 'Invalid rate law'}) 205 elif match.group(1) not in wids: 206 raise ValidationError({prop_name: 'Invalid rate law, unknown molecule: "%s"' % (match.group(1), )}) 207 208 #Vmax 209 if usedVmax and kinetics['vmax'] is None: 210 raise ValidationError({prop_name: 'Invalid rate law'}) 211 if usedVmax and kinetics['vmax_unit'] not in ["1/min", "U/mg"]: 212 raise ValidationError({prop_name: 'Invalid rate law'}) 213 214 #Km 215 if not ((kinetics['rate_law'] is None or kinetics['rate_law'] == '') or (kinetics['km'] == '' and usedKmMax == 0) or (kinetics['km'] != '' and usedKmMax == len(kinetics['km'].split(', ')))): 216 raise ValidationError({prop_name: 'Invalid rate law'})
217 218 ''' 219 Test cases: 220 tests = [ 221 "!(ATP)", 222 " ( ATP ) ", 223 "CTP ( ATP ) ", 224 "CTP | ( ATP ) ", 225 "CTP | ( ATP & !ATP & GTP ) ", 226 "CTP | ( ATP & !ATP & GTP)( ) ", 227 "CTP | ( ATP & !ATP & GTP ) | ()", 228 "CTP | ( ATP & !ATP & GTP ) | (H)", 229 "CTP | ( ATP & !ATP & GTP ) | (H) K", 230 "!!CTP", 231 "CTP | !!( ATP & !ATP & GTP ) | (H) & K", 232 "CTP | !( ATP & !ATP & GTP ) | (H) & K", 233 "CTP | !( ATP & (!ATP | UTP) & GTP ) | (H) & K", 234 ] 235 for test in tests: 236 try: 237 models.parse_regulatory_rule(test) 238 except: 239 print test 240 '''
241 -def parse_regulatory_rule(equation, all_obj_data, species_wid):
242 from public.helpers import getModel, getEntry 243 import settings 244 245 pre = '' 246 blocks = [] 247 posts = [] 248 pattern = '%s' 249 begin = 0 250 end = 0 251 sense = 0 252 253 equation = equation or '' 254 equation = equation.replace(" ", "") 255 256 match = re.match(r'^([!\-]{0,1})\((.+)\)$', equation) 257 if match: 258 pattern = pattern.replace('%s', match.group(1) + '(%s)') 259 equation = match.group(2) 260 261 match = re.match(r'^([!\-]{0,1})(.+)$', equation) 262 if match: 263 pattern = pattern.replace('%s', match.group(1) + '%s') 264 equation = match.group(2) 265 266 if equation == '': 267 raise ValidationError({'regulatory_rule': 'Invalid regulatory rule'}) 268 269 #split into blocks 270 for i in range(len(equation)): 271 if equation[i] == '(': 272 sense += 1 273 if equation[i] == ')': 274 sense -= 1 275 if sense < 0: 276 raise ValidationError({'regulatory_rule': 'Invalid regulatory rule'}) 277 278 if sense == 0: 279 if len(blocks) == 0: 280 pre = equation[0:begin] 281 if equation[i] in ["|", "&", "-", "+"]: 282 blocks.append(equation[begin:i]) 283 posts.append(equation[i]) 284 begin = i + 1 285 elif equation[i:i+2] in [">=", "<=", "=="]: 286 blocks.append(equation[begin:i]) 287 posts.append(equation[i:i+1]) 288 begin = i + 2 289 elif equation[i] in [">", "<"]: 290 blocks.append(equation[begin:i]) 291 posts.append(equation[i]) 292 begin = i + 1 293 blocks.append(equation[begin:]) 294 posts.append('') 295 296 #check parenthesis match 297 if sense != 0: 298 raise ValidationError({'regulatory_rule': 'Invalid regulatory rule'}) 299 300 #if no further substructure 301 if len(blocks) == 1: 302 if '(' in blocks[0]: 303 raise ValidationError({'regulatory_rule': 'Invalid regulatory rule'}) 304 elif '!' in blocks[0]: 305 raise ValidationError({'regulatory_rule': 'Invalid regulatory rule'}) 306 elif blocks[0].isnumeric() or blocks[0] in ["true", "false"]: 307 return pattern % (pre + ' '.join([block + ' ' + post for block, post in zip(blocks, posts)])) 308 309 wid = blocks[0] 310 molecule = None 311 if all_obj_data is None: 312 molecule = getEntry(species_wid=species_wid, wid=wid) 313 elif all_obj_data.has_key(wid): 314 molecule = all_obj_data[wid] 315 316 if molecule is not None: 317 if isinstance(molecule, Entry): 318 molecule_model = molecule.__class__ 319 else: 320 molecule_model = getModel(molecule['model_type']) 321 if issubclass(molecule_model, Molecule): 322 return pattern % (pre + ' '.join([block + ' ' + post for block, post in zip(blocks, posts)])) 323 else: 324 raise ValidationError({'regulatory_rule': 'Invalid regulatory rule, referenced invalid object %s' % wid}) 325 try: 326 obj = Molecule.objects.get(species__wid=species_wid, wid=wid) 327 blocks[0] = '<a href="%s">%s</a>' % (obj.get_absolute_url(), wid, ) 328 return pattern % (pre + ' '.join([block + ' ' + post for block, post in zip(blocks, posts)])) 329 except: 330 pass 331 raise ValidationError({'regulatory_rule': 'Invalid object "%s" in regulatory rule' % wid}) 332 333 #recurse on blocks 334 for i in range(len(blocks)): 335 blocks[i] = parse_regulatory_rule(blocks[i], all_obj_data, species_wid) 336 337 return pattern % (pre + ' '.join([block + ' ' + post for block, post in zip(blocks, posts)]))
338 339 ''' END: validators '''
340 341 #User profile 342 -class UserProfile(Model):
343 user = OneToOneField(User) 344 affiliation = CharField(max_length=255, blank=True, default='', verbose_name='Affiliation') 345 website = URLField(max_length=255, blank=True, default='', verbose_name='Website') 346 phone = CharField(max_length=255, blank=True, default='', verbose_name='Phone') 347 address = CharField(max_length=255, blank=True, default='', verbose_name='Address') 348 city = CharField(max_length=255, blank=True, default='', verbose_name='City') 349 state = CharField(max_length=255, blank=True, default='', verbose_name='State') 350 zip = CharField(max_length=255, blank=True, default='', verbose_name='Zip') 351 country = CharField(max_length=255, blank=True, default='', verbose_name='Country') 352
353 - class Meta:
354 verbose_name='User profile' 355 verbose_name_plural = 'User profiles' 356 ordering = ['user__last_name', 'user__first_name'] 357 get_latest_by = 'user__date_joined'
358 359 ''' BEGIN: helper models '''
360 -class Evidence(Model):
361 value = TextField(blank=True, default='', verbose_name='Value') 362 units = CharField(max_length=255, blank=True, null=True, verbose_name='Units') 363 is_experimentally_constrained = BooleanField(verbose_name='Is experimentally <br/>constrained') 364 species = CharField(max_length=255, blank=True, null=True, verbose_name='Species') 365 media = CharField(max_length=255, blank=True, null=True, verbose_name='Media') 366 pH = FloatField(blank=True, null=True, verbose_name='pH') 367 temperature = FloatField(blank=True, null=True, verbose_name='Temperature (C)') 368 comments = TextField(blank=True, default='', verbose_name='Comments') 369 references = ManyToManyField('Reference', blank=True, null=True, related_name='evidence', verbose_name='References') 370 371 species_component = ForeignKey('SpeciesComponent', verbose_name='Species omponent', related_name='+') 372
373 - def __unicode__(self):
374 arr = [] 375 for field in self.__class__._meta.fields: 376 if not field.auto_created and getattr(self, field.name) is not None and not (isinstance(getattr(self, field.name), (str, unicode, )) and getattr(self, field.name) == ''): 377 arr.append('%s: %s' % (field.name, unicode(getattr(self, field.name)))) 378 for field in self.__class__._meta.many_to_many: 379 arr.append('%s: %s' % (field.name, unicode(getattr(self, field.name).all()))) 380 return ', '.join(arr)
381
382 - class Meta:
383 ordering = ['value', 'units'] 384 verbose_name = 'Evidence' 385 verbose_name_plural = 'Evidence'
386
387 -class EntryData(Model):
388
389 - def __unicode__(self):
390 arr = [] 391 txt = unicode('') 392 nFields = 0 393 for field in self.__class__._meta.fields: 394 if not field.auto_created: 395 nFields += 1 396 try: 397 txt = unicode(getattr(self, field.name)) 398 arr.append('%s: %s' % (field.name, txt)) 399 except ObjectDoesNotExist: 400 pass 401 for field in self.__class__._meta.many_to_many: 402 nFields += 1 403 txt = unicode(getattr(self, field.name).all()) 404 arr.append('%s: %s' % (field.name, txt)) 405 406 if nFields == 1: 407 return txt 408 else: 409 return ', '.join(arr)
410
411 - class Meta:
412 abstract = True
413
414 -class EvidencedEntryData(EntryData):
415 evidence = ManyToManyField(Evidence, blank=True, null=True, verbose_name='Evidence') 416
417 - class Meta:
418 abstract = True
419
420 #Basic entry data 421 -class EntryBooleanData(EvidencedEntryData):
422 value = BooleanField(verbose_name='Value') 423
424 - class Meta:
425 ordering = ['value'] 426 verbose_name = 'Entry Boolean data' 427 verbose_name_plural = 'Entry Boolean data'
428
429 -class EntryCharData(EvidencedEntryData):
430 value = CharField(max_length = 255, blank=True, default='', verbose_name='Value') 431 units = CharField(max_length = 255, blank=True, default='', verbose_name='Units') 432
433 - class Meta:
434 ordering = ['value', 'units'] 435 verbose_name = 'Entry char data' 436 verbose_name_plural = 'Entry char data'
437
438 -class EntryFloatData(EvidencedEntryData):
439 value = FloatField(verbose_name='Value') 440 units = CharField(max_length = 255, blank=True, default='', verbose_name='Units') 441
442 - class Meta:
443 ordering = ['value', 'units'] 444 verbose_name = 'Entry float data' 445 verbose_name_plural = 'Entry float data'
446
447 -class EntryPositiveFloatData(EvidencedEntryData):
448 value = FloatField(verbose_name='Value', validators=[validators.MinValueValidator(0)]) 449 units = CharField(max_length = 255, blank=True, default='', verbose_name='Units') 450
451 - class Meta:
452 ordering = ['value', 'units'] 453 verbose_name = 'Entry positive float data' 454 verbose_name_plural = 'Entry positive float data'
455
456 -class EntryTextData(EvidencedEntryData):
457 value = TextField(blank=True, default='', verbose_name='Value') 458 units = CharField(max_length = 255, blank=True, default='', verbose_name='Units') 459
460 - class Meta:
461 ordering = ['value', 'units'] 462 verbose_name = 'Entry text data' 463 verbose_name_plural = 'Entry text data'
464
465 #Complex entry data 466 -class BindingSite(EvidencedEntryData):
467 coordinate = PositiveIntegerField(verbose_name='Coordinate (nt)') 468 length = PositiveIntegerField(verbose_name='Length (nt)') 469 direction = CharField(max_length=10, choices=CHOICES_DIRECTION, verbose_name='Direction') 470 471 #getter
472 - def get_chromosome(self):
473 return self.transcriptional_regulations.all()[0].transcription_unit.get_chromosome()
474
475 - class Meta:
476 ordering = ['coordinate', 'length'] 477 verbose_name = 'Binding site' 478 verbose_name_plural = 'Binding sites'
479
480 -class BiomassComposition(EvidencedEntryData):
481 concentration = FloatField(verbose_name='Concentration (mmol gDCW<sup>-1</sup>)', validators=[validators.MinValueValidator(0)]) 482 compartment = ForeignKey('Compartment', related_name='biomass_compositions', verbose_name='Compartment') 483
484 - class Meta:
485 ordering = ['-concentration'] 486 verbose_name = 'Biomass composition' 487 verbose_name_plural = 'Biomass composition'
488
489 -class Codon(EvidencedEntryData):
490 sequence = CharField(max_length=3, verbose_name='Sequence', validators=[ 491 validate_dna_sequence, 492 validators.MinLengthValidator(3), 493 ]) 494
495 - class Meta:
496 ordering = ['sequence'] 497 verbose_name='Codon' 498 verbose_name_plural = 'Codons'
499
500 -class CoenzymeParticipant(EvidencedEntryData):
501 metabolite = ForeignKey('Metabolite', related_name='coenzyme_participants', verbose_name='Metabolite') 502 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 503 coefficient = FloatField(blank=True, null=True, verbose_name='Coefficient', validators=[validators.MinValueValidator(0)]) 504
505 - class Meta:
506 ordering = [] 507 verbose_name='Coenzyme participant' 508 verbose_name_plural = 'Coenzyme participants'
509
510 -class CrossReference(EntryData):
511 xid = CharField(max_length=255, verbose_name='External ID') 512 source = CharField(max_length=20, choices=CHOICES_CROSS_REFERENCE_SOURCES, verbose_name='Source') 513
514 - class Meta:
515 ordering = ['xid'] 516 verbose_name='Cross reference' 517 verbose_name_plural = 'Cross references'
518
519 -class DisulfideBond(EvidencedEntryData):
520 protein_monomer = ForeignKey('ProteinMonomer', related_name = 'disulfide_bonds', verbose_name='Protein monomer') 521 residue_1 = PositiveIntegerField(verbose_name='Residue-1') 522 residue_2 = PositiveIntegerField(verbose_name='Residue-2') 523
524 - class Meta:
525 ordering = [] 526 verbose_name = 'Disulfide bond' 527 verbose_name_plural = 'Disulfide bonds'
528
529 -class DNAFootprint(EvidencedEntryData):
530 length = PositiveIntegerField(null=True, blank=True, verbose_name='Length (nt)') 531 binding = CharField(max_length=10, blank=True, default='', verbose_name='Binding', choices=CHOICES_STRANDEDNESS) 532 region = CharField(max_length=10, blank=True, default='', verbose_name='Region', choices=CHOICES_STRANDEDNESS) 533
534 - class Meta:
535 ordering = [] 536 verbose_name = 'DNA footprint' 537 verbose_name_plural = 'DNA footprints'
538
539 -class EnzymeParticipant(EvidencedEntryData):
540 protein = ForeignKey('Protein', related_name='enzyme_participants', verbose_name='Protein') 541 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 542
543 - class Meta:
544 ordering = [] 545 verbose_name='Enzyme participant' 546 verbose_name_plural = 'Enzyme participants'
547
548 -class Homolog(EvidencedEntryData):
549 xid = CharField(max_length=255, verbose_name='External ID') 550 species = CharField(max_length=20, choices=CHOICES_HOMOLOG_SPECIES, verbose_name='Species') 551
552 - class Meta:
553 ordering = ['xid'] 554 verbose_name='Homolog' 555 verbose_name_plural = 'Homologs'
556
557 -class Kinetics(EvidencedEntryData):
558 rate_law = CharField(blank=True, default='', max_length=255, verbose_name='Rate law') 559 km = CharField(max_length=255, blank=True, verbose_name='K<sub>m</sub> (&mu;M)', validators=[validators.RegexValidator(r'^([0-9\.]+)(, [0-9\.]+)*$')]) 560 vmax = FloatField(blank=True, null=True, verbose_name='V<sub>max</sub>', validators=[validators.MinValueValidator(0)]) 561 vmax_unit = CharField(blank=True, max_length=255, choices=CHOICES_VMAX_UNITS, verbose_name='V<sub>max</sub> Unit') 562
563 - def get_vmax_normalized(self):
564 from public.helpers import getModel 565 566 if vmax_unit.name == 'U/mg': 567 enz = self.reactions.all()[0].enzyme 568 enz = getModel(enz.model_type).objects.get(id=enz.id) 569 return self.vmax * enz.get_molecular_weight() * 1e-3 570 else: 571 return self.vmax
572
573 - class Meta:
574 ordering = [] 575 verbose_name='Kinetics' 576 verbose_name_plural = 'Kinetics'
577
578 -class MediaComposition(EvidencedEntryData):
579 concentration = FloatField(verbose_name='Concentration (mM)', validators=[validators.MinValueValidator(0)]) 580 is_diffused = BooleanField(verbose_name='Is diffused') 581
582 - class Meta:
583 ordering = ['-concentration'] 584 verbose_name='Media composition' 585 verbose_name_plural = 'Media composition'
586
587 -class MetaboliteMapCoordinate(EntryData):
588 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 589 x = FloatField(verbose_name='X') 590 y = FloatField(verbose_name='Y') 591
592 - class Meta:
593 ordering = ['x', 'y', 'compartment'] 594 verbose_name='Metabolite map coordinate' 595 verbose_name_plural = 'Metabolite map coordinates'
596
597 -class ModificationReaction(EvidencedEntryData):
598 molecule = ForeignKey('Molecule', related_name='modification_reactions', verbose_name='Molecule') 599 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 600 position = PositiveIntegerField(blank=True, null=True, verbose_name='Position') 601
602 - class Meta:
603 ordering = [] 604 verbose_name='Protein monomer' 605 verbose_name_plural = 'Protein monomers'
606
607 -class ProstheticGroupParticipant(EvidencedEntryData):
608 metabolite = ForeignKey('Metabolite', related_name='prosthetic_group_participants', verbose_name='Metabolite') 609 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 610 coefficient = PositiveIntegerField(blank=True, null=True, verbose_name='Coefficient') 611
612 - class Meta:
613 ordering = [] 614 verbose_name='Prosthetic group participant' 615 verbose_name_plural = 'Prosthetic group participants'
616
617 -class ProteinComplexBiosythesisParticipant(EvidencedEntryData):
618 molecule = ForeignKey('Molecule', related_name='protein_complex_biosythesis_participants', verbose_name='Molecule') 619 residue = PositiveIntegerField(blank=True, null=True, verbose_name='Residue') 620 coefficient = FloatField(verbose_name='Coefficient') 621 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 622
623 - class Meta:
624 ordering = [] 625 verbose_name='Protein complex biosythesis participant' 626 verbose_name_plural = 'Protein complex biosythesis participants'
627
628 -class ReactionMapCoordinate(EntryData):
629 path = TextField(verbose_name='Path') 630 value_x = FloatField(verbose_name='Label-X') 631 value_y = FloatField(verbose_name='Label-Y') 632 label_x = FloatField(verbose_name='Value-X') 633 label_y = FloatField(verbose_name='Value-Y') 634
635 - class Meta:
636 ordering = ['value_x', 'value_y', 'label_x', 'label_y'] 637 verbose_name='Reaction map coordinate' 638 verbose_name_plural = 'Reaction map coordinates'
639
640 -class ReactionStoichiometryParticipant(EvidencedEntryData):
641 molecule = ForeignKey('Molecule', related_name='reaction_stoichiometry_participants', verbose_name='Molecule') 642 coefficient = FloatField(verbose_name='Coefficient') 643 compartment = ForeignKey('Compartment', related_name='+', verbose_name='Compartment') 644
645 - class Meta:
646 ordering = [] 647 verbose_name='Molecule coefficient compartment' 648 verbose_name_plural = 'Molecule coefficient compartments'
649
650 -class SignalSequence(EvidencedEntryData):
651 type = CharField(max_length=20, choices=CHOICES_SIGNAL_SEQUENCE_TYPE, verbose_name='Type') 652 location = CharField(max_length=1, choices=CHOICES_SIGNAL_SEQUENCE_LOCATION, verbose_name='Location') 653 length = PositiveIntegerField(verbose_name='Length (nt)') 654
655 - class Meta:
656 ordering = ['type', 'location', 'length'] 657 verbose_name = 'Signal sequence' 658 verbose_name_plural = 'Signal sequences'
659
660 -class SimulationProperty(EntryData):
661 class_name = TextField(verbose_name = 'Class', db_index=True) 662 property_name = CharField(max_length = 255, verbose_name = 'Property', db_index=True) 663
664 - def __unicode__(self):
665 return '%s.%s' % (self.class_name, self.property_name)
666
667 - class Meta:
668 ordering = ['class_name', 'property_name'] 669 verbose_name = 'Simulation property' 670 verbose_name_plural = 'Simulation properties'
671
672 -class Synonym(EntryData):
673 name = CharField(max_length=255, verbose_name='Name') 674
675 - class Meta:
676 ordering = ['name'] 677 verbose_name = 'Synonym' 678 verbose_name_plural = 'Synonyms'
679 680 ''' END: helper models ''' 681 682 683 ''' BEGIN: Base classes for all knowledge base objects '''
684 -class Entry(Model):
685 model_type = CharField(max_length=255, editable=False, verbose_name='Model') 686 wid = SlugField(max_length=150, verbose_name='WID', validators=[validators.validate_slug]) 687 name = CharField(max_length=255, blank=True, default='', verbose_name='Name') 688 synonyms = ManyToManyField(Synonym, blank=True, null=True, related_name='entry', verbose_name='Synonyms') 689 cross_references = ManyToManyField(CrossReference, blank=True, null=True, related_name='cross_referenced_entries', verbose_name='Cross references') 690 comments = TextField(blank=True, default='', verbose_name='Comments') 691 692 created_user = ForeignKey(User, related_name='+', editable=False, verbose_name='Created user') 693 created_date = DateTimeField(auto_now=False, auto_now_add=True, verbose_name='Created date') 694 695 last_updated_user = ForeignKey(User, related_name='+', editable=False, verbose_name='Last updated user') 696 last_updated_date = DateTimeField(auto_now=True, auto_now_add=True, verbose_name='Last updated date') 697
698 - def __unicode__(self):
699 return self.wid
700
701 - def natural_key(self):
702 return self.wid
703
704 - def save(self, *args, **kwargs):
705 setattr(self, 'model_type', self.__class__.__name__) 706 super(Entry, self).save(*args, **kwargs)
707 708 #html formatting
709 - def get_as_html_synonyms(self, is_user_anonymous):
710 return format_list_html([x.name for x in self.synonyms.all()], comma_separated=True)
711
712 - def get_as_html_cross_references(self, is_user_anonymous):
713 results = [] 714 for cr in self.cross_references.all(): 715 results.append('%s: <a href="%s">%s</a>' % (cr.source, CROSS_REFERENCE_SOURCE_URLS[cr.source] % cr.xid, cr.xid)) 716 return format_list_html(results, separator=', ')
717
718 - def get_as_html_created_user(self, is_user_anonymous):
719 if is_user_anonymous: 720 return '%s' % (self.created_date.strftime("%Y-%m-%d %H:%M:%S")) 721 else: 722 return '<a href="%s">%s %s</a> on %s' % (self.created_user.get_absolute_url(), self.created_user.first_name, self.created_user.last_name, self.created_date.strftime("%Y-%m-%d %H:%M:%S"))
723
724 - def get_as_html_last_updated_user(self, is_user_anonymous):
725 if is_user_anonymous: 726 return '%s' % (self.last_updated_date.strftime("%Y-%m-%d %H:%M:%S")) 727 else: 728 return '<a href="%s">%s %s</a> on %s' % (self.last_updated_user.get_absolute_url(), self.last_updated_user.first_name, self.last_updated_user.last_name, self.last_updated_date.strftime("%Y-%m-%d %H:%M:%S"))
729 730 #meta information
731 - class Meta:
732 concrete_entry_model = False 733 fieldsets = [ 734 ('Type', {'fields': ['model_type']}), 735 ('Name', {'fields': ['wid', 'name', 'synonyms', 'cross_references']}), 736 ('Comments', {'fields': ['comments']}), 737 ('Metadata', {'fields': [{'verbose_name': 'Created', 'name': 'created_user'}, {'verbose_name': 'Last updated', 'name': 'last_updated_user'}]}), 738 ] 739 field_list = [ 740 'id', 'wid', 'name', 'synonyms', 'cross_references', 'comments', 'created_user', 'created_date', 'last_updated_user', 'last_updated_date', 741 ] 742 facet_fields = [] 743 ordering = ['wid'] 744 get_latest_by = 'createdDate' 745 verbose_name = 'Entry' 746 verbose_name_plural = 'Entries'
747
748 -class SpeciesComponent(Entry):
749 #parent pointer 750 parent_ptr_entry = OneToOneField(Entry, related_name='child_ptr_species_component', parent_link=True, verbose_name='Entry') 751 752 #additional fields 753 species = ForeignKey('Species', related_name='components', verbose_name='Species') 754 type = ManyToManyField('Type', blank=True, null=True, related_name='members', verbose_name='Type') 755 references = ManyToManyField('Reference', blank=True, null=True, related_name='referenced_entries', verbose_name='References') 756 757 #getters 758 @permalink
759 - def get_absolute_url(self):
760 return ('public.views.detail', (), {'species_wid':self.species.wid, 'wid': self.wid})
761
762 - def get_all_references(self):
763 return self.references.all() | Reference.objects.filter(evidence__species_component__id = self.id)
764 765 #html formatting
766 - def get_as_html_parameters(self, is_user_anonymous):
767 results = [] 768 for p in self.parameters.all(): 769 results.append('<a href="%s">%s</a>: <i>%s</i> = %s %s' % (p.get_absolute_url(), p.wid, p.name, p.value.value, p.value.units)) 770 return format_list_html(results)
771
772 - def get_as_html_comments(self, is_user_anonymous):
773 txt = self.comments 774 775 #provide links to references 776 return re.sub(r'\[(PUB_\d{4,4})(, PUB_\d{4,4})*\]', 777 lambda match: '[' + ', '.join(['<a href="%s">%s</a>' % (reverse('public.views.detail', kwargs={'species_wid':self.species.wid, 'wid': x}), x, ) for x in match.group(0)[1:-1].split(', ')]) + ']', 778 txt)
779
780 - def get_as_html_references(self, is_user_anonymous):
781 results = {} 782 for r in self.get_all_references(): 783 key = r.authors + ' ' + r.editors 784 results[key] = r.get_citation(True) 785 786 keys = results.keys() 787 keys.sort() 788 ordered_results = [] 789 for key in keys: 790 ordered_results.append(results[key]) 791 return format_list_html(ordered_results, numbered=True, force_list=True)
792 793 #meta information
794 - class Meta:
795 concrete_entry_model = False 796 fieldsets = [ 797 ('Type', {'fields': ['model_type']}), 798 ('Name', {'fields': ['wid', 'name', 'synonyms', 'cross_references']}), 799 ('Classification', {'fields': ['type']}), 800 ('Comments', {'fields': ['comments', 'references']}), 801 ('Metadata', {'fields': [{'verbose_name': 'Created', 'name': 'created_user'}, {'verbose_name': 'Last updated', 'name': 'last_updated_user'}]}), 802 ] 803 field_list = [ 804 'id', 'wid', 'name', 'synonyms', 'cross_references', 'type', 'comments', 'references', 'created_user', 'created_date', 'last_updated_user', 'last_updated_date', 805 ] 806 facet_fields = ['type'] 807 verbose_name = 'Species component' 808 verbose_name_plural = 'Species components'
809
810 -class Molecule(SpeciesComponent):
811 #parent pointer 812 parent_ptr_species_component = OneToOneField(SpeciesComponent, related_name='child_ptr_molecule', parent_link=True, verbose_name='Species component') 813 814 #additional fields 815 816 #getters
817 - def get_empirical_formula(self):
818 from public.helpers import EmpiricalFormula 819 return EmpiricalFormula()
820
821 - def get_molecular_weight(self):
823
824 - def get_atoms(self):
825 return sum(self.get_empirical_formula().values())
826
827 - def get_absorbance_factor(self):
828 return 1 / self.get_extinction_coefficient()
829 830 #html formatting
831 - def get_as_html_empirical_formula(self, is_user_anonymous):
832 return self.get_empirical_formula().get_as_html()
833
834 - def get_as_html_molecular_weight(self, is_user_anonymous):
835 return self.get_molecular_weight()
836
837 - def get_as_html_extinction_coefficient(self, is_user_anonymous):
838 return self.get_extinction_coefficient()
839
840 - def get_as_html_absorbance_factor(self, is_user_anonymous):
841 return self.get_absorbance_factor()
842
843 - def get_as_html_pi(self, is_user_anonymous):
844 if hasattr(self, 'pi'): 845 return self.pi 846 else: 847 return self.get_pi()
848
849 - def get_as_html_modification_reactions(self, is_user_anonymous):
850 results = [] 851 for obj in self.modification_reactions.all(): 852 if len(obj.reactions.all()) == 0: 853 continue 854 rxn = obj.reactions.all()[0] 855 results.append('<a href="%s">%s</a><br/>%s' % (rxn.get_absolute_url(), rxn.name, rxn.get_as_html_stoichiometry(is_user_anonymous))) 856 return format_list_html(results, vertical_spacing=True)
857
858 - def get_as_html_reaction_stoichiometry_participants(self, is_user_anonymous):
859 results = [] 860 for obj in self.reaction_stoichiometry_participants.all(): 861 if len(obj.reactions.all()) == 0: 862 continue 863 rxn = obj.reactions.all()[0] 864 results.append('<a href="%s">%s</a><br/>%s' % (rxn.get_absolute_url(), rxn.name, rxn.get_as_html_stoichiometry(is_user_anonymous))) 865 return format_list_html(list(set(results)), vertical_spacing=True)
866
867 - def get_as_html_protein_complex_biosythesis_participants(self, is_user_anonymous):
868 results = [] 869 for obj in self.protein_complex_biosythesis_participants.all(): 870 if len(obj.protein_complexes.all()) == 0: 871 continue 872 pc = obj.protein_complexes.all()[0] 873 results.append('<a href="%s">%s</a><br/>%s' % (pc.get_absolute_url(), pc.name, pc.get_as_html_biosynthesis(is_user_anonymous))) 874 return format_list_html(results)
875 876 #meta information
877 - class Meta:
878 concrete_entry_model = False 879 fieldsets = [ 880 ('Type', {'fields': ['model_type']}), 881 ('Name', {'fields': ['wid', 'name', 'synonyms', 'cross_references']}), 882 ('Classification', {'fields': ['type']}), 883 ('Function', {'fields': [ 884 {'verbose_name': 'Reaction participant', 'name':'reaction_stoichiometry_participants'}, 885 {'verbose_name': 'Complex subunit', 'name':'protein_complex_biosythesis_participants'}, 886 ]}), 887 ('Parameters', {'fields': ['parameters']}), 888 ('Comments', {'fields': ['comments', 'references']}), 889 ('Metadata', {'fields': [{'verbose_name': 'Created', 'name': 'created_user'}, {'verbose_name': 'Last updated', 'name': 'last_updated_user'}]}), 890 ] 891 field_list = [ 892 'id', 'wid', 'name', 'synonyms', 'cross_references', 'type', 'comments', 'references', 'created_user', 'created_date', 'last_updated_user', 'last_updated_date', 893 ] 894 facet_fields = ['type'] 895 verbose_name = 'Molecule' 896 verbose_name_plural = 'Molecules'
897
898 -class Protein(Molecule):
899 #parent pointer 900 parent_ptr_molecule = OneToOneField(Molecule, related_name='child_ptr_protein', parent_link=True, verbose_name='Molecule') 901 902 #additional fields 903 prosthetic_groups = ManyToManyField(ProstheticGroupParticipant, blank=True, null=True, related_name='proteins', verbose_name='Prosthetic groups') 904 chaperones = ManyToManyField('self', symmetrical=False, blank=True, null=True, related_name='chaperone_substrates', verbose_name='Chaperones') 905 dna_footprint = ForeignKey(DNAFootprint, null=True, blank=True, related_name='proteins', verbose_name='DNA footprint') 906 regulatory_rule = ForeignKey(EntryCharData, null=True, blank=True, on_delete=SET_NULL, verbose_name='Regulatory rule', related_name='+') 907 908 #html formatting
909 - def get_as_html_prosthetic_groups(self, is_user_anonymous):
910 results = [] 911 for p in self.prosthetic_groups.all(): 912 results.append(format_with_evidence(list_item = True, obj = p, txt = '(%s) <a href="%s">%s</a> [<a href="%s">%s</a>]' % (p.coefficient, p.metabolite.get_absolute_url(), p.metabolite.wid, p.compartment.get_absolute_url(), p.compartment.wid))) 913 return format_list_html(results, force_list=True)
914
915 - def get_as_html_chaperones(self, is_user_anonymous):
916 results = []; 917 for c in self.chaperones.all(): 918 results.append('<a href="%s">%s</a>' % (c.get_absolute_url(), c.wid)) 919 return format_list_html(results, comma_separated=True)
920
921 - def get_as_html_chaperone_substrates(self, is_user_anonymous):
922 results = []; 923 for p in self.chaperone_substrates.all(): 924 results.append('<a href="%s">%s</a>' % (p.get_absolute_url(), p.wid)) 925 return format_list_html(results, comma_separated=True)
926
927 - def get_as_html_dna_footprint(self, is_user_anonymous):
928 if self.dna_footprint is None: 929 return None 930 return format_with_evidence(obj = self.dna_footprint, txt = 'Length: %s (nt), Binding: %s, Region: %s' % (self.dna_footprint.length, self.dna_footprint.binding, self.dna_footprint.region))
931
932 - def get_as_html_regulatory_rule(self, is_user_anonymous):
933 if self.regulatory_rule is not None and self.regulatory_rule.value is not None and self.regulatory_rule.value != '': 934 return parse_regulatory_rule(self.regulatory_rule.value, {}, self.species.wid) 935 return ''
936
937 - def get_as_html_transcriptional_regulations(self, is_user_anonymous):
938 results = []; 939 for r in self.transcriptional_regulations.all(): 940 results.append('<a href="%s">%s</a>: <a href="%s">%s</a>' % (r.get_absolute_url(), r.wid, r.transcription_unit.get_absolute_url(), r.transcription_unit.wid)) 941 return format_list_html(results)
942
943 - def get_as_html_enzyme_participants(self, is_user_anonymous):
944 results = []; 945 for obj in self.enzyme_participants.all(): 946 if len(obj.reactions.all()) == 0: 947 continue 948 rxn = obj.reactions.all()[0] 949 results.append('<a href="%s">%s</a><br/>%s' % (rxn.get_absolute_url(), rxn.name, rxn.get_as_html_stoichiometry(is_user_anonymous))) 950 return format_list_html(results, vertical_spacing=True)
951
952 - def get_as_html_half_life(self, is_user_anonymous):
953 return self.get_half_life()
954 955 #meta information
956 - class Meta:
957 concrete_entry_model = False 958 fieldsets = [ 959 ('Type', {'fields': ['model_type']}), 960 ('Name', {'fields': ['wid', 'name', 'synonyms', 'cross_references']}), 961 ('Classification', {'fields': ['type']}), 962 ('Structure', {'fields': ['prosthetic_groups', 'chaperones', 'dna_footprint']}), 963 ('Regulation', {'fields': ['regulatory_rule']}), 964