From ecf0680a0bb9e3718e93e914c55402492ed47fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20=20MENAGER?= <herve.menager@pasteur.fr> Date: Thu, 18 May 2017 13:54:27 +0200 Subject: [PATCH] multiple model modifications - separate Ppi into Ppi and PpiComplex classes (fixes #36) - replace ActivityType table with an enumerated type - remove complex from foreign keys when not required --- ippisite/db.sqlite3 | Bin 569344 -> 569344 bytes ippisite/ippidb/forms.py | 10 ++- .../migrations/0010_auto_20170518_1142.py | 68 ++++++++++++++++++ ippisite/ippidb/models.py | 36 +++++++--- 4 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 ippisite/ippidb/migrations/0010_auto_20170518_1142.py diff --git a/ippisite/db.sqlite3 b/ippisite/db.sqlite3 index d0f0db2af8cd80bce56b7148c24a74b09675dec6..585c05bac9b5bb713a7f404b36981aaa02f2ba17 100644 GIT binary patch delta 3966 zcmb7HeQXow8Najd{5rPJPD+CLOfD$}GH~KM+i}cjN}M>xhI5GH?`fFxoguc*jzdBc zr=`T0BoJCCSlyMX+ccQEYF)=is(D|PM#Z*%No&=r>eR87O{=C(s`kgGsw<SeXB#C8 zP^o7>ukU?6e(&=<?{n{Sp8lw1`lFVo*48-@1UU(59+DbCWUez?O&TX+xHW=9jzah6 z%c-pH%pS$YT%iXt+VT&<alFt5NB_|P99Q?kLElVBqsmtVwFa5Wx6n;Cs=Bu8ab)#3 zyA#=Lf{z)ElrhR48XJz%M#^fmP@PeVGSkw;V6+&hPLiUwnN8a)wl<pXgxrO*YYyU? zb(#zH#9wv4)@8I`7Hi3sYls@+SDM!eMstD4X*cTrM*KwcF|kE^L;J&px-6$DUf9}; z338WG9Pok0VvXTJEG>5`#T)ZreUUNjZNP|>yjfZCDGEI!Lz0;FLh*IOT2x>B-0%(M zw-M~Y19C<wzVp12EbeN10Mizc`Xbv#8MNB>kUALb;#EH^Z(jn)4X3k0d~($$xm77% z{!rGS(}fTzJ(a6IbGE&Hy{;9}>E1!=74j`In3SPtHhwUj7#&RvXONeR_3gbYp>*XP zo5kN^rhOIKRh(--EN|AFD+1$|Rho0UHtlEHt=hLW7qz=}dx?J%LCq(+dAJAtnw1UM z>SnYOEey8mTi3$H?S6R$VTT<(S5d9+!jRR?%XJqn?uM!y&K1F)s|x&S{4tz^`cGp! zS7B(otc#Be@v*p+$tpPVaz(%mv6%z^Cs=5}&{kO&%kxXsuPnUik8IQ7A0XOSiDQ~R zwX)_7`~$eeHC#D2tiYL5>F(mDi)3fS=S?tE2PQndLL!nB(oC->>7f~OIL%D;`%=bm z+BM}#hNmLQ6b&U%Cp8gHX01ifclGF0@xRZl>kLl?l941g)!)mR`<(-(i0^<AnwS{c zlk)VsXeP+yBFO_(Bp8OvgeMrEJ>ki@MvKos7dpbzLPt2{PK9V;f^!P{@626r4Ohlv zjD+m_BWYnY5@HhlnbE{x(Cdtu++&h_|KLPoAT1<fPGLM^au0FNA&WO{%f$v=9dQ$7 z+uh@y;+=ts5bYh}osPTgT`A#6s$xK8BR#H;V9u3erW_V#%4PQC+&lzGawc~=78Ftu z=cK^XeYr@;JI>O9L(qYkD;Y5IK~IpOv!ni`y`SxK_r-&gcFrH6JZWYk-p7ZSG;jCO zmPCX$PqL-~OC&VJ_?>P#<`+^-<_K?xN<Ma{J>?1YMZ$Dyk~2;uLNGEmWS?LIHWM4l z4sq#$v9KwS<I;gce4nixRoQ`*Pz}J%e`Pju?jAPi9%X~J443YkV0Q}TEbcxM@{IE# zcQOJ~7R<Q^e1qkv@2w285Hc;*|C-rK!E(+79C0C%W@yGV2y5a9GbM}!9FvZiW0K}l z92K78*@z$v1bl(W0PS}AL+&sWiiEwrg4-VqPkDR&CVv3Z&S5iS9GYTG-CrYH?rb<c znT^qWE*!EC$NiDPaV~94ghJ(F5#l?&xp2Ui3WvNycM6O%c?AyEhD)$J22zo-{{E2h z;mR4?VK7qC8Ec}pS?IP-D^1az3vZm;)*<JJY!SSDRY5#XOb}UkjGotsm-foBx-R5p zg&dW2Aq$nCD}|ZsrJ3vKTg|KQg{EkfccaBmpItL|>^2HXRyuZD{@9Ky(^%~Sw(n;9 zl`K|U!k)rzMkUq-Y#&*|Nc>jQ7R4x{#v2ixmbj(6hChQhsy|e>s>VT5@e<Lq^n<N{ z=fdAnvCfXS6!(xjF~z9tLb02C5#FHxhO*5lyhC1ttZSu42ku8P<v);ZaM~f(jDlB6 zN-uaE#Rw8T1$kHD4e~m?`E6<3jsIAN!E^ROIBgR94}xEoZZWvrdu1B0E#a5&FWS#% z$?~`MeD;1{5=KZ$p*B@?r0GU2GK6R;;&ILU8Y2#{-(z!_69e+!$!BCY(W6j^pu3PW zaM`ZX$3IoBK}+w?f=5x&dK$d0>?)87{lFHP_^)}iQ8bK!n$mwpfm(yAN~lqJyJ<6_ z=|yzN2%?@;iOOfdWk4wQ;uO{>ACRHwdSpZwBAy{?kr4>rs?KW*n}ELCCd(VdA3O;f zN-HiX-&JGUB=Q!Nzt*0|3Ozu-s~OF=CNg|{a<NH9M|0_PeC%K@n&6{WQ_R#su_o~{ zgKwz6rzFGR%@5^01v}6;H={=njIl8xz6iH?=Mb;HCu<Pjm<1~F_71#FoSp@Z;vc$k zyR;dfLbpB<Q|Fa0+zI$+mjG5PU!{6M1it}W6l$gF1Jw%)-DFmE#E>`R2-<GDyCe0m z8M4x*NU7mh`;;MXs_5U<s3iRgbE*EOJPq|*P0RY1qzpS6xnc?Q_B;huA9fWl`B#?) z-vlphC^bjHUuqY~Y_qOX8YN>Cyj^?0abXYnnfh!NqfWpq9EUUqX%^B9q)AARLOKR1 z4`~|GM3uI~r}vuf>@?P*LwJPvf@sxv)H5}2D))kU#ZkN-%gR5Ly^21Ie2(xixm`W^ zx`Ger``ghxwQSF;X_8vCwChHj#bV@b+~VF<6(+=y6QJS9;tRlXix#rqL$=&y*FtV? zsbIIX=GPYnfj$Vm=)R8^cf652F`6ti%Py%WD8!|AF7_^0?qT*7?ksAzv>x41@B)2% zJ6f1nR=riNy8_zR<8^fV9HjUQJFZnN=0m*Uz%8Po8($+%cjNuy_hx}j-B1`%>hn$Q z@Zpf2MLk_jvjjeTD8oxBXSuP&I6DU438U#@9==NPF^NslHXCO#nMM8>=(2u&yXc`z zVno!Q25TRw=6@^MvSi&BI9;~1#Hy?g_o}Y2r&=*(&$U$#telbJ%{KAfQ^2&GN1O)h zqz44L4*|$RKhTHaW;@CN8<(sdwVJIqn(MGuv&*HkyyOc^fwZ?P#q24ds=t?k6i`!r zHFa?Nk_D!`eKT?qp8=cDJ<>_DQmi=z2=Vz7C?!Up0Ju0e12&2;&Ve0bWELD&V})IC zS5`RDqc*YoG^mH?F3_S;F@F-QtBKm!{VZ=WS;~JV#Ow)RST11a!6xzcanPjNQ*Z<Q zh!f2pS_Bg3FDeNo#oA(KV~4eZvUMJ!oV=*iEb_}BZ=C>y=4hheg8mFR(Sl<cOnGH} zr9={Ax#(Cf8;^3GCX2Ph)+zpJ9>l&{<NFttYs7sgfrn^mEp!xi0=?6T&ZNW}v(>1@ zk4}R1=(j3adt?p(N&5k@?Frx!e>4YN;_OMVO4J+&t>Sfff{4i{!4@(11gLFpA;;N+ z@ccZu+0sgS`hBFwzk7FU-cfJ@eZqz2<Kme?ut8TXo6b(N(X!uWE&pE=zvBaKcS-7e zfKmkueL#N*njbEkH*j%5hzf~JTzVvUquBym5?=xF`$5}%<Pd7J`14VqRm1M&0QzAF zI8p|DAeM*;iD3!NW;N2CG)+~|Rt7-Zy@X|`FAoBB?J=~_3-m4*I(xW$R8;I<3V=qm VuhQTC=mTp@Hv_<jim!x#`G4x{>8$_& delta 2739 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%0Dk!_%0a<kaBESKb8GzS1hw(v!;jCQ z#V{eL9fPU(&J@z&&T}Y7y((&!9kj$9;OUp3UKW3TmLx563}(utcM2tI!evp?ilq6r z$@8_j?1iLUSH<VUe>{gQXpS{ElR-*|M7#3G8=bWCNNUss_50i)vRj_}_!QEjqCj!L zSy*Vt?@Xb#c`EP*>P(db=8vv`dbgWCR4V7F^p$|Wd;?|D)TkV~zl=dBubQccg?P_2 z%0!9z@$DmEIsSGOz(om`w4y}L;*-<p7g|%8i7towaw{FqiKjqDqNJZRp!qKWYD5n5 zyOT&G*Ld?J(yLK)nPd;bmbiU-aiQH73=}#4$9~_uIZ7yD?<C5=@C@QKVHjO1J2J-| z!6%&oYbKF%j#xj5zRE2~Hxq>)*29}XJpo#U7`Ox#Ns3gB1H<dkJW(!a&@*8>OMx<v zc&TAYY{Spsg<MJZk}wNeA6sXIkvsg;5UJ5~7A2{sBLv0>942sxz(E3o1cC%S1WE~X z6R04i{SJS+7P8rN52gD|m(CSyO4J)v7gZGeKzW`0oS9HuQ>4&6r1IP}ZaM*{Vmn6Q M2bx%CEldah0fhxfpa1{> diff --git a/ippisite/ippidb/forms.py b/ippisite/ippidb/forms.py index d7364dac..d7ef84c1 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'})) @@ -24,8 +24,12 @@ class ProteinDomainComplexForm(ModelForm): model = ProteinDomainComplex fields = ['protein', 'domain', 'ppc_copy_nb'] - class PpiForm(ModelForm): class Meta: model = Ppi - fields = ['complex', 'cc_nb', 'pdb_id', 'symmetry'] + fields = ['pdb_id', 'symmetry'] + +class PpiComplexForm(ModelForm): + class Meta: + model = PpiComplex + fields = ['complex', 'cc_nb'] 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 00000000..520ae2dc --- /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/models.py b/ippisite/ippidb/models.py index 226227f7..50288a93 100644 --- a/ippisite/ippidb/models.py +++ b/ippisite/ippidb/models.py @@ -150,12 +150,17 @@ class Symmetry(models.Model): return '{} ({})'.format(self.code, self.description) class Ppi(models.Model): - ppi_id = models.IntegerField('PPI identifier') - 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 = models.ForeignKey(Symmetry) +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" + class Disease(models.Model): ppi = models.ForeignKey(Ppi) disease_name = models.CharField('Disease', max_length=30) # is there any database/nomenclature for diseases? @@ -261,7 +266,6 @@ class TestActivityDescription(models.Model): ('I', 'Inhibition'), ('S', 'Stabilization') ) - complex = models.ForeignKey(ProteinDomainBoundComplex) biblio = models.ForeignKey(Bibliography) ppi = models.ForeignKey(Ppi, blank=True, null=True) test_name = models.CharField('Test name', max_length=100) @@ -270,8 +274,12 @@ class TestActivityDescription(models.Model): 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) + def get_complexes(self): + return None + # if test_modulation_type is Binding, return all bound complexes for the Ppi + # if test_modulation_type is Inhibition, return all Ppi complexes + # if test_modulation_type is Stabilization, return all bound complexes for the Ppi + # this should be added to the Ppi class as well class CompoundActivityResult(models.Model): MODULATION_TYPES = ( @@ -279,9 +287,15 @@ class CompoundActivityResult(models.Model): ('I', 'Inhibition'), ('S', 'Stabilization') ) + 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.ForeignKey(ActivityType) + 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) @@ -334,7 +348,6 @@ class CmpdAction(models.Model): ('O', 'Orthosteric'), ('A', 'Allosteric') ) - complex = models.ForeignKey(ProteinDomainBoundComplex) compound = models.ForeignKey(Compound) activation_mode = models.CharField('Activation mode', max_length=1, choices=ACTIVATION_MODES) ppi = models.ForeignKey(Ppi) @@ -342,7 +355,12 @@ class CmpdAction(models.Model): nb_copy_compounds = models.IntegerField('Number of copies for the compound') class Meta: - unique_together = (('complex', 'compound', 'pdb_id'),) + unique_together = (('ppi', 'compound', 'activation_mode', 'pdb_id'),) + + def get_complexes(self): + return None + # return all bound complexes for the Ppi + # this should be added to the Ppi class as well class RefCompoundBiblio(models.Model): compound = models.ForeignKey(Compound) -- GitLab