diff --git a/ippisite/db.sqlite3 b/ippisite/db.sqlite3
deleted file mode 100644
index 54ce807e6aa9dacb5042158a9d03fbb8b299edf6..0000000000000000000000000000000000000000
Binary files a/ippisite/db.sqlite3 and /dev/null differ
diff --git a/ippisite/db.sqlite3.REMOVED.git-id b/ippisite/db.sqlite3.REMOVED.git-id
new file mode 100644
index 0000000000000000000000000000000000000000..cb9c5e1693a3710e494a1ba429eeae1858babf29
--- /dev/null
+++ b/ippisite/db.sqlite3.REMOVED.git-id
@@ -0,0 +1 @@
+067f156ca37c93920f1a15dac26337a9956fccbb
\ No newline at end of file
diff --git a/ippisite/ippidb/admin.py b/ippisite/ippidb/admin.py
index 84f0182710f92258d32496198e4c561823df6199..3b32cd020b9245306579d28bf9379a49357f6f86 100644
--- a/ippisite/ippidb/admin.py
+++ b/ippisite/ippidb/admin.py
@@ -20,13 +20,29 @@ class DomainAdmin(admin.ModelAdmin):
 
 @admin.register(ProteinDomainBoundComplex)
 class ProteinDomainBoundComplexAdmin(admin.ModelAdmin):
-    list_display = ('protein_id', 'domain_id', 'ppc_copy_nb', 'ppp_copy_nb_per_p', 'pockets_nb')
-    list_display_links = ('protein_id', 'domain_id', 'ppc_copy_nb', 'ppp_copy_nb_per_p', 'pockets_nb')
+    list_display = ('protein', 'domain', 'ppc_copy_nb', 'ppp_copy_nb_per_p')
+    list_display_links = ('protein', 'domain', 'ppc_copy_nb', 'ppp_copy_nb_per_p')
 
 @admin.register(ProteinDomainPartnerComplex)
 class ProteinDomainPartnerComplexAdmin(admin.ModelAdmin):
-    list_display = ('protein_id', 'domain_id', 'ppc_copy_nb')
-    list_display_links = ('protein_id', 'domain_id', 'ppc_copy_nb')
+    list_display = ('protein', 'domain', 'ppc_copy_nb')
+    list_display_links = ('protein', 'domain', 'ppc_copy_nb')
+
+@admin.register(Symmetry)
+class Symmetry(admin.ModelAdmin):
+    list_display = ('code', 'description')
+
+@admin.register(MDDRCompoundImport)
+class MDDRCompoundImport(admin.ModelAdmin):
+    list_display = ('mddr_name', 'dvpmt_phase', 'canonical_smile')
+
+@admin.register(Compound)
+class Compound(admin.ModelAdmin):
+    list_display = ('iupac_name', 'common_name', 'canonical_smile')
+
+@admin.register(TestActivityDescription)
+class TextActivityDescription(admin.ModelAdmin):
+    list_display = ('test_name', 'test_type', 'test_modulation_type')
 
 for model in apps.get_app_config('ippidb').models.values():
     try:
diff --git a/ippisite/ippidb/forms.py b/ippisite/ippidb/forms.py
index 955a5f723bce8600bb86e28d87dc9b057c4b7255..7b6f3a5464387d28aa106a1e543b2809b470162e 100644
--- a/ippisite/ippidb/forms.py
+++ b/ippisite/ippidb/forms.py
@@ -1,7 +1,7 @@
 from django.forms import ModelForm
 from django import forms
 from django.db import models
-from .models import Bibliography, Protein, ProteinDomainComplex, Ppi
+from .models import Bibliography, Protein, ProteinDomainComplex, Ppi, PpiComplex
 
 class IdForm(forms.Form):
 	id = forms.CharField(label="",max_length=100, widget=forms.TextInput(attrs={'placeholder': 'PubMed ID / DOI / Patent ID'}))
@@ -62,10 +62,14 @@ class ProteinDomainComplexTypeForm(forms.Form):
 class ProteinDomainComplexForm(ModelForm):
 	class Meta:
 		model = ProteinDomainComplex
-		fields = ['protein_id', 'domain_id', 'ppc_copy_nb']
-
+		fields = ['protein', 'domain', 'ppc_copy_nb']
 
 class PpiForm(ModelForm):
 	class Meta:
 		model = Ppi
-		fields = ['complex_id', 'cc_nb', 'pdb_id', 'symmetry_id']
+		fields = ['pdb_id', 'symmetry']
+
+class PpiComplexForm(ModelForm):
+	class Meta:
+		model = PpiComplex
+		fields = ['complex', 'cc_nb']
diff --git a/ippisite/ippidb/management/commands/import_v1_data.py b/ippisite/ippidb/management/commands/import_v1_data.py
index 7de942bfb3338deea3efe79377b0147a6ab3eea3..30a3cab000463365178d210dce125f41096eac6e 100644
--- a/ippisite/ippidb/management/commands/import_v1_data.py
+++ b/ippisite/ippidb/management/commands/import_v1_data.py
@@ -1,8 +1,25 @@
+import glob
+
+from django.utils import timezone
 from django.core.management import BaseCommand, CommandError
 import mysql.connector
+from pybel import readfile
 
 from ippidb.models import Bibliography, Protein, Taxonomy, MolecularFunction, \
-    Domain, ProteinDomainBoundComplex, ProteinDomainPartnerComplex
+    Domain, ProteinDomainBoundComplex, ProteinDomainPartnerComplex, Symmetry, Ppi, PpiComplex, Disease, \
+    Compound, MDDRCompoundImport, MDDRActivityClass, TestActivityDescription, CellLine
+
+class MyConverter(mysql.connector.conversion.MySQLConverter):
+
+    def row_to_python(self, row, fields):
+        row = super(MyConverter, self).row_to_python(row, fields)
+
+        def to_unicode(col):
+            if type(col) == bytearray:
+                return col.decode('utf-8')
+            return col
+
+        return[to_unicode(col) for col in row]
 
 class Command(BaseCommand):
 
@@ -31,11 +48,39 @@ class Command(BaseCommand):
             help='Flush and migrate domains',
         )
         parser.add_argument(
-            '--complexes',
+            '--symmetries',
             action='store_true',
-            dest='complexes',
+            dest='symmetries',
             default=False,
-            help='Flush and migrate complexes',
+            help='Flush and create symmetries',
+        )
+        parser.add_argument(
+            '--ppi',
+            action='store_true',
+            dest='ppi',
+            default=False,
+            help='Flush and migrate ppis and complexes',
+        )
+        parser.add_argument(
+            '--mddr',
+            action='store_true',
+            dest='mddr',
+            default=False,
+            help='Flush and import MDDR database',
+        )
+        parser.add_argument(
+            '--compound',
+            action='store_true',
+            dest='compound',
+            default=False,
+            help='Flush and migrate compounds',
+        )
+        parser.add_argument(
+            '--test-activity-description',
+            action='store_true',
+            dest='test-activity-description',
+            default=False,
+            help='Flush and migrate test activity descriptions',
         )
         parser.add_argument(
             '--stoponfail',
@@ -47,7 +92,7 @@ class Command(BaseCommand):
 
 
     def handle(self, *args, **options):
-        conn = mysql.connector.connect(host="localhost",user="root",password="ippidb", database="ippidb")
+        conn = mysql.connector.connect(converter_class=MyConverter, host="localhost", user="root", password="ippidb", database="ippidb")
         cursor = conn.cursor()
         if options['bibliographies']:
             cursor.execute("""SELECT * FROM biblio""")
@@ -110,29 +155,83 @@ class Command(BaseCommand):
                         self.stdout.write(self.style.ERROR('Failed inserting {} {}'.format(row[1], row[2])))
                 else:
                     self.stdout.write(self.style.SUCCESS('Successfully inserted {} {}'.format(row[1], row[2])))
-        if options['complexes']:
+        if options['symmetries']:
+            Symmetry.objects.all().delete()
+            self.stdout.write(self.style.SUCCESS('Successfully flushed symmetries table'))
+            rows = [
+                    ['AS', 'asymmetric'],
+                    ['C2', 'C2 symmetry'],
+                    ['D2', 'D2 symmetry'],
+                    ['C3', 'C3 symmetry'],
+                    ['D3', 'D3 symmetry'],
+                    ['C4', 'C4 symmetry'],
+                    ['D4', 'D4 symmetry'],
+                    ['C5', 'C5 symmetry'],
+                    ['D5', 'D5 symmetry'],
+            ]
+            for row in rows:
+                try:
+                    symmetry = Symmetry()
+                    symmetry.code = row[0]
+                    symmetry.description = row[1]
+                    symmetry.save()
+                except Exception as e:
+                    if options['stoponfail']:
+                        import traceback
+                        self.stderr.write(traceback.format_exc())
+                        raise CommandError('Failed inserting {} {}'.format(row[0], row[1]))
+                    else:
+                        self.stdout.write(self.style.ERROR('Failed inserting {} {}'.format(row[0], row[1])))
+                else:
+                    self.stdout.write(self.style.SUCCESS('Successfully inserted {} {}'.format(row[0], row[1])))
+        if options['ppi']:
             sql_request_string = '''
-                select distinct protein.NumUniprot, domain.PfamNumAccession  , complexe.NbCopy, cmpdAction.IDComplexeBound  from bindingSite inner join ppi on (bindingSite.IDBindingSite=ppi.IDBindingSite1) inner join complexe on (ppi.IDComplexe1=complexe.IDComplexe) left outer join cmpdAction on (complexe.IDComplexe=cmpdAction.IDComplexeBound) inner join protein on (bindingSite.IDProtein=protein.IDProtein) inner join domain on (bindingSite.IDDomain=domain.IDDomain) union select distinct protein.NumUniprot, domain.PfamNumAccession  , complexe.NbCopy, cmpdAction.IDComplexeBound  from bindingSite inner join ppi on (bindingSite.IDBindingSite=ppi.IDBindingSite2) inner join complexe on (ppi.IDComplexe2=complexe.IDComplexe) left outer join cmpdAction on (complexe.IDComplexe=cmpdAction.IDComplexeBound) inner join protein on (bindingSite.IDProtein=protein.IDProtein) inner join domain on (bindingSite.IDDomain=domain.IDDomain)'''
+select distinct protein.NumUniprot, domain.PfamNumAccession, complexe.NbCopy, cmpdAction.IDComplexeBound, bindingSiteEvidence.CodePDB, 'part1', ppi.IDPPI, disease.Disease  from bindingSite inner join ppi on (bindingSite.IDBindingSite=ppi.IDBindingSite1) inner join complexe on (ppi.IDComplexe1=complexe.IDComplexe) left outer join cmpdAction on (complexe.IDComplexe=cmpdAction.IDComplexeBound) inner join protein on (bindingSite.IDProtein=protein.IDProtein) inner join domain on (bindingSite.IDDomain=domain.IDDomain) inner join disease on (disease.IDPPI=ppi.IDPPI) left outer join bindingSiteEvidence on (ppi.IDPPI=bindingSiteEvidence.IDPPI)
+union 
+select distinct protein.NumUniprot, domain.PfamNumAccession  , complexe.NbCopy, cmpdAction.IDComplexeBound, null, 'part2', ppi.IDPPI, disease.Disease  from bindingSite inner join ppi on (bindingSite.IDBindingSite=ppi.IDBindingSite2) inner join complexe on (ppi.IDComplexe2=complexe.IDComplexe) left outer join cmpdAction on (complexe.IDComplexe=cmpdAction.IDComplexeBound) inner join protein on (bindingSite.IDProtein=protein.IDProtein) inner join domain on (bindingSite.IDDomain=domain.IDDomain) inner join disease on (disease.IDPPI=ppi.IDPPI)'''
             cursor.execute(sql_request_string)
             rows = cursor.fetchall()
             ProteinDomainBoundComplex.objects.all().delete()
             ProteinDomainPartnerComplex.objects.all().delete()
-            self.stdout.write(self.style.SUCCESS('Successfully flushed protein domain complex tables'))
+            Disease.objects.all().delete()
+            Ppi.objects.all().delete()
+            PpiComplex.objects.all().delete()
+            self.stdout.write(self.style.SUCCESS('Successfully flushed protein domain complex, PPI, and disease tables'))
+            ppi_ids_mapping = {}
             for row in rows:
                 try:
-                    if row[3]=="null":
+                    # create or retrieve Ppi object
+                    if row[5]=='part1':
+                        ppi = Ppi()
+                        disease, created = Disease.objects.get_or_create(name=row[7])
+                        ppi.pdb_id = row[4]
+                        ppi.pockets_nb = 1
+                        ppi.symmetry = Symmetry.objects.get(code='AS')
+                        ppi.save()
+                        ppi.diseases.add(disease)
+                        ppi.save()
+                        ppi_ids_mapping[row[6]]=ppi.id
+                    else:
+                        ppi = Ppi.objects.get(id=ppi_ids_mapping[row[6]])
+                    # create a complex
+                    if row[3] is None:
                         c = ProteinDomainPartnerComplex()
                     else:
                         c = ProteinDomainBoundComplex()
                     protein = Protein.objects.get(uniprot_id=row[0])
-                    c.protein_id = protein
+                    c.protein = protein
                     domain = Domain.objects.get(pfam_acc=row[1])
-                    c.domain_id = domain
+                    c.domain = domain
                     c.ppc_copy_nb = row[2]
                     if isinstance(c, ProteinDomainBoundComplex):
                         c.ppp_copy_nb_per_p = 1
-                        c.pockets_nb = 1
                     c.save()
+                    # create the PpiComplex object
+                    ppi_complex = PpiComplex()
+                    ppi_complex.ppi = ppi
+                    ppi_complex.complex = c
+                    ppi_complex.cc_nb = 1
+                    ppi_complex.save()
                 except Exception as e:
                     if options['stoponfail']:
                         import traceback
@@ -142,3 +241,129 @@ class Command(BaseCommand):
                         self.stdout.write(self.style.ERROR('Failed inserting {} {}'.format(row[0], row[1])))
                 else:
                     self.stdout.write(self.style.SUCCESS('Successfully inserted {} {}'.format(row[0], row[1])))
+            cursor.execute("""SELECT * FROM testActivityDescription""")
+            rows = cursor.fetchall()
+            TestActivityDescription.objects.all().delete()
+            CellLine.objects.all().delete()
+            self.stdout.write(self.style.SUCCESS('Successfully flushed test activity descriptions table and cell lines table'))
+            for row in rows:
+                try:
+                    tad = TestActivityDescription()
+                    cursor.execute("""select IDSource from biblio where IDBiblio={}""".format(row[2]))
+                    biblio_row = cursor.fetchone()
+                    biblio = Bibliography.objects.get(id_source=biblio_row[0])
+                    tad.biblio = biblio
+                    tad.ppi = Ppi.objects.get(id=ppi_ids_mapping[row[3]])
+                    tad.test_name = row[4]
+                    tad.test_type = row[7].upper()
+                    tad.test_modulation_type = row[8][0]
+                    tad.nb_active_compounds = row[9]
+                    if row[16] is not None:
+                        tad.cell_line, created = CellLine.objects.get_or_create(name=row[16])
+                    tad.save()
+                except Exception as e:
+                    if options['stoponfail']:
+                        import traceback
+                        self.stderr.write(traceback.format_exc())
+                        raise CommandError('Failed inserting {} {}'.format(row[1], row[2]))
+                    else:
+                        self.stdout.write(self.style.ERROR('Failed inserting {} {}'.format(row[1], row[2])))
+                else:
+                    self.stdout.write(self.style.SUCCESS('Successfully inserted {}'.format(row[2])))
+        if options['mddr']:
+            MDDRCompoundImport.objects.all().delete()
+            MDDRActivityClass.objects.all().delete()
+            self.stdout.write(self.style.SUCCESS('Successfully flushed MDDR Compound and Activity class tables'))
+            for sdf_file in glob.glob('/home/hmenager/iPPIDB/mddr20151_2d.sdf/*.sdf'):
+                for item in readfile("sdf", sdf_file):
+                    try:
+                        m = MDDRCompoundImport()
+                        m.mddr_name = item.data['MOLNAME']
+                        m.canonical_smile = str(item)
+                        m.dvpmt_phase = item.data['PHASE']
+                        m.db_import_date = timezone.now()
+                        m.save()
+                        for activity_class_name in item.data['ACTIV_CLASS'].split(','):
+                            activity_class, created = MDDRActivityClass.objects.get_or_create(name=activity_class_name)
+                            m.activity_classes.add(activity_class)
+                        m.save()
+                    except Exception as e:
+                        if options['stoponfail']:
+                            import traceback
+                            self.stderr.write(traceback.format_exc())
+                            raise CommandError('Failed inserting {}'.format(str(item)))
+                        else:
+                            self.stdout.write(self.style.ERROR('Failed inserting {}'.format(str(item))))
+                    else:
+                        self.stdout.write(self.style.SUCCESS('Successfully inserted {} {}'.format(item.data['MOLNAME'], str(item))))
+        if options['compound']:
+            cursor.execute("""SELECT * FROM compound""")
+            rows = cursor.fetchall()
+            Compound.objects.all().delete()
+            self.stdout.write(self.style.SUCCESS('Successfully flushed compound table'))
+            for row in rows:
+                try:
+                    compound = Compound()
+                    compound.canonical_smile = row[1]
+                    compound.is_macrocycle = (row[4]=='Y')
+                    compound.aromatic_ratio = row[5]
+                    compound.balaban_index = row[6]
+                    compound.fsp3 = row[7] #Csp3Ratio
+                    compound.gc_molar_refractivity=row[10] #GCMolarRefractivity
+                    compound.log_d = row[13] #LogD
+                    compound.a_log_p = row[14] #ALogP
+                    compound.mean_atom_vol_vdw = row[15] #MeanAtomVolVdW
+                    compound.molecular_weight = row[16] #MolecularWeight
+                    compound.nb_acceptor_h = row[17] #NbAcceptorH
+                    compound.nb_aliphatic_amines = row[18] #NbAliphaticsAmines
+                    compound.nb_aromatic_bonds = row[19] #NbAromaticBonds
+                    compound.nb_aromatic_ether = row[20] #NbAromaticsEther
+                    compound.nb_aromatic_sssr = row[21] #NbAromaticsSSSR
+                    compound.nb_atom = row[22] #NbAtom
+                    compound.nb_atom_non_h = row[23] #NbAtomNonH
+                    compound.nb_benzene_like_rings = row[24] #NbBenzLikeRings
+                    compound.nb_bonds = row[25] #NbBonds
+                    compound.nb_bonds_non_h = row[26] #NbBondsNonH
+                    compound.nb_br = row[27] #NbBr
+                    compound.nb_c = row[28] #NbC
+                    compound.nb_chiral_centers = row[29] #NbChiralCenters
+                    compound.nb_circuits = row[30] #NbCircuits
+                    compound.nb_cl = row[31] #NbCl
+                    compound.nb_csp2 = row[32] #NbCsp2
+                    compound.nb_csp3 = row[33] #NbCsp3
+                    compound.nb_donor_h = row[34] #NbDonorH
+                    compound.nb_double_bonds = row[35] #NbDoubleBonds
+                    compound.nb_f = row[36] #NbF
+                    compound.nb_i = row[37] #NbI
+                    compound.nb_multiple_bonds = row[38] #NbMultBonds
+                    compound.nb_n = row[39] #NbN
+                    compound.nb_o = row[40] #NbO
+                    compound.nb_rings = row[41] #NbRings
+                    compound.nb_rotatable_bonds = row[42] #NbRotatableBonds
+                    compound.randic_index = row[44] #RandicIndex
+                    compound.rdf070m = row[45] #RDF070m
+                    compound.rotatable_bond_fraction = row[46] #RotatableBondFraction
+                    compound.sum_atom_polar = row[47] #SumAtomPolar
+                    compound.sum_atom_vol_vdw = row[48] #SumAtomVolVdW
+                    compound.tpsa = row[51] #TPSA
+                    compound.ui = row[52] #Ui
+                    compound.wiener_index = row[54] #WienerIndex
+                    if row[55]!='N':
+                        compound.common_name = row[55] #CmpdNameSh
+                    compound.pubchem_id = row[56] #IdPubchem
+                    if row[57]!='N':
+                        compound.chemspider_id = row[57] #IdPubchem
+                    compound.chembl_id = row[58]
+                    compound.iupac_name = row[59]
+                    #compound.mddr_compound = row[60]
+                    #FIXME lookup MDDR to get the right ID
+                    compound.save()
+                except Exception as e:
+                    if options['stoponfail']:
+                        import traceback
+                        self.stderr.write(traceback.format_exc())
+                        raise CommandError('Failed inserting {} {}'.format(row[1], row[2]))
+                    else:
+                        self.stdout.write(self.style.ERROR('Failed inserting {} {}'.format(row[1], row[2])))
+                else:
+                    self.stdout.write(self.style.SUCCESS('Successfully inserted {} {}'.format(row[1], row[2])))
diff --git a/ippisite/ippidb/migrations/0009_auto_20170517_2020.py b/ippisite/ippidb/migrations/0009_auto_20170517_2020.py
new file mode 100644
index 0000000000000000000000000000000000000000..51fbffd8892fe1bb4048f3d395ac5d898cfd12cf
--- /dev/null
+++ b/ippisite/ippidb/migrations/0009_auto_20170517_2020.py
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-05-17 20:20
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0008_auto_20170329_1441'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='cmpdaction',
+            old_name='complex_id',
+            new_name='complex',
+        ),
+        migrations.RenameField(
+            model_name='cmpdaction',
+            old_name='compound_id',
+            new_name='compound',
+        ),
+        migrations.RenameField(
+            model_name='cmpdaction',
+            old_name='ppi_id',
+            new_name='ppi',
+        ),
+        migrations.RenameField(
+            model_name='compound',
+            old_name='mddr_compound_id',
+            new_name='mddr_compound',
+        ),
+        migrations.RenameField(
+            model_name='compoundactivityresult',
+            old_name='activity_type_id',
+            new_name='activity_type',
+        ),
+        migrations.RenameField(
+            model_name='compoundactivityresult',
+            old_name='compound_id',
+            new_name='compound',
+        ),
+        migrations.RenameField(
+            model_name='compoundactivityresult',
+            old_name='test_activity_description_id',
+            new_name='test_activity_description',
+        ),
+        migrations.RenameField(
+            model_name='compoundcytotoxicityresult',
+            old_name='compound_id',
+            new_name='compound',
+        ),
+        migrations.RenameField(
+            model_name='compoundcytotoxicityresult',
+            old_name='test_cytotoxicity_description_id',
+            new_name='test_cytotoxicity_description',
+        ),
+        migrations.RenameField(
+            model_name='compoundpkresult',
+            old_name='compound_id',
+            new_name='compound',
+        ),
+        migrations.RenameField(
+            model_name='compoundpkresult',
+            old_name='test_pk_description_id',
+            new_name='test_pk_description',
+        ),
+        migrations.RenameField(
+            model_name='disease',
+            old_name='ppi_id',
+            new_name='ppi',
+        ),
+        migrations.RenameField(
+            model_name='mddrcompoundactivityclass',
+            old_name='mddr_compound_id',
+            new_name='mddr_compound',
+        ),
+        migrations.RenameField(
+            model_name='ppi',
+            old_name='complex_id',
+            new_name='complex',
+        ),
+        migrations.RenameField(
+            model_name='ppi',
+            old_name='symmetry_id',
+            new_name='symmetry',
+        ),
+        migrations.RenameField(
+            model_name='proteindomaincomplex',
+            old_name='domain_id',
+            new_name='domain',
+        ),
+        migrations.RenameField(
+            model_name='proteindomaincomplex',
+            old_name='protein_id',
+            new_name='protein',
+        ),
+        migrations.RenameField(
+            model_name='refcompoundbiblio',
+            old_name='bibliography_id',
+            new_name='bibliography',
+        ),
+        migrations.RenameField(
+            model_name='refcompoundbiblio',
+            old_name='compound_id',
+            new_name='compound',
+        ),
+        migrations.RenameField(
+            model_name='testactivitydescription',
+            old_name='biblio_id',
+            new_name='biblio',
+        ),
+        migrations.RenameField(
+            model_name='testactivitydescription',
+            old_name='complex_id',
+            new_name='complex',
+        ),
+        migrations.RenameField(
+            model_name='testactivitydescription',
+            old_name='ppi_id',
+            new_name='ppi',
+        ),
+        migrations.RenameField(
+            model_name='testcytotoxdescription',
+            old_name='biblio_id',
+            new_name='biblio',
+        ),
+        migrations.RenameField(
+            model_name='testpkdescription',
+            old_name='biblio_id',
+            new_name='biblio',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='dh_Petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='diam_graph_non_h_petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='g_petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='ig_petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='radius_graph_non_h_petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='surface_vdw_petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='thickness_petitjean',
+        ),
+        migrations.RemoveField(
+            model_name='compound',
+            name='vol_vdw_petitjean',
+        ),
+        migrations.AlterField(
+            model_name='compound',
+            name='canonical_smile',
+            field=models.CharField(max_length=500, unique=True, verbose_name='Canonical Smile'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='cmpdaction',
+            unique_together=set([('complex', 'compound', 'pdb_id')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='compoundactivityresult',
+            unique_together=set([('compound', 'test_activity_description', 'activity_type')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='compoundcytotoxicityresult',
+            unique_together=set([('compound', 'test_cytotoxicity_description')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='compoundpkresult',
+            unique_together=set([('compound', 'test_pk_description')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='mddrcompoundactivityclass',
+            unique_together=set([('mddr_compound', 'activity_class')]),
+        ),
+        migrations.AlterUniqueTogether(
+            name='refcompoundbiblio',
+            unique_together=set([('compound', 'bibliography')]),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0010_auto_20170518_1142.py b/ippisite/ippidb/migrations/0010_auto_20170518_1142.py
new file mode 100644
index 0000000000000000000000000000000000000000..520ae2dce8e60f030c0fe11f1d218dbad969ca5f
--- /dev/null
+++ b/ippisite/ippidb/migrations/0010_auto_20170518_1142.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-18 11:42
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0009_auto_20170517_2020'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PpiComplex',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('cc_nb', models.IntegerField(default=1, verbose_name='Number of copies of the complex in the PPI')),
+                ('complex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ippidb.ProteinDomainComplex')),
+            ],
+            options={
+                'verbose_name_plural': 'Ppi complexes',
+            },
+        ),
+        migrations.RemoveField(
+            model_name='ppi',
+            name='cc_nb',
+        ),
+        migrations.RemoveField(
+            model_name='ppi',
+            name='complex',
+        ),
+        migrations.RemoveField(
+            model_name='ppi',
+            name='ppi_id',
+        ),
+        migrations.RemoveField(
+            model_name='testactivitydescription',
+            name='complex',
+        ),
+        migrations.AlterField(
+            model_name='compoundactivityresult',
+            name='activity_type',
+            field=models.CharField(choices=[('pIC50', 'pIC50 (half maximal inhibitory concentration, -log10)'), ('pEC50', 'pEC50 (half maximal effective concentration, -log10)'), ('pKd', 'pKd (dissociation constant, -log10)'), ('pKi', 'pKi (inhibition constant, -log10)')], max_length=5, verbose_name='Activity type'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='cmpdaction',
+            unique_together=set([('compound', 'activation_mode', 'pdb_id')]),
+        ),
+        migrations.RemoveField(
+            model_name='cmpdaction',
+            name='complex',
+        ),
+        migrations.AlterUniqueTogether(
+            name='cmpdaction',
+            unique_together=set([('ppi', 'compound', 'activation_mode', 'pdb_id')]),
+        ),
+        migrations.DeleteModel(
+            name='ActivityType',
+        ),
+        migrations.AddField(
+            model_name='ppicomplex',
+            name='ppi',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ippidb.Ppi'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0011_auto_20170518_1404.py b/ippisite/ippidb/migrations/0011_auto_20170518_1404.py
new file mode 100644
index 0000000000000000000000000000000000000000..9abe3d730559d93e1f8dfbcf9b6caa7543b582dd
--- /dev/null
+++ b/ippisite/ippidb/migrations/0011_auto_20170518_1404.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-18 14:04
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0010_auto_20170518_1142'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='ppi',
+            name='pdb_id',
+            field=models.CharField(max_length=4, null=True, verbose_name='PDB ID'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0012_auto_20170518_1913.py b/ippisite/ippidb/migrations/0012_auto_20170518_1913.py
new file mode 100644
index 0000000000000000000000000000000000000000..118254a486dd69356b14d2072192c33a593b080b
--- /dev/null
+++ b/ippisite/ippidb/migrations/0012_auto_20170518_1913.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-18 19:13
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0011_auto_20170518_1404'),
+    ]
+
+    operations = [
+        migrations.RenameModel(
+            old_name='CmpdAction',
+            new_name='CompoundAction',
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0013_auto_20170518_1943.py b/ippisite/ippidb/migrations/0013_auto_20170518_1943.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc133acbe966237dbe441307e21939d0b1f84aa0
--- /dev/null
+++ b/ippisite/ippidb/migrations/0013_auto_20170518_1943.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-18 19:43
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0012_auto_20170518_1913'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='disease',
+            name='disease_name',
+        ),
+        migrations.RemoveField(
+            model_name='disease',
+            name='ppi',
+        ),
+        migrations.AddField(
+            model_name='disease',
+            name='name',
+            field=models.CharField(default=None, max_length=30, unique=True, verbose_name='Disease'),
+            preserve_default=False,
+        ),
+        migrations.AddField(
+            model_name='ppi',
+            name='diseases',
+            field=models.ManyToManyField(to='ippidb.Disease'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0014_auto_20170519_1243.py b/ippisite/ippidb/migrations/0014_auto_20170519_1243.py
new file mode 100644
index 0000000000000000000000000000000000000000..3aef4f320ee659e960a6df1b4ea503dbabb02eb3
--- /dev/null
+++ b/ippisite/ippidb/migrations/0014_auto_20170519_1243.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-19 12:43
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0013_auto_20170518_1943'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='proteindomainboundcomplex',
+            name='pockets_nb',
+        ),
+        migrations.AddField(
+            model_name='ppi',
+            name='pockets_nb',
+            field=models.IntegerField(default=1, verbose_name='Total number of pockets in the complex'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0015_auto_20170519_1442.py b/ippisite/ippidb/migrations/0015_auto_20170519_1442.py
new file mode 100644
index 0000000000000000000000000000000000000000..1fca3005fce46c0e16f303ccfd014beb9c630e59
--- /dev/null
+++ b/ippisite/ippidb/migrations/0015_auto_20170519_1442.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-19 14:42
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0014_auto_20170519_1243'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='compound',
+            name='pubchem_id',
+            field=models.CharField(blank=True, max_length=10, null=True, verbose_name='Pubchem ID'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0016_auto_20170519_1453.py b/ippisite/ippidb/migrations/0016_auto_20170519_1453.py
new file mode 100644
index 0000000000000000000000000000000000000000..f33ad147e4718a43b7b49bbe90ed61510e000e8e
--- /dev/null
+++ b/ippisite/ippidb/migrations/0016_auto_20170519_1453.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-19 14:53
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0015_auto_20170519_1442'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='compound',
+            name='iupac_name',
+            field=models.CharField(blank=True, max_length=255, null=True, verbose_name='IUPAC name'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0017_auto_20170519_1500.py b/ippisite/ippidb/migrations/0017_auto_20170519_1500.py
new file mode 100644
index 0000000000000000000000000000000000000000..f62efeeaadac44fe2c45aed5f9a3e3a704165015
--- /dev/null
+++ b/ippisite/ippidb/migrations/0017_auto_20170519_1500.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-19 15:00
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0016_auto_20170519_1453'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='compound',
+            name='chembl_id',
+            field=models.CharField(blank=True, max_length=30, null=True, verbose_name='Chembl ID'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0018_auto_20170522_1501.py b/ippisite/ippidb/migrations/0018_auto_20170522_1501.py
new file mode 100644
index 0000000000000000000000000000000000000000..2986ba98f06fc35caa42f508d3876dc97978a3d9
--- /dev/null
+++ b/ippisite/ippidb/migrations/0018_auto_20170522_1501.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-22 15:01
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0017_auto_20170519_1500'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='MDDRActivityClass',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('activity_class', models.CharField(max_length=100, unique=True, verbose_name='Activity Class')),
+            ],
+            options={
+                'verbose_name_plural': 'MDDR activity classes',
+            },
+        ),
+        migrations.AlterUniqueTogether(
+            name='mddrcompoundactivityclass',
+            unique_together=set([]),
+        ),
+        migrations.RemoveField(
+            model_name='mddrcompoundactivityclass',
+            name='mddr_compound',
+        ),
+        migrations.DeleteModel(
+            name='MDDRCompoundActivityClass',
+        ),
+        migrations.AddField(
+            model_name='mddrcompoundimport',
+            name='activity_classes',
+            field=models.ManyToManyField(to='ippidb.MDDRActivityClass'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0019_auto_20170522_1931.py b/ippisite/ippidb/migrations/0019_auto_20170522_1931.py
new file mode 100644
index 0000000000000000000000000000000000000000..4b1fb04481fafdec61d4558534bdf62b5fa66ae4
--- /dev/null
+++ b/ippisite/ippidb/migrations/0019_auto_20170522_1931.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-22 19:31
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0018_auto_20170522_1501'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mddrcompoundimport',
+            name='db_import_date',
+            field=models.DateTimeField(verbose_name='MDDR release year/month'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='mddrcompoundimport',
+            unique_together=set([('mddr_name', 'dvpmt_phase', 'canonical_smile')]),
+        ),
+        migrations.RemoveField(
+            model_name='mddrcompoundimport',
+            name='mddr_compound_id',
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0020_auto_20170522_1945.py b/ippisite/ippidb/migrations/0020_auto_20170522_1945.py
new file mode 100644
index 0000000000000000000000000000000000000000..1aee4b1079c721331c6ba6b99acf0ad0e3faae37
--- /dev/null
+++ b/ippisite/ippidb/migrations/0020_auto_20170522_1945.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-22 19:45
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0019_auto_20170522_1931'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='mddractivityclass',
+            old_name='activity_class',
+            new_name='name',
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0021_auto_20170522_1949.py b/ippisite/ippidb/migrations/0021_auto_20170522_1949.py
new file mode 100644
index 0000000000000000000000000000000000000000..fcb415f24dc5e13d83edaf296c11b704dc711bfc
--- /dev/null
+++ b/ippisite/ippidb/migrations/0021_auto_20170522_1949.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-22 19:49
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0020_auto_20170522_1945'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mddrcompoundimport',
+            name='canonical_smile',
+            field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Canonical Smile'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0022_auto_20170523_1311.py b/ippisite/ippidb/migrations/0022_auto_20170523_1311.py
new file mode 100644
index 0000000000000000000000000000000000000000..24690d2087d0133ef86a6d373b7c6ddf25e7dd1d
--- /dev/null
+++ b/ippisite/ippidb/migrations/0022_auto_20170523_1311.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-23 13:11
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0021_auto_20170522_1949'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='mddrcompoundimport',
+            name='dvpmt_phase',
+            field=models.CharField(choices=[('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', '')], max_length=20, verbose_name='Development phase'),
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0023_auto_20170523_1858.py b/ippisite/ippidb/migrations/0023_auto_20170523_1858.py
new file mode 100644
index 0000000000000000000000000000000000000000..904209de44969541baed91edb7396d3cb0cbe27e
--- /dev/null
+++ b/ippisite/ippidb/migrations/0023_auto_20170523_1858.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-23 18:58
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0022_auto_20170523_1311'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='testpkdescription',
+            name='administration_mode',
+            field=models.CharField(blank=True, choices=[('IV', ''), ('PO', ''), ('IP', ''), ('SL', 'SL')], max_length=2, null=True, verbose_name='Administration mode'),
+        ),
+        migrations.DeleteModel(
+            name='AdministrationMode',
+        ),
+    ]
diff --git a/ippisite/ippidb/migrations/0024_auto_20170523_2000.py b/ippisite/ippidb/migrations/0024_auto_20170523_2000.py
new file mode 100644
index 0000000000000000000000000000000000000000..d107a840d0bc9cda5e69578a73dcba995c0968a0
--- /dev/null
+++ b/ippisite/ippidb/migrations/0024_auto_20170523_2000.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.1 on 2017-05-23 20:00
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('ippidb', '0023_auto_20170523_1858'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='testactivitydescription',
+            name='cell_line',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ippidb.CellLine'),
+        ),
+    ]
diff --git a/ippisite/ippidb/models.py b/ippisite/ippidb/models.py
index dbd49d260c49d0af56530de3bf49e79b077dcfc4..2b188a844e95c994bdab6b68ff3c26a33c801887 100644
--- a/ippisite/ippidb/models.py
+++ b/ippisite/ippidb/models.py
@@ -40,6 +40,8 @@ class Bibliography(models.Model):
     class Meta:
         verbose_name_plural = "bibliographies"
 
+    def __str__(self):
+        return '{}, {}'.format(self.source, self.id_source)
 
 
 class Taxonomy(models.Model):
@@ -119,16 +121,18 @@ class Domain(models.Model):
         return '{} ({}-{})'.format(self.pfam_acc, self.pfam_id, self.pfam_description)
 
 class ProteinDomainComplex(models.Model):
-    protein_id = models.ForeignKey('Protein')
-    domain_id = models.ForeignKey('Domain')
+    protein = models.ForeignKey('Protein')
+    domain = models.ForeignKey('Domain')
     ppc_copy_nb = models.IntegerField('Number of copies of the protein in the complex')
     
     class Meta:
         verbose_name_plural = "complexes"
 
+    def __str__(self):
+        return '{}-{}'.format(self.protein_id, self.domain_id)
+
 class ProteinDomainBoundComplex(ProteinDomainComplex):
     ppp_copy_nb_per_p = models.IntegerField('Number of copies of the protein in the pocket')
-    pockets_nb = models.IntegerField('Total number of pockets in the complex')
     class Meta:
         verbose_name_plural = "bound complexes"
     
@@ -143,21 +147,53 @@ class Symmetry(models.Model):
     class Meta:
         verbose_name_plural = "symmetries"
 
+    def __str__(self):
+        return '{} ({})'.format(self.code, self.description)
+
+
+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
+
 class Ppi(models.Model):
-    ppi_id = models.IntegerField('PPI identifier')
-    complex_id = models.ForeignKey(ProteinDomainComplex)
+    pdb_id = models.CharField('PDB ID', max_length=4, null=True)
+    pockets_nb = models.IntegerField('Total number of pockets in the complex', default=1)
+    symmetry = models.ForeignKey(Symmetry)
+    diseases = models.ManyToManyField(Disease)
+
+    def __str__(self):
+        return '{} PPI, PDB:{}'.format(self.symmetry.description, self.pdb_id or 'unknown')
+
+    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())
+
+
+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)
-    pdb_id = models.CharField('PDB ID', max_length=4)
-    symmetry_id = models.ForeignKey(Symmetry)
 
-class Disease(models.Model):
-    ppi_id = models.ForeignKey(Ppi)
-    disease_name = models.CharField('Disease', max_length=30) # is there any database/nomenclature for diseases?
+    class Meta:
+        verbose_name_plural = "Ppi complexes"
+
+    def __str__(self):
+        return 'PPI {}, Complex {} ({})'.format(self.ppi, self.complex, self.cc_nb)
 
 
 class Compound(models.Model):
     canonical_smile = models.CharField('Canonical Smile', unique=True, max_length=500)
-    #TODO index this table on canonical_smile
     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)  
@@ -202,37 +238,56 @@ class Compound(models.Model):
     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)  
-    pubchem_id = models.CharField('Pubchem ID', unique=True, max_length=10, blank=True, null=True)  
+    pubchem_id = models.CharField('Pubchem ID', max_length=10, blank=True, null=True)  
     chemspider_id = models.CharField('Chemspider ID', unique=True, max_length=10, blank=True, null=True)  
-    chembl_id = models.CharField('Chembl ID', unique=True, max_length=30, blank=True, null=True)  
-    iupac_name = models.CharField('IUPAC name', unique=True, max_length=255, blank=True, null=True)  
-    mddr_compound_id = models.ForeignKey('MDDRCompoundImport', blank=True, null=True)  
+    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)  
+    mddr_compound = models.ForeignKey('MDDRCompoundImport', blank=True, null=True)  
+
 
+class MDDRActivityClass(models.Model):
+    name = models.CharField('Activity Class', max_length=100, unique=True)  
+
+    class Meta:
+        verbose_name_plural = "MDDR activity classes"
+
+    def __str__(self):
+        return self.name
 
 class MDDRCompoundImport(models.Model):
-    mddr_compound_id = models.IntegerField('MDDR compound ID')  
+
+    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',''),
+    )
+
     mddr_name = models.CharField('MDDR name', max_length=40)  
-    dvpmt_phase = models.CharField('Development phase', max_length=20)  
-    canonical_smile = models.CharField('Canonical Smile', max_length=500, unique=True, blank=True, null=True)  
+    dvpmt_phase = models.CharField('Development phase', max_length=20, choices=MDDR_DEVELOPMENT_PHASES)  
+    canonical_smile = models.CharField('Canonical Smile', max_length=500, blank=True, null=True)  
     #TODO index this table on canonical_smile
-    db_import_date = models.DecimalField('MDDR release year/month', max_digits=6, decimal_places=0)  
+    db_import_date = models.DateTimeField('MDDR release year/month')  
+    activity_classes = models.ManyToManyField(MDDRActivityClass)
 
     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
-        unique_together = (('mddr_compound_id', 'mddr_name', 'dvpmt_phase'),)
+        unique_together = (('mddr_name', 'dvpmt_phase', 'canonical_smile'),)
         verbose_name_plural = "MDDR compound imports"
 
-
-class MDDRCompoundActivityClass(models.Model):
-    mddr_compound_id = models.ForeignKey(MDDRCompoundImport)  
-    activity_class = models.CharField('Activity Class', max_length=100)  
-
-    class Meta:
-        unique_together = (('mddr_compound_id', 'activity_class'),)
-        verbose_name_plural = "MDDR compound activity classes"
-
-
 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)
@@ -245,6 +300,9 @@ class MDDRSimilarity(models.Model):
 class CellLine(models.Model):
     name = models.CharField('Name', max_length=50, unique=True)
 
+    def __str__(self):
+        return self.name
+
 class TestActivityDescription(models.Model):
     TEST_TYPES = (
         ('BIOCH', 'Biochemical assay'),
@@ -255,17 +313,23 @@ class TestActivityDescription(models.Model):
         ('I', 'Inhibition'),
         ('S', 'Stabilization')
     )
-    complex_id = models.ForeignKey(ProteinDomainBoundComplex)
-    biblio_id = models.ForeignKey(Bibliography)
-    ppi_id = models.ForeignKey(Ppi, blank=True, null=True)  
+    biblio = models.ForeignKey(Bibliography)
+    ppi = models.ForeignKey(Ppi, blank=True, null=True)  
     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')  
-    cell_line = models.ForeignKey(CellLine)
-
-class ActivityType(models.Model):
-    name = models.CharField('Name', max_length=50, unique=True)
+    cell_line = models.ForeignKey(CellLine, blank=True, null=True)
+
+    def get_complexes(self):
+        """
+        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()
 
 class CompoundActivityResult(models.Model):
     MODULATION_TYPES = (
@@ -273,43 +337,52 @@ class CompoundActivityResult(models.Model):
         ('I', 'Inhibition'),
         ('S', 'Stabilization')
     )
-    compound_id = models.ForeignKey(Compound)  
-    test_activity_description_id = models.ForeignKey(TestActivityDescription)  
-    activity_type_id = models.ForeignKey(ActivityType)  
+    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)'),
+    )
+    compound = models.ForeignKey(Compound)  
+    test_activity_description = models.ForeignKey(TestActivityDescription)  
+    activity_type = models.CharField('Activity type', max_length=5, choices=ACTIVITY_TYPES)
     activity = models.DecimalField('Activity', max_digits=12, decimal_places=10)  
     modulation_type = models.CharField('Modulation type', max_length=1, choices=MODULATION_TYPES)  
 
     class Meta:
-        unique_together = (('compound_id', 'test_activity_description_id', 'activity_type_id'),)
+        unique_together = (('compound', 'test_activity_description', 'activity_type'),)
 
 class TestCytotoxDescription(models.Model):
-    biblio_id = models.ForeignKey(Bibliography)  
+    biblio = models.ForeignKey(Bibliography)  
     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)  
 
 class CompoundCytotoxicityResult(models.Model):
-    compound_id = models.ForeignKey(Compound)  
-    test_cytotoxicity_description_id = models.ForeignKey(TestCytotoxDescription)
+    compound = models.ForeignKey(Compound)  
+    test_cytotoxicity_description = models.ForeignKey(TestCytotoxDescription)
     toxicity = models.BooleanField('Toxicity', default=False)
 
     class Meta:
-        unique_together = (('compound_id', 'test_cytotoxicity_description_id'),)
-
-class AdministrationMode(models.Model):
-    name = models.CharField('Administration mode', max_length=20, blank=True, null=True)
+        unique_together = (('compound', 'test_cytotoxicity_description'),)
 
 class TestPKDescription(models.Model):
-    biblio_id = models.ForeignKey(Bibliography)  
+    ADMINISTRATION_MODES = (
+        ('IV', ''),
+        ('PO', ''),
+        ('IP', ''),
+        ('SL', 'SL')
+    )
+    biblio = models.ForeignKey(Bibliography)  
     test_name = models.CharField('Pharmacokinetic test name', max_length=100)  
     organism = models.ForeignKey(Taxonomy)
-    administration_mode = models.ForeignKey(AdministrationMode, blank=True, null=True)  
+    administration_mode = models.CharField('Administration mode', max_length=2, choices=ADMINISTRATION_MODES,blank=True, null=True)  
     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):
-    compound_id = models.ForeignKey(Compound)  
-    test_pk_description_id = models.ForeignKey(TestPKDescription)
+    compound = models.ForeignKey(Compound)  
+    test_pk_description = models.ForeignKey(TestPKDescription)
     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)  
@@ -320,28 +393,34 @@ class CompoundPKResult(models.Model):
     voldistribution = models.DecimalField('Volume distribution (Vd)', max_digits=5, decimal_places=2, blank=True, null=True)  
 
     class Meta:
-        unique_together = (('compound_id', 'test_pk_description_id'),)
+        unique_together = (('compound', 'test_pk_description'),)
 
 
-class CmpdAction(models.Model):
+class CompoundAction(models.Model):
     ACTIVATION_MODES = (
         ('O', 'Orthosteric'),
         ('A', 'Allosteric')
     )
-    complex_id = models.ForeignKey(ProteinDomainBoundComplex)
-    compound_id = models.ForeignKey(Compound)  
+    compound = models.ForeignKey(Compound)  
     activation_mode = models.CharField('Activation mode', max_length=1, choices=ACTIVATION_MODES)  
-    ppi_id = models.ForeignKey(Ppi)
+    ppi = models.ForeignKey(Ppi)
     pdb_id = models.CharField('PDB ID', max_length=4)
     nb_copy_compounds = models.IntegerField('Number of copies for the compound')  
 
     class Meta:
-        unique_together = (('complex_id', 'compound_id', 'pdb_id'),)
+        unique_together = (('ppi', 'compound', 'activation_mode', 'pdb_id'),)
+
+    def get_complexes(self):
+        """
+        get the complexes involved in the compound action
+        which are always the bound complexes
+        """
+        return ppi.get_ppi_bound_complexes()
 
 class RefCompoundBiblio(models.Model):
-    compound_id = models.ForeignKey(Compound)  
-    bibliography_id = models.ForeignKey(Bibliography)  
+    compound = models.ForeignKey(Compound)  
+    bibliography = models.ForeignKey(Bibliography)  
     compound_name = models.CharField('Compound name in the publication', max_length=50)  
 
     class Meta:
-        unique_together = (('compound_id', 'bibliography_id'),)
+        unique_together = (('compound', 'bibliography'),)