diff --git a/ippisite/ippidb/tests/__init__.py b/ippisite/ippidb/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_2380_002_e-9.yaml b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_2380_002_e-9.yaml new file mode 100644 index 0000000000000000000000000000000000000000..842a1db3b1005febf7a9428c0043816b08c0011d --- /dev/null +++ b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_2380_002_e-9.yaml @@ -0,0 +1,43 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 2380.002 + activity_type: pIC50 + activity_unit: 1e-9 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '2' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: PF05053 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: O00255 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF05965 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: CCC +diseases: +- ;toto;1; +- ;titi;sfddf; +- ;tata;3232; +family_name: Menin (PF05053) +id_source: '15072770' +in_silico: true +pdb_id: 3u85 +source: PM diff --git a/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_4_9846073_e-6.yaml b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_4_9846073_e-6.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0a2e61b2d45c324e1ce6c7f00592a3ce046abe87 --- /dev/null +++ b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_4_9846073_e-6.yaml @@ -0,0 +1,43 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 4.9846073 + activity_type: pIC50 + activity_unit: 1e-6 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '2' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: PF05053 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: O00255 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF05965 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: CCC +diseases: +- ;toto;1; +- ;titi;sfddf; +- ;tata;3232; +family_name: Menin (PF05053) +id_source: '15072770' +in_silico: true +pdb_id: 3u85 +source: PM diff --git a/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_639_39406_e-3.yaml b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_639_39406_e-3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..459d512dd9b0f3557f5bfb3e50dcaf0140eae7a0 --- /dev/null +++ b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_639_39406_e-3.yaml @@ -0,0 +1,43 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 639.39406 + activity_type: pIC50 + activity_unit: 1e-3 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '2' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: PF05053 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: O00255 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF05965 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: CCC +diseases: +- ;toto;1; +- ;titi;sfddf; +- ;tata;3232; +family_name: Menin (PF05053) +id_source: '15072770' +in_silico: true +pdb_id: 3u85 +source: PM diff --git a/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_6_85_e-0.yaml b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_6_85_e-0.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b61f094338e568128575ea14d577a93e7f9b7088 --- /dev/null +++ b/ippisite/ippidb/tests/test_activity_computation_and_storage_pIC50_6_85_e-0.yaml @@ -0,0 +1,43 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 6.85 + activity_type: pIC50 + activity_unit: 1e-0 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '2' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: PF05053 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: O00255 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF05965 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: CCC +diseases: +- ;toto;1; +- ;titi;sfddf; +- ;tata;3232; +family_name: Menin (PF05053) +id_source: '15072770' +in_silico: true +pdb_id: 3u85 +source: PM diff --git a/ippisite/ippidb/tests/test_basic_entry.yaml b/ippisite/ippidb/tests/test_basic_entry.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c1baf06500ae82f25c3fa1063b0373433336c7f8 --- /dev/null +++ b/ippisite/ippidb/tests/test_basic_entry.yaml @@ -0,0 +1,43 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 6.85 + activity_type: pIC50 + activity_unit: 1e-6 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '2' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: PF05053 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: O00255 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF05965 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: CCC +diseases: +- ;toto;1; +- ;titi;sfddf; +- ;tata;3232; +family_name: Menin (PF05053) +id_source: '15072770' +in_silico: true +pdb_id: 3u85 +source: PM diff --git a/ippisite/ippidb/tests/test_complex_no_pfam.yaml b/ippisite/ippidb/tests/test_complex_no_pfam.yaml new file mode 100644 index 0000000000000000000000000000000000000000..03e6c2cf616df4f93a3aefccbeb06d7a7c47b682 --- /dev/null +++ b/ippisite/ippidb/tests/test_complex_no_pfam.yaml @@ -0,0 +1,40 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 6.85 + activity_type: pIC50 + activity_unit: 1e-3 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '1' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF00400 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: P61964 +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: null + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: C +diseases: [] +family_name: WD40 (PF00400) +id_source: '26958703' +in_silico: true +pdb_id: 3emh +source: PM diff --git a/ippisite/ippidb/tests/test_entry_28.yaml b/ippisite/ippidb/tests/test_entry_28.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d22b2cb8274900a4497432bd1b2b68df806fe256 --- /dev/null +++ b/ippisite/ippidb/tests/test_entry_28.yaml @@ -0,0 +1,41 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 3.59 + activity_type: pKd + activity_unit: 1e-9 + compound_name: '2' + modulation_type: I + is_primary: true + nb_active_compounds: 1 + protein_bound_construct: F + protein_complex: '1' + test_modulation_type: B + test_name: biolayer interferometry assay + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: null + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q60795 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF01344 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q9Z2X8 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: '' + compound_name: '2' + molecule_smiles: c1cc2c(cc1)c(ccc2N(S(=O)(=O)c1ccc(cc1)OC)CC(=O)O)N(S(=O)(=O)c1ccc(cc1)OC)CC(=O)O +diseases: +- ;cancer;MONDO:0004992; +family_name: KEAP1 / NRF2 +id_source: '24512214 ' +in_vitro: true +pdb_id: 3wn7 +source: PM diff --git a/ippisite/ippidb/tests/test_simple_heterodimer.yaml b/ippisite/ippidb/tests/test_simple_heterodimer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c3c5f7c7e926a4ff22dea3dc30fe61645921323b --- /dev/null +++ b/ippisite/ippidb/tests/test_simple_heterodimer.yaml @@ -0,0 +1,51 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 6.85 + activity_type: pIC50 + activity_unit: 1e-3 + compound_name: 16d + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '1' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF00400 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: P61964 +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: null + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: officially 16d + compound_name: 16d + ligand_id: ABC + molecule_smiles: CN1CCN(CC1)C1=C(C=C(C=C1)C1=CC(=CC=C1)CN1CCOCC1)NC(=O)C1=CNC(C=C1C(F)(F)F)=O +- common_name: officially 14a + compound_name: 14a + is_macrocycle: false + molecule_iupac: N-(2-(4-Methylpiperazin-1-yl)-5-nitrophenyl)-6-oxo-4-(trifluoromethyl)-1,6-dihydropyridine-3-carboxamide +- common_name: stuck + compound_name: lili + is_macrocycle: false + molecule_smiles: C1CCC1 +diseases: [] +family_name: WD40 (PF00400) +id_source: '26958703' +in_silico: true +in_vitro: true +pdb_id: 3emh +source: PM +xray: true diff --git a/ippisite/ippidb/tests/test_simple_stabilized_heterodimer.yaml b/ippisite/ippidb/tests/test_simple_stabilized_heterodimer.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1d79dd1e8f4868957a4919195a9feecb6eae92fd --- /dev/null +++ b/ippisite/ippidb/tests/test_simple_stabilized_heterodimer.yaml @@ -0,0 +1,44 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 0.25 + activity_type: pIC50 + activity_unit: 1e-6 + compound_name: FC + modulation_type: S + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '1' + test_modulation_type: I + test_name: pull down + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF00244 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: P31947 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: null + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: P03372 +complexChoice: stabilized +complexType: Stab_Hetero2merAB +compounds: +- common_name: fusicoccin + compound_name: FC + ligand_id: FSC + molecule_smiles: COC[C@H]1CC[C@H]2[C@@H](C)[C@@H](O)[C@H](O[C@H]3O[C@H](COC(C)(C)C=C)[C@@H](O)[C@H](OC(C)=O)[C@H]3O)C3=C(C[C@H](O)[C@]3(C)\C=C1/2)[C@H](C)COC(C)=O +diseases: +- ;breast cancer;MONDO:0007254; +family_name: 14-3-3 / ER +id_source: '23676274' +in_cellulo: true +in_vitro: true +pdb_id: 4jc3 +source: PM +xray: true diff --git a/ippisite/ippidb/tests/test_stabilizer_204.yaml b/ippisite/ippidb/tests/test_stabilizer_204.yaml new file mode 100644 index 0000000000000000000000000000000000000000..59cd362476b8f5d6fe7f041faa8fec36b90a23c2 --- /dev/null +++ b/ippisite/ippidb/tests/test_stabilizer_204.yaml @@ -0,0 +1,40 @@ +source: PM +id_source: '23808890' +in_vitro: true +xray: true +pdb_id: 4ihl +complexChoice: stabilized +complexType: Stab_Hetero2merAB +complex: + - cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF00244 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: P63104 + - cc_nb: 1 + complex_type: Bound + domain_pfam_acc: null + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: P04049 +family_name: 14-3-3 / C-RAF +diseases: + - ;melanoma;(MONDO_0005105); +compounds: + - common_name: Cotylenin A + compound_name: Cotylenin A + molecule_smiles: COC[C@H]1O[C@H](O[C@H]2[C@H](O)[C@H](C)[C@@H]3CC[C@](O)(COC)C3=C[C@@]3(C)CCC(C(C)C)=C23)[C@H](O)[C@H]2O[C@H]3O[C@@]12O[C@@]3(C)[C@@H]1CO1 +activity_tests: +- test_type: BIOCH + test_name: Fluorescence Polarization + protein_bound_construct: F + test_modulation_type: B + nb_active_compounds: 1 + protein_complex: '1' + compound_activity_results: + - activity_mol: 20 + activity_type: pKd + activity_unit: 1e-10 + compound_name: Cotylenin A + modulation_type: S \ No newline at end of file diff --git a/ippisite/ippidb/tests/test_with_all_tests.yaml b/ippisite/ippidb/tests/test_with_all_tests.yaml new file mode 100644 index 0000000000000000000000000000000000000000..3b8fefd311af4176a2e92deb7d9f5cef535b9cd5 --- /dev/null +++ b/ippisite/ippidb/tests/test_with_all_tests.yaml @@ -0,0 +1,72 @@ +activity_tests: +- cell_line_name: '' + compound_activity_results: + - activity_mol: 6.85 + activity_type: pIC50 + activity_unit: 1e-3 + compound_name: toto + modulation_type: I + is_primary: true + nb_active_compounds: 2 + protein_bound_construct: F + protein_complex: '2' + test_modulation_type: I + test_name: test + test_type: BIOCH +complex: +- cc_nb: 1 + complex_type: Partner + domain_pfam_acc: PF05053 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: '' + uniprot_id: O00255 +- cc_nb: 1 + complex_type: Bound + domain_pfam_acc: PF05965 + ppc_copy_nb: 1 + ppp_copy_nb_per_p: 1 + uniprot_id: Q03164 +complexChoice: inhibited +complexType: Inhib_Hetero2merAB +compounds: +- common_name: super + compound_name: toto + molecule_smiles: CCC +cytotox: true +cytotox_tests: +- cell_line_name: '1043SK ' + compound_concentration: '2' + compound_cytotox_results: + - compound_name: toto + toxicity: 'True' + test_name: test +diseases: +- '' +- ;titi;sfddf; +- ;tutu;5456456; +- ;tutu;5456456; +- '' +family_name: Menin (PF05053) +id_source: '15072770' +in_silico: true +pdb_id: 3u85 +pharmacokinetic: true +pharmacokinetic_tests: +- administration_mode: IV + cell_line_name: '1043SK ' + compound_pk_results: + - auc: '1' + c_max: '5.2' + clearance: '1.2' + compound_name: toto + oral_bioavailability: '22' + t_demi: '5' + t_max: '5' + tolerated: 'True' + voldistribution: '5.5' + concentration: '1.2' + dose: '3.5' + dose_interval: '2' + organism: 1 + test_name: test +source: PM diff --git a/ippisite/ippidb/tests/tests_contribute.py b/ippisite/ippidb/tests/tests_contribute.py index cd123dda54d8d512d8a3e4838a42199bdc60101d..5ecf48a3b84c3d319a703bbc6b52bce956c8bc9a 100644 --- a/ippisite/ippidb/tests/tests_contribute.py +++ b/ippisite/ippidb/tests/tests_contribute.py @@ -1,23 +1,15 @@ """ iPPI-DB contribution module tests """ -import math -from decimal import Decimal -from tempfile import NamedTemporaryFile - from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType -from django.db.models import Value -from django.db.models.functions import Concat from django.test import TestCase from django.urls import reverse from ippidb import models -from ippidb.admin import grant_contribution_permission from ippidb.forms import PDBForm -from ippidb.ws import convert_iupac_to_smiles, get_uniprot_info from live_settings import live_settings import requests_cache @@ -125,1046 +117,10 @@ class PDBFormTestCase(TestCase): def test_invalid_nouniprot(self): form = PDBForm({"pdb_id": "3wn8"}) self.assertFalse( - form.is_valid(), "PDB code with no Uniprot mapping (3wn8) should be rejected" + form.is_valid(), + "PDB code with no Uniprot mapping (3wn8) should be rejected", ) self.assertTrue( form.has_error("pdb_id", code="no_mapping"), "PDB code with no Uniprot mapping (3wn8) should be rejected with no_mappping error code", ) - - -class ContributionViewsTestCase(TestCase): - """ - This class tests the Contribution Wizard by interacting with it - in the same way a user would do. - """ - - def setUp(self): - login = "contributor" - password = "12345" - User = get_user_model() - User.objects.create_user(username=login, password=password) - self.client.login(username=login, password=password) - grant_contribution_permission(None, None, User.objects.all()) - symmetry = models.Symmetry() - symmetry.code = "AS" - symmetry.description = "asymmetric" - symmetry.save() - models.Taxonomy.objects.create(taxonomy_id=9606, name="Homo sapiens") - - @staticmethod - def get_step_url(step_id): - step_url = reverse("admin-session-add") - if step_id is not None: - step_url += step_id + "/" - return step_url - - def _process_contribution_wizard(self, entry_data): - def compute_ppi_name(entry_data): - bound_uniprots = [ - item["uniprot_id"] - for item in entry_data["complex"] - if item["complex_type"] == "Bound" - ] - partner_uniprots = [ - item["uniprot_id"] - for item in entry_data["complex"] - if item["complex_type"] == "Partner" - ] - bound_protein_names = [ - get_uniprot_info(uniprot_id)["short_name"] - for uniprot_id in bound_uniprots - ] - partner_protein_names = [ - get_uniprot_info(uniprot_id)["short_name"] - for uniprot_id in partner_uniprots - ] - bound_protein_names.sort() - partner_protein_names.sort() - bound_str = ",".join(bound_protein_names) - partner_str = ",".join(partner_protein_names) - name = bound_str - if partner_str != "": - name += " / " + partner_str - return name - - future_expected_equals = [ - ( - models.Bibliography.objects.count, - models.Bibliography.objects.count() + 1, - "Bibliography count", - ), - ( - models.Contribution.objects.count, - models.Contribution.objects.count() + 1, - "Contribution count", - ), - ( - models.Compound.objects.count, - models.Compound.objects.count() + len(entry_data["compounds"]), - "Compounds count", - ), - ( - models.Compound.objects.validated().count, - models.Compound.objects.validated().count(), - "Validated Compounds count should remains the same", - ), - ( - models.CompoundAction.objects.count, - models.CompoundAction.objects.count() + len(entry_data["compounds"]), - "Compound Actions count", - ), - ( - models.TestActivityDescription.objects.count, - models.TestActivityDescription.objects.count() - + len(entry_data.get("activity_tests", [])), - "Activity tests count", - ), - ( - models.TestCytotoxDescription.objects.count, - models.TestCytotoxDescription.objects.count() - + len(entry_data.get("cytotox_tests", [])), - "Cytotox tests count", - ), - ( - models.TestPKDescription.objects.count, - models.TestPKDescription.objects.count() - + len(entry_data.get("pharmacokinetic_tests", [])), - "Pharmacokinetic tests count", - ), - ( - lambda: models.PpiFamily.objects.get().name, - entry_data.get("family_name"), - "Ppi family name", - ), - ( - lambda: models.Ppi.objects.get().family.name, - entry_data.get("family_name"), - "Ppi family name from PPI", - ), - ( - lambda: models.Ppi.objects.get().name, - compute_ppi_name(entry_data), - "Ppi name", - ), - ] - for activity_tests in entry_data["activity_tests"]: - for compound_activity_results in activity_tests[ - "compound_activity_results" - ]: - if ( - "activity_mol" in compound_activity_results - and compound_activity_results["activity_mol"] != "" - ): - compound_activity_results["activity"] = -math.log10( - Decimal(str(compound_activity_results["activity_mol"])) - * Decimal(str(compound_activity_results["activity_unit"])) - ) - - self._process_contribution_wizard_without_sanity_check(entry_data) - - for fcn, results, msg in future_expected_equals: - self.assertEqual(fcn(), results, msg=msg) - post_validation_expected_equals = [ - ( - models.Compound.objects.validated().count, - models.Compound.objects.count(), - "Validated Compounds count should have increased", - ) - ] - contribution_to_be_validated = models.Contribution.objects.get(validated=False) - contribution_to_be_validated.validated = True - contribution_to_be_validated.save() - for fcn, results, msg in post_validation_expected_equals: - self.assertEqual(fcn(), results, msg=msg) - - def _process_contribution_wizard_without_sanity_check( - self, entry_data, error_expected_in_step=None - ): - """ - The contribution add "wizard" - returns a 200 - """ - wizard_data = self._generate_wizard_data(entry_data) - for step in wizard_data: - # import json - # print(json.dumps(step, indent=4)) - step_url = self.get_step_url(step.get("step-id")) - response = self.client.get(step_url) - # post form data - if step.get("form-data") is not None: - err_msg = ( - f"Response code not ok when getting form " - f"for {step.get('step-id')} at {step_url}" - ) - self.assertEqual(response.status_code, 200, err_msg) - form_data = { - step["step-id"] + "-" + param_name: value - for param_name, value in step.get("form-data").items() - } - form_data["ippi_wizard-current_step"] = step["step-id"] - response = self.client.post(step_url, form_data) - error_is_now = ( - error_expected_in_step == step.get("step-id") - and response.status_code != 302 - ) - if ( - response.status_code != 302 - and step.get("step-id") != "done" - or error_is_now - ): - file_path = self.write_in_tmp_file(response) - err_msg = ( - f"Response code not ok when getting form for " - f"{step.get('step-id')} at {step_url}. Server" - f" response is stored in {file_path}" - ) - if error_is_now: - self.assertEqual(response.status_code, 200, err_msg) - return - self.assertEqual(response.status_code, 302, err_msg) - # check redirect to next step (if there is one) - if step.get("next") is not None: - redirect_url = self.get_step_url(step.get("next")) - self.assertEqual( - response.url, - redirect_url, - f"wrong redirection URL after step {step['step-id']}", - ) - self.assertEqual( - response.url, - reverse("contribution-detail", kwargs={"contribution_pk": 1}), - f"wrong final URL, should be the contribution permanent URL", - ) - - def _generate_wizard_data(self, entry_data): - """ - Generate the wizard form data dynamically based on iPPI-DB entry data - """ - - def get_id_form(): - return { - "source": entry_data["source"], - "id_source": entry_data["id_source"], - } - - def get_bibliography_form(): - ret = { - "source": entry_data["source"], - "id_source": entry_data["id_source"], - "title": "Opportunistic+amoebae:+challenges" - "+in+prophylaxis+and+treatment.", - "journal_name": "Drug+resistance+updates+:+reviews+and+" - "commentaries+in+antimicrobial+and+anticancer" - "+chemotherapy", - "authors_list": "Schuster+FL,+Visvesvara+GS", - "biblio_year": "2004", - } - for k in [ - "cytotox", - "xray", - "in_silico", - "in_vitro", - "in_cellulo", - "in_vivo", - "pharmacokinetic", - ]: - if entry_data.get(k, False): - ret[k] = "on" - return ret - - def get_pdb_form(): - return {"pdb_id": entry_data["pdb_id"]} - - def get_complex_type_form(): - return { - "complexType": entry_data["complexType"], - "complexChoice": entry_data["complexChoice"], - } - - def get_complex_form(): - data = { - "TOTAL_FORMS": len(entry_data["complex"]), - "INITIAL_FORMS": len(entry_data["complex"]), - "MIN_NUM_FORMS": 0, - "MAX_NUM_FORMS": 1000, - } - for idx, entry_complex in enumerate(entry_data["complex"]): - data["{}-protein".format(idx)] = models.Protein.objects.get( - uniprot_id=entry_complex["uniprot_id"] - ).id - data["{}-complex_type".format(idx)] = entry_complex["complex_type"] - if entry_complex["domain_pfam_acc"] is not None: - try: - data["{}-domain".format(idx)] = models.Domain.objects.get( - pfam_acc=entry_complex["domain_pfam_acc"] - ).id - except models.Domain.DoesNotExist as dne: - print( - "No domain in DB for PFAM ACC {}".format( - entry_complex["domain_pfam_acc"] - ) - ) - raise dne - else: - data["{}-domain".format(idx)] = "" - data["{}-ppc_copy_nb".format(idx)] = entry_complex["ppc_copy_nb"] - data["{}-cc_nb".format(idx)] = entry_complex["cc_nb"] - data["{}-ppp_copy_nb_per_p".format(idx)] = entry_complex[ - "ppp_copy_nb_per_p" - ] - return data - - def get_ppi_form(): - return { - "family": "", - "pdb_id": entry_data["pdb_id"], - "family_name": entry_data["family_name"], - "symmetry": 1, # FIXME - "pockets_nb": 1, # FIXME - "selected_diseases": "\n".join(entry_data["diseases"]), - } - - def get_compound_form(): - data = { - "TOTAL_FORMS": len(entry_data["compounds"]), - # FIXME there should be a way to list more than one compound - "INITIAL_FORMS": len(entry_data["compounds"]), - "MIN_NUM_FORMS": 1, - "MAX_NUM_FORMS": 1000, - } - for idx, compound_data_item in enumerate(entry_data["compounds"]): - data[f"{idx}-common_name"] = (compound_data_item["common_name"],) - data[f"{idx}-compound_name"] = (compound_data_item["compound_name"],) - if "molecule_smiles" in compound_data_item: - data[f"{idx}-molecule_smiles"] = ( - compound_data_item["molecule_smiles"], - ) - elif "molecule_iupac" in compound_data_item: - # if a IUPAC is provided for tests assume convert it to SMILES - molecule_smiles = convert_iupac_to_smiles( - compound_data_item["molecule_iupac"] - ) - data[f"{idx}-molecule_smiles"] = molecule_smiles - data[f"{idx}-is_macrocycle"] = compound_data_item.get( - "is_macrocycle", False - ) - return data - - def get_activity_description_form(): - data = { - "TOTAL_FORMS": len(entry_data.get("activity_tests", [])), - "INITIAL_FORMS": 0, - "MIN_NUM_FORMS": 0, - "MAX_NUM_FORMS": 1000, - } - for idx, activity_test in enumerate(entry_data.get("activity_tests", [])): - data[f"{idx}-ppi"] = "" - data[f"{idx}-test_name"] = activity_test["test_name"] - data[f"{idx}-is_primary"] = activity_test["is_primary"] - data[f"{idx}-protein_bound_construct"] = activity_test[ - "protein_bound_construct" - ] - data[f"{idx}-test_type"] = activity_test["test_type"] - data[f"{idx}-test_modulation_type"] = activity_test[ - "test_modulation_type" - ] - data[f"{idx}-nb_active_compounds"] = activity_test[ - "nb_active_compounds" - ] - data[f"{idx}-cell_line_name"] = activity_test["cell_line_name"] - data[f"{idx}-protein_complex"] = activity_test["protein_complex"] - data[ - f"{idx}-compoundactivityresult_set-activity-results-TOTAL_FORMS" - ] = 1 # len(activity_test.get("compound_activity_results",[])) - data[ - f"{idx}-compoundactivityresult_set-activity-results-INITIAL_FORMS" - ] = 0 - data[ - f"{idx}-compoundactivityresult_set-activity-results-MIN_NUM_FORMS" - ] = 0 - data[ - f"{idx}-compoundactivityresult_set-activity-results-MAX_NUM_FORMS" - ] = 1000 - for nidx, compound_activity_result in enumerate( - activity_test["compound_activity_results"] - ): - data[ - f"{idx}-compoundactivityresult_set-activity-results-{nidx}-compound_name" - ] = compound_activity_result["compound_name"] - data[ - f"{idx}-compoundactivityresult_set-activity-results-{nidx}-activity_type" - ] = compound_activity_result["activity_type"] - try: - data[ - f"{idx}-compoundactivityresult_set-activity-results-{nidx}-activity_mol" - ] = compound_activity_result["activity_mol"] - data[ - f"{idx}-compoundactivityresult_set-activity-results-{nidx}-activity_unit" - ] = compound_activity_result["activity_unit"] - except KeyError: - pass - data[ - f"{idx}-compoundactivityresult_set-activity-results-{nidx}-modulation_type" - ] = compound_activity_result["modulation_type"] - # "id": "", - # "test_activity_description": "", - return data - - def get_cytotox_description_form(): - data = { - "TOTAL_FORMS": len(entry_data.get("cytotox_tests", [])), - "INITIAL_FORMS": 0, - "MIN_NUM_FORMS": 1, - "MAX_NUM_FORMS": 1000, - } - for idx, cytotox_test in enumerate(entry_data.get("cytotox_tests", [])): - data[f"{idx}-test_name"] = cytotox_test["test_name"] - data[f"{idx}-compound_concentration"] = cytotox_test[ - "compound_concentration" - ] - data[f"{idx}-cell_line_name"] = cytotox_test["cell_line_name"] - - data[ - f"{idx}-compoundcytotoxicityresult_set-cytotox-results-TOTAL_FORMS" - ] = len(cytotox_test.get("compound_cytotox_results", [])) - data[ - f"{idx}-compoundcytotoxicityresult_set-cytotox-results-INITIAL_FORMS" - ] = 0 - data[ - f"{idx}-compoundcytotoxicityresult_set-cytotox-results-MIN_NUM_FORMS" - ] = 0 - data[ - f"{idx}-compoundcytotoxicityresult_set-cytotox-results-MAX_NUM_FORMS" - ] = 1000 - for nidx, result in enumerate(cytotox_test["compound_cytotox_results"]): - data[ - f"{idx}-compoundcytotoxicityresult_set-cytotox-results-{nidx}-compound_name" - ] = result["compound_name"] - data[ - f"{idx}-compoundcytotoxicityresult_set-cytotox-results-{nidx}-toxicity" - ] = result["toxicity"] - return data - - def get_pk_description_form(): - data = { - "TOTAL_FORMS": len(entry_data.get("pharmacokinetic_tests", [])), - "INITIAL_FORMS": 0, - "MIN_NUM_FORMS": 1, - "MAX_NUM_FORMS": 1000, - } - for idx, pk_test in enumerate(entry_data.get("pharmacokinetic_tests", [])): - data[f"{idx}-test_name"] = pk_test["test_name"] - data[f"{idx}-organism"] = pk_test["organism"] - data[f"{idx}-administration_mode"] = pk_test["administration_mode"] - data[f"{idx}-concentration"] = pk_test["concentration"] - data[f"{idx}-dose"] = pk_test["dose"] - data[f"{idx}-dose_interval"] = pk_test["dose_interval"] - data[f"{idx}-cell_line_name"] = pk_test["cell_line_name"] - - data[f"{idx}-compoundpkresult_set-pk-results-TOTAL_FORMS"] = len( - pk_test.get("compound_pk_results", []) - ) - data[f"{idx}-compoundpkresult_set-pk-results-INITIAL_FORMS"] = 0 - data[f"{idx}-compoundpkresult_set-pk-results-MIN_NUM_FORMS"] = 0 - data[f"{idx}-compoundpkresult_set-pk-results-MAX_NUM_FORMS"] = 1000 - for nidx, result in enumerate(pk_test["compound_pk_results"]): - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-tolerated" - ] = result["tolerated"] - data[f"{idx}-compoundpkresult_set-pk-results-{nidx}-auc"] = result[ - "auc" - ] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-clearance" - ] = result["clearance"] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-c_max" - ] = result["c_max"] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-oral_bioavailability" - ] = result["oral_bioavailability"] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-t_demi" - ] = result["t_demi"] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-t_max" - ] = result["t_max"] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-voldistribution" - ] = result["voldistribution"] - data[ - f"{idx}-compoundpkresult_set-pk-results-{nidx}-compound_name" - ] = result["compound_name"] - return data - - form_callables = { - None: lambda: None, - "IdForm": get_id_form, - "BibliographyForm": get_bibliography_form, - "PDBForm": get_pdb_form, - "ProteinDomainComplexTypeForm": get_complex_type_form, - "ProteinDomainComplexForm": get_complex_form, - "PpiForm": get_ppi_form, - "CompoundForm": get_compound_form, - "ActivityDescriptionFormSet": get_activity_description_form, - } - if entry_data.get("cytotox", False): - form_callables[ - "TestCytotoxDescriptionFormSet" - ] = get_cytotox_description_form - if entry_data.get("pharmacokinetic", False): - form_callables["TestPKDescriptionFormSet"] = get_pk_description_form - - # Adding a step for "done" in order to also save the data in the db - form_callables["done"] = lambda: None - - form_ids = iter(list(form_callables.keys())[1:]) - for step in form_callables.keys(): - item = { - "step-id": step, - "form-data": form_callables[step](), - "next": next(form_ids, None), - } - yield item - - def get_basic_entry(self): - """ - Basic entry test - """ - return { - "source": "PM", - "id_source": "15072770", - "in_silico": True, - "pdb_id": "3u85", - "diseases": [";toto;1;", ";titi;sfddf;", ";tata;3232;"], - "complexChoice": "inhibited", - "complexType": "Inhib_Hetero2merAB", - "complex": [ - { - "uniprot_id": "O00255", - "complex_type": "Partner", - "domain_pfam_acc": "PF05053", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": "", - }, - { - "uniprot_id": "Q03164", - "complex_type": "Bound", - "domain_pfam_acc": "PF05965", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - ], - "family_name": "Menin (PF05053)", - "compounds": [ - { - "molecule_smiles": "CCC", - "compound_name": "toto", - "common_name": "super", - } - ], - "activity_tests": [ - { - "test_name": "test", - "is_primary": True, - "protein_bound_construct": "F", - "test_type": "BIOCH", - "test_modulation_type": "I", - "nb_active_compounds": 2, - "cell_line_name": "", - # means first in the list above - "protein_complex": "2", - "compound_activity_results": [ - { - "compound_name": "toto", - "activity_type": "pIC50", - "activity_mol": 6.85, - "activity_unit": "1e-6", - "modulation_type": "I", - } - ], - } - ], - } - - def test_basic_entry(self): - self._process_contribution_wizard(self.get_basic_entry()) - - def get_entry_28(self): - """ - Test for the "contribution #28" - """ - return { - "source": "PM", - "id_source": "24512214 ", - "in_vitro": True, - "pdb_id": "3wn7", - "diseases": ["cancer (MONDO:0004992)"], - # FIXME continue here - "complexChoice": "inhibited", - "complexType": "Inhib_Hetero2merAB", - "complex": [ - { - "uniprot_id": "Q60795", - "complex_type": "Partner", - "domain_pfam_acc": None, - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - { - "uniprot_id": "Q9Z2X8", - "complex_type": "Bound", - "domain_pfam_acc": "PF01344", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - ], - "family_name": "KEAP1 / NRF2", - "compounds": [ - { - "molecule_smiles": "c1cc2c(cc1)c(ccc2N(S(=O)(=O)c1ccc(cc1)" - "OC)CC(=O)O)N(S(=O)(=O)c1ccc(cc1)OC)CC(=O)O", - "compound_name": "2", - "common_name": "", - } - ], - "activity_tests": [ - { - "test_name": "biolayer interferometry assay", - "is_primary": True, - "protein_bound_construct": "F", - "test_type": "BIOCH", - "test_modulation_type": "B", - "nb_active_compounds": 1, - "cell_line_name": "", - # means first in the list above - "protein_complex": "1", - "compound_activity_results": [ - { - "compound_name": "2", - "activity_type": "pKd", - "activity_mol": 3.59, - "activity_unit": "1e-9", - "modulation_type": "I", - } - ], - } - ], - } - - def test_entry_28(self): - self._process_contribution_wizard(self.get_entry_28()) - - def _test_activity_computation_and_storage(self, value, power): - basic_entry = self.get_basic_entry() - for activity_tests in basic_entry["activity_tests"]: - for compound_activity_results in activity_tests[ - "compound_activity_results" - ]: - compound_activity_results["activity_unit"] = "1e-%i" % power - try: - del compound_activity_results["activity"] - except KeyError: - pass - self._process_contribution_wizard(basic_entry) - bibliography = models.Bibliography.objects.get( - id_source=basic_entry["id_source"] - ) - for activity_tests in basic_entry["activity_tests"]: - for compound_activity_results in activity_tests[ - "compound_activity_results" - ]: - compound = models.RefCompoundBiblio.objects.get( - bibliography=bibliography, - compound_name=compound_activity_results["compound_name"], - ).compound - results = models.CompoundActivityResult.objects.get( - activity_type=compound_activity_results["activity_type"], - modulation_type=compound_activity_results["modulation_type"], - compound=compound, - ) - self.assertEqual( - Decimal(compound_activity_results["activity"]).quantize( - Decimal(10) - ** -models.CompoundActivityResult._meta.get_field( - "activity" - ).decimal_places - ), - results.activity, - ) - - def test_activity_computation_and_storage(self): - self._test_activity_computation_and_storage(0.685, 0) - - def test_activity_computation_and_storage_3(self): - self._test_activity_computation_and_storage(639.39406, 3) - - def test_activity_computation_and_storage_6(self): - self._test_activity_computation_and_storage(4.9846073, 6) - - def test_activity_computation_and_storage_9(self): - self._test_activity_computation_and_storage(2380.002, 9) - - def test_activity_computation_and_storage_12(self): - self._test_activity_computation_and_storage(1.018, 12) - - def test_no_activity_fails(self): - basic_entry = self.get_basic_entry() - for activity_tests in basic_entry["activity_tests"]: - for compound_activity_results in activity_tests[ - "compound_activity_results" - ]: - compound_activity_results["activity_unit"] = "" - compound_activity_results["activity_mol"] = "" - try: - del compound_activity_results["activity"] - except KeyError: - pass - self._process_contribution_wizard_without_sanity_check( - basic_entry, error_expected_in_step="ActivityDescriptionFormSet", - ) - - def test_with_all_tests(self): - """ - Basic entry test - """ - entry_data = { - "source": "PM", - "id_source": "15072770", - "in_silico": True, - "pharmacokinetic": True, - "cytotox": True, - "pdb_id": "3u85", - "diseases": ["", ";titi;sfddf;", ";tutu;5456456;", ";tutu;5456456;", ""], - "complexChoice": "inhibited", - "complexType": "Inhib_Hetero2merAB", - "complex": [ - { - "uniprot_id": "O00255", - "complex_type": "Partner", - "domain_pfam_acc": "PF05053", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": "", - }, - { - "uniprot_id": "Q03164", - "complex_type": "Bound", - "domain_pfam_acc": "PF05965", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - ], - "family_name": "Menin (PF05053)", - "compounds": [ - { - "molecule_smiles": "CCC", - "compound_name": "toto", - "common_name": "super", - } - ], - "activity_tests": [ - { - "test_name": "test", - "is_primary": True, - "protein_bound_construct": "F", - "test_type": "BIOCH", - "test_modulation_type": "I", - "nb_active_compounds": 2, - "cell_line_name": "", - # means first in the list above - "protein_complex": "2", - "compound_activity_results": [ - { - "compound_name": "toto", - "activity_type": "pIC50", - "activity_mol": 6.85, - "activity_unit": "1e-3", - "modulation_type": "I", - } - ], - } - ], - "pharmacokinetic_tests": [ - { - "test_name": "test", - "organism": 1, - "administration_mode": "IV", - "concentration": "1.2", - "dose": "3.5", - "dose_interval": "2", - "cell_line_name": "1043SK ", - # means position in the list above - "compound_pk_results": [ - { - "tolerated": "True", - "auc": "1", - "clearance": "1.2", - "c_max": "5.2", - "oral_bioavailability": "22", - "t_demi": "5", - "t_max": "5", - "voldistribution": "5.5", - "compound_name": "toto", - } - ], - } - ], - "cytotox_tests": [ - { - "test_name": "test", - "compound_concentration": "2", - "cell_line_name": "1043SK ", - "compound_cytotox_results": [ - {"compound_name": "toto", "toxicity": "True"} - ], - } - ], - } - self._process_contribution_wizard(entry_data) - input_diseases = set(entry_data["diseases"]) - input_diseases.remove("") - self.assertSetEqual( - input_diseases, - set( - models.Ppi.objects.get(pdb_id=entry_data["pdb_id"]) - .diseases.annotate( - raw=Concat(Value(";"), "name", Value(";"), "identifier", Value(";")) - ) - .values_list("raw", flat=True) - ), - ) - - def test_complex_no_pfam(self): - """ - Test that it is possible to not select any domain - for one of the partners in the complex - (see #77) - """ - entry_data = { - "source": "PM", - "id_source": "26958703", - "in_silico": True, - "pdb_id": "3emh", - "diseases": [], - "complexChoice": "inhibited", - "complexType": "Inhib_Hetero2merAB", - "complex": [ - { - "uniprot_id": "P61964", - "complex_type": "Bound", - "domain_pfam_acc": "PF00400", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - { - "uniprot_id": "Q03164", - "complex_type": "Partner", - "domain_pfam_acc": None, - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": "", - }, - ], - "family_name": "WD40 (PF00400)", - "compounds": [ - { - "molecule_smiles": "C", - "compound_name": "toto", - "common_name": "super", - } - ], - "activity_tests": [ - { - "test_name": "test", - "is_primary": True, - "protein_bound_construct": "F", - "test_type": "BIOCH", - "test_modulation_type": "I", - "nb_active_compounds": 2, - "cell_line_name": "", - # means position in the list above - "protein_complex": "1", - "compound_activity_results": [ - { - "compound_name": "toto", - "activity_type": "pIC50", - "activity_mol": 6.85, - "activity_unit": "1e-3", - "modulation_type": "I", - } - ], - } - ], - } - self._process_contribution_wizard(entry_data) - - def test_simple_heterodimer(self): - """ - Test provided for simple heterodimer - """ - entry_data = { - "source": "PM", - "id_source": "26958703", - "in_silico": True, - "in_vitro": True, - "xray": True, - "pdb_id": "3emh", - "diseases": [], - "complexChoice": "inhibited", - "complexType": "Inhib_Hetero2merAB", - "complex": [ - { - "uniprot_id": "P61964", - "complex_type": "Bound", - "domain_pfam_acc": "PF00400", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - { - "uniprot_id": "Q03164", - "complex_type": "Partner", - "domain_pfam_acc": None, - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": "", - }, - ], - "family_name": "WD40 (PF00400)", - "compounds": [ - { - "molecule_smiles": r"CN1CCN(CC1)C1=C(C=C(C=C1)C1=CC(=CC=C1)" - r"CN1CCOCC1)NC(=O)C1=CNC(C=C1C(F)(F)F)=O", - "compound_name": "16d", - "common_name": "officially 16d", - "ligand_id": "ABC", - }, - { - "molecule_iupac": "N-(2-(4-Methylpiperazin-1-yl)-5-nitrophenyl)" - "-6-oxo-4-(trifluoromethyl)-1,6-dihydropyridine-3-carboxamide", - "compound_name": "14a", - "common_name": "officially 14a", - "is_macrocycle": False, - }, - { - "molecule_smiles": "C1CCC1", - "compound_name": "lili", - "common_name": "stuck", - "is_macrocycle": False, - }, - ], - "activity_tests": [ - { - "test_name": "test", - "is_primary": True, - "protein_bound_construct": "F", - "test_type": "BIOCH", - "test_modulation_type": "I", - "nb_active_compounds": 2, - "cell_line_name": "", - # means position in the list above - "protein_complex": "1", - "compound_activity_results": [ - { - "compound_name": "16d", - "activity_type": "pIC50", - "activity_mol": 6.85, - "activity_unit": "1e-3", - "modulation_type": "I", - } - ], - } - ], - } - - self._process_contribution_wizard(entry_data) - - def test_simple_stabilized_heterodimer(self): - """ - Test provided for stabilized heterodimer - """ - entry_data = { - "source": "PM", - "id_source": "23676274", - "in_cellulo": True, - "in_vitro": True, - "xray": True, - "pdb_id": "4jc3", - "complexChoice": "stabilized", - "complexType": "Stab_Hetero2merAB", - "complex": [ - { - "uniprot_id": "P31947", - "complex_type": "Bound", - "domain_pfam_acc": "PF00244", - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - { - "uniprot_id": "P03372", - "complex_type": "Bound", - "domain_pfam_acc": None, - "ppc_copy_nb": 1, - "cc_nb": 1, - "ppp_copy_nb_per_p": 1, - }, - ], - "family_name": "14-3-3 / ER", - "diseases": ["breast cancer, MONDO:0007254"], - "compounds": [ - { - "molecule_smiles": r"COC[C@H]1CC[C@H]2[C@@H](C)[C@@H](O)" - r"[C@H](O[C@H]3O[C@H](COC(C)(C)C=C)[C@@H](O)[C@H](OC(C)=O)" - r"[C@H]3O)C3=C(C[C@H](O)[C@]3(C)\C=C1/2)[C@H](C)COC(C)=O", - "compound_name": "FC", - "common_name": "fusicoccin", - "ligand_id": "FSC", - } - ], - "activity_tests": [ - { - "test_name": "pull down", - "is_primary": True, - "protein_bound_construct": "F", - "test_type": "BIOCH", - "test_modulation_type": "I", - "nb_active_compounds": 2, - "cell_line_name": "", - # means position in the list above - "protein_complex": "1", - "compound_activity_results": [ - { - "compound_name": "FC", - "activity_type": "pIC50", - "activity_mol": 0.25, - "activity_unit": "1e-6", - "modulation_type": "S", - } - ], - } - ], - } - - self._process_contribution_wizard(entry_data) - - def write_in_tmp_file(self, response): - """ - Write an HTTP response contents into a temporary file for debug - - :param response: response that will be saved - :type respons: django.http.HttpResponse - :return: path to the temporary file created - :rtype: str - """ - with NamedTemporaryFile(delete=False, suffix=".html") as f: - f.write(response.content) - return f.name diff --git a/ippisite/ippidb/tests/tests_contribute_e2e.py b/ippisite/ippidb/tests/tests_contribute_e2e.py new file mode 100644 index 0000000000000000000000000000000000000000..373fc4a9cf9f9f40efbbeba3335388fc58aa414e --- /dev/null +++ b/ippisite/ippidb/tests/tests_contribute_e2e.py @@ -0,0 +1,600 @@ +""" +iPPI-DB contribution module "end to end" tests +""" +import math +import os +from decimal import Decimal +from tempfile import NamedTemporaryFile +import yaml +import glob + +from django.contrib.auth import get_user_model +from django.db.models import Value +from django.db.models.functions import Concat +from django.test import TestCase +from django.urls import reverse +from parameterized import parameterized +import requests_cache + +from ippidb import models +from ippidb.admin import grant_contribution_permission +from ippidb.ws import get_uniprot_info + + +requests_cache.install_cache("tests_contribute_cache", backend="sqlite") + + +def compute_ppi_name(entry_data): + """ + return the expected PPI name from a given contribution entry_data + """ + bound_uniprots = [ + item["uniprot_id"] + for item in entry_data["complex"] + if item["complex_type"] == "Bound" + ] + partner_uniprots = [ + item["uniprot_id"] + for item in entry_data["complex"] + if item["complex_type"] == "Partner" + ] + bound_protein_names = [ + get_uniprot_info(uniprot_id)["short_name"] + for uniprot_id in bound_uniprots + ] + partner_protein_names = [ + get_uniprot_info(uniprot_id)["short_name"] + for uniprot_id in partner_uniprots + ] + bound_protein_names.sort() + partner_protein_names.sort() + bound_str = ",".join(bound_protein_names) + partner_str = ",".join(partner_protein_names) + name = bound_str + if partner_str != "": + name += " / " + partner_str + return name + + +class ContributionE2ETestCase(TestCase): + """ + This class tests the Contribution Wizard by interacting with it + in the same way a user would do. + """ + + def setUp(self): + login = "contributor" + password = "12345" + User = get_user_model() + User.objects.create_user(username=login, password=password) + self.client.login(username=login, password=password) + grant_contribution_permission(None, None, User.objects.all()) + symmetry = models.Symmetry() + symmetry.code = "AS" + symmetry.description = "asymmetric" + symmetry.save() + models.Taxonomy.objects.create(taxonomy_id=9606, name="Homo sapiens") + + @staticmethod + def get_step_url(step_id): + step_url = reverse("admin-session-add") + if step_id is not None: + step_url += step_id + "/" + return step_url + + def _process_contribution_wizard(self, entry_data): + future_expected_equals = [ + ( + models.Bibliography.objects.count, + models.Bibliography.objects.count() + 1, + "Bibliography count", + ), + ( + models.Contribution.objects.count, + models.Contribution.objects.count() + 1, + "Contribution count", + ), + ( + models.Compound.objects.count, + models.Compound.objects.count() + len(entry_data["compounds"]), + "Compounds count", + ), + ( + models.Compound.objects.validated().count, + models.Compound.objects.validated().count(), + "Validated Compounds count should remains the same", + ), + ( + models.CompoundAction.objects.count, + models.CompoundAction.objects.count() + len(entry_data["compounds"]), + "Compound Actions count", + ), + ( + models.TestActivityDescription.objects.count, + models.TestActivityDescription.objects.count() + + len(entry_data.get("activity_tests", [])), + "Activity tests count", + ), + ( + models.TestCytotoxDescription.objects.count, + models.TestCytotoxDescription.objects.count() + + len(entry_data.get("cytotox_tests", [])), + "Cytotox tests count", + ), + ( + models.TestPKDescription.objects.count, + models.TestPKDescription.objects.count() + + len(entry_data.get("pharmacokinetic_tests", [])), + "Pharmacokinetic tests count", + ), + ( + lambda: models.PpiFamily.objects.get().name, + entry_data.get("family_name"), + "Ppi family name", + ), + ( + lambda: models.Ppi.objects.get().family.name, + entry_data.get("family_name"), + "Ppi family name from PPI", + ), + ( + lambda: models.Ppi.objects.get().name, + compute_ppi_name(entry_data), + "Ppi name", + ), + ] + for activity_tests in entry_data["activity_tests"]: + for compound_activity_results in activity_tests[ + "compound_activity_results" + ]: + if ( + "activity_mol" in compound_activity_results + and compound_activity_results["activity_mol"] != "" + ): + compound_activity_results["activity"] = -math.log10( + Decimal(str(compound_activity_results["activity_mol"])) + * Decimal(str(compound_activity_results["activity_unit"])) + ) + + self._process_contribution_wizard_without_sanity_check(entry_data) + + for fcn, results, msg in future_expected_equals: + self.assertEqual(fcn(), results, msg=msg) + post_validation_expected_equals = [ + ( + models.Compound.objects.validated().count, + models.Compound.objects.count(), + "Validated Compounds count should have increased", + ) + ] + contribution_to_be_validated = models.Contribution.objects.get(validated=False) + contribution_to_be_validated.validated = True + contribution_to_be_validated.save() + for fcn, results, msg in post_validation_expected_equals: + self.assertEqual(fcn(), results, msg=msg) + + def _process_contribution_wizard_without_sanity_check( + self, entry_data, error_expected_in_step=None + ): + """ + The contribution add "wizard" + returns a 200 + """ + wizard_data = self._generate_wizard_data(entry_data) + for step in wizard_data: + step_url = self.get_step_url(step.get("step-id")) + response = self.client.get(step_url) + # post form data + if step.get("form-data") is not None: + err_msg = ( + f"Response code not ok when getting form " + f"for {step.get('step-id')} at {step_url}" + ) + self.assertEqual(response.status_code, 200, err_msg) + form_data = { + step["step-id"] + "-" + param_name: value + for param_name, value in step.get("form-data").items() + } + form_data["ippi_wizard-current_step"] = step["step-id"] + response = self.client.post(step_url, form_data) + error_is_now = ( + error_expected_in_step == step.get("step-id") + and response.status_code != 302 + ) + if ( + response.status_code != 302 + and step.get("step-id") != "done" + or error_is_now + ): + file_path = self.write_in_tmp_file(response) + err_msg = ( + f"Response code not ok when getting form for " + f"{step.get('step-id')} at {step_url}. Server" + f" response is stored in {file_path}" + ) + if error_is_now: + self.assertEqual(response.status_code, 200, err_msg) + return + self.assertEqual(response.status_code, 302, err_msg) + # check redirect to next step (if there is one) + if step.get("next") is not None: + redirect_url = self.get_step_url(step.get("next")) + self.assertEqual( + response.url, + redirect_url, + f"wrong redirection URL after step {step['step-id']}", + ) + self.assertEqual( + response.url, + reverse("contribution-detail", kwargs={"contribution_pk": 1}), + f"wrong final URL, should be the contribution permanent URL", + ) + + def _generate_wizard_data(self, entry_data): + """ + Generate the wizard form data dynamically based on iPPI-DB entry data + """ + + def get_id_form(): + return { + "source": entry_data["source"], + "id_source": entry_data["id_source"], + } + + def get_bibliography_form(): + ret = { + "source": entry_data["source"], + "id_source": entry_data["id_source"], + "title": "Opportunistic+amoebae:+challenges" + "+in+prophylaxis+and+treatment.", + "journal_name": "Drug+resistance+updates+:+reviews+and+" + "commentaries+in+antimicrobial+and+anticancer" + "+chemotherapy", + "authors_list": "Schuster+FL,+Visvesvara+GS", + "biblio_year": "2004", + } + for k in [ + "cytotox", + "xray", + "in_silico", + "in_vitro", + "in_cellulo", + "in_vivo", + "pharmacokinetic", + ]: + if entry_data.get(k, False): + ret[k] = "on" + return ret + + def get_pdb_form(): + return {"pdb_id": entry_data["pdb_id"]} + + def get_complex_type_form(): + return { + "complexType": entry_data["complexType"], + "complexChoice": entry_data["complexChoice"], + } + + def get_complex_form(): + data = { + "TOTAL_FORMS": len(entry_data["complex"]), + "INITIAL_FORMS": len(entry_data["complex"]), + "MIN_NUM_FORMS": 0, + "MAX_NUM_FORMS": 1000, + } + for idx, entry_complex in enumerate(entry_data["complex"]): + data["{}-protein".format(idx)] = models.Protein.objects.get( + uniprot_id=entry_complex["uniprot_id"] + ).id + data["{}-complex_type".format(idx)] = entry_complex["complex_type"] + if entry_complex["domain_pfam_acc"] is not None: + try: + data["{}-domain".format(idx)] = models.Domain.objects.get( + pfam_acc=entry_complex["domain_pfam_acc"] + ).id + except models.Domain.DoesNotExist as dne: + print( + "No domain in DB for PFAM ACC {}".format( + entry_complex["domain_pfam_acc"] + ) + ) + raise dne + else: + data["{}-domain".format(idx)] = "" + data["{}-ppc_copy_nb".format(idx)] = entry_complex["ppc_copy_nb"] + data["{}-cc_nb".format(idx)] = entry_complex["cc_nb"] + data["{}-ppp_copy_nb_per_p".format(idx)] = entry_complex[ + "ppp_copy_nb_per_p" + ] + return data + + def get_ppi_form(): + return { + "family": "", + "pdb_id": entry_data["pdb_id"], + "family_name": entry_data["family_name"], + "symmetry": 1, # FIXME + "pockets_nb": 1, # FIXME + "selected_diseases": "\n".join(entry_data["diseases"]), + } + + def get_compound_form(): + data = { + "TOTAL_FORMS": len(entry_data["compounds"]), + # FIXME there should be a way to list more than one compound + "INITIAL_FORMS": len(entry_data["compounds"]), + "MIN_NUM_FORMS": 1, + "MAX_NUM_FORMS": 1000, + } + for idx, compound_data_item in enumerate(entry_data["compounds"]): + data[f"{idx}-common_name"] = (compound_data_item["common_name"],) + data[f"{idx}-compound_name"] = (compound_data_item["compound_name"],) + if "molecule_smiles" in compound_data_item: + data[f"{idx}-molecule_smiles"] = ( + compound_data_item["molecule_smiles"], + ) + elif "molecule_iupac" in compound_data_item: + # if a IUPAC is provided for tests assume convert it to SMILES + # molecule_smiles = convert_iupac_to_smiles( + # compound_data_item["molecule_iupac"] + # ) + data[f"{idx}-molecule_iupac"] = compound_data_item["molecule_iupac"] + data[f"{idx}-is_macrocycle"] = compound_data_item.get( + "is_macrocycle", False + ) + if "ligand_id" in compound_data_item: + data[f"{idx}-ligand_id"] = compound_data_item["ligand_id"] + return data + + def get_activity_description_form(): + data = { + "TOTAL_FORMS": len(entry_data.get("activity_tests", [])), + "INITIAL_FORMS": 0, + "MIN_NUM_FORMS": 0, + "MAX_NUM_FORMS": 1000, + } + for idx, activity_test in enumerate(entry_data.get("activity_tests", [])): + data[f"{idx}-ppi"] = "" + data[f"{idx}-test_name"] = activity_test["test_name"] + data[f"{idx}-is_primary"] = activity_test.get("is_primary", False) + data[f"{idx}-protein_bound_construct"] = activity_test[ + "protein_bound_construct" + ] + data[f"{idx}-test_type"] = activity_test["test_type"] + data[f"{idx}-test_modulation_type"] = activity_test[ + "test_modulation_type" + ] + data[f"{idx}-nb_active_compounds"] = activity_test[ + "nb_active_compounds" + ] + if "cell_line_name" in activity_test: + data[f"{idx}-cell_line_name"] = activity_test["cell_line_name"] + data[f"{idx}-protein_complex"] = activity_test["protein_complex"] + data[ + f"{idx}-compoundactivityresult_set-activity-results-TOTAL_FORMS" + ] = 1 # len(activity_test.get("compound_activity_results",[])) + data[ + f"{idx}-compoundactivityresult_set-activity-results-INITIAL_FORMS" + ] = 0 + data[ + f"{idx}-compoundactivityresult_set-activity-results-MIN_NUM_FORMS" + ] = 0 + data[ + f"{idx}-compoundactivityresult_set-activity-results-MAX_NUM_FORMS" + ] = 1000 + for nidx, compound_activity_result in enumerate( + activity_test["compound_activity_results"] + ): + data[ + f"{idx}-compoundactivityresult_set-activity-results-{nidx}-compound_name" + ] = compound_activity_result["compound_name"] + data[ + f"{idx}-compoundactivityresult_set-activity-results-{nidx}-activity_type" + ] = compound_activity_result["activity_type"] + try: + data[ + f"{idx}-compoundactivityresult_set-activity-results-{nidx}-activity_mol" + ] = compound_activity_result["activity_mol"] + data[ + f"{idx}-compoundactivityresult_set-activity-results-{nidx}-activity_unit" + ] = compound_activity_result["activity_unit"] + except KeyError: + pass + data[ + f"{idx}-compoundactivityresult_set-activity-results-{nidx}-modulation_type" + ] = compound_activity_result["modulation_type"] + # "id": "", + # "test_activity_description": "", + return data + + def get_cytotox_description_form(): + data = { + "TOTAL_FORMS": len(entry_data.get("cytotox_tests", [])), + "INITIAL_FORMS": 0, + "MIN_NUM_FORMS": 1, + "MAX_NUM_FORMS": 1000, + } + for idx, cytotox_test in enumerate(entry_data.get("cytotox_tests", [])): + data[f"{idx}-test_name"] = cytotox_test["test_name"] + data[f"{idx}-compound_concentration"] = cytotox_test[ + "compound_concentration" + ] + data[f"{idx}-cell_line_name"] = cytotox_test["cell_line_name"] + + data[ + f"{idx}-compoundcytotoxicityresult_set-cytotox-results-TOTAL_FORMS" + ] = len(cytotox_test.get("compound_cytotox_results", [])) + data[ + f"{idx}-compoundcytotoxicityresult_set-cytotox-results-INITIAL_FORMS" + ] = 0 + data[ + f"{idx}-compoundcytotoxicityresult_set-cytotox-results-MIN_NUM_FORMS" + ] = 0 + data[ + f"{idx}-compoundcytotoxicityresult_set-cytotox-results-MAX_NUM_FORMS" + ] = 1000 + for nidx, result in enumerate(cytotox_test["compound_cytotox_results"]): + data[ + f"{idx}-compoundcytotoxicityresult_set-cytotox-results-{nidx}-compound_name" + ] = result["compound_name"] + data[ + f"{idx}-compoundcytotoxicityresult_set-cytotox-results-{nidx}-toxicity" + ] = result["toxicity"] + return data + + def get_pk_description_form(): + data = { + "TOTAL_FORMS": len(entry_data.get("pharmacokinetic_tests", [])), + "INITIAL_FORMS": 0, + "MIN_NUM_FORMS": 1, + "MAX_NUM_FORMS": 1000, + } + for idx, pk_test in enumerate(entry_data.get("pharmacokinetic_tests", [])): + data[f"{idx}-test_name"] = pk_test["test_name"] + data[f"{idx}-organism"] = pk_test["organism"] + data[f"{idx}-administration_mode"] = pk_test["administration_mode"] + data[f"{idx}-concentration"] = pk_test["concentration"] + data[f"{idx}-dose"] = pk_test["dose"] + data[f"{idx}-dose_interval"] = pk_test["dose_interval"] + data[f"{idx}-cell_line_name"] = pk_test["cell_line_name"] + + data[f"{idx}-compoundpkresult_set-pk-results-TOTAL_FORMS"] = len( + pk_test.get("compound_pk_results", []) + ) + data[f"{idx}-compoundpkresult_set-pk-results-INITIAL_FORMS"] = 0 + data[f"{idx}-compoundpkresult_set-pk-results-MIN_NUM_FORMS"] = 0 + data[f"{idx}-compoundpkresult_set-pk-results-MAX_NUM_FORMS"] = 1000 + for nidx, result in enumerate(pk_test["compound_pk_results"]): + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-tolerated" + ] = result["tolerated"] + data[f"{idx}-compoundpkresult_set-pk-results-{nidx}-auc"] = result[ + "auc" + ] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-clearance" + ] = result["clearance"] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-c_max" + ] = result["c_max"] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-oral_bioavailability" + ] = result["oral_bioavailability"] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-t_demi" + ] = result["t_demi"] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-t_max" + ] = result["t_max"] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-voldistribution" + ] = result["voldistribution"] + data[ + f"{idx}-compoundpkresult_set-pk-results-{nidx}-compound_name" + ] = result["compound_name"] + return data + + form_callables = { + None: lambda: None, + "IdForm": get_id_form, + "BibliographyForm": get_bibliography_form, + "PDBForm": get_pdb_form, + "ProteinDomainComplexTypeForm": get_complex_type_form, + "ProteinDomainComplexForm": get_complex_form, + "PpiForm": get_ppi_form, + "CompoundForm": get_compound_form, + "ActivityDescriptionFormSet": get_activity_description_form, + } + if entry_data.get("cytotox", False): + form_callables[ + "TestCytotoxDescriptionFormSet" + ] = get_cytotox_description_form + if entry_data.get("pharmacokinetic", False): + form_callables["TestPKDescriptionFormSet"] = get_pk_description_form + + # Adding a step for "done" in order to also save the data in the db + form_callables["done"] = lambda: None + + form_ids = iter(list(form_callables.keys())[1:]) + for step in form_callables.keys(): + item = { + "step-id": step, + "form-data": form_callables[step](), + "next": next(form_ids, None), + } + yield item + + @parameterized.expand( + [ + entry_path + for entry_path in glob.glob( + os.path.join(os.path.dirname(__file__), "*.yaml") + ) + ] + ) + def test_entry(self, entry_path): + # load the test data + entry_data = yaml.load(open(entry_path, "r"), Loader=yaml.FullLoader) + # process the wizard + self._process_contribution_wizard(entry_data) + # test diseases + input_diseases = set(entry_data["diseases"]) + input_diseases.discard("") + self.assertSetEqual( + input_diseases, + set( + models.Ppi.objects.get(pdb_id=entry_data["pdb_id"]) + .diseases.annotate( + raw=Concat(Value(";"), "name", Value(";"), "identifier", Value(";")) + ) + .values_list("raw", flat=True) + ), + ) + # test compounds + for c in entry_data["compounds"]: + if "molecule_smiles" in c: + comp = models.Compound.objects.get(canonical_smile=c["molecule_smiles"]) + elif "molecule_iupac" in c: + comp = models.Compound.objects.get(iupac_name=c["molecule_iupac"]) + # test ligand ID has been stored if the X ray data option is checked + if entry_data.get("xray", False) is True: + self.assertEqual(comp.ligand_id, c.get("ligand_id", None)) + # test activity + bibliography = models.Bibliography.objects.get( + id_source=entry_data["id_source"].strip() + ) + for activity_tests in entry_data["activity_tests"]: + for compound_activity_results in activity_tests[ + "compound_activity_results" + ]: + compound = models.RefCompoundBiblio.objects.get( + bibliography=bibliography, + compound_name=compound_activity_results["compound_name"], + ).compound + results = models.CompoundActivityResult.objects.get( + activity_type=compound_activity_results["activity_type"], + modulation_type=compound_activity_results["modulation_type"], + compound=compound, + ) + self.assertEqual( + Decimal(compound_activity_results["activity"]).quantize( + Decimal(10) + ** -models.CompoundActivityResult._meta.get_field( + "activity" + ).decimal_places + ), + results.activity, + ) + + def write_in_tmp_file(self, response): + """ + Write an HTTP response contents into a temporary file for debug + + :param response: response that will be saved + :type respons: django.http.HttpResponse + :return: path to the temporary file created + :rtype: str + """ + with NamedTemporaryFile(delete=False, suffix=".html") as f: + f.write(response.content) + return f.name diff --git a/ippisite/ippidb/tests/tests_utils.py b/ippisite/ippidb/tests/tests_utils.py index bf59015d9be616de248045867607c9bda862589a..5c73f34e75dc2ebeb17c8b63d4eb9470b7da2a0b 100644 --- a/ippisite/ippidb/tests/tests_utils.py +++ b/ippisite/ippidb/tests/tests_utils.py @@ -6,7 +6,7 @@ import re from django.test import TestCase from openbabel import vectorUnsignedInt, OBFingerprint -from .utils import FingerPrinter, mol2smi, smi2mol, smi2inchi, smi2inchikey, smi2sdf +from ippidb.utils import FingerPrinter, mol2smi, smi2mol, smi2inchi, smi2inchikey, smi2sdf class MolSmiTestCase(TestCase):