
Tooluniverse Network Pharmacology
- 372 installs
- 1.6k repo stars
- Updated August 4, 2026
- mims-harvard/tooluniverse
tooluniverse-network-pharmacology is a Harvard ToolUniverse agent skill that constructs compound-target-disease networks from 60+ biomedical data sources for developers and researchers prioritizing drug repurposing and p
About
tooluniverse-network-pharmacology is a systems pharmacology skill from mims-harvard/ToolUniverse that builds and analyzes multi-layer compound-target-disease networks using data from ChEMBL, OpenTargets, STRING, DrugBank, Reactome, FAERS, and 60+ other ToolUniverse tools. It calculates Network Pharmacology Scores from 0 to 100 based on network proximity, clinical evidence, and mechanistic plausibility, then identifies repurposing candidates, predicts drug mechanisms, and analyzes polypharmacology effects. The workflow covers network node identification, edge construction, proximity analysis, repurposing predictions, and report generation with bundled reference files including ANALYSIS_PROCEDURES.md, SCORING_REFERENCE.md, and test_network_pharmacology.py. Developers and computational biologists reach for this skill when asking whether a drug can be repurposed for a disease, mapping multi-target effects, or ranking candidates by network metrics before lab validation.
- Drug-target and disease network exploration
- Polypharmacology and repurposing support
- ToolUniverse graph-oriented pharmacology access
- Mechanism hypothesis scaffolding for agents
- Useful in early drug discovery brainstorming
Tooluniverse Network Pharmacology by the numbers
- 372 all-time installs (skills.sh)
- +5 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #526 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mims-harvard/tooluniverse --skill tooluniverse-network-pharmacologyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 372 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | August 4, 2026 |
| Repository | mims-harvard/tooluniverse ↗ |
How do you map drug-target-disease networks?
Map drug-target-disease networks and polypharmacology relationships via ToolUniverse when prioritizing compounds, repurposing candidates, or explaining mechanism hypotheses.
Who is it for?
Computational biology developers using ToolUniverse who need ranked drug repurposing candidates from multi-source biomedical network analysis.
Skip if: Skip tooluniverse-network-pharmacology when the task is general web API development unrelated to compound, target, or disease entity analysis.
When should I use this skill?
Trigger tooluniverse-network-pharmacology when evaluating drug repurposing via network analysis, polypharmacology effects, or compound-target-disease proximity for a named entity.
What you get
Compound-target-disease network graph, Network Pharmacology Score rankings, repurposing candidate list, and mechanism prediction report.
- C-T-D network analysis
- Network Pharmacology Score rankings
- repurposing candidate report
By the numbers
- Integrates 60+ ToolUniverse biomedical data source tools
- Calculates Network Pharmacology Scores on a 0–100 scale
- References 6 primary databases: ChEMBL, OpenTargets, STRING, DrugBank, Reactome, FAERS
Files
COMPUTE, DON'T DESCRIBE
When analysis requires computation (statistics, data processing, scoring, enrichment), write and run Python code via Bash. Don't describe what you would do — execute it and report actual results. Use ToolUniverse tools to retrieve data, then Python (pandas, scipy, statsmodels, matplotlib) to analyze it.
Network Pharmacology Pipeline
Construct and analyze compound-target-disease (C-T-D) networks to identify drug repurposing opportunities, understand polypharmacology, and predict drug mechanisms using systems pharmacology approaches.
LOOK UP DON'T GUESS - Retrieve actual target lists, network data, and clinical evidence from tools. Do not infer network relationships from drug class alone.
IMPORTANT: Always use English terms in tool calls, even if the user writes in another language. Respond in the user's language.
---
Polypharmacology Reasoning (Start Here)
Before building any network, reason about what kind of multi-target effect you are dealing with:
A drug hitting multiple targets is either polypharmacology (desired multi-target) or promiscuity (undesired off-target). The distinction depends on whether the additional targets contribute to efficacy or cause toxicity.
Use this framework to guide the analysis:
- Desired polypharmacology: multiple targets all lie within the same disease module or pathway. Example: a kinase inhibitor that hits both EGFR and ERBB2 in the same signaling cascade. Look for pathway co-membership and disease module overlap. This is a network proximity argument.
- Off-target promiscuity: additional targets are in unrelated pathways, especially those associated with known toxicity (hERG for cardiotoxicity, CYP3A4 for drug interactions, COX-1 for GI toxicity). Look for these in the safety phase before claiming benefit.
- Repurposing hypothesis: the drug's known targets have strong genetic/functional evidence for the new disease. Network proximity (Z-score) quantifies this. A Z < -2 with p < 0.01 is meaningful signal; a Z near 0 means the targets are essentially unconnected to the disease module.
- Mechanism ambiguity: if a drug has 10+ known targets, do not treat all as therapeutically relevant. Start with primary mechanism-of-action targets, then ask whether secondary targets add to or subtract from the therapeutic window.
Document this reasoning explicitly in the report before listing candidates.
---
When to Use This Skill
Apply when users:
- Ask "Can [drug] be repurposed for [disease] based on network analysis?"
- Want to understand multi-target (polypharmacology) effects of a compound
- Need compound-target-disease network construction and analysis
- Ask about network proximity between drug targets and disease genes
- Want systems pharmacology analysis of a drug or target
- Ask about drug repurposing candidates ranked by network metrics
- Need mechanism prediction for a drug in a new indication
- Want to identify hub genes in disease networks as therapeutic targets
NOT for (use other skills instead):
- Simple drug repurposing without network analysis ->
tooluniverse-drug-repurposing - Single target validation ->
tooluniverse-drug-target-validation - Adverse event detection only ->
tooluniverse-adverse-event-detection
---
Key Principles
1. Report-first approach - Create report file FIRST, then populate progressively 2. Entity disambiguation FIRST - Resolve all identifiers before analysis 3. Reason about polypharmacology type - Desired vs. promiscuous (see above) 4. Bidirectional network - Construct C-T-D network from both directions 5. Rank candidates - Prioritize by composite Network Pharmacology Score 6. Mechanism prediction - Explain HOW drug could work via network paths 7. Clinical feasibility - FDA-approved drugs ranked higher than preclinical 8. Safety context - Flag known adverse events and off-target liabilities 9. Evidence grading - Grade all evidence T1-T4 10. Negative results documented - "No data" is data; empty sections are failures 11. Source references - Every finding must cite the source tool/database
---
Network Pharmacology Score (0-100)
Five components with explicit reasoning at each step:
- Network Proximity (35 pts): Z < -2, p < 0.01 earns full points. A drug whose targets are in a different network neighborhood from the disease module scores near zero here. Do not claim proximity without computing the Z-score.
- Clinical Evidence (25 pts): Approved for related indication earns full points. Clinical trial evidence earns partial credit. Computational prediction alone earns none.
- Target-Disease Association (20 pts): Strong genetic evidence (GWAS, rare variants) for the drug's primary targets in the new disease.
- Safety Profile (10 pts): FDA-approved, favorable safety in target population.
- Mechanism Plausibility (10 pts): A clear pathway mechanism with functional evidence, not just co-mention in literature.
Priority tiers: 80-100 = high repurposing potential (proceed to experimental validation); 60-79 = good potential (needs mechanistic validation); 40-59 = moderate potential (high-risk/high-reward); 0-39 = low potential.
Evidence grades: T1 = human clinical proof; T2 = functional experimental evidence (IC50 < 1 uM, CRISPR screen); T3 = association/computational (GWAS hit, network proximity); T4 = prediction or text-mining only.
Full scoring details: SCORING_REFERENCE.md
---
Workflow Overview
Phase 0: Entity Disambiguation and Report Setup
- Create report file immediately
- Resolve entity to all required IDs (ChEMBL, DrugBank, PubChem CID, Ensembl, MONDO/EFO)
- Tools:
OpenTargets_get_drug_chembId_by_generic_name,drugbank_get_drug_basic_info_by_drug_name_or_id,PubChem_get_CID_by_compound_name,OpenTargets_get_target_id_description_by_name,OpenTargets_get_disease_id_description_by_name
Phase 1: Network Node Identification
- Compound nodes: Drug targets, mechanism of action, current indications
- Target nodes: Disease-associated genes, GWAS targets, druggability levels
- Disease nodes: Related diseases, hierarchy, phenotypes
- Tools:
OpenTargets_get_drug_mechanisms_of_action_by_chemblId,OpenTargets_get_associated_targets_by_drug_chemblId,drugbank_get_targets_by_drug_name_or_drugbank_id,DGIdb_get_drug_gene_interactions,CTD_get_chemical_gene_interactions,OpenTargets_get_associated_targets_by_disease_efoId,Pharos_get_target
Phase 2: Network Edge Construction
- C-T edges: Bioactivity data (ChEMBL, DrugBank, BindingDB)
- T-D edges: Genetic/functional associations (OpenTargets evidence, GWAS, CTD)
- C-D edges: Clinical trials, CTD chemical-disease, literature co-mentions
- T-T edges: PPI network (STRING, IntAct, OpenTargets interactions, HumanBase)
- Tools:
ChEMBL_get_target_activities,OpenTargets_target_disease_evidence,GWAS_search_associations_by_gene,search_clinical_trials,CTD_get_chemical_diseases,STRING_get_interaction_partners,STRING_get_network,intact_search_interactions,humanbase_ppi_analysis
Phase 3: Network Analysis
- Hub identification: which targets are most connected in the drug-disease subnetwork
- Shortest paths between drug targets and disease genes: how many hops, through which intermediaries
- Network proximity Z-score: are drug targets closer to disease module than random expectation
- Use the `Network_proximity` tool — Guney/Barabasi (2016) + Menche (2015) set-distance with a degree-matched Z-score, computed deterministically from a graph you supply (inline
edgesor anedgelist_path) plus two node sets (set_a/set_b, or the aliasestargets/disease_genes).measure=closest(default),shortest, orseparation(s_AB < 0 ⇒ overlapping modules). Returnsvalue,z_score,p_value. - Feed it the edges from Phase 2. Tested end-to-end:
STRING_get_networkreturns rows withpreferredName_A/preferredName_B(gene symbols) — map each to a[A, B]pair and pass asedges; the IDs line up with symbol-based gene sets natively (no conversion). - Use a LARGE interactome for the null. A small query-centered subnetwork (e.g.
STRING_get_networkwith a lowlimit) makes the degree-matched random sets nearly identical to the real ones, giving an uninformativez>0, p≈1. For a meaningful Z, pull the broad interactome (highlimit, or a full network viaNDEx_get_network), not just the immediate neighborhood. (The skill'sscripts/network_proximity.py, which downloads the full STRING network, is the CLI equivalent.) - Functional enrichment to identify shared biological processes
- Tools:
Network_proximity,STRING_functional_enrichment,STRING_ppi_enrichment,enrichr_gene_enrichment_analysis,ReactomeAnalysis_pathway_enrichment
Phase 4: Drug Repurposing Predictions
- Identify drugs targeting disease genes (disease-to-compound mode)
- Find diseases associated with drug targets (compound-to-disease mode)
- Rank candidates by composite Network Pharmacology Score
- Predict mechanisms via shared pathways and network paths
- Tools:
OpenTargets_get_associated_drugs_by_target_ensemblID,drugbank_get_drug_name_and_description_by_target_name,drugbank_get_pathways_reactions_by_drug_or_id
Phase 5: Polypharmacology Analysis
- Classify each secondary target as contributing to efficacy or representing off-target risk
- Disease module coverage: what fraction of disease genes are hit directly or within 1 hop
- Target family analysis and selectivity
- Tools:
OpenTargets_get_target_classes_by_ensemblID,DGIdb_get_gene_druggability,OpenTargets_get_target_tractability_by_ensemblID
Phase 6: Safety and Toxicity Context
- Adverse event profiling (FAERS disproportionality, OpenTargets AEs)
- Target safety (gene constraints, expression, safety profiles)
- FDA warnings, black box status
- Tools:
FAERS_calculate_disproportionality,FAERS_filter_serious_events,FAERS_count_death_related_by_drug,FDA_get_warnings_and_cautions_by_drug_name,OpenTargets_get_drug_adverse_events_by_chemblId,OpenTargets_get_target_safety_profile_by_ensemblID,gnomad_get_gene_constraints
Phase 7: Validation Evidence
- Clinical trials for drug-disease pair
- Literature evidence (PubMed, EuropePMC)
- ADMET predictions if SMILES available
- Pharmacogenomics data
- Tools:
search_clinical_trials,get_clinical_trial_descriptions,PubMed_search_articles,EuropePMC_search_articles,ADMETAI_predict_toxicity,PharmGKB_get_drug_details
Phase 8: Report Generation
- Compute Network Pharmacology Score from components
- Document polypharmacology reasoning (desired vs. promiscuous)
- Generate report using template
- Include completeness checklist
Full step-by-step code examples: ANALYSIS_PROCEDURES.md
Report template: REPORT_TEMPLATE.md
---
Critical Tool Parameter Notes
- DrugBank tools: ALL require
query,case_sensitive,exact_match,limit(4 params, ALL required) - FAERS analytics tools: ALL require
operationparameter - FAERS count tools: Use
medicinalproductNOTdrug_name - OpenTargets tools: Return nested
{data: {entity: {field: ...}}}structure - PubMed_search_articles: Returns plain list of dicts, NOT
{articles: [...]} - ReactomeAnalysis_pathway_enrichment: Takes space-separated
identifiersstring, NOT array - ensembl_lookup_gene: REQUIRES
species='homo_sapiens'parameter
Full tool parameter reference and response structures: TOOL_REFERENCE.md
---
Fallback Strategies
When a tool fails, try the next in chain before reporting "no data":
- Compound ID: OpenTargets drug lookup -> ChEMBL search -> PubChem CID lookup
- Target ID: OpenTargets target lookup -> ensembl_lookup_gene -> MyGene_query_genes
- Disease ID: OpenTargets disease lookup -> ols_search_efo_terms -> CTD_get_chemical_diseases
- Drug targets: OpenTargets drug mechanisms -> DrugBank targets -> DGIdb interactions
- Disease targets: OpenTargets disease targets -> CTD gene-diseases -> GWAS associations
- PPI network: STRING interactions -> OpenTargets interactions -> IntAct interactions
- Pathways: ReactomeAnalysis enrichment -> enrichr enrichment -> STRING functional enrichment
- Clinical trials: search_clinical_trials -> ClinicalTrials_search_studies -> PubMed clinical
- Safety: FAERS + FDA -> OpenTargets AEs -> DrugBank safety
- Literature: PubMed search -> EuropePMC search -> OpenTargets publications
---
Reference Files
- ANALYSIS_PROCEDURES.md - Full code examples for each phase
- REPORT_TEMPLATE.md - Markdown template for final report output
- SCORING_REFERENCE.md - Detailed scoring rubric and computation method
- TOOL_REFERENCE.md - Tool signatures, response structures, troubleshooting
- USE_PATTERNS.md - Common analysis patterns and edge case strategies
- QUICK_START.md - Quick-start guide with minimal examples
---
Related Skills
- tooluniverse-drug-repurposing - Drug repurposing without network analysis
- tooluniverse-drug-target-validation - Target validation
- tooluniverse-adverse-event-detection - Adverse event detection
- tooluniverse-systems-biology - Systems biology
- tooluniverse-protein-interactions - Protein interactions
Network Pharmacology - Detailed Analysis Procedures
Detailed code examples for each phase of the network pharmacology pipeline.
---
Phase 0: Entity Disambiguation and Report Setup
Step 0.1: Create the report file immediately.
report_path = "[entity]_network_pharmacology_report.md"
# Write header and placeholder sectionsStep 0.2: Resolve the input entity to all required identifiers.
from tooluniverse import ToolUniverse
tu = ToolUniverse(use_cache=True)
tu.load_tools()
# === COMPOUND DISAMBIGUATION ===
drug_info = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="metformin")
# Returns: {data: {search: {hits: [{id: "CHEMBL1431", name: "METFORMIN", ...}]}}}
chembl_id = drug_info['data']['search']['hits'][0]['id']
drug_desc = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName="metformin")
drugbank_info = tu.tools.drugbank_get_drug_basic_info_by_drug_name_or_id(
query="metformin", case_sensitive=False, exact_match=True, limit=1
)
# Returns: {status: "success", data: {drug_name: ..., drugbank_id: ..., ...}}
pubchem_cid = tu.tools.PubChem_get_CID_by_compound_name(name="metformin")
# Returns: {IdentifierList: {CID: [4091]}}
cid = pubchem_cid['IdentifierList']['CID'][0]
pubchem_props = tu.tools.PubChem_get_compound_properties_by_CID(cid=cid)
# Returns: {CID: ..., MolecularWeight: ..., ConnectivitySMILES: ..., IUPACName: ...}
# === TARGET DISAMBIGUATION ===
target_info = tu.tools.OpenTargets_get_target_id_description_by_name(targetName="PSEN1")
# Returns: {data: {search: {hits: [{id: "ENSG00000080815", name: "PSEN1", ...}]}}}
ensembl_id = target_info['data']['search']['hits'][0]['id']
gene_details = tu.tools.ensembl_lookup_gene(gene_id=ensembl_id, species='homo_sapiens')
mygene = tu.tools.MyGene_query_genes(query="PSEN1")
# === DISEASE DISAMBIGUATION ===
disease_info = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName="Alzheimer disease")
# Returns: {data: {search: {hits: [{id: "MONDO_0004975", name: "Alzheimer disease", ...}]}}}
disease_id = disease_info['data']['search']['hits'][0]['id']
disease_desc = tu.tools.OpenTargets_get_disease_description_by_efoId(efoId=disease_id)
disease_ids = tu.tools.OpenTargets_get_disease_ids_by_efoId(efoId=disease_id)---
Phase 1: Network Node Identification
Step 1.1: Identify compound nodes.
# Drug targets and mechanism of action
drug_moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
# Returns: {data: {drug: {mechanismsOfAction: {rows: [{mechanismOfAction, actionType, targetName, targets: [{id, approvedSymbol}]}]}}}}
drug_targets_ot = tu.tools.OpenTargets_get_associated_targets_by_drug_chemblId(chemblId=chembl_id, size=50)
drug_targets_db = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query="metformin", case_sensitive=False, exact_match=True, limit=1
)
dgidb_interactions = tu.tools.DGIdb_get_drug_gene_interactions(genes=["PSEN1", "APP", "BACE1"])
ctd_genes = tu.tools.CTD_get_chemical_gene_interactions(input_terms="Metformin")
stitch_id = tu.tools.STITCH_resolve_identifier(identifier="metformin", species=9606)
stitch_interactions = tu.tools.STITCH_get_chemical_protein_interactions(
identifiers=["CIDm000004091"], species=9606
)
drug_indications = tu.tools.OpenTargets_get_drug_indications_by_chemblId(chemblId=chembl_id, size=50)
fda_approval = tu.tools.OpenTargets_get_drug_approval_status_by_chemblId(chemblId=chembl_id)
drug_diseases = tu.tools.OpenTargets_get_associated_diseases_by_drug_chemblId(chemblId=chembl_id, size=50)Step 1.2: Identify target nodes (disease-associated targets).
disease_targets = tu.tools.OpenTargets_get_associated_targets_by_disease_efoId(efoId=disease_id, limit=50)
for target in disease_targets['data']['disease']['associatedTargets']['rows'][:10]:
evidence = tu.tools.OpenTargets_target_disease_evidence(
efoId=disease_id, ensemblId=target['target']['id']
)
gwas_studies = tu.tools.OpenTargets_search_gwas_studies_by_disease(diseaseIds=[disease_id], size=20)
ctd_diseases = tu.tools.CTD_get_gene_diseases(input_terms="PSEN1")
for gene in ["PSEN1", "APP", "BACE1"]:
pharos = tu.tools.Pharos_get_target(target_name=gene)Step 1.3: Identify disease nodes and related conditions.
related_diseases = tu.tools.OpenTargets_get_similar_entities_by_disease_efoId(efoId=disease_id, size=10, threshold=0.5)
disease_children = tu.tools.OpenTargets_get_disease_descendants_children_by_efoId(efoId=disease_id)
disease_parents = tu.tools.OpenTargets_get_disease_ancestors_parents_by_efoId(efoId=disease_id)
disease_phenotypes = tu.tools.OpenTargets_get_associated_phenotypes_by_disease_efoId(efoId=disease_id, size=20)
disease_areas = tu.tools.OpenTargets_get_disease_therapeutic_areas_by_efoId(efoId=disease_id)---
Phase 2: Network Edge Construction
Step 2.1: Compound-target edges (bioactivity data).
chembl_activities = tu.tools.ChEMBL_get_target_activities(target_chembl_id__exact="CHEMBL2111455", limit=50)
all_mechanisms = tu.tools.ChEMBL_search_mechanisms(query="metformin", limit=50)
db_targets = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query="metformin", case_sensitive=False, exact_match=True, limit=1
)
db_pharmacology = tu.tools.drugbank_get_pharmacology_by_drug_name_or_drugbank_id(
query="metformin", case_sensitive=False, exact_match=True, limit=1
)Step 2.2: Target-disease edges (genetic and functional associations).
for target in top_disease_targets[:10]:
td_evidence = tu.tools.OpenTargets_target_disease_evidence(
efoId=disease_id, ensemblId=target['target']['id']
)
for gene_symbol in ["PSEN1", "APP", "APOE"]:
gwas_assoc = tu.tools.GWAS_search_associations_by_gene(gene_name=gene_symbol)
ctd_gene_diseases = tu.tools.CTD_get_gene_diseases(input_terms="PSEN1")
pharmgkb_gene = tu.tools.PharmGKB_get_gene_details(gene_symbol="PSEN1")Step 2.3: Compound-disease edges (clinical evidence).
trials = tu.tools.search_clinical_trials(query_term="metformin", condition="Alzheimer", pageSize=20)
trials2 = tu.tools.ClinicalTrials_search_studies(query="metformin Alzheimer disease", limit=20)
ctd_chem_diseases = tu.tools.CTD_get_chemical_diseases(input_terms="Metformin")
pubmed_results = tu.tools.PubMed_search_articles(query="metformin Alzheimer disease", max_results=50)
europepmc_results = tu.tools.EuropePMC_search_articles(query="metformin Alzheimer disease", limit=50)Step 2.4: Target-target edges (PPI network).
string_ppi = tu.tools.STRING_get_interaction_partners(
protein_ids=["PSEN1", "APP", "APOE", "BACE1", "MAPT"], species=9606, limit=20
)
string_network = tu.tools.STRING_get_network(
protein_ids=["PSEN1", "APP", "APOE", "BACE1", "MAPT"], species=9606
)
intact_results = tu.tools.intact_search_interactions(query="PSEN1", max=20)
ot_interactions = tu.tools.OpenTargets_get_target_interactions_by_ensemblID(
ensemblId="ENSG00000080815", size=20
)
humanbase_ppi = tu.tools.humanbase_ppi_analysis(
gene_list=["PSEN1", "APP", "APOE", "BACE1", "MAPT"],
tissue="brain", max_node=50, interaction="sn", string_mode="physical"
)---
Phase 3: Network Analysis
Step 3.1: Network topology analysis (computed from collected data).
Compute from Phase 2 data:
1. Node Degree: Count connections per node from STRING + IntAct + OpenTargets interactions
2. Hub Identification: Nodes with degree > mean + 2*SD are hubs
3. Betweenness Centrality: Nodes on shortest paths between drug targets and disease genes
4. Network Modules: Disease module vs drug module clusters; overlap = direct relevance
5. Shortest Paths:
- Path length < 2 = direct interaction
- Path length 2-3 = close proximity
- Path length > 4 = distant, weaker associationStep 3.2: Network proximity calculation.
The true Guney/Barabasi proximity Z-score needs the full interactome plus a degree-matched random null — graph computation, not a REST call. Use the bundled script scripts/network_proximity.py (downloads STRING v12 high-confidence human PPI once, cached; computes closest-distance d_c and the degree-matched-null Z):
python scripts/network_proximity.py \
--targets EGFR,ERBB2,MET \
--disease TP53,KRAS,STK11,KEAP1,ALK \
--n-rand 1000
# -> {"d_c":..., "z_score": -2.07, "empirical_p":..., "interpretation": "..."}Score: Z < -2 (35pts), Z < -1 (20pts), Z < -0.5 (10pts), else 0pts (Z < -0.15 is the Guney significance threshold). Use --n-rand 1000 for a stable empirical p.
Quick approximation (only if you cannot run the script): count direct target–disease interactions + shared PPI partners (overlap coefficient = shared_partners / min(degree_t, degree_d)). This is a proxy, NOT the Z-score — do not report it as the Guney proximity statistic.
Step 3.3: Functional enrichment analysis.
disease_gene_symbols = [t['target']['approvedSymbol']
for t in disease_targets['data']['disease']['associatedTargets']['rows'][:20]]
string_enrichment = tu.tools.STRING_functional_enrichment(protein_ids=disease_gene_symbols, species=9606)
string_ppi_enrich = tu.tools.STRING_ppi_enrichment(protein_ids=disease_gene_symbols, species=9606)
enrichr_results = tu.tools.enrichr_gene_enrichment_analysis(
gene_list=disease_gene_symbols,
libs=["KEGG_2021_Human", "Reactome_2022", "GO_Biological_Process_2023"]
)
reactome_enrichment = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers=" ".join(disease_gene_symbols)
)---
Phase 4: Drug Repurposing Predictions
Step 4.1: Identify and rank repurposing candidates.
# Disease-to-compound mode: Find drugs targeting disease genes
for target in disease_targets['data']['disease']['associatedTargets']['rows'][:20]:
gene_symbol = target['target']['approvedSymbol']
ensembl_id = target['target']['id']
target_drugs = tu.tools.OpenTargets_get_associated_drugs_by_target_ensemblID(ensemblId=ensembl_id, size=20)
dgidb_drugs = tu.tools.DGIdb_get_drug_gene_interactions(genes=[gene_symbol])
drugbank_drugs = tu.tools.drugbank_get_drug_name_and_description_by_target_name(
query=gene_symbol, case_sensitive=False, exact_match=False, limit=20
)
# Compound-to-disease mode: Find diseases for each drug target
for target in drug_targets:
target_diseases = tu.tools.OpenTargets_get_diseases_phenotypes_by_target_ensembl(
ensemblId=target['id'], size=20
)Step 4.2: Mechanism prediction for repurposing candidates.
drug_moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=candidate_chembl_id)
drug_target_genes = [t['approvedSymbol'] for t in drug_moa_targets]
combined_genes = list(set(drug_target_genes + disease_gene_symbols[:10]))
combined_pathways = tu.tools.ReactomeAnalysis_pathway_enrichment(identifiers=" ".join(combined_genes))
drug_pathways = tu.tools.drugbank_get_pathways_reactions_by_drug_or_id(
query=drug_name, case_sensitive=False, exact_match=True, limit=1
)---
Phase 5: Polypharmacology Analysis
Step 5.1: Multi-target profiling.
all_drug_targets = tu.tools.OpenTargets_get_associated_targets_by_drug_chemblId(chemblId=chembl_id, size=100)
db_full_targets = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query=drug_name, case_sensitive=False, exact_match=True, limit=1
)
ctd_interactions = tu.tools.CTD_get_chemical_gene_interactions(input_terms=drug_name)
# Disease module coverage
drug_target_set = set(drug_target_genes)
disease_gene_set = set(disease_gene_symbols[:50])
overlap = drug_target_set & disease_gene_set
coverage = len(overlap) / len(disease_gene_set) if disease_gene_set else 0
# Target family analysis
for gene in drug_target_genes[:10]:
target_class = tu.tools.OpenTargets_get_target_classes_by_ensemblID(ensemblId=gene_ensembl_id)Step 5.2: Selectivity analysis.
for gene in drug_target_genes[:10]:
druggability = tu.tools.DGIdb_get_gene_druggability(genes=[gene])
pharos_info = tu.tools.Pharos_get_target(target_name=gene)
tractability = tu.tools.OpenTargets_get_target_tractability_by_ensemblID(ensemblId=gene_ensembl_id)---
Phase 6: Safety and Toxicity Context
Step 6.1: Adverse event profiling.
faers_ae = tu.tools.FAERS_search_reports_by_drug_and_reaction(drug_name=drug_name, limit=100)
faers_serious = tu.tools.FAERS_filter_serious_events(
operation="filter_serious_events", drug_name=drug_name, seriousness_type="all"
)
faers_death = tu.tools.FAERS_count_death_related_by_drug(medicinalproduct=drug_name)
faers_signal = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality", drug_name=drug_name, adverse_event="lactic acidosis"
)
fda_warnings = tu.tools.FDA_get_warnings_and_cautions_by_drug_name(drug_name=drug_name)
bbox_warning = tu.tools.OpenTargets_get_drug_blackbox_status_by_chembl_ID(chemblId=chembl_id)
ot_ae = tu.tools.OpenTargets_get_drug_adverse_events_by_chemblId(chemblId=chembl_id)
drug_warnings = tu.tools.OpenTargets_get_drug_warnings_by_chemblId(chemblId=chembl_id)Step 6.2: Target safety profiling.
for target_ensembl_id in drug_target_ensembl_ids[:10]:
safety = tu.tools.OpenTargets_get_target_safety_profile_by_ensemblID(ensemblId=target_ensembl_id)
constraints = tu.tools.gnomad_get_gene_constraints(gene_symbol=gene_symbol)
# High pLI (>0.9) = loss-of-function intolerant = essential gene = safety concern
expression = tu.tools.HPA_get_rna_expression_by_source(
gene_name=gene_symbol, source_type="tissue", source_name="brain"
)---
Phase 7: Validation Evidence
Step 7.1: Clinical precedent.
trials = tu.tools.search_clinical_trials(query_term=drug_name, condition=disease_name, pageSize=20)
for trial in trials.get('studies', [])[:5]:
nct_id = trial['NCT ID']
trial_details = tu.tools.ClinicalTrials_get_study(nct_id=nct_id)
trial_outcomes = tu.tools.extract_clinical_trial_outcomes(nct_id=nct_id)
trial_ae = tu.tools.extract_clinical_trial_adverse_events(nct_id=nct_id)
approved = tu.tools.OpenTargets_get_approved_indications_by_drug_chemblId(chemblId=chembl_id)Step 7.2: Literature evidence.
pubmed_evidence = tu.tools.PubMed_search_articles(
query=f"{drug_name} {disease_name} repurposing OR repositioning OR network pharmacology",
max_results=50
)
europepmc_evidence = tu.tools.EuropePMC_search_articles(query=f"{drug_name} {disease_name}", limit=50)
ot_drug_pubs = tu.tools.OpenTargets_get_publications_by_drug_chemblId(chemblId=chembl_id, size=20)
ot_disease_pubs = tu.tools.OpenTargets_get_publications_by_disease_efoId(efoId=disease_id, size=20)
guidelines = tu.tools.PubMed_Guidelines_Search(query=f"{drug_name} {disease_name}")Step 7.3: Experimental evidence.
chembl_bioactivity = tu.tools.ChEMBL_search_drugs(query=drug_name, limit=10)
if smiles:
admet = tu.tools.ADMETAI_predict_toxicity(smiles=[smiles])
bbb = tu.tools.ADMETAI_predict_BBB_penetrance(smiles=[smiles])
bioavail = tu.tools.ADMETAI_predict_bioavailability(smiles=[smiles])
pharmgkb_drug = tu.tools.PharmGKB_get_drug_details(drug_name=drug_name)
pharmgkb_clin = tu.tools.PharmGKB_get_clinical_annotations(query=drug_name)Network Pharmacology - Quick Start Guide
Build compound-target-disease networks for drug repurposing, polypharmacology, and systems pharmacology using 60+ ToolUniverse tools.
Setup
from tooluniverse import ToolUniverse
tu = ToolUniverse(use_cache=True)
tu.load_tools()---
Use Case 1: Drug Repurposing via Network Analysis
Question: "Can metformin be repurposed for Alzheimer's disease?"
# Step 1: Resolve entities
drug_info = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="metformin")
chembl_id = drug_info['data']['search']['hits'][0]['id'] # CHEMBL1431
disease_info = tu.tools.OpenTargets_get_disease_id_description_by_name(
diseaseName="Alzheimer disease"
)
disease_id = disease_info['data']['search']['hits'][0]['id'] # MONDO_0004975
# Step 2: Get drug targets
drug_moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(
chemblId=chembl_id
)
drug_targets = drug_moa['data']['drug']['mechanismsOfAction']['rows']
drug_target_genes = []
for mech in drug_targets:
for t in mech.get('targets', []):
drug_target_genes.append(t['approvedSymbol'])
# Step 3: Get disease genes
disease_targets = tu.tools.OpenTargets_get_associated_targets_by_disease_efoId(
efoId=disease_id, limit=30
)
disease_genes = [
t['target']['approvedSymbol']
for t in disease_targets['data']['disease']['associatedTargets']['rows']
]
# Step 4: Build PPI network between drug targets and disease genes
combined_genes = list(set(drug_target_genes[:10] + disease_genes[:10]))
ppi_network = tu.tools.STRING_get_interaction_partners(
protein_ids=combined_genes, species=9606, limit=50
)
# Step 5: Calculate proximity (shared interactions)
drug_set = set(drug_target_genes)
disease_set = set(disease_genes)
direct_overlap = drug_set & disease_set
print(f"Direct target-disease gene overlap: {direct_overlap}")
# Step 6: Get pathway enrichment
pathways = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers=" ".join(combined_genes)
)
# Step 7: Check clinical evidence
trials = tu.tools.search_clinical_trials(
query_term="metformin", condition="Alzheimer", pageSize=10
)
# Step 8: Literature co-mentions
papers = tu.tools.PubMed_search_articles(
query="metformin Alzheimer disease repurposing", max_results=20
)
print(f"Found {len(papers)} supporting publications")---
Use Case 2: Indication Expansion
Question: "What other diseases could sorafenib treat?"
# Step 1: Resolve sorafenib
drug_info = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="sorafenib")
chembl_id = drug_info['data']['search']['hits'][0]['id']
# Step 2: Get ALL targets of sorafenib
drug_targets = tu.tools.OpenTargets_get_associated_targets_by_drug_chemblId(
chemblId=chembl_id, size=50
)
# Step 3: Get current indications
current_indications = tu.tools.OpenTargets_get_drug_indications_by_chemblId(
chemblId=chembl_id, size=50
)
# Step 4: Get ALL diseases linked to drug (investigations + trials)
all_diseases = tu.tools.OpenTargets_get_associated_diseases_by_drug_chemblId(
chemblId=chembl_id, size=100
)
# Step 5: For each drug target, find additional diseases
new_indications = []
for target in drug_targets['data']['drug']['linkedTargets']['rows'][:15]:
target_diseases = tu.tools.OpenTargets_get_diseases_phenotypes_by_target_ensembl(
ensemblId=target['id'], size=20
)
# Add to new_indications if not in current_indications
# Step 6: Rank by network proximity (shared pathway analysis)
target_symbols = [t.get('approvedSymbol', '') for t in
drug_targets['data']['drug']['linkedTargets']['rows'][:15]]
enrichment = tu.tools.enrichr_gene_enrichment_analysis(
gene_list=[s for s in target_symbols if s],
libs=["KEGG_2021_Human", "Reactome_2022"]
)
# Step 7: Safety check for each new indication
drug_ae = tu.tools.OpenTargets_get_drug_adverse_events_by_chemblId(chemblId=chembl_id)---
Use Case 3: Target-Driven Compound Discovery
Question: "What compounds modulate EGFR and related pathways?"
# Step 1: Resolve EGFR
target_info = tu.tools.OpenTargets_get_target_id_description_by_name(targetName="EGFR")
ensembl_id = target_info['data']['search']['hits'][0]['id']
# Step 2: Get compounds targeting EGFR
egfr_drugs = tu.tools.OpenTargets_get_associated_drugs_by_target_ensemblID(
ensemblId=ensembl_id, size=50
)
# Step 3: Get PPI partners of EGFR
egfr_ppi = tu.tools.OpenTargets_get_target_interactions_by_ensemblID(
ensemblId=ensembl_id, size=30
)
ppi_genes = [
row['targetB']['approvedSymbol']
for row in egfr_ppi['data']['target']['interactions']['rows']
]
# Step 4: Get drugs for PPI partners (expanding to pathway)
for neighbor_gene in ppi_genes[:5]:
neighbor_drugs = tu.tools.DGIdb_get_drug_gene_interactions(genes=[neighbor_gene])
# Step 5: Get pathway context
egfr_pathways = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers=f"EGFR {' '.join(ppi_genes[:10])}"
)
# Step 6: Druggability assessment
druggability = tu.tools.DGIdb_get_gene_druggability(genes=["EGFR"])
tractability = tu.tools.OpenTargets_get_target_tractability_by_ensemblID(
ensemblId=ensembl_id
)---
Use Case 4: Disease-Driven Drug Discovery
Question: "Find FDA-approved drugs that could treat lupus"
# Step 1: Resolve lupus
disease_info = tu.tools.OpenTargets_get_disease_id_description_by_name(
diseaseName="systemic lupus erythematosus"
)
disease_id = disease_info['data']['search']['hits'][0]['id']
# Step 2: Get disease targets
disease_targets = tu.tools.OpenTargets_get_associated_targets_by_disease_efoId(
efoId=disease_id, limit=30
)
# Step 3: Get drugs already investigated for lupus
lupus_drugs = tu.tools.OpenTargets_get_associated_drugs_by_disease_efoId(
efoId=disease_id, size=50
)
# Step 4: Find NEW drugs for disease targets via DGIdb
disease_gene_symbols = [
t['target']['approvedSymbol']
for t in disease_targets['data']['disease']['associatedTargets']['rows'][:15]
]
new_drug_candidates = tu.tools.DGIdb_get_drug_gene_interactions(
genes=disease_gene_symbols[:10]
)
# Step 5: Build disease PPI network
string_ppi = tu.tools.STRING_get_interaction_partners(
protein_ids=disease_gene_symbols[:15], species=9606, limit=30
)
# Step 6: Check CTD for chemical-disease links
ctd_chemicals = tu.tools.CTD_get_disease_chemicals(
input_terms="Lupus Erythematosus, Systemic"
)
# Step 7: Filter to FDA-approved only
for drug_candidate in new_drug_candidates['data']['genes']['nodes']:
for interaction in drug_candidate.get('interactions', []):
drug_name = interaction['drug']['name']
# Check FDA approval status
fda_info = tu.tools.ChEMBL_search_drugs(query=drug_name, limit=1)---
Use Case 5: Polypharmacology Analysis
Question: "What are the multi-target effects of aspirin?"
# Step 1: Resolve aspirin
drug_info = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="aspirin")
chembl_id = drug_info['data']['search']['hits'][0]['id']
# Step 2: Get ALL targets from multiple sources
# OpenTargets mechanisms
moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
# OpenTargets linked targets
all_targets = tu.tools.OpenTargets_get_associated_targets_by_drug_chemblId(
chemblId=chembl_id, size=100
)
# DrugBank targets
db_targets = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query="aspirin", case_sensitive=False, exact_match=True, limit=1
)
# CTD chemical-gene interactions
ctd_genes = tu.tools.CTD_get_chemical_gene_interactions(input_terms="Aspirin")
# Step 3: Classify targets (primary vs off-target)
primary_targets = [row for row in moa['data']['drug']['mechanismsOfAction']['rows']]
all_target_genes = [t.get('approvedSymbol', '') for t in
all_targets['data']['drug']['linkedTargets']['rows']]
# Step 4: Map targets to diseases
for gene in all_target_genes[:10]:
target_info = tu.tools.OpenTargets_get_target_id_description_by_name(targetName=gene)
if target_info['data']['search']['hits']:
eid = target_info['data']['search']['hits'][0]['id']
diseases = tu.tools.OpenTargets_get_diseases_phenotypes_by_target_ensembl(
ensemblId=eid, size=10
)
# Step 5: Pathway coverage analysis
enrichment = tu.tools.enrichr_gene_enrichment_analysis(
gene_list=[g for g in all_target_genes[:20] if g],
libs=["KEGG_2021_Human", "GO_Biological_Process_2023"]
)
# Step 6: Safety from polypharmacology
aspirin_ae = tu.tools.OpenTargets_get_drug_adverse_events_by_chemblId(chemblId=chembl_id)---
Use Case 6: Mechanism Elucidation
Question: "How might rapamycin affect longevity?"
# Step 1: Resolve rapamycin (sirolimus)
drug_info = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="sirolimus")
chembl_id = drug_info['data']['search']['hits'][0]['id']
# Step 2: Get mechanism of action
moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
# Step 3: Get all drug targets
drug_targets = tu.tools.OpenTargets_get_associated_targets_by_drug_chemblId(
chemblId=chembl_id, size=50
)
target_genes = [t.get('approvedSymbol', '') for t in
drug_targets['data']['drug']['linkedTargets']['rows']]
# Step 4: Build mTOR pathway network
mtor_ppi = tu.tools.STRING_get_interaction_partners(
protein_ids=["MTOR", "RPTOR", "RICTOR", "TSC1", "TSC2"],
species=9606, limit=30
)
# Step 5: Pathway analysis for mTOR signaling
mtor_pathways = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers="MTOR RPTOR RICTOR TSC1 TSC2 RPS6KB1 EIF4EBP1 AKT1 ULK1"
)
# Step 6: Link to aging-related processes
# Get GO biological process enrichment
aging_enrichment = tu.tools.enrichr_gene_enrichment_analysis(
gene_list=["MTOR", "RPTOR", "RPS6KB1", "EIF4EBP1", "ULK1", "BECN1", "ATG13"],
libs=["GO_Biological_Process_2023"]
)
# Step 7: Literature evidence for rapamycin + aging
papers = tu.tools.PubMed_search_articles(
query="rapamycin sirolimus aging longevity mTOR",
max_results=30
)
# Step 8: Clinical trials
trials = tu.tools.search_clinical_trials(
query_term="sirolimus", condition="aging", pageSize=10
)
# Step 9: Drug indications (approved + investigational)
indications = tu.tools.OpenTargets_get_drug_indications_by_chemblId(
chemblId=chembl_id, size=50
)
# Step 10: Pharmacology
pharmacology = tu.tools.drugbank_get_pharmacology_by_drug_name_or_drugbank_id(
query="sirolimus", case_sensitive=False, exact_match=True, limit=1
)---
Output Format
The skill generates a comprehensive markdown report with:
1. Executive Summary - Key findings in 2-3 sentences 2. Network Pharmacology Score (0-100) with component breakdown 3. Network Topology - Nodes, edges, hubs, modules 4. Top 10 Repurposing Candidates - Ranked with scores 5. Mechanism Predictions - Network paths explaining drug-disease connection 6. Polypharmacology Profile - Multi-target analysis 7. Safety Considerations - AE data, target safety 8. Clinical Precedent - Trials, approvals, literature 9. Evidence Summary - All findings with T1-T4 grading 10. Completeness Checklist - Analysis coverage tracking
---
Key Tool Count by Phase
| Phase | Tools | Primary Sources |
|---|---|---|
| Entity Disambiguation | 8 | OpenTargets, DrugBank, PubChem, Ensembl |
| Network Node ID | 12 | OpenTargets, DrugBank, DGIdb, CTD, Pharos |
| Network Edges | 10 | STRING, OpenTargets, IntAct, HumanBase |
| Drug-Target Edges | 8 | ChEMBL, DrugBank, DGIdb, CTD, STITCH |
| Target-Disease Edges | 6 | OpenTargets, GWAS, CTD, PharmGKB |
| Drug-Disease Edges | 5 | OpenTargets, CTD, ClinicalTrials, PubMed |
| Pathway Analysis | 4 | Reactome, Enrichr, STRING, DrugBank |
| Safety | 10 | FAERS, FDA, OpenTargets, gnomAD, HPA |
| Clinical Evidence | 6 | ClinicalTrials, PubMed, EuropePMC, OpenTargets |
| Total unique | 60+ | 15+ databases |
Network Pharmacology Report Template
Use this template when generating the final report in Phase 8.
---
# Network Pharmacology Analysis: [Entity]
## Executive Summary
[2-3 sentence summary of key findings]
## Network Pharmacology Score: [X]/100 - [Tier]
| Component | Score | Max | Evidence |
|-----------|-------|-----|----------|
| Network Proximity | X | 35 | [summary] |
| Clinical Evidence | X | 25 | [summary] |
| Target-Disease Association | X | 20 | [summary] |
| Safety Profile | X | 10 | [summary] |
| Mechanism Plausibility | X | 10 | [summary] |
| **TOTAL** | **X** | **100** | |
## 1. Entity Profile
### Compound: [Name]
- ChEMBL ID: [ID]
- DrugBank ID: [ID]
- SMILES: [SMILES]
- Mechanism: [MOA]
- Approval status: [status]
- Current indications: [list]
### Disease: [Name]
- MONDO/EFO ID: [ID]
- Description: [brief]
- Top associated targets: [list with scores]
- Related diseases: [list]
## 2. Network Topology Summary
- **Total nodes**: X (Y compounds, Z targets, W diseases)
- **Total edges**: X (Y C-T, Z T-D, W C-D, V T-T)
- **Network density**: X
- **Hub nodes**: [list of top hub genes]
- **Modules detected**: X
### Drug Target Module
[List drug targets with degree and betweenness]
### Disease Gene Module
[List disease genes with degree and betweenness]
### Module Overlap
[Shared genes, shared pathways, overlap coefficient]
## 3. Network Proximity
- **Proximity measure**: [metric used]
- **Z-score**: [value]
- **Direct interactions**: X drug target-disease gene pairs
- **Shared PPI partners**: X genes
- **Shared pathways**: X pathways
- **Interpretation**: [strong/moderate/weak proximity]
## 4. Top Repurposing Candidates (Ranked)
### Candidate 1: [Drug Name] - Score: X/100
**ChEMBL ID**: [ID] | **Status**: [Approved/Clinical/Preclinical]
**Current indications**: [list]
**Network path**: Drug -> [target1, target2] -> [PPI] -> [disease gene1, gene2]
**Mechanism prediction**: [how drug could work for disease]
**Clinical evidence**: [trials, literature]
**Safety**: [key concerns]
**Evidence grade**: [T1-T4]
[Repeat for top 10 candidates]
## 5. Polypharmacology Profile
### Target Coverage
- Total drug targets: X
- Disease module targets hit: Y (Z%)
- Primary targets: [list with actions]
- Off-targets: [list with potential effects]
### Multi-Target Effects
[Analysis of synergistic vs antagonistic target modulation]
### Disease Module Coverage
[How well drug targets cover the disease network]
## 6. Pathway Analysis
### Drug-Affected Pathways
[Ranked list of pathways affected by drug]
### Disease-Associated Pathways
[Ranked list of pathways associated with disease]
### Overlapping Pathways (Mechanism)
[Pathways shared between drug and disease - these explain the mechanism]
## 7. Safety Considerations
### Adverse Events
[Top AEs with PRR/ROR where available]
### Target Safety Flags
[Targets with known safety liabilities]
### Off-Target Risks
[Off-targets in critical tissues]
### Drug-Drug Interaction Context
[Key DDI considerations]
## 8. Clinical Precedent
### Clinical Trials
[List of relevant trials with NCT IDs and status]
### Literature Evidence
[Key publications supporting or refuting repurposing hypothesis]
- N papers found for [drug] + [disease]
- Key findings: [summary]
### Pharmacogenomics
[Relevant PGx data]
## 9. Evidence Summary Table
| Finding | Source | Evidence Grade | Confidence |
|---------|--------|---------------|------------|
| [finding1] | [tool/database] | [T1-T4] | [High/Medium/Low] |
| ... | ... | ... | ... |
## 10. Recommendations
### Immediate Actions
1. [Action 1 - e.g., review clinical trial NCT00620191]
2. [Action 2 - e.g., validate mechanism in cell model]
### Further Investigation
1. [Investigation 1]
2. [Investigation 2]
### Risk Mitigation
1. [Risk 1 and mitigation strategy]
## Completeness Checklist
| Phase | Status | Tools Used | Key Findings |
|-------|--------|------------|--------------|
| Entity Disambiguation | Done/Partial/Failed | [tools] | [summary] |
| Compound Node ID | Done/Partial/Failed | [tools] | [summary] |
| Target Node ID | Done/Partial/Failed | [tools] | [summary] |
| Disease Node ID | Done/Partial/Failed | [tools] | [summary] |
| C-T Edges | Done/Partial/Failed | [tools] | [summary] |
| T-D Edges | Done/Partial/Failed | [tools] | [summary] |
| C-D Edges | Done/Partial/Failed | [tools] | [summary] |
| T-T Edges (PPI) | Done/Partial/Failed | [tools] | [summary] |
| Network Topology | Done/Partial/Failed | [computed] | [summary] |
| Network Proximity | Done/Partial/Failed | [computed] | [summary] |
| Pathway Enrichment | Done/Partial/Failed | [tools] | [summary] |
| Repurposing Candidates | Done/Partial/Failed | [tools] | [summary] |
| Mechanism Prediction | Done/Partial/Failed | [analysis] | [summary] |
| Polypharmacology | Done/Partial/Failed | [tools] | [summary] |
| Safety/Toxicity | Done/Partial/Failed | [tools] | [summary] |
| Clinical Precedent | Done/Partial/Failed | [tools] | [summary] |
| Literature Evidence | Done/Partial/Failed | [tools] | [summary] |
| Report Generation | Done/Partial/Failed | - | [summary] |Network Pharmacology Score Reference
Score Components (0-100 total)
Network Proximity (0-35 points)
- Strong proximity (Z < -2, p < 0.01): 35 points
- Moderate proximity (Z < -1, p < 0.05): 20 points
- Weak proximity (Z < -0.5): 10 points
- No proximity: 0 points
Clinical Evidence (0-25 points)
- Approved for related indication: 25 points
- Active clinical trials: 15 points
- Completed trials with positive results: 10 points
- Preclinical only: 5 points
Target-Disease Association (0-20 points)
- Strong genetic evidence (GWAS, rare variants): 20 points
- Moderate evidence (pathways, literature): 12 points
- Weak evidence (computational only): 5 points
Safety Profile (0-10 points)
- FDA-approved, favorable safety: 10 points
- Known manageable adverse events: 7 points
- Significant safety concerns: 3 points
- Black box warning relevant to indication: 0 points
Mechanism Plausibility (0-10 points)
- Clear pathway mechanism with functional evidence: 10 points
- Indirect mechanism via network neighbors: 6 points
- Purely computational prediction: 2 points
---
Priority Tiers
| Score | Tier | Recommendation |
|---|---|---|
| 80-100 | Tier 1 | High repurposing potential - proceed with experimental validation |
| 60-79 | Tier 2 | Good potential - needs mechanistic validation |
| 40-59 | Tier 3 | Moderate potential - high-risk/high-reward, needs extensive validation |
| 0-39 | Tier 4 | Low potential - consider alternative approaches |
---
Evidence Grading System
| Tier | Symbol | Criteria | Examples |
|---|---|---|---|
| T1 | [T1] | Human clinical proof, regulatory evidence | FDA-approved indication, Phase III trial, patient genomics |
| T2 | [T2] | Functional experimental evidence | Bioactivity data (IC50 < 1 uM), CRISPR screen, animal model |
| T3 | [T3] | Association/computational evidence | GWAS hit, network proximity, pathway enrichment, expression |
| T4 | [T4] | Prediction, annotation, text-mining | AlphaFold prediction, database annotation, literature co-mention |
---
Score Calculation Details
1. Network Proximity Score (0-35)
- Count direct drug target <-> disease gene interactions in PPI
- Count shared PPI partners
- Count shared pathways
- Map to Z-score equivalent based on overlap significance
2. Clinical Evidence Score (0-25)
- Search clinical trials for drug-disease pair
- Check approved indications for related diseases
- Check max clinical trial phase
3. Target-Disease Association Score (0-20)
- Average OpenTargets association score for drug targets in disease
- Weight by evidence type (genetic > functional > computational)
4. Safety Score (0-10)
- FDA approval status (+5)
- Black box warning (-3)
- Death reports proportion
- Off-target count penalty
5. Mechanism Plausibility Score (0-10)
- Known mechanism for related indication (+5)
- Pathway evidence (+3)
- Network path length to disease module (+2)
Total: sum of components (0-100)
#!/usr/bin/env python3
"""Network proximity Z-score between drug targets and a disease gene set.
Implements the Guney/Barabasi (2016) closest-distance network proximity — the
largest component of the Network Pharmacology Score — which ToolUniverse has no
tool for (it needs the full interactome + a degree-matched random null, i.e.
graph computation, not a REST call).
Method:
d_c(T, S) = mean over drug targets t of min over disease genes s of
shortest_path_length(t, s) in the human PPI network
Z = (d_c - mean(d_c_random)) / sd(d_c_random)
where the random reference is `n_rand` target-sized gene sets drawn
degree-matched to the real targets. Z < -0.15 (and a low empirical p) indicates
the drug targets are significantly closer to the disease module than chance.
Network: STRING v12 human, high-confidence edges (combined_score >= 700),
downloaded once and cached. Requires networkx + a one-time ~MB download.
Usage:
python network_proximity.py --targets EGFR,ERBB2 --disease TP53,KRAS,PIK3CA
python network_proximity.py --targets-file t.txt --disease-file d.txt --n-rand 1000
"""
from __future__ import annotations
import argparse
import gzip
import os
import random
import statistics
import sys
import tempfile
import urllib.request
STRING_LINKS = "https://stringdb-downloads.org/download/protein.links.v12.0/9606.protein.links.v12.0.txt.gz"
STRING_INFO = "https://stringdb-downloads.org/download/protein.info.v12.0/9606.protein.info.v12.0.txt.gz"
CACHE_DIR = os.path.join(tempfile.gettempdir(), "string_cache")
MIN_SCORE = 700 # high-confidence
def _cached(url, name):
os.makedirs(CACHE_DIR, exist_ok=True)
path = os.path.join(CACHE_DIR, name)
if not os.path.exists(path) or os.path.getsize(path) == 0:
sys.stderr.write(f"Downloading {name} (one-time)...\n")
urllib.request.urlretrieve(url, path)
return path
def build_graph():
"""Build the high-confidence human PPI graph with gene-symbol nodes."""
import networkx as nx
# STRING protein ID -> gene symbol
id2sym = {}
with gzip.open(_cached(STRING_INFO, "string_info.txt.gz"), "rt") as fh:
next(fh, None)
for line in fh:
parts = line.rstrip("\n").split("\t")
if len(parts) >= 2:
id2sym[parts[0]] = parts[1]
g = nx.Graph()
with gzip.open(_cached(STRING_LINKS, "string_links.txt.gz"), "rt") as fh:
next(fh, None)
for line in fh:
a, b, score = line.split()
if int(score) < MIN_SCORE:
continue
sa, sb = id2sym.get(a), id2sym.get(b)
if sa and sb and sa != sb:
g.add_edge(sa, sb)
return g
def _closest_distance(g, sources, targets_set, sp_cache):
import networkx as nx
dists = []
for s in sources:
if s not in g:
continue
if s not in sp_cache:
sp_cache[s] = nx.single_source_shortest_path_length(g, s)
d = sp_cache[s]
reachable = [d[t] for t in targets_set if t in d]
if reachable:
dists.append(min(reachable))
return statistics.mean(dists) if dists else None
def _degree_bins(g):
bins = {}
for node, deg in g.degree():
bins.setdefault(deg, []).append(node)
return bins
def _degree_matched_sample(g, ref_nodes, bins, rng):
out = []
for n in ref_nodes:
if n not in g:
continue
deg = g.degree(n)
# widen the bin until it has candidates
for w in range(0, 200):
pool = []
for d in range(max(1, deg - w), deg + w + 1):
pool += bins.get(d, [])
if len(pool) > 5:
out.append(rng.choice(pool))
break
return out
def proximity(targets, disease, n_rand=1000, seed=42):
g = build_graph()
targets = [t for t in targets if t in g]
disease = set(d for d in disease if d in g)
if not targets or not disease:
return {"error": "no targets or disease genes map to the STRING network",
"mapped_targets": targets, "mapped_disease": len(disease)}
sp_cache = {}
d_c = _closest_distance(g, targets, disease, sp_cache)
rng = random.Random(seed)
bins = _degree_bins(g)
rand_d = []
for _ in range(n_rand):
rand_targets = _degree_matched_sample(g, targets, bins, rng)
val = _closest_distance(g, rand_targets, disease, sp_cache)
if val is not None:
rand_d.append(val)
mean_r = statistics.mean(rand_d)
sd_r = statistics.pstdev(rand_d) or 1e-9
z = (d_c - mean_r) / sd_r
p = sum(1 for v in rand_d if v <= d_c) / len(rand_d)
return {
"d_c": round(d_c, 4),
"z_score": round(z, 3),
"empirical_p": round(p, 4),
"random_mean": round(mean_r, 4),
"random_sd": round(sd_r, 4),
"n_random": len(rand_d),
"mapped_targets": len(targets),
"mapped_disease": len(disease),
# Guney et al. use Z < -0.15 as the proximity threshold; empirical_p is a
# coarser support statistic (use n_rand >= 1000 for a stable p).
"interpretation": (
f"targets proximal to the disease module (Z={round(z, 2)} < -0.15)"
if z < -0.15 else
"no significant proximity (targets not closer than chance)"
),
}
def _read_list(path):
return [x.strip() for x in open(path) if x.strip()]
def main(argv=None):
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--targets")
p.add_argument("--disease")
p.add_argument("--targets-file")
p.add_argument("--disease-file")
p.add_argument("--n-rand", type=int, default=1000)
args = p.parse_args(argv)
targets = _read_list(args.targets_file) if args.targets_file else (args.targets or "").split(",")
disease = _read_list(args.disease_file) if args.disease_file else (args.disease or "").split(",")
targets = [t for t in targets if t]
disease = [d for d in disease if d]
if not targets or not disease:
p.error("provide --targets and --disease (comma-separated) or the -file forms")
import json
print(json.dumps(proximity(targets, disease, args.n_rand), indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""
Comprehensive Test Suite for Network Pharmacology Skill
Tests all phases of the network pharmacology pipeline with real examples:
- Metformin-Alzheimer's (drug repurposing)
- Aspirin-Cancer (polypharmacology)
- Statins-Inflammation (indication expansion)
- Rapamycin-Aging (mechanism elucidation)
Run: python test_network_pharmacology.py
"""
import json
import sys
import time
import traceback
from typing import Any
# ============================================================
# Test Infrastructure
# ============================================================
RESULTS = []
START_TIME = time.time()
def log_test(test_name: str, status: str, details: str = "", duration: float = 0):
"""Record test result."""
RESULTS.append({
"name": test_name,
"status": status,
"details": details,
"duration": round(duration, 2)
})
icon = "PASS" if status == "pass" else "FAIL" if status == "fail" else "WARN"
print(f" [{icon}] {test_name} ({duration:.1f}s)")
if details and status != "pass":
for line in details.split("\n")[:3]:
print(f" {line}")
def run_test(test_name: str, test_func):
"""Run a test function and capture result."""
t0 = time.time()
try:
test_func()
log_test(test_name, "pass", duration=time.time() - t0)
except AssertionError as e:
log_test(test_name, "fail", str(e), duration=time.time() - t0)
except Exception as e:
log_test(test_name, "fail", f"{type(e).__name__}: {e}", duration=time.time() - t0)
class AssertionError(Exception):
"""Custom assertion error for test framework."""
pass
def assert_true(condition, msg="Assertion failed"):
if not condition:
raise AssertionError(msg)
def assert_not_none(value, msg="Value is None"):
if value is None:
raise AssertionError(msg)
def assert_gt(a, b, msg=None):
if not a > b:
raise AssertionError(msg or f"Expected {a} > {b}")
def assert_in(item, container, msg=None):
if item not in container:
raise AssertionError(msg or f"'{item}' not found in container")
def assert_isinstance(obj, cls, msg=None):
if not isinstance(obj, cls):
raise AssertionError(msg or f"Expected {cls.__name__}, got {type(obj).__name__}")
# ============================================================
# Setup
# ============================================================
print("=" * 70)
print("Network Pharmacology Skill - Comprehensive Test Suite")
print("=" * 70)
print()
print("Loading ToolUniverse...")
t0 = time.time()
from tooluniverse import ToolUniverse
tu = ToolUniverse()
tu.load_tools()
print(f"Loaded {len(tu.all_tool_dict)} tools in {time.time()-t0:.1f}s")
print()
# ============================================================
# Phase 0: Entity Disambiguation Tests
# ============================================================
print("-" * 70)
print("PHASE 0: Entity Disambiguation")
print("-" * 70)
# Store resolved IDs for subsequent phases
RESOLVED = {}
def test_resolve_compound_metformin():
"""Test compound disambiguation for metformin."""
r = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="metformin")
assert_not_none(r)
assert_in('data', r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for metformin")
chembl_id = hits[0]['id']
assert_true(chembl_id.startswith("CHEMBL"), f"Expected CHEMBL ID, got {chembl_id}")
RESOLVED['metformin_chembl'] = chembl_id
RESOLVED['metformin_name'] = hits[0]['name']
def test_resolve_compound_pubchem():
"""Test PubChem CID resolution for metformin."""
r = tu.tools.PubChem_get_CID_by_compound_name(name="metformin")
assert_not_none(r)
assert_in('IdentifierList', r)
cids = r['IdentifierList']['CID']
assert_gt(len(cids), 0, "No CID for metformin")
RESOLVED['metformin_cid'] = cids[0]
def test_resolve_compound_drugbank():
"""Test DrugBank resolution for metformin."""
r = tu.tools.drugbank_get_drug_basic_info_by_drug_name_or_id(
query="metformin", case_sensitive=False, exact_match=True, limit=1
)
assert_not_none(r)
assert_isinstance(r, dict)
# DrugBank response structure
if 'status' in r and r['status'] == 'success':
data = r.get('data', {})
assert_not_none(data, "No data in DrugBank response")
RESOLVED['metformin_drugbank'] = data.get('drugbank_id', 'unknown')
elif 'data' in r:
RESOLVED['metformin_drugbank'] = r['data'].get('drugbank_id', 'unknown')
else:
# Some versions return differently
RESOLVED['metformin_drugbank'] = 'DB00331'
def test_resolve_disease_alzheimer():
"""Test disease disambiguation for Alzheimer's."""
r = tu.tools.OpenTargets_get_disease_id_description_by_name(
diseaseName="Alzheimer disease"
)
assert_not_none(r)
assert_in('data', r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for Alzheimer disease")
disease_id = hits[0]['id']
RESOLVED['alzheimer_id'] = disease_id
RESOLVED['alzheimer_name'] = hits[0]['name']
def test_resolve_target_psen1():
"""Test target disambiguation for PSEN1."""
r = tu.tools.OpenTargets_get_target_id_description_by_name(targetName="PSEN1")
assert_not_none(r)
assert_in('data', r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for PSEN1")
ensembl_id = hits[0]['id']
assert_true(ensembl_id.startswith("ENSG"), f"Expected ENSG ID, got {ensembl_id}")
RESOLVED['psen1_ensembl'] = ensembl_id
def test_resolve_target_ensembl():
"""Test Ensembl gene lookup."""
ensembl_id = RESOLVED.get('psen1_ensembl', 'ENSG00000080815')
r = tu.tools.ensembl_lookup_gene(gene_id=ensembl_id, species='homo_sapiens')
assert_not_none(r)
assert_in('status', r)
assert_true(r['status'] == 'success', f"Ensembl lookup failed: {r.get('status')}")
data = r['data']
assert_in('display_name', data)
def test_resolve_compound_aspirin():
"""Test compound disambiguation for aspirin."""
r = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="aspirin")
assert_not_none(r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for aspirin")
RESOLVED['aspirin_chembl'] = hits[0]['id']
def test_resolve_compound_sirolimus():
"""Test compound disambiguation for sirolimus (rapamycin)."""
r = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="sirolimus")
assert_not_none(r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for sirolimus")
RESOLVED['sirolimus_chembl'] = hits[0]['id']
def test_resolve_compound_atorvastatin():
"""Test compound disambiguation for atorvastatin (statin)."""
r = tu.tools.OpenTargets_get_drug_chembId_by_generic_name(drugName="atorvastatin")
assert_not_none(r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for atorvastatin")
RESOLVED['atorvastatin_chembl'] = hits[0]['id']
# Run Phase 0 tests
for test_func in [
test_resolve_compound_metformin,
test_resolve_compound_pubchem,
test_resolve_compound_drugbank,
test_resolve_disease_alzheimer,
test_resolve_target_psen1,
test_resolve_target_ensembl,
test_resolve_compound_aspirin,
test_resolve_compound_sirolimus,
test_resolve_compound_atorvastatin,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 1: Network Node Identification
# ============================================================
print("-" * 70)
print("PHASE 1: Network Node Identification")
print("-" * 70)
NETWORK_DATA = {}
def test_drug_targets_opentargets():
"""Test drug target identification from OpenTargets."""
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
r = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
assert_not_none(r)
assert_in('data', r)
moa = r['data']['drug']['mechanismsOfAction']
assert_in('rows', moa)
assert_gt(len(moa['rows']), 0, "No mechanisms found for metformin")
NETWORK_DATA['metformin_moa'] = moa['rows']
# Extract target genes
target_genes = []
for mech in moa['rows']:
for t in mech.get('targets', []):
target_genes.append(t.get('approvedSymbol', ''))
NETWORK_DATA['metformin_target_genes'] = [g for g in target_genes if g]
assert_gt(len(NETWORK_DATA['metformin_target_genes']), 0, "No target genes extracted")
def test_drug_targets_drugbank():
"""Test drug target identification from DrugBank."""
r = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query="metformin", case_sensitive=False, exact_match=True, limit=1
)
assert_not_none(r)
assert_isinstance(r, dict)
data = r.get('data', r)
if isinstance(data, dict) and 'targets' in data:
targets = data['targets']
assert_gt(len(targets), 0, "No DrugBank targets for metformin")
NETWORK_DATA['metformin_db_targets'] = targets
def test_drug_targets_dgidb():
"""Test drug-gene interactions from DGIdb."""
r = tu.tools.DGIdb_get_drug_gene_interactions(genes=["PSEN1", "BACE1"])
assert_not_none(r)
assert_in('data', r)
nodes = r['data']['genes']['nodes']
assert_gt(len(nodes), 0, "No DGIdb results for PSEN1/BACE1")
total_interactions = sum(len(n.get('interactions', [])) for n in nodes)
assert_gt(total_interactions, 0, "No drug-gene interactions found")
def test_disease_targets_opentargets():
"""Test disease target identification from OpenTargets."""
disease_id = RESOLVED.get('alzheimer_id', 'MONDO_0004975')
r = tu.tools.OpenTargets_get_associated_targets_by_disease_efoId(
efoId=disease_id, limit=30
)
assert_not_none(r)
assert_in('data', r)
targets = r['data']['disease']['associatedTargets']
assert_in('rows', targets)
assert_gt(len(targets['rows']), 0, "No disease targets for Alzheimer")
assert_gt(targets['count'], 100, "Expected >100 Alzheimer targets")
NETWORK_DATA['alzheimer_targets'] = targets['rows']
NETWORK_DATA['alzheimer_genes'] = [
t['target']['approvedSymbol'] for t in targets['rows']
]
def test_disease_drugs_opentargets():
"""Test disease-associated drugs from OpenTargets."""
disease_id = RESOLVED.get('alzheimer_id', 'MONDO_0004975')
r = tu.tools.OpenTargets_get_associated_drugs_by_disease_efoId(
efoId=disease_id, size=10
)
assert_not_none(r)
assert_in('data', r)
drugs = r['data']['disease']['knownDrugs']
assert_gt(drugs['count'], 0, "No drugs investigated for Alzheimer")
NETWORK_DATA['alzheimer_drugs'] = drugs['rows']
def test_related_diseases():
"""Test related disease identification."""
disease_id = RESOLVED.get('alzheimer_id', 'MONDO_0004975')
r = tu.tools.OpenTargets_get_similar_entities_by_disease_efoId(
efoId=disease_id, size=10, threshold=0.5
)
assert_not_none(r)
assert_in('data', r)
similar = r['data']['disease']['similarEntities']
assert_gt(len(similar), 0, "No similar entities for Alzheimer")
def test_ctd_chemical_genes():
"""Test CTD chemical-gene interactions."""
r = tu.tools.CTD_get_chemical_gene_interactions(input_terms="Metformin")
assert_not_none(r)
if isinstance(r, dict) and 'data' in r:
data = r['data']
if isinstance(data, list):
assert_gt(len(data), 0, "No CTD gene interactions for metformin")
NETWORK_DATA['metformin_ctd_genes'] = data
def test_drug_indications():
"""Test drug indication retrieval."""
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
r = tu.tools.OpenTargets_get_drug_indications_by_chemblId(
chemblId=chembl_id, size=20
)
assert_not_none(r)
assert_in('data', r)
indications = r['data']['drug']['indications']['rows']
assert_gt(len(indications), 0, "No indications for metformin")
# Run Phase 1 tests
for test_func in [
test_drug_targets_opentargets,
test_drug_targets_drugbank,
test_drug_targets_dgidb,
test_disease_targets_opentargets,
test_disease_drugs_opentargets,
test_related_diseases,
test_ctd_chemical_genes,
test_drug_indications,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 2: Network Edge Construction
# ============================================================
print("-" * 70)
print("PHASE 2: Network Edge Construction")
print("-" * 70)
def test_string_ppi():
"""Test STRING protein-protein interaction retrieval."""
genes = NETWORK_DATA.get('alzheimer_genes', ['PSEN1', 'APP', 'APOE'])[:5]
r = tu.tools.STRING_get_interaction_partners(
protein_ids=genes, species=9606, limit=20
)
assert_not_none(r)
assert_isinstance(r, dict)
if 'data' in r:
data = r['data']
assert_isinstance(data, list)
assert_gt(len(data), 0, "No STRING PPI data")
# Verify expected fields
first = data[0]
assert_in('preferredName_A', first)
assert_in('preferredName_B', first)
assert_in('score', first)
NETWORK_DATA['string_ppi'] = data
def test_string_network():
"""Test STRING network retrieval."""
genes = NETWORK_DATA.get('alzheimer_genes', ['PSEN1', 'APP', 'APOE'])[:5]
r = tu.tools.STRING_get_network(protein_ids=genes, species=9606)
assert_not_none(r)
def test_opentargets_ppi():
"""Test OpenTargets target interactions (PPI)."""
ensembl_id = RESOLVED.get('psen1_ensembl', 'ENSG00000080815')
r = tu.tools.OpenTargets_get_target_interactions_by_ensemblID(
ensemblId=ensembl_id, size=10
)
assert_not_none(r)
assert_in('data', r)
interactions = r['data']['target']['interactions']
assert_gt(interactions['count'], 0, "No OT PPI for PSEN1")
NETWORK_DATA['psen1_ppi'] = interactions['rows']
def test_intact_interactions():
"""Test IntAct interaction search."""
r = tu.tools.intact_search_interactions(query="PSEN1", max=10)
assert_not_none(r)
def test_drug_disease_clinical_trials():
"""Test clinical trial evidence for drug-disease edge."""
r = tu.tools.search_clinical_trials(
query_term="metformin", condition="Alzheimer", pageSize=5
)
assert_not_none(r)
assert_in('studies', r)
assert_gt(len(r['studies']), 0, "No clinical trials for metformin + Alzheimer")
NETWORK_DATA['metformin_alzheimer_trials'] = r['studies']
def test_ctd_chemical_diseases():
"""Test CTD chemical-disease associations."""
r = tu.tools.CTD_get_chemical_diseases(input_terms="Metformin")
assert_not_none(r)
if isinstance(r, dict) and 'data' in r:
data = r['data']
if isinstance(data, list):
assert_gt(len(data), 0, "No CTD chemical-disease data")
# Check for therapeutic entries
therapeutic = [d for d in data if d.get('DirectEvidence') == 'therapeutic']
assert_gt(len(therapeutic), 0, "No therapeutic CTD entries for metformin")
def test_pubmed_comention():
"""Test PubMed literature co-mention search."""
r = tu.tools.PubMed_search_articles(
query="metformin Alzheimer disease", max_results=10
)
assert_not_none(r)
assert_isinstance(r, list)
assert_gt(len(r), 0, "No PubMed papers for metformin + Alzheimer")
# Verify structure
first = r[0]
assert_in('pmid', first)
assert_in('title', first)
NETWORK_DATA['metformin_alzheimer_papers'] = r
def test_target_disease_evidence():
"""Test OpenTargets target-disease evidence."""
disease_id = RESOLVED.get('alzheimer_id', 'MONDO_0004975')
r = tu.tools.OpenTargets_target_disease_evidence(
efoId=disease_id, ensemblId='ENSG00000080815' # PSEN1
)
assert_not_none(r)
# Run Phase 2 tests
for test_func in [
test_string_ppi,
test_string_network,
test_opentargets_ppi,
test_intact_interactions,
test_drug_disease_clinical_trials,
test_ctd_chemical_diseases,
test_pubmed_comention,
test_target_disease_evidence,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 3: Network Analysis
# ============================================================
print("-" * 70)
print("PHASE 3: Network Analysis & Enrichment")
print("-" * 70)
def test_string_functional_enrichment():
"""Test STRING functional enrichment for disease genes."""
genes = NETWORK_DATA.get('alzheimer_genes', ['PSEN1', 'APP', 'APOE', 'BACE1', 'MAPT'])[:10]
r = tu.tools.STRING_functional_enrichment(
protein_ids=genes, species=9606
)
assert_not_none(r)
def test_string_ppi_enrichment():
"""Test STRING PPI enrichment statistics."""
genes = NETWORK_DATA.get('alzheimer_genes', ['PSEN1', 'APP', 'APOE', 'BACE1', 'MAPT'])[:10]
r = tu.tools.STRING_ppi_enrichment(
protein_ids=genes, species=9606
)
assert_not_none(r)
def test_reactome_pathway_enrichment():
"""Test Reactome pathway enrichment analysis."""
genes = NETWORK_DATA.get('alzheimer_genes', ['PSEN1', 'APP', 'APOE', 'BACE1', 'MAPT'])[:10]
r = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers=" ".join(genes)
)
assert_not_none(r)
assert_in('data', r)
pathways = r['data']['pathways']
assert_gt(len(pathways), 0, "No Reactome pathways found")
# Check structure
first_pw = pathways[0]
assert_in('pathway_id', first_pw)
assert_in('name', first_pw)
assert_in('p_value', first_pw)
NETWORK_DATA['alzheimer_pathways'] = pathways
def test_enrichr_analysis():
"""Test Enrichr gene enrichment analysis."""
genes = NETWORK_DATA.get('alzheimer_genes', ['PSEN1', 'APP', 'APOE', 'BACE1', 'MAPT'])[:10]
r = tu.tools.enrichr_gene_enrichment_analysis(
gene_list=genes,
libs=["KEGG_2021_Human", "Reactome_2022"]
)
assert_not_none(r)
def test_network_topology_computation():
"""Test network topology computation from PPI data."""
ppi_data = NETWORK_DATA.get('string_ppi', [])
if not ppi_data:
raise AssertionError("No PPI data available for topology computation")
# Build adjacency from PPI data
adjacency = {}
for edge in ppi_data:
a = edge.get('preferredName_A', '')
b = edge.get('preferredName_B', '')
if a and b:
adjacency.setdefault(a, set()).add(b)
adjacency.setdefault(b, set()).add(a)
assert_gt(len(adjacency), 0, "Empty adjacency network")
# Compute degree
degrees = {node: len(neighbors) for node, neighbors in adjacency.items()}
max_degree_node = max(degrees, key=degrees.get)
assert_gt(degrees[max_degree_node], 0, "Max degree is 0")
# Hub identification
mean_degree = sum(degrees.values()) / len(degrees)
hubs = [n for n, d in degrees.items() if d > mean_degree * 1.5]
NETWORK_DATA['adjacency'] = adjacency
NETWORK_DATA['degrees'] = degrees
NETWORK_DATA['hubs'] = hubs
def test_network_proximity_computation():
"""Test network proximity calculation between drug and disease targets."""
drug_targets = set(NETWORK_DATA.get('metformin_target_genes', []))
disease_genes = set(NETWORK_DATA.get('alzheimer_genes', [])[:30])
adjacency = NETWORK_DATA.get('adjacency', {})
if not drug_targets or not disease_genes:
raise AssertionError("Missing drug targets or disease genes for proximity")
# Direct overlap
direct_overlap = drug_targets & disease_genes
NETWORK_DATA['direct_overlap'] = direct_overlap
# Shared PPI partners (if adjacency available)
if adjacency:
drug_neighbors = set()
for g in drug_targets:
drug_neighbors.update(adjacency.get(g, set()))
disease_neighbors = set()
for g in disease_genes:
disease_neighbors.update(adjacency.get(g, set()))
shared_neighbors = drug_neighbors & disease_neighbors
NETWORK_DATA['shared_ppi_partners'] = shared_neighbors
# This test passes if we can compute any proximity metric
assert_true(True, "Proximity computation completed")
# Run Phase 3 tests
for test_func in [
test_string_functional_enrichment,
test_string_ppi_enrichment,
test_reactome_pathway_enrichment,
test_enrichr_analysis,
test_network_topology_computation,
test_network_proximity_computation,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 4: Drug Repurposing Predictions (Metformin-Alzheimer's)
# ============================================================
print("-" * 70)
print("PHASE 4: Drug Repurposing Predictions")
print("-" * 70)
def test_repurposing_candidates_for_disease():
"""Test repurposing candidate identification for Alzheimer's."""
disease_id = RESOLVED.get('alzheimer_id', 'MONDO_0004975')
# Get top disease targets
targets = NETWORK_DATA.get('alzheimer_targets', [])[:5]
assert_gt(len(targets), 0, "No disease targets to query")
candidates = []
for target in targets[:3]:
gene = target['target']['approvedSymbol']
ensembl = target['target']['id']
# Find drugs for this target
r = tu.tools.OpenTargets_get_associated_drugs_by_target_ensemblID(
ensemblId=ensembl, size=5
)
if r and 'data' in r:
drugs_data = r['data']['target'].get('knownDrugs', {})
for drug_row in drugs_data.get('rows', []):
candidates.append({
'drug': drug_row['drug']['name'],
'target': gene,
'phase': drug_row.get('phase', 0),
'mechanism': drug_row.get('mechanismOfAction', 'unknown')
})
assert_gt(len(candidates), 0, "No repurposing candidates found")
NETWORK_DATA['repurposing_candidates'] = candidates
def test_mechanism_prediction():
"""Test mechanism prediction via pathway analysis."""
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
assert_not_none(moa)
mechanisms = moa['data']['drug']['mechanismsOfAction']['rows']
assert_gt(len(mechanisms), 0, "No mechanisms for metformin")
# Extract mechanism names
mech_names = [m['mechanismOfAction'] for m in mechanisms]
assert_gt(len(mech_names), 0, "No mechanism names extracted")
def test_shared_pathway_analysis():
"""Test shared pathway analysis between drug targets and disease genes."""
drug_genes = NETWORK_DATA.get('metformin_target_genes', [])[:5]
disease_genes = NETWORK_DATA.get('alzheimer_genes', [])[:5]
if not drug_genes:
drug_genes = ['NDUFA10', 'MT-ND6'] # metformin targets
if not disease_genes:
disease_genes = ['PSEN1', 'APP', 'APOE']
combined = list(set(drug_genes + disease_genes))
r = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers=" ".join(combined)
)
assert_not_none(r)
assert_in('data', r)
assert_gt(len(r['data']['pathways']), 0, "No shared pathways found")
# Run Phase 4 tests
for test_func in [
test_repurposing_candidates_for_disease,
test_mechanism_prediction,
test_shared_pathway_analysis,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 5: Polypharmacology Analysis (Aspirin)
# ============================================================
print("-" * 70)
print("PHASE 5: Polypharmacology Analysis (Aspirin)")
print("-" * 70)
def test_aspirin_all_targets():
"""Test multi-target identification for aspirin."""
chembl_id = RESOLVED.get('aspirin_chembl', 'CHEMBL25')
r = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
assert_not_none(r)
moa = r['data']['drug']['mechanismsOfAction']['rows']
assert_gt(len(moa), 0, "No mechanisms for aspirin")
target_genes = []
for mech in moa:
for t in mech.get('targets', []):
sym = t.get('approvedSymbol', '')
if sym:
target_genes.append(sym)
assert_gt(len(target_genes), 1, "Aspirin should have multiple targets (polypharmacology)")
NETWORK_DATA['aspirin_targets'] = target_genes
def test_aspirin_linked_targets():
"""Test aspirin linked targets from OpenTargets."""
chembl_id = RESOLVED.get('aspirin_chembl', 'CHEMBL25')
r = tu.tools.OpenTargets_get_associated_targets_by_drug_chemblId(
chemblId=chembl_id, size=30
)
assert_not_none(r)
assert_in('data', r)
linked = r['data']['drug']['linkedTargets']
# Aspirin has 2 primary linked targets (PTGS1, PTGS2) in OpenTargets
assert_gt(linked['count'], 0, "Expected linked targets for aspirin")
# Verify PTGS targets are present
target_symbols = [t.get('approvedSymbol', '') for t in linked['rows']]
assert_true('PTGS1' in target_symbols or 'PTGS2' in target_symbols,
f"Expected PTGS1/PTGS2 in aspirin targets, got: {target_symbols}")
def test_aspirin_disease_coverage():
"""Test aspirin target coverage across diseases."""
chembl_id = RESOLVED.get('aspirin_chembl', 'CHEMBL25')
r = tu.tools.OpenTargets_get_associated_diseases_by_drug_chemblId(
chemblId=chembl_id, size=30
)
assert_not_none(r)
diseases = r['data']['drug']['linkedDiseases']
assert_gt(diseases['count'], 10, "Aspirin should be linked to many diseases")
def test_aspirin_ctd_interactions():
"""Test CTD chemical-gene interactions for aspirin."""
r = tu.tools.CTD_get_chemical_gene_interactions(input_terms="Aspirin")
assert_not_none(r)
if isinstance(r, dict) and 'data' in r:
data = r['data']
if isinstance(data, list):
assert_gt(len(data), 10, "Expected many CTD gene interactions for aspirin")
def test_aspirin_pathway_enrichment():
"""Test pathway enrichment for aspirin targets."""
targets = NETWORK_DATA.get('aspirin_targets', ['PTGS1', 'PTGS2'])[:10]
if len(targets) > 1:
r = tu.tools.enrichr_gene_enrichment_analysis(
gene_list=targets,
libs=["KEGG_2021_Human"]
)
assert_not_none(r)
# Run Phase 5 tests
for test_func in [
test_aspirin_all_targets,
test_aspirin_linked_targets,
test_aspirin_disease_coverage,
test_aspirin_ctd_interactions,
test_aspirin_pathway_enrichment,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 6: Safety and Toxicity Context
# ============================================================
print("-" * 70)
print("PHASE 6: Safety and Toxicity Context")
print("-" * 70)
def test_faers_adverse_events():
"""Test FAERS adverse event reports for metformin."""
r = tu.tools.FAERS_search_reports_by_drug_and_reaction(
drug_name="metformin", limit=10
)
assert_not_none(r)
def test_faers_death_reports():
"""Test FAERS death-related reports for metformin."""
r = tu.tools.FAERS_count_death_related_by_drug(medicinalproduct="metformin")
assert_not_none(r)
assert_isinstance(r, list)
assert_gt(len(r), 0, "No FAERS death data for metformin")
# Should have alive and death counts
terms = [item['term'] for item in r]
assert_true('death' in terms or 'alive' in terms, "No death/alive terms in FAERS")
def test_faers_disproportionality():
"""Test FAERS disproportionality analysis."""
r = tu.tools.FAERS_calculate_disproportionality(
operation="calculate_disproportionality",
drug_name="metformin",
adverse_event="lactic acidosis"
)
assert_not_none(r)
assert_isinstance(r, dict)
# Check for metrics
if 'metrics' in r:
assert_in('PRR', r['metrics'])
assert_in('ROR', r['metrics'])
def test_opentargets_adverse_events():
"""Test OpenTargets drug adverse events."""
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
r = tu.tools.OpenTargets_get_drug_adverse_events_by_chemblId(chemblId=chembl_id)
assert_not_none(r)
assert_in('data', r)
ae = r['data']['drug']['adverseEvents']
assert_gt(ae['count'], 0, "No OpenTargets AEs for metformin")
def test_drug_warnings():
"""Test drug warning retrieval."""
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
r = tu.tools.OpenTargets_get_drug_warnings_by_chemblId(chemblId=chembl_id)
assert_not_none(r)
def test_target_safety_profile():
"""Test target safety profile from OpenTargets."""
# Check safety for a known essential gene
r = tu.tools.OpenTargets_get_target_safety_profile_by_ensemblID(
ensemblId="ENSG00000080815" # PSEN1
)
assert_not_none(r)
def test_gene_constraint():
"""Test gnomAD gene constraint data."""
r = tu.tools.gnomad_get_gene_constraints(gene_symbol="PSEN1")
assert_not_none(r)
def test_fda_warnings():
"""Test FDA warnings and cautions."""
r = tu.tools.FDA_get_warnings_and_cautions_by_drug_name(drug_name="metformin")
assert_not_none(r)
# Run Phase 6 tests
for test_func in [
test_faers_adverse_events,
test_faers_death_reports,
test_faers_disproportionality,
test_opentargets_adverse_events,
test_drug_warnings,
test_target_safety_profile,
test_gene_constraint,
test_fda_warnings,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 7: Validation Evidence
# ============================================================
print("-" * 70)
print("PHASE 7: Validation Evidence")
print("-" * 70)
def test_clinical_trial_details():
"""Test clinical trial detail retrieval."""
trials = NETWORK_DATA.get('metformin_alzheimer_trials', [])
if not trials:
# Run search
r = tu.tools.search_clinical_trials(
query_term="metformin", condition="Alzheimer", pageSize=3
)
trials = r.get('studies', [])
if trials:
nct_id = trials[0]['NCT ID']
r = tu.tools.ClinicalTrials_get_study(nct_id=nct_id)
assert_not_none(r)
def test_literature_evidence_pubmed():
"""Test PubMed literature evidence for metformin-Alzheimer."""
r = tu.tools.PubMed_search_articles(
query="metformin Alzheimer disease repurposing", max_results=20
)
assert_not_none(r)
assert_isinstance(r, list)
assert_gt(len(r), 0, "No PubMed papers found")
# Check article structure
for article in r[:3]:
assert_in('pmid', article)
assert_in('title', article)
def test_literature_evidence_europepmc():
"""Test EuropePMC literature evidence."""
r = tu.tools.EuropePMC_search_articles(
query="metformin Alzheimer disease", limit=10
)
assert_not_none(r)
def test_pharmacogenomics_evidence():
"""Test PharmGKB pharmacogenomics data."""
r = tu.tools.PharmGKB_get_drug_details(drug_name="metformin")
assert_not_none(r)
def test_opentargets_publications():
"""Test OpenTargets publication data for drug."""
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
r = tu.tools.OpenTargets_get_publications_by_drug_chemblId(
chemblId=chembl_id, size=10
)
assert_not_none(r)
# Run Phase 7 tests
for test_func in [
test_clinical_trial_details,
test_literature_evidence_pubmed,
test_literature_evidence_europepmc,
test_pharmacogenomics_evidence,
test_opentargets_publications,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 8: Cross-Example Tests (Diverse Inputs)
# ============================================================
print("-" * 70)
print("PHASE 8: Cross-Example Tests (Diverse Inputs)")
print("-" * 70)
def test_statin_inflammation():
"""Test statin (atorvastatin) - inflammation network."""
chembl_id = RESOLVED.get('atorvastatin_chembl', 'CHEMBL1487')
# Get statin targets
moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
assert_not_none(moa)
mechanisms = moa['data']['drug']['mechanismsOfAction']['rows']
assert_gt(len(mechanisms), 0, "No mechanisms for atorvastatin")
# Should include HMGCR inhibitor
mech_names = [m['mechanismOfAction'] for m in mechanisms]
assert_true(
any('hmg' in m.lower() or 'reductase' in m.lower() for m in mech_names),
f"Expected HMG-CoA reductase inhibition, got: {mech_names}"
)
def test_statin_diseases():
"""Test statin associated diseases (looking for inflammation)."""
chembl_id = RESOLVED.get('atorvastatin_chembl', 'CHEMBL1487')
r = tu.tools.OpenTargets_get_associated_diseases_by_drug_chemblId(
chemblId=chembl_id, size=30
)
assert_not_none(r)
diseases = r['data']['drug']['linkedDiseases']['rows']
assert_gt(len(diseases), 5, "Expected many diseases for atorvastatin")
disease_names = [d['name'].lower() for d in diseases]
# Statins are investigated for many conditions beyond hypercholesterolemia
assert_gt(len(disease_names), 5, "Atorvastatin should have diverse indications")
def test_rapamycin_mechanism():
"""Test rapamycin (sirolimus) mechanism and target network."""
chembl_id = RESOLVED.get('sirolimus_chembl', 'CHEMBL413')
moa = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
assert_not_none(moa)
mechanisms = moa['data']['drug']['mechanismsOfAction']['rows']
assert_gt(len(mechanisms), 0, "No mechanisms for sirolimus")
# Should target mTOR
all_targets = []
for m in mechanisms:
for t in m.get('targets', []):
all_targets.append(t.get('approvedSymbol', ''))
assert_true(
'MTOR' in all_targets or 'FKBP1A' in all_targets,
f"Expected MTOR or FKBP1A in targets, got: {all_targets}"
)
def test_rapamycin_aging_trials():
"""Test rapamycin (sirolimus) clinical trials for aging."""
r = tu.tools.search_clinical_trials(
query_term="sirolimus OR rapamycin", condition="aging", pageSize=10
)
assert_not_none(r)
# May or may not find trials - aging is broad
assert_isinstance(r, dict)
def test_rapamycin_mtor_pathway():
"""Test mTOR pathway network for rapamycin mechanism."""
mtor_genes = ["MTOR", "RPTOR", "RICTOR", "TSC1", "TSC2", "RPS6KB1"]
r = tu.tools.ReactomeAnalysis_pathway_enrichment(
identifiers=" ".join(mtor_genes)
)
assert_not_none(r)
assert_in('data', r)
pathways = r['data']['pathways']
assert_gt(len(pathways), 0, "No pathways for mTOR genes")
# Check for mTOR-related pathways
pathway_names = [p['name'].lower() for p in pathways[:20]]
assert_true(
any('mtor' in name or 'pi3k' in name or 'signaling' in name for name in pathway_names),
f"Expected mTOR-related pathways, got: {pathway_names[:5]}"
)
def test_disease_to_compound_mode():
"""Test disease-to-compound analysis mode (lupus example)."""
# Resolve lupus
r = tu.tools.OpenTargets_get_disease_id_description_by_name(
diseaseName="systemic lupus erythematosus"
)
assert_not_none(r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "No hits for lupus")
lupus_id = hits[0]['id']
# Get disease targets
targets = tu.tools.OpenTargets_get_associated_targets_by_disease_efoId(
efoId=lupus_id, limit=10
)
assert_not_none(targets)
target_rows = targets['data']['disease']['associatedTargets']['rows']
assert_gt(len(target_rows), 0, "No targets for lupus")
# Get drugs for top target
top_target = target_rows[0]['target']['id']
drugs = tu.tools.OpenTargets_get_associated_drugs_by_target_ensemblID(
ensemblId=top_target, size=5
)
assert_not_none(drugs)
def test_target_centric_mode():
"""Test target-centric analysis mode (EGFR example)."""
# Resolve EGFR
r = tu.tools.OpenTargets_get_target_id_description_by_name(targetName="EGFR")
assert_not_none(r)
egfr_id = r['data']['search']['hits'][0]['id']
# Get drugs targeting EGFR
drugs = tu.tools.OpenTargets_get_associated_drugs_by_target_ensemblID(
ensemblId=egfr_id, size=10
)
assert_not_none(drugs)
assert_gt(
drugs['data']['target']['knownDrugs']['count'], 10,
"EGFR should have many drugs"
)
# Get PPI neighbors
ppi = tu.tools.OpenTargets_get_target_interactions_by_ensemblID(
ensemblId=egfr_id, size=10
)
assert_not_none(ppi)
assert_gt(
ppi['data']['target']['interactions']['count'], 0,
"EGFR should have PPI interactions"
)
def test_edge_case_novel_target():
"""Test edge case: less-studied target (Tdark)."""
# Try a less common gene
r = tu.tools.Pharos_get_target(target_name="GPR151")
assert_not_none(r) # Should return something even for less-studied targets
def test_edge_case_orphan_disease():
"""Test edge case: orphan disease with limited data."""
r = tu.tools.OpenTargets_get_disease_id_description_by_name(
diseaseName="Niemann-Pick disease"
)
assert_not_none(r)
hits = r['data']['search']['hits']
assert_gt(len(hits), 0, "Should find Niemann-Pick disease")
# Run Phase 8 tests
for test_func in [
test_statin_inflammation,
test_statin_diseases,
test_rapamycin_mechanism,
test_rapamycin_aging_trials,
test_rapamycin_mtor_pathway,
test_disease_to_compound_mode,
test_target_centric_mode,
test_edge_case_novel_target,
test_edge_case_orphan_disease,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 9: Scoring System Validation
# ============================================================
print("-" * 70)
print("PHASE 9: Scoring System Validation")
print("-" * 70)
def test_scoring_network_proximity():
"""Test Network Proximity scoring component (0-35 pts)."""
# Use collected data to compute proximity score
drug_targets = set(NETWORK_DATA.get('metformin_target_genes', []))
disease_genes = set(NETWORK_DATA.get('alzheimer_genes', [])[:50])
ppi_data = NETWORK_DATA.get('string_ppi', [])
# Count direct overlaps
direct = drug_targets & disease_genes
# Count shared PPI partners
shared = NETWORK_DATA.get('shared_ppi_partners', set())
# Score logic
score = 0
if len(direct) > 3:
score = 35 # Strong proximity
elif len(direct) > 0 or len(shared) > 5:
score = 20 # Moderate
elif len(shared) > 0 or len(ppi_data) > 10:
score = 10 # Weak
# else 0
assert_true(score >= 0, "Network proximity score should be >= 0")
assert_true(score <= 35, "Network proximity score should be <= 35")
NETWORK_DATA['proximity_score'] = score
def test_scoring_clinical_evidence():
"""Test Clinical Evidence scoring component (0-25 pts)."""
trials = NETWORK_DATA.get('metformin_alzheimer_trials', [])
score = 0
if trials:
score = 15 # Active trials found
else:
# Check if any trials exist
r = tu.tools.search_clinical_trials(
query_term="metformin", condition="Alzheimer", pageSize=5
)
if r and r.get('studies'):
score = 15
else:
score = 5 # Preclinical only
assert_true(0 <= score <= 25, f"Clinical score out of range: {score}")
NETWORK_DATA['clinical_score'] = score
def test_scoring_target_disease():
"""Test Target-Disease Association scoring (0-20 pts)."""
targets = NETWORK_DATA.get('alzheimer_targets', [])
if targets:
avg_score = sum(t['score'] for t in targets[:10]) / min(len(targets), 10)
if avg_score > 0.5:
score = 20
elif avg_score > 0.3:
score = 12
else:
score = 5
else:
score = 0
assert_true(0 <= score <= 20, f"Target-disease score out of range: {score}")
NETWORK_DATA['td_score'] = score
def test_scoring_safety():
"""Test Safety Profile scoring (0-10 pts)."""
# Metformin is FDA-approved with known safety
score = 7 # Known manageable AEs (lactic acidosis warning but rare)
# Check black box
chembl_id = RESOLVED.get('metformin_chembl', 'CHEMBL1431')
r = tu.tools.OpenTargets_get_drug_blackbox_status_by_chembl_ID(chemblId=chembl_id)
if r and isinstance(r, dict):
has_bbox = r.get('data', {}).get('drug', {}).get('hasBeenWithdrawn', False)
if has_bbox:
score = max(score - 3, 0)
assert_true(0 <= score <= 10, f"Safety score out of range: {score}")
NETWORK_DATA['safety_score'] = score
def test_scoring_mechanism():
"""Test Mechanism Plausibility scoring (0-10 pts)."""
pathways = NETWORK_DATA.get('alzheimer_pathways', [])
moa = NETWORK_DATA.get('metformin_moa', [])
score = 2 # Default: computational prediction
if moa and pathways:
score = 6 # Indirect mechanism via network neighbors
if NETWORK_DATA.get('direct_overlap'):
score = 10 # Clear pathway mechanism
assert_true(0 <= score <= 10, f"Mechanism score out of range: {score}")
NETWORK_DATA['mechanism_score'] = score
def test_total_score_computation():
"""Test total Network Pharmacology Score computation."""
total = (
NETWORK_DATA.get('proximity_score', 0) +
NETWORK_DATA.get('clinical_score', 0) +
NETWORK_DATA.get('td_score', 0) +
NETWORK_DATA.get('safety_score', 0) +
NETWORK_DATA.get('mechanism_score', 0)
)
assert_true(0 <= total <= 100, f"Total score out of range: {total}")
NETWORK_DATA['total_score'] = total
# Determine tier
if total >= 80:
tier = "Tier 1"
elif total >= 60:
tier = "Tier 2"
elif total >= 40:
tier = "Tier 3"
else:
tier = "Tier 4"
NETWORK_DATA['tier'] = tier
print(f" Metformin-Alzheimer Network Pharmacology Score: {total}/100 ({tier})")
# Run Phase 9 tests
for test_func in [
test_scoring_network_proximity,
test_scoring_clinical_evidence,
test_scoring_target_disease,
test_scoring_safety,
test_scoring_mechanism,
test_total_score_computation,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Phase 10: Integration Tests
# ============================================================
print("-" * 70)
print("PHASE 10: Integration Tests")
print("-" * 70)
def test_full_pipeline_metformin_alzheimer():
"""Test full pipeline: metformin -> Alzheimer's (compound-to-disease)."""
# Verify all phases produced data
assert_true('metformin_chembl' in RESOLVED, "Missing metformin ChEMBL ID")
assert_true('alzheimer_id' in RESOLVED, "Missing Alzheimer disease ID")
assert_true('metformin_target_genes' in NETWORK_DATA, "Missing metformin targets")
assert_true('alzheimer_genes' in NETWORK_DATA, "Missing Alzheimer genes")
assert_true('string_ppi' in NETWORK_DATA, "Missing PPI data")
assert_true('alzheimer_pathways' in NETWORK_DATA, "Missing pathway data")
assert_true('total_score' in NETWORK_DATA, "Missing total score")
def test_full_pipeline_aspirin_polypharmacology():
"""Test full pipeline: aspirin polypharmacology profile."""
assert_true('aspirin_chembl' in RESOLVED, "Missing aspirin ChEMBL ID")
assert_true('aspirin_targets' in NETWORK_DATA, "Missing aspirin targets")
# Aspirin should have multiple targets (polypharmacology)
assert_gt(
len(NETWORK_DATA.get('aspirin_targets', [])), 1,
"Aspirin should have multiple targets"
)
def test_report_data_completeness():
"""Test that all data needed for report generation is available."""
required_data = [
'metformin_chembl', 'alzheimer_id', # Entity resolution
'metformin_target_genes', 'alzheimer_genes', # Node identification
'string_ppi', # Edge construction
'alzheimer_pathways', # Network analysis
'total_score', # Scoring
]
missing = []
for key in required_data:
if key not in RESOLVED and key not in NETWORK_DATA:
missing.append(key)
assert_true(len(missing) == 0, f"Missing data for report: {missing}")
def test_network_statistics():
"""Test network statistics computation."""
ppi = NETWORK_DATA.get('string_ppi', [])
adjacency = NETWORK_DATA.get('adjacency', {})
# Compute basic statistics
stats = {
'ppi_edges': len(ppi),
'unique_nodes': len(adjacency),
'drug_targets': len(NETWORK_DATA.get('metformin_target_genes', [])),
'disease_genes': len(NETWORK_DATA.get('alzheimer_genes', [])),
'repurposing_candidates': len(NETWORK_DATA.get('repurposing_candidates', [])),
'clinical_trials': len(NETWORK_DATA.get('metformin_alzheimer_trials', [])),
'pubmed_papers': len(NETWORK_DATA.get('metformin_alzheimer_papers', [])),
}
for key, val in stats.items():
assert_true(val >= 0, f"Negative stat: {key}={val}")
print(f" Network stats: {json.dumps(stats, indent=8)}")
# Run Phase 10 tests
for test_func in [
test_full_pipeline_metformin_alzheimer,
test_full_pipeline_aspirin_polypharmacology,
test_report_data_completeness,
test_network_statistics,
]:
run_test(test_func.__doc__ or test_func.__name__, test_func)
print()
# ============================================================
# Final Report
# ============================================================
total_time = time.time() - START_TIME
print("=" * 70)
print("TEST RESULTS SUMMARY")
print("=" * 70)
print()
passed = sum(1 for r in RESULTS if r['status'] == 'pass')
failed = sum(1 for r in RESULTS if r['status'] == 'fail')
warned = sum(1 for r in RESULTS if r['status'] == 'warn')
total = len(RESULTS)
print(f"Total tests: {total}")
print(f"Passed: {passed}")
print(f"Failed: {failed}")
print(f"Warnings: {warned}")
print(f"Pass rate: {passed/total*100:.1f}%")
print(f"Total time: {total_time:.1f}s")
print()
if failed > 0:
print("FAILED TESTS:")
for r in RESULTS:
if r['status'] == 'fail':
print(f" - {r['name']}: {r['details'][:100]}")
print()
# Phase summary
phases = {
"Phase 0 - Entity Disambiguation": RESULTS[:9],
"Phase 1 - Network Node ID": RESULTS[9:17],
"Phase 2 - Network Edges": RESULTS[17:25],
"Phase 3 - Network Analysis": RESULTS[25:31],
"Phase 4 - Repurposing": RESULTS[31:34],
"Phase 5 - Polypharmacology": RESULTS[34:39],
"Phase 6 - Safety": RESULTS[39:47],
"Phase 7 - Validation": RESULTS[47:52],
"Phase 8 - Cross-Example": RESULTS[52:61],
"Phase 9 - Scoring": RESULTS[61:67],
"Phase 10 - Integration": RESULTS[67:],
}
print("PHASE BREAKDOWN:")
for phase_name, phase_results in phases.items():
if phase_results:
p = sum(1 for r in phase_results if r['status'] == 'pass')
f = sum(1 for r in phase_results if r['status'] == 'fail')
print(f" {phase_name}: {p}/{len(phase_results)} passed" +
(f" ({f} failed)" if f > 0 else ""))
print()
print(f"{'PASS' if failed == 0 else 'FAIL'} - {passed}/{total} tests passed")
print()
sys.exit(0 if failed == 0 else 1)
Network Pharmacology - Tool Parameter Reference
Verified tool signatures, response structures, and troubleshooting.
---
Compound Identification Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
OpenTargets_get_drug_chembId_by_generic_name | drugName: str | {data: {search: {hits: [{id, name, description}]}}} |
OpenTargets_get_drug_id_description_by_name | drugName: str | {data: {search: {hits: [{id, name, description}]}}} |
drugbank_get_drug_basic_info_by_drug_name_or_id | query: str, case_sensitive: bool, exact_match: bool, limit: int (ALL required) | {status, data: {drug_name, drugbank_id, ...}} |
PubChem_get_CID_by_compound_name | name: str | {IdentifierList: {CID: [int]}} |
PubChem_get_compound_properties_by_CID | cid: int | {CID, MolecularWeight, ConnectivitySMILES, IUPACName} |
ChEMBL_search_drugs | query: str, limit: int | {status, data: {drugs: [...]}} |
Target Identification Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
OpenTargets_get_target_id_description_by_name | targetName: str | {data: {search: {hits: [{id, name, description}]}}} |
ensembl_lookup_gene | gene_id: str, species: str (REQUIRED, e.g., "homo_sapiens") | {status, data: {display_name, biotype, ...}} |
MyGene_query_genes | query: str | Gene info with cross-references |
Pharos_get_target | target_name: str | Target with development level |
Disease Identification Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
OpenTargets_get_disease_id_description_by_name | diseaseName: str | {data: {search: {hits: [{id, name, description}]}}} |
OpenTargets_get_disease_description_by_efoId | efoId: str | {data: {disease: {id, name, description}}} |
OpenTargets_get_disease_ids_by_efoId | efoId: str | Disease cross-references |
Network Edge Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
STRING_get_interaction_partners | protein_ids: list[str], species: int (9606), limit: int | {status, data: [{stringId_A, stringId_B, preferredName_A, preferredName_B, score}]} |
STRING_get_network | protein_ids: list[str], species: int | Network data |
STRING_functional_enrichment | protein_ids: list[str], species: int | Enrichment results |
STRING_ppi_enrichment | protein_ids: list[str], species: int | PPI enrichment statistics |
OpenTargets_get_target_interactions_by_ensemblID | ensemblId: str, size: int | {data: {target: {interactions: {count, rows: [{intA, targetA, intB, targetB, score}]}}}} |
intact_search_interactions | query: str, max: int | Interaction data |
humanbase_ppi_analysis | gene_list: list, tissue: str, max_node: int, interaction: str, string_mode: str (ALL required) | Tissue-specific PPI |
Drug-Target Edge Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
OpenTargets_get_drug_mechanisms_of_action_by_chemblId | chemblId: str | {data: {drug: {mechanismsOfAction: {rows: [{mechanismOfAction, actionType, targets}]}}}} |
OpenTargets_get_associated_targets_by_drug_chemblId | chemblId: str, size: int | {data: {drug: {linkedTargets: {count, rows}}}} |
drugbank_get_targets_by_drug_name_or_drugbank_id | query, case_sensitive, exact_match, limit (ALL required) | {status, data: {targets: [{id, name, organism, actions}]}} |
DGIdb_get_drug_gene_interactions | genes: list[str] | {data: {genes: {nodes: [{name, interactions}]}}} |
CTD_get_chemical_gene_interactions | input_terms: str | {data: [{ChemicalName, GeneSymbol, InteractionActions}]} |
ChEMBL_get_target_activities | target_chembl_id__exact: str | Activity data with pchembl_value |
Target-Disease Edge Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
OpenTargets_get_associated_targets_by_disease_efoId | efoId: str, limit: int | {data: {disease: {associatedTargets: {count, rows: [{target: {id, approvedSymbol}, score}]}}}} |
OpenTargets_target_disease_evidence | efoId: str, ensemblId: str (BOTH required) | Evidence data across datasources |
CTD_get_gene_diseases | input_terms: str | {data: [{GeneName, DiseaseName, DirectEvidence}]} |
GWAS_search_associations_by_gene | gene_name: str | GWAS association data |
Drug-Disease Edge Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
OpenTargets_get_drug_indications_by_chemblId | chemblId: str, size: int | {data: {drug: {indications: {rows: [{disease, maxPhaseForIndication}]}}}} |
OpenTargets_get_associated_diseases_by_drug_chemblId | chemblId: str, size: int | {data: {drug: {linkedDiseases: {count, rows}}}} |
CTD_get_chemical_diseases | input_terms: str | {data: [{ChemicalName, DiseaseName, DirectEvidence}]} |
search_clinical_trials | query_term: str (REQUIRED), condition: str, pageSize: int | {studies: [{NCT ID, brief_title, ...}]} |
Pathway Analysis Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
ReactomeAnalysis_pathway_enrichment | identifiers: str (space-separated, NOT array) | {data: {pathways: [{pathway_id, name, p_value, fdr, entities_found}]}} |
enrichr_gene_enrichment_analysis | gene_list: list[str], libs: list[str] (REQUIRED) | Enrichment per library |
drugbank_get_pathways_reactions_by_drug_or_id | query, case_sensitive, exact_match, limit | Pathway data |
Safety Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
FAERS_calculate_disproportionality | operation: str, drug_name: str, adverse_event: str | {metrics: {PRR, ROR, IC}, signal_detection} |
FAERS_filter_serious_events | operation: str, drug_name: str, seriousness_type: str | Serious event data |
FAERS_count_death_related_by_drug | medicinalproduct: str | [{term, count}] |
OpenTargets_get_drug_adverse_events_by_chemblId | chemblId: str | {data: {drug: {adverseEvents: {count, rows}}}} |
OpenTargets_get_drug_warnings_by_chemblId | chemblId: str | Drug warning data |
OpenTargets_get_target_safety_profile_by_ensemblID | ensemblId: str | Target safety data |
gnomad_get_gene_constraints | gene_symbol: str | Gene constraint (pLI, LOEUF) |
FDA_get_warnings_and_cautions_by_drug_name | drug_name: str | FDA warning text |
Literature Tools
| Tool | Key Parameters | Response Structure |
|---|---|---|
PubMed_search_articles | query: str, max_results: int | list of {pmid, title, authors, journal, pub_date} |
EuropePMC_search_articles | query: str, limit: int | Article list |
OpenTargets_get_publications_by_drug_chemblId | chemblId: str, size: int | Publication data |
---
Response Format Notes
- DrugBank tools: ALL require
query,case_sensitive,exact_match,limit(4 params, ALL required). - FAERS analytics tools (disproportionality, compare, filter, stratify, rollup, trends): ALL require
operationparameter. - FAERS count tools (count_death, count_reactions, etc.): Use
medicinalproductNOTdrug_name. - OpenTargets tools: Return nested
{data: {entity: {field: ...}}}structure. - PubMed_search_articles: Returns plain list of dicts, NOT
{articles: [...]}. - PubChem CID lookup: Returns
{IdentifierList: {CID: [...]}}(NO data wrapper). - ReactomeAnalysis_pathway_enrichment: Takes space-separated
identifiersstring, NOT array. - ensembl_lookup_gene: REQUIRES
species='homo_sapiens'parameter. - STRING tools: Return
{status: "success", data: [...]}. - CTD tools: Return
{data: [...]}with potentially large result sets.
---
Fallback Strategies
| Phase | Primary Tool | Fallback 1 | Fallback 2 |
|---|---|---|---|
| Compound ID | OpenTargets drug lookup | ChEMBL search | PubChem CID lookup |
| Target ID | OpenTargets target lookup | ensembl_lookup_gene | MyGene_query_genes |
| Disease ID | OpenTargets disease lookup | ols_search_efo_terms | CTD_get_chemical_diseases |
| Drug targets | OpenTargets drug mechanisms | DrugBank targets | DGIdb interactions |
| Disease targets | OpenTargets disease targets | CTD gene-diseases | GWAS associations |
| PPI network | STRING interactions | OpenTargets interactions | IntAct interactions |
| Pathways | ReactomeAnalysis enrichment | enrichr enrichment | STRING functional enrichment |
| Clinical trials | search_clinical_trials | ClinicalTrials_search_studies | PubMed clinical |
| Safety | FAERS + FDA | OpenTargets AEs | DrugBank safety |
| Literature | PubMed search | EuropePMC search | OpenTargets publications |
---
Troubleshooting
"Disease not found":
- Try disease synonyms (e.g., "Alzheimer's disease" vs "Alzheimer disease")
- Use EFO/MONDO ID directly if known
- Search with
OpenTargets_multi_entity_search_by_query_string(queryString=...)for broader matching - Try parent disease category
"No drugs found for target":
- Target may be Tdark (no chemical tools) - check with Pharos
- Expand to target family or pathway
- Search DGIdb which aggregates multiple sources
- Check chemical probes as starting points
"No PPI data":
- Try different protein identifiers (gene symbol, UniProt, Ensembl protein)
- Use multiple PPI databases (STRING + IntAct + OpenTargets)
- Lower confidence threshold in STRING
- Use pathway co-membership as proxy for interaction
"Network proximity not significant":
- Drug targets may be functionally distant from disease module
- Try expanding disease gene set (increase limit)
- Consider indirect mechanisms via shared pathways
- Report honestly - not all drug-disease pairs have network support
"DrugBank parameter errors":
- ALL DrugBank tools require 4 params:
query,case_sensitive,exact_match,limit - Use
case_sensitive=False,exact_match=Truefor exact drug name matching - Use
exact_match=Falsefor broader searches
"FAERS operation errors":
- Analytics tools (disproportionality, compare, filter, stratify) need
operationparam - Count tools use
medicinalproductNOTdrug_name - Check FAERS tool name carefully to determine which pattern
Network Pharmacology - Use Patterns and Edge Cases
Common analysis patterns and strategies for handling edge cases.
---
Common Use Patterns
Pattern 1: Drug Repurposing via Network Proximity
Input: compound (metformin) + disease (Alzheimer disease)
Mode: compound-to-disease
Flow:
1. Resolve metformin -> CHEMBL1431, DB00331, CID:4091
2. Get metformin targets (OpenTargets, DrugBank, DGIdb)
3. Get Alzheimer disease genes (OpenTargets, GWAS)
4. Build PPI network (STRING, OpenTargets interactions)
5. Calculate proximity between drug targets and disease genes
6. Score and rank by Network Pharmacology Score
7. Predict mechanism via shared pathways
8. Validate with clinical trials and literaturePattern 2: Disease-Driven Drug Discovery
Input: disease (lupus)
Mode: disease-to-compound
Flow:
1. Resolve lupus -> MONDO/EFO ID
2. Get disease-associated targets (top 50)
3. For each target, find approved drugs (OpenTargets, DGIdb, DrugBank)
4. Build C-T-D network from all drug-target-disease edges
5. Rank drugs by: number of disease targets hit, network proximity, safety
6. Identify polypharmacology advantages (drugs hitting multiple disease targets)Pattern 3: Target-Centric Network
Input: target (EGFR)
Mode: target-centric
Flow:
1. Resolve EGFR -> ENSG00000146648
2. Get all compounds targeting EGFR (with bioactivity)
3. Get all diseases associated with EGFR
4. Build PPI network around EGFR
5. Identify which compounds could bridge to which diseases
6. Rank compound-disease pairs by network metricsPattern 4: Polypharmacology Profiling
Input: compound (aspirin)
Mode: bidirectional
Flow:
1. Resolve aspirin -> CHEMBL25
2. Get ALL targets (not just primary)
3. Map targets to disease modules
4. Identify multi-target coverage across diseases
5. Analyze synergistic vs antagonistic effects
6. Compare selectivity across target familiesPattern 5: Mechanism Elucidation
Input: compound (rapamycin) + disease (aging/longevity)
Mode: compound-to-disease
Flow:
1. Resolve rapamycin -> CHEMBL413 (sirolimus)
2. Get mechanism: mTOR inhibitor
3. Map mTOR pathway to aging-related genes
4. Trace network paths: rapamycin -> mTOR -> autophagy -> aging genes
5. Assess pathway overlap and functional enrichment
6. Provide mechanistic explanation---
Edge Cases
Promiscuous Compounds (many targets)
- Limit initial target retrieval to top 50 by confidence
- Classify into primary (mechanism) vs secondary (off-target)
- Focus network analysis on primary targets first
- Note polypharmacology implications
Orphan Diseases (limited data)
- Expand to parent disease categories in ontology
- Use related diseases from OpenTargets similar entities
- Leverage pathway-level analysis over gene-level
- Note data limitations in report
Novel Targets (no known drugs)
- Focus on target biology and disease association
- Use DGIdb druggability assessment
- Search for chemical probes (OpenTargets chemical probes)
- Suggest target-based screening approaches
Large Networks (>100 nodes)
- Prioritize top-scored edges
- Use network modules rather than full network
- Focus on shortest paths between entities
- Summarize statistics rather than listing all nodes
Disconnected Networks
- Report disconnection explicitly
- Analyze drug module and disease module separately
- Look for pathway-level connections as bridge
- Note that disconnection suggests low repurposing potential
Related skills
How it compares
Pick tooluniverse-network-pharmacology over generic literature-search skills when you need scored compound-target-disease network analysis from integrated biomedical databases.
FAQ
What data sources does tooluniverse-network-pharmacology use?
tooluniverse-network-pharmacology pulls from ChEMBL, OpenTargets, STRING, DrugBank, Reactome, FAERS, and 60+ other ToolUniverse tools to construct compound-target-disease networks for repurposing and polypharmacology analysis.
How does the Network Pharmacology Score work?
tooluniverse-network-pharmacology calculates a composite Network Pharmacology Score from 0 to 100 based on network proximity between drug targets and disease gene modules, clinical evidence, and mechanistic plausibility to rank repurposing candidates.