| import requests |
| import logging |
| import json |
| from typing import List, Dict, Optional |
|
|
| logger = logging.getLogger(__name__) |
|
|
| class CivicAPIClient: |
| """ |
| Client for CIViC (Clinical Interpretations of Variants in Cancer). |
| Uses GraphQL (API v2) to fetch evidence for molecular profiles. |
| """ |
| BASE_URL = "https://civicdb.org/api/graphql" |
|
|
| def search_variant_evidence(self, gene: str, variant: str) -> List[Dict]: |
| """ |
| Searches for evidence related to a specific gene and variant using GraphQL. |
| """ |
| logger.info(f"Searching CIViC GraphQL for {gene} {variant}...") |
| |
| |
| mp_names = [f"{gene} {variant}", f"{gene}-{variant}", f"{gene}_{variant}"] |
| |
| query = """ |
| query GetEvidenceByMolecularProfile($name: String!) { |
| molecularProfiles(name: $name, first: 1) { |
| nodes { |
| id |
| name |
| evidenceItems(first: 5) { |
| nodes { |
| id |
| status |
| description |
| evidenceType |
| evidenceDirection |
| evidenceLevel |
| clinicalSignificance |
| } |
| } |
| } |
| } |
| } |
| """ |
| |
| for name in mp_names: |
| variables = {"name": name.upper()} |
| try: |
| response = requests.post( |
| self.BASE_URL, |
| json={'query': query, 'variables': variables}, |
| timeout=15 |
| ) |
| response.raise_for_status() |
| data = response.json() |
| |
| profiles = data.get("data", {}).get("molecularProfiles", {}).get("nodes", []) |
| if profiles: |
| evidence_items = profiles[0].get("evidenceItems", {}).get("nodes", []) |
| if evidence_items: |
| return evidence_items |
| except Exception as e: |
| logger.error(f"Error querying CIViC for {name}: {e}") |
| continue |
| |
| return [] |
|
|
| class ClinicalTrialsClient: |
| """ |
| Client for ClinicalTrials.gov API v2. |
| Fetches active Phase II/III trials for specific conditions. |
| """ |
| BASE_URL = "https://clinicaltrials.gov/api/v2/studies" |
|
|
| def search_trials(self, condition: str, phases: List[str] = ["Phase 2", "Phase 3"]) -> List[Dict]: |
| """ |
| Searches for recruiting/active trials for a condition. |
| Uses query.term for phases to be more flexible. |
| """ |
| logger.info(f"Searching ClinicalTrials.gov for {condition} trials...") |
| |
| processed_trials = [] |
| |
| for phase in phases: |
| |
| params = { |
| "format": "json", |
| "query.cond": condition, |
| "query.term": phase, |
| "filter.overallStatus": "RECRUITING", |
| "pageSize": 3 |
| } |
| try: |
| resp = requests.get(self.BASE_URL, params=params, timeout=10) |
| resp.raise_for_status() |
| studies = resp.json().get("studies", []) |
| |
| for study in studies: |
| info = study.get("protocolSection", {}) |
| processed_trials.append({ |
| "nctId": info.get("identificationModule", {}).get("nctId"), |
| "title": info.get("identificationModule", {}).get("briefTitle"), |
| "status": info.get("statusModule", {}).get("overallStatus"), |
| "phase": phase, |
| "briefSummary": info.get("descriptionModule", {}).get("briefSummary", ""), |
| "eligibility": info.get("eligibilityModule", {}).get("eligibilityCriteria", "") |
| }) |
| |
| if len(processed_trials) >= 5: |
| break |
| |
| except Exception as e: |
| logger.error(f"Error querying ClinicalTrials API for phase {phase}: {e}") |
| continue |
| |
| return processed_trials |
|
|
| if __name__ == "__main__": |
| logging.basicConfig(level=logging.INFO) |
| civic = CivicAPIClient() |
| trials = ClinicalTrialsClient() |
| |
| print("\n--- Testing CIViC (BRAF V600E) ---") |
| results = civic.search_variant_evidence("BRAF", "V600E") |
| print(f"Found {len(results)} evidence items.") |
| for res in results: |
| print(f"- [{res['evidenceLevel']}] {res['clinicalSignificance']}: {res['description'][:100]}...") |
| |
| print("\n--- Testing ClinicalTrials.gov (Lung Cancer) ---") |
| results_trials = trials.search_trials("Non-Small Cell Lung Cancer") |
| print(f"Found {len(results_trials)} recruiting trials.") |
| for t in results_trials: |
| print(f"- {t['nctId']} ({t['phase']}): {t['title']}") |
|
|