ailixir-drug-repurposing / app /pipelines /pdb_structures.py
AILIXIR Bot
Auto-sync: ea9a2035cd92f6b1521e3c5f9d31e8c453a93d43
4a8b134
Raw
History Blame Contribute Delete
4.34 kB
"""
Stage 3: PDB Structure & UniProt ID Fetch
Retrieves PDB structure IDs and UniProt accession numbers for target proteins.
Includes file-based caching to avoid redundant API calls.
"""
import requests
import json
import logging
import os
from datetime import datetime
from typing import List, Dict, Optional
logger = logging.getLogger(__name__)
class PdbStructurePipeline:
def __init__(self, api_url: str = "https://rest.uniprot.org/uniprotkb/search",
cache_dir: str = None):
self.api_url = api_url
if cache_dir is None:
cache_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "data")
self.cache_dir = cache_dir
os.makedirs(self.cache_dir, exist_ok=True)
def _get_cache_path(self, symbol: str) -> str:
return os.path.join(self.cache_dir, f"cache_pdb_{symbol.lower()}.json")
def _load_from_cache(self, symbol: str) -> Optional[Dict]:
cache_path = self._get_cache_path(symbol)
if os.path.exists(cache_path):
try:
with open(cache_path, "r") as f:
data = json.load(f)
if data.get("symbol") == symbol:
logger.info(f" 📦 Loaded PDB data from cache for: {symbol}")
return data
except (json.JSONDecodeError, IOError) as e:
logger.warning(f" ⚠️ Failed to read cache for {symbol}: {e}")
return None
def _save_to_cache(self, symbol: str, data: Dict):
cache_path = self._get_cache_path(symbol)
data["fetched_at"] = datetime.utcnow().isoformat()
try:
with open(cache_path, "w") as f:
json.dump(data, f, indent=2)
except IOError as e:
logger.warning(f" ⚠️ Failed to write cache for {symbol}: {e}")
def fetch_pdb_ids(self, targets: List[Dict]) -> List[Dict]:
logger.info(f"Fetching PDB structures for {len(targets)} targets...")
enriched = []
for t in targets:
symbol = t["symbol"]
cached = self._load_from_cache(symbol)
if cached is not None:
t["pdb_ids"] = cached.get("pdb_ids", [])
t["uniprot_id"] = cached.get("uniprot_id", "")
enriched.append(t)
continue
try:
params = {
"query": f"{symbol} AND taxonomy_id:9606",
"format": "json",
"size": 1,
}
response = requests.get(self.api_url, params=params, timeout=30)
response.raise_for_status()
data = response.json()
uniprot_id = ""
pdb_ids = []
if data.get("results") and len(data["results"]) > 0:
result = data["results"][0]
uniprot_id = result.get("primaryAccession", "")
xrefs = result.get("uniProtKBCrossReferences", [])
for xref in xrefs:
if xref.get("database") == "PDB":
pdb_id = xref.get("id")
if pdb_id:
pdb_ids.append(pdb_id)
pdb_ids = pdb_ids[:3]
t["pdb_ids"] = pdb_ids
t["uniprot_id"] = uniprot_id
if pdb_ids:
logger.info(f" ✅ Found {len(pdb_ids)} PDB IDs for: {symbol}")
else:
logger.warning(f" ⚠️ No PDB structures found for: {symbol}")
self._save_to_cache(symbol, {
"symbol": symbol,
"uniprot_id": uniprot_id,
"pdb_ids": pdb_ids,
})
except requests.exceptions.RequestException as e:
logger.warning(f" ⚠️ Failed to fetch PDB for {symbol}: {e}")
t["pdb_ids"] = []
t["uniprot_id"] = ""
except Exception as e:
logger.warning(f" ⚠️ Failed to fetch PDB for {symbol}: {e}")
t["pdb_ids"] = []
t["uniprot_id"] = ""
enriched.append(t)
logger.info(f"✅ PDB fetch complete for {len(enriched)}/{len(targets)} targets")
return enriched