models.py 19 KB
Newer Older
Hervé  MENAGER's avatar
Hervé MENAGER committed
1
2
3
4
from __future__ import unicode_literals

from django.db import models

5
from .ws import get_pubmed_info, get_epo_info, get_uniprot_info, get_taxonomy_info, get_go_info, get_pfam_info
6

Hervé  MENAGER's avatar
Hervé MENAGER committed
7
8
9
10
11
12
13
14
15
16
17
class Bibliography(models.Model):
    """
    Bibliography data table
    """
    SOURCES = (
        ('PM', 'PubMed article'),
        ('PT', 'Patent')
    )
    source = models.CharField('Bibliographic type', max_length=2, choices=SOURCES)
    id_source = models.CharField('Bibliographic ID', max_length=25)
    title = models.CharField('Title', max_length=300)
18
    journal_name = models.CharField('Journal name', max_length=50, null=True)
Hervé  MENAGER's avatar
Hervé MENAGER committed
19
20
    authors_list = models.CharField('Authors list', max_length=500)
    biblio_year = models.PositiveSmallIntegerField('Year')
21
22
23
24
25
26
27
    cytotox = models.BooleanField('Cytotoxicity data', default=False)
    in_silico = models.BooleanField('in silico study performed', default=False)
    in_vitro = models.BooleanField('in vitro study performed', default=False)
    in_vivo = models.BooleanField('in vivo study performed', default=False)
    in_cellulo = models.BooleanField('in cellulo study performed', default=False)
    pharmacokinetic = models.BooleanField('pharmacokinetic study performed', default=False)
    xray = models.BooleanField('contains xray data', default=False)
Hervé  MENAGER's avatar
Hervé MENAGER committed
28
29

    def save(self, *args, **kwargs):
30
31
32
33
34
35
36
37
        if self.source == 'PM':
            info = get_pubmed_info(self.id_source)
        else:
            info = get_epo_info(self.id_source)
        self.title = info['title']
        self.journal_name = info['journal_name']
        self.authors_list = info['authors_list']
        self.biblio_year = info['biblio_year']
Hervé  MENAGER's avatar
Hervé MENAGER committed
38
39
        super(Bibliography, self).save(*args, **kwargs)

Hervé  MENAGER's avatar
Hervé MENAGER committed
40
41
42
    class Meta:
        verbose_name_plural = "bibliographies"

43
44
    def __str__(self):
        return '{}, {}'.format(self.source, self.id_source)
45

Hervé  MENAGER's avatar
Hervé MENAGER committed
46
47

class Taxonomy(models.Model):
48
    taxonomy_id = models.DecimalField('NCBI TaxID', unique=True, max_digits=9, decimal_places=0)
Hervé  MENAGER's avatar
Hervé MENAGER committed
49
    name = models.CharField('Organism name', max_length=200)
50
51
52
53
54
55

    def save(self, *args, **kwargs):
        info = get_taxonomy_info(self.taxonomy_id)
        self.name = info['scientific_name']
        super(Taxonomy, self).save(*args, **kwargs)

56
57
58
    def __str__(self):
        return self.name

Hervé  MENAGER's avatar
Hervé MENAGER committed
59
60
    class Meta:
        verbose_name_plural = "taxonomies"
Hervé  MENAGER's avatar
Hervé MENAGER committed
61
62
63
64
65

class MolecularFunction(models.Model):
    go_id = models.CharField('Gene Ontology ID', unique=True, max_length=10) # GO term id format: 'GO:0000000' 
    description = models.CharField('description', max_length=500)

66
67
68
69
70
    def save(self, *args, **kwargs):
        info = get_go_info(self.go_id)
        self.description = info['label']
        super(MolecularFunction, self).save(*args, **kwargs)

71
72
73
    def __str__(self):
        return self.description

Hervé  MENAGER's avatar
Hervé MENAGER committed
74
75
76
77
78
79
80
81
82
class Protein(models.Model):
    uniprot_id = models.CharField('Uniprot ID', unique=True, max_length=10)
    recommended_name_long = models.CharField('Uniprot Recommended Name (long)', max_length=75)
    short_name = models.CharField('Short name', max_length=50)
    gene_name = models.CharField('Gene name', unique=True, max_length=30)
    entry_name = models.CharField('Entry name', max_length=30)
    organism = models.ForeignKey('Taxonomy')
    molecular_functions = models.ManyToManyField(MolecularFunction)

83
    def save(self, *args, **kwargs):
84
        info = get_uniprot_info(self.uniprot_id)
85
        self.recommended_name_long = info['recommended_name']
86
87
88
89
90
91
92
93
94
        self.gene_name = info['gene']
        self.entry_name = info['entry_name']
        try:
            taxonomy = Taxonomy.objects.get(taxonomy_id=info['organism'])
        except Taxonomy.DoesNotExist:
            taxonomy = Taxonomy()
            taxonomy.taxonomy_id = info['organism']
            taxonomy.save()
        self.organism = taxonomy
95
        super(Protein, self).save(*args, **kwargs)
96
97
98
99
100
101
102
103
        for go_id in info['molecular_functions']:
            try:
                mol_function = MolecularFunction.objects.get(go_id=go_id)
            except MolecularFunction.DoesNotExist:
                mol_function = MolecularFunction()
                mol_function.go_id = go_id
                mol_function.save()
            self.molecular_functions.add(mol_function)
104

105
106
107
    def __str__(self):
        return '{} ({})'.format(self.uniprot_id, self.recommended_name_long)

Hervé  MENAGER's avatar
Hervé MENAGER committed
108
109
110
111
class Domain(models.Model):
    pfam_acc = models.CharField('Pfam Accession', max_length=10, unique=True) 
    pfam_id = models.CharField('Pfam Family Identifier', max_length=20) 
    pfam_description = models.CharField('Pfam Description', max_length=100)
112
113
114
115
116
117
118
    domain_family = models.CharField('Domain family', max_length=25)  #TODO: what is this field? check database contents

    def save(self, *args, **kwargs):
        info = get_pfam_info(self.pfam_acc)
        self.pfam_id = info['id']
        self.pfam_description = info['description']
        super(Domain, self).save(*args, **kwargs)
Hervé  MENAGER's avatar
Hervé MENAGER committed
119

120
121
122
    def __str__(self):
        return '{} ({}-{})'.format(self.pfam_acc, self.pfam_id, self.pfam_description)

123
class ProteinDomainComplex(models.Model):
124
125
    protein = models.ForeignKey('Protein')
    domain = models.ForeignKey('Domain')
126
127
    ppc_copy_nb = models.IntegerField('Number of copies of the protein in the complex')
    
Hervé  MENAGER's avatar
Hervé MENAGER committed
128
129
    class Meta:
        verbose_name_plural = "complexes"
130

131
132
133
    def __str__(self):
        return '{}-{}'.format(self.protein_id, self.domain_id)

134
135
136
137
class ProteinDomainBoundComplex(ProteinDomainComplex):
    ppp_copy_nb_per_p = models.IntegerField('Number of copies of the protein in the pocket')
    class Meta:
        verbose_name_plural = "bound complexes"
Hervé  MENAGER's avatar
Hervé MENAGER committed
138
    
139
140
141
class ProteinDomainPartnerComplex(ProteinDomainComplex):
    class Meta:
        verbose_name_plural = "partner complexes"
Hervé  MENAGER's avatar
Hervé MENAGER committed
142

143
144
145
class Symmetry(models.Model):
    code = models.CharField('Symmetry code', max_length=2)
    description = models.CharField('Description', max_length=300)
Hervé  MENAGER's avatar
Hervé MENAGER committed
146

147
148
149
    class Meta:
        verbose_name_plural = "symmetries"

150
151
152
    def __str__(self):
        return '{} ({})'.format(self.code, self.description)

153
154
155
156
157
158
159

class Disease(models.Model):
    name = models.CharField('Disease', max_length=30, unique=True) # is there any database/nomenclature for diseases?

    def __str__(self):
        return self.name

160
class Ppi(models.Model):
161
    pdb_id = models.CharField('PDB ID', max_length=4, null=True)
162
    pockets_nb = models.IntegerField('Total number of pockets in the complex', default=1)
163
    symmetry = models.ForeignKey(Symmetry)
164
    diseases = models.ManyToManyField(Disease)
Hervé  MENAGER's avatar
Hervé MENAGER committed
165

166
167
168
    def __str__(self):
        return '{} PPI, PDB:{}'.format(self.symmetry.description, self.pdb_id or 'unknown')

169
170
171
172
173
174
175
176
177
178
179
180
181
    def get_ppi_complexes(self):
        """
        return all ppi complexes belonging to this ppi
        """
        return PpiComplex.objects.filter(ppi=self)

    def get_ppi_bound_complexes(self):
        """
        return bound ppi complexes belonging to this ppi
        """
        #this is the less efficient query ever seen, FIXME
        return PpiComplex.objects.filter(ppi=self, complex__in=ProteinDomainBoundComplex.objects.all())

182

Hervé  MENAGER's avatar
Hervé MENAGER committed
183
184
185
186
187
188
189
190
class PpiComplex(models.Model):
    ppi = models.ForeignKey(Ppi)
    complex = models.ForeignKey(ProteinDomainComplex)
    cc_nb = models.IntegerField('Number of copies of the complex in the PPI', default=1)

    class Meta:
        verbose_name_plural = "Ppi complexes"

191
192
193
    def __str__(self):
        return 'PPI {}, Complex {} ({})'.format(self.ppi, self.complex, self.cc_nb)

Hervé  MENAGER's avatar
Hervé MENAGER committed
194

195
class Compound(models.Model):
Hervé  MENAGER's avatar
Hervé MENAGER committed
196
    canonical_smile = models.CharField('Canonical Smile', unique=True, max_length=500)
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
    is_macrocycle = models.BooleanField('Contains one or more macrocycles') 
    aromatic_ratio = models.DecimalField('Aromatic ratio', max_digits=3, decimal_places=2)
    balaban_index = models.DecimalField('Balaban index', max_digits=3, decimal_places=2)  
    fsp3 = models.DecimalField('Fsp3', max_digits=3, decimal_places=2)
    gc_molar_refractivity = models.DecimalField('GC Molar Refractivity', max_digits=5, decimal_places=2)
    log_d = models.DecimalField('LogD (Partition coefficient octanol-1/water, with pKa information)', max_digits=4, decimal_places=2)
    a_log_p = models.DecimalField('ALogP (Partition coefficient octanol-1/water)', max_digits=4, decimal_places=2)
    mean_atom_vol_vdw = models.DecimalField('Mean atom volume computed with VdW radii', max_digits=4, decimal_places=2)
    molecular_weight = models.DecimalField('Molecular weight', max_digits=6, decimal_places=2)
    nb_acceptor_h = models.IntegerField('Number of hydrogen bond acceptors')
    nb_aliphatic_amines = models.IntegerField('Number of aliphatics amines')
    nb_aromatic_bonds = models.IntegerField('Number of aromatic bonds')
    nb_aromatic_ether = models.IntegerField('Number of aromatic ethers')
    nb_aromatic_sssr = models.IntegerField('Number of aromatic Smallest Set of System Rings (SSSR)')
    nb_atom = models.IntegerField('Number of atoms') 
    nb_atom_non_h = models.IntegerField('Number of non hydrogen atoms') 
    nb_benzene_like_rings = models.IntegerField('Number of benzene-like rings')
    nb_bonds = models.IntegerField('Number of bonds')
    nb_bonds_non_h = models.IntegerField('Number of bonds not involving a hydrogen')  
    nb_br = models.IntegerField('Number of Bromine atoms')  
    nb_c = models.IntegerField('Number of Carbon atoms')  
    nb_chiral_centers = models.IntegerField('Number of chiral centers')  
    nb_circuits = models.IntegerField('Number of circuits')  
    nb_cl = models.IntegerField('Number of Chlorine atoms')  
    nb_csp2 = models.IntegerField('Number of sp2-hybridized carbon atoms')  
    nb_csp3 = models.IntegerField('Number of sp3-hybridized carbon atoms')  
    nb_donor_h = models.IntegerField('Number of hydrogen bond donors')  
    nb_double_bonds = models.IntegerField('Number of double bonds')  
    nb_f = models.IntegerField('Number of fluorine atoms')  
    nb_i = models.IntegerField('Number of iodine atoms')  
    nb_multiple_bonds = models.IntegerField('Number of multiple bonds')  
    nb_n = models.IntegerField('Number of nitrogen atoms')
    nb_o = models.IntegerField('Number of oxygen atoms')  
    nb_rings = models.IntegerField('Number of rings')  
    nb_rotatable_bonds = models.IntegerField('Number of rotatable bonds')  
    randic_index = models.DecimalField('Randic index', max_digits=4, decimal_places=2)  
    rdf070m = models.DecimalField('RDF070m, radial distribution function weighted by the atomic masses at 7Å', max_digits=5, decimal_places=2)  
    rotatable_bond_fraction = models.DecimalField('Fraction of rotatable bonds', max_digits=3, decimal_places=2)  
    sum_atom_polar = models.DecimalField('Sum of atomic polarizabilities', max_digits=5, decimal_places=2)  
    sum_atom_vol_vdw = models.DecimalField('Sum of atom volumes computed with VdW radii', max_digits=6, decimal_places=2)  
    tpsa = models.DecimalField('Topological Polar Surface Area (TPSA)', max_digits=5, decimal_places=2)  
    ui = models.DecimalField('Unsaturation index', max_digits=4, decimal_places=2)  
    wiener_index = models.IntegerField('Wiener index')  
    common_name = models.CharField('Common name', unique=True, max_length=20, blank=True, null=True)  
241
    pubchem_id = models.CharField('Pubchem ID', max_length=10, blank=True, null=True)  
242
    chemspider_id = models.CharField('Chemspider ID', unique=True, max_length=10, blank=True, null=True)  
243
244
    chembl_id = models.CharField('Chembl ID', max_length=30, blank=True, null=True)  
    iupac_name = models.CharField('IUPAC name', max_length=255, blank=True, null=True)  
245
    mddr_compound = models.ForeignKey('MDDRCompoundImport', blank=True, null=True)  
246
247


248
class MDDRActivityClass(models.Model):
249
    name = models.CharField('Activity Class', max_length=100, unique=True)  
250
251
252
253
254

    class Meta:
        verbose_name_plural = "MDDR activity classes"

    def __str__(self):
255
        return self.name
256

257
class MDDRCompoundImport(models.Model):
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

    MDDR_DEVELOPMENT_PHASES = (
        ('Biological Testing',''),
        ('Preclinical',''),
        ('Phase III',''),
        ('Phase II',''),
        ('Phase I/II',''),
        ('Phase I',''),
        ('Launched',''),
        ('Pre-Registered',''),
        ('Not Applicable',''),
        ('Discontinued',''),
        ('Clinical',''),
        ('Withdrawn',''),
        ('Registered',''),
        ('Not Determined',''),
        ('Phase II/III',''),
        ('IND Filed',''),
    )

278
    mddr_name = models.CharField('MDDR name', max_length=40)  
279
    dvpmt_phase = models.CharField('Development phase', max_length=20, choices=MDDR_DEVELOPMENT_PHASES)  
280
    canonical_smile = models.CharField('Canonical Smile', max_length=500, blank=True, null=True)  
281
    #TODO index this table on canonical_smile
282
    db_import_date = models.DateTimeField('MDDR release year/month')  
283
    activity_classes = models.ManyToManyField(MDDRActivityClass)
Hervé  MENAGER's avatar
Hervé MENAGER committed
284

285
286
287
    class Meta:
        # over multiple releases of the MDDR database, the same compound can evolve in its development phase
        # the same compound can have different names and development phases in the same MDDR release
288
        unique_together = (('mddr_name', 'dvpmt_phase', 'canonical_smile'),)
Hervé  MENAGER's avatar
Hervé MENAGER committed
289
        verbose_name_plural = "MDDR compound imports"
Hervé  MENAGER's avatar
Hervé MENAGER committed
290

291
292
293
    def __str__(self):
        return "{}, {}".format(self.mddr_name, self.dvpmt_phase)

294
295
296
297
class MDDRSimilarity(models.Model):
    canonical_smile_ippidb = models.CharField('Canonical Smile for IPPIDB compound', max_length=500, unique=True, blank=True, null=True)
    canonical_smile_mddr = models.CharField('Canonical Smile for MDDR Compound', max_length=500, unique=True, blank=True, null=True)
    tanimoto = models.DecimalField('Tanimoto', max_digits=6, decimal_places=5)  
Hervé  MENAGER's avatar
Hervé MENAGER committed
298
299

    class Meta:
300
        unique_together = (('canonical_smile_ippidb', 'canonical_smile_mddr'),)
Hervé  MENAGER's avatar
Hervé MENAGER committed
301
        verbose_name_plural = "MDDR similarities"
302

303
304
class CellLine(models.Model):
    name = models.CharField('Name', max_length=50, unique=True)
Hervé  MENAGER's avatar
Hervé MENAGER committed
305

306
307
308
    def __str__(self):
        return self.name

309
310
311
312
313
314
315
316
317
318
class TestActivityDescription(models.Model):
    TEST_TYPES = (
        ('BIOCH', 'Biochemical assay'),
        ('CELL', 'Cellular assay')
    )
    TEST_MODULATION_TYPES = (
        ('B', 'Binding'),
        ('I', 'Inhibition'),
        ('S', 'Stabilization')
    )
319
320
    biblio = models.ForeignKey(Bibliography)
    ppi = models.ForeignKey(Ppi, blank=True, null=True)  
321
322
323
324
    test_name = models.CharField('Test name', max_length=100)  
    test_type = models.CharField('Test type', max_length=5, choices=TEST_TYPES)
    test_modulation_type = models.CharField('Test modulation type', max_length=1, choices=TEST_MODULATION_TYPES)
    nb_active_compounds = models.IntegerField('Total number of active compounds')  
325
    cell_line = models.ForeignKey(CellLine, blank=True, null=True)
326

Hervé  MENAGER's avatar
Hervé MENAGER committed
327
    def get_complexes(self):
328
329
330
331
332
333
334
335
        """
        get the complexes tested for this PPI
        depends on the modulation type
        """
        if self.test_modulation_type=='I':
            return self.ppi.get_ppi_complexes()
        else:
            return self.ppi.get_ppi_bound_complexes()
336
337

class CompoundActivityResult(models.Model):
338
339
340
341
342
    MODULATION_TYPES = (
        ('B', 'Binding'),
        ('I', 'Inhibition'),
        ('S', 'Stabilization')
    )
Hervé  MENAGER's avatar
Hervé MENAGER committed
343
344
345
346
347
348
    ACTIVITY_TYPES = (
        ('pIC50','pIC50 (half maximal inhibitory concentration, -log10)'),
        ('pEC50','pEC50 (half maximal effective concentration, -log10)'),
        ('pKd','pKd (dissociation constant, -log10)'),
        ('pKi','pKi (inhibition constant, -log10)'),
    )
349
350
    compound = models.ForeignKey(Compound)  
    test_activity_description = models.ForeignKey(TestActivityDescription)  
Hervé  MENAGER's avatar
Hervé MENAGER committed
351
    activity_type = models.CharField('Activity type', max_length=5, choices=ACTIVITY_TYPES)
352
    activity = models.DecimalField('Activity', max_digits=12, decimal_places=10)  
353
    modulation_type = models.CharField('Modulation type', max_length=1, choices=MODULATION_TYPES)  
Hervé  MENAGER's avatar
Hervé MENAGER committed
354
355

    class Meta:
356
        unique_together = (('compound', 'test_activity_description', 'activity_type'),)
Hervé  MENAGER's avatar
Hervé MENAGER committed
357

358
class TestCytotoxDescription(models.Model):
359
    biblio = models.ForeignKey(Bibliography)  
360
361
362
    test_name = models.CharField('Cytotoxicity test name', max_length=100)  
    cell_line = models.ForeignKey(CellLine)
    compound_concentration = models.DecimalField('Compound concentration in μM', max_digits=7, decimal_places=3, blank=True, null=True)  
Hervé  MENAGER's avatar
Hervé MENAGER committed
363

364
class CompoundCytotoxicityResult(models.Model):
365
366
    compound = models.ForeignKey(Compound)  
    test_cytotoxicity_description = models.ForeignKey(TestCytotoxDescription)
367
    toxicity = models.BooleanField('Toxicity', default=False)
Hervé  MENAGER's avatar
Hervé MENAGER committed
368
369

    class Meta:
370
        unique_together = (('compound', 'test_cytotoxicity_description'),)
371
372

class TestPKDescription(models.Model):
373
374
375
376
377
378
    ADMINISTRATION_MODES = (
        ('IV', ''),
        ('PO', ''),
        ('IP', ''),
        ('SL', 'SL')
    )
379
    biblio = models.ForeignKey(Bibliography)  
380
381
    test_name = models.CharField('Pharmacokinetic test name', max_length=100)  
    organism = models.ForeignKey(Taxonomy)
382
    administration_mode = models.CharField('Administration mode', max_length=2, choices=ADMINISTRATION_MODES,blank=True, null=True)  
383
384
385
386
    dose = models.DecimalField('Dose in mg/kg', max_digits=7, decimal_places=4, blank=True, null=True)  
    dose_interval = models.IntegerField('Dose interval, in hours', blank=True, null=True)  

class CompoundPKResult(models.Model):
387
388
    compound = models.ForeignKey(Compound)  
    test_pk_description = models.ForeignKey(TestPKDescription)
389
390
391
392
393
394
395
396
    tolerated = models.NullBooleanField('Tolerated', null=True)
    auc = models.IntegerField('Area under curve (ng.mL-1.hr)', blank=True, null=True)  
    clearance = models.DecimalField('Clearance (mL/hr)', max_digits=7, decimal_places=3, blank=True, null=True)  
    cmax = models.DecimalField('Maximal concentration (ng/mL)', max_digits=7, decimal_places=3, blank=True, null=True)  
    oral_bioavailability = models.IntegerField('Oral Bioavailability (%F)', blank=True, null=True)  
    t_demi = models.IntegerField('t½', blank=True, null=True)  
    t_max = models.IntegerField('tmax', blank=True, null=True)  
    voldistribution = models.DecimalField('Volume distribution (Vd)', max_digits=5, decimal_places=2, blank=True, null=True)  
Hervé  MENAGER's avatar
Hervé MENAGER committed
397
398

    class Meta:
399
        unique_together = (('compound', 'test_pk_description'),)
Hervé  MENAGER's avatar
Hervé MENAGER committed
400
401


402
class CompoundAction(models.Model):
403
404
    ACTIVATION_MODES = (
        ('O', 'Orthosteric'),
405
        ('A', 'Allosteric')
406
    )
407
    compound = models.ForeignKey(Compound)  
408
    activation_mode = models.CharField('Activation mode', max_length=1, choices=ACTIVATION_MODES)  
409
    ppi = models.ForeignKey(Ppi)
410
411
    pdb_id = models.CharField('PDB ID', max_length=4)
    nb_copy_compounds = models.IntegerField('Number of copies for the compound')  
Hervé  MENAGER's avatar
Hervé MENAGER committed
412
413

    class Meta:
Hervé  MENAGER's avatar
Hervé MENAGER committed
414
415
416
        unique_together = (('ppi', 'compound', 'activation_mode', 'pdb_id'),)

    def get_complexes(self):
417
418
419
420
421
        """
        get the complexes involved in the compound action
        which are always the bound complexes
        """
        return ppi.get_ppi_bound_complexes()
Hervé  MENAGER's avatar
Hervé MENAGER committed
422

423
class RefCompoundBiblio(models.Model):
424
425
    compound = models.ForeignKey(Compound)  
    bibliography = models.ForeignKey(Bibliography)  
426
    compound_name = models.CharField('Compound name in the publication', max_length=50)  
Hervé  MENAGER's avatar
Hervé MENAGER committed
427
428

    class Meta:
429
        unique_together = (('compound', 'bibliography'),)