351 lines
12 KiB
Python
351 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
DrugBank Helper Functions
|
|
|
|
Utility functions for common DrugBank operations including:
|
|
- Drug information extraction
|
|
- Interaction analysis
|
|
- Target identification
|
|
- Chemical property extraction
|
|
|
|
Usage:
|
|
from drugbank_helper import DrugBankHelper
|
|
|
|
db = DrugBankHelper()
|
|
drug_info = db.get_drug_info('DB00001')
|
|
interactions = db.get_interactions('DB00001')
|
|
"""
|
|
|
|
from typing import Dict, List, Optional, Any
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
class DrugBankHelper:
|
|
"""Helper class for DrugBank data access and analysis"""
|
|
|
|
NAMESPACE = {'db': 'http://www.drugbank.ca'}
|
|
|
|
def __init__(self, root=None):
|
|
"""
|
|
Initialize DrugBankHelper
|
|
|
|
Args:
|
|
root: Pre-loaded XML root element. If None, will load from drugbank-downloader
|
|
"""
|
|
self.root = root
|
|
self._drug_cache = {}
|
|
|
|
def _get_root(self):
|
|
"""Lazy load DrugBank root element"""
|
|
if self.root is None:
|
|
from drugbank_downloader import get_drugbank_root
|
|
self.root = get_drugbank_root()
|
|
return self.root
|
|
|
|
def _get_text_safe(self, element) -> Optional[str]:
|
|
"""Safely extract text from XML element"""
|
|
return element.text if element is not None else None
|
|
|
|
def find_drug(self, drugbank_id: str):
|
|
"""
|
|
Find drug element by DrugBank ID
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID (e.g., 'DB00001')
|
|
|
|
Returns:
|
|
XML element for the drug or None if not found
|
|
"""
|
|
if drugbank_id in self._drug_cache:
|
|
return self._drug_cache[drugbank_id]
|
|
|
|
root = self._get_root()
|
|
for drug in root.findall('db:drug', self.NAMESPACE):
|
|
primary_id = drug.find('db:drugbank-id[@primary="true"]', self.NAMESPACE)
|
|
if primary_id is not None and primary_id.text == drugbank_id:
|
|
self._drug_cache[drugbank_id] = drug
|
|
return drug
|
|
return None
|
|
|
|
def get_drug_info(self, drugbank_id: str) -> Dict[str, Any]:
|
|
"""
|
|
Get comprehensive drug information
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID
|
|
|
|
Returns:
|
|
Dictionary with drug information including name, type, description, etc.
|
|
"""
|
|
drug = self.find_drug(drugbank_id)
|
|
if drug is None:
|
|
return {}
|
|
|
|
info = {
|
|
'drugbank_id': drugbank_id,
|
|
'name': self._get_text_safe(drug.find('db:name', self.NAMESPACE)),
|
|
'type': drug.get('type'),
|
|
'description': self._get_text_safe(drug.find('db:description', self.NAMESPACE)),
|
|
'cas_number': self._get_text_safe(drug.find('db:cas-number', self.NAMESPACE)),
|
|
'indication': self._get_text_safe(drug.find('db:indication', self.NAMESPACE)),
|
|
'pharmacodynamics': self._get_text_safe(drug.find('db:pharmacodynamics', self.NAMESPACE)),
|
|
'mechanism_of_action': self._get_text_safe(drug.find('db:mechanism-of-action', self.NAMESPACE)),
|
|
}
|
|
|
|
return info
|
|
|
|
def get_interactions(self, drugbank_id: str) -> List[Dict[str, str]]:
|
|
"""
|
|
Get all drug-drug interactions
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID
|
|
|
|
Returns:
|
|
List of interaction dictionaries
|
|
"""
|
|
drug = self.find_drug(drugbank_id)
|
|
if drug is None:
|
|
return []
|
|
|
|
interactions = []
|
|
ddi_elem = drug.find('db:drug-interactions', self.NAMESPACE)
|
|
|
|
if ddi_elem is not None:
|
|
for interaction in ddi_elem.findall('db:drug-interaction', self.NAMESPACE):
|
|
interactions.append({
|
|
'partner_id': self._get_text_safe(interaction.find('db:drugbank-id', self.NAMESPACE)),
|
|
'partner_name': self._get_text_safe(interaction.find('db:name', self.NAMESPACE)),
|
|
'description': self._get_text_safe(interaction.find('db:description', self.NAMESPACE)),
|
|
})
|
|
|
|
return interactions
|
|
|
|
def get_targets(self, drugbank_id: str) -> List[Dict[str, Any]]:
|
|
"""
|
|
Get drug targets
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID
|
|
|
|
Returns:
|
|
List of target dictionaries
|
|
"""
|
|
drug = self.find_drug(drugbank_id)
|
|
if drug is None:
|
|
return []
|
|
|
|
targets = []
|
|
targets_elem = drug.find('db:targets', self.NAMESPACE)
|
|
|
|
if targets_elem is not None:
|
|
for target in targets_elem.findall('db:target', self.NAMESPACE):
|
|
target_data = {
|
|
'id': self._get_text_safe(target.find('db:id', self.NAMESPACE)),
|
|
'name': self._get_text_safe(target.find('db:name', self.NAMESPACE)),
|
|
'organism': self._get_text_safe(target.find('db:organism', self.NAMESPACE)),
|
|
'known_action': self._get_text_safe(target.find('db:known-action', self.NAMESPACE)),
|
|
}
|
|
|
|
# Extract actions
|
|
actions_elem = target.find('db:actions', self.NAMESPACE)
|
|
if actions_elem is not None:
|
|
target_data['actions'] = [
|
|
action.text for action in actions_elem.findall('db:action', self.NAMESPACE)
|
|
]
|
|
|
|
# Extract polypeptide info
|
|
polypeptide = target.find('db:polypeptide', self.NAMESPACE)
|
|
if polypeptide is not None:
|
|
target_data['uniprot_id'] = polypeptide.get('id')
|
|
target_data['gene_name'] = self._get_text_safe(
|
|
polypeptide.find('db:gene-name', self.NAMESPACE)
|
|
)
|
|
|
|
targets.append(target_data)
|
|
|
|
return targets
|
|
|
|
def get_properties(self, drugbank_id: str) -> Dict[str, Dict[str, Any]]:
|
|
"""
|
|
Get chemical properties
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID
|
|
|
|
Returns:
|
|
Dictionary with 'calculated' and 'experimental' property dictionaries
|
|
"""
|
|
drug = self.find_drug(drugbank_id)
|
|
if drug is None:
|
|
return {'calculated': {}, 'experimental': {}}
|
|
|
|
properties = {'calculated': {}, 'experimental': {}}
|
|
|
|
# Calculated properties
|
|
calc_props = drug.find('db:calculated-properties', self.NAMESPACE)
|
|
if calc_props is not None:
|
|
for prop in calc_props.findall('db:property', self.NAMESPACE):
|
|
kind = self._get_text_safe(prop.find('db:kind', self.NAMESPACE))
|
|
value = self._get_text_safe(prop.find('db:value', self.NAMESPACE))
|
|
if kind and value:
|
|
properties['calculated'][kind] = value
|
|
|
|
# Experimental properties
|
|
exp_props = drug.find('db:experimental-properties', self.NAMESPACE)
|
|
if exp_props is not None:
|
|
for prop in exp_props.findall('db:property', self.NAMESPACE):
|
|
kind = self._get_text_safe(prop.find('db:kind', self.NAMESPACE))
|
|
value = self._get_text_safe(prop.find('db:value', self.NAMESPACE))
|
|
if kind and value:
|
|
properties['experimental'][kind] = value
|
|
|
|
return properties
|
|
|
|
def check_interaction(self, drug1_id: str, drug2_id: str) -> Optional[Dict[str, str]]:
|
|
"""
|
|
Check if two drugs interact
|
|
|
|
Args:
|
|
drug1_id: First drug DrugBank ID
|
|
drug2_id: Second drug DrugBank ID
|
|
|
|
Returns:
|
|
Interaction dictionary if interaction exists, None otherwise
|
|
"""
|
|
interactions1 = self.get_interactions(drug1_id)
|
|
for interaction in interactions1:
|
|
if interaction['partner_id'] == drug2_id:
|
|
return interaction
|
|
|
|
# Check reverse direction
|
|
interactions2 = self.get_interactions(drug2_id)
|
|
for interaction in interactions2:
|
|
if interaction['partner_id'] == drug1_id:
|
|
return interaction
|
|
|
|
return None
|
|
|
|
def check_polypharmacy(self, drug_ids: List[str]) -> List[Dict[str, Any]]:
|
|
"""
|
|
Check interactions in a drug regimen
|
|
|
|
Args:
|
|
drug_ids: List of DrugBank IDs
|
|
|
|
Returns:
|
|
List of all interactions found between the drugs
|
|
"""
|
|
all_interactions = []
|
|
|
|
for i, drug1 in enumerate(drug_ids):
|
|
for drug2 in drug_ids[i + 1:]:
|
|
interaction = self.check_interaction(drug1, drug2)
|
|
if interaction:
|
|
interaction['drug1'] = drug1
|
|
interaction['drug2'] = drug2
|
|
all_interactions.append(interaction)
|
|
|
|
return all_interactions
|
|
|
|
def get_smiles(self, drugbank_id: str) -> Optional[str]:
|
|
"""
|
|
Get SMILES structure for a drug
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID
|
|
|
|
Returns:
|
|
SMILES string or None
|
|
"""
|
|
props = self.get_properties(drugbank_id)
|
|
return props.get('calculated', {}).get('SMILES')
|
|
|
|
def get_inchi(self, drugbank_id: str) -> Optional[str]:
|
|
"""
|
|
Get InChI structure for a drug
|
|
|
|
Args:
|
|
drugbank_id: DrugBank ID
|
|
|
|
Returns:
|
|
InChI string or None
|
|
"""
|
|
props = self.get_properties(drugbank_id)
|
|
return props.get('calculated', {}).get('InChI')
|
|
|
|
def search_by_name(self, name: str, exact: bool = False) -> List[Dict[str, str]]:
|
|
"""
|
|
Search drugs by name
|
|
|
|
Args:
|
|
name: Drug name to search for
|
|
exact: If True, require exact match (case-insensitive)
|
|
|
|
Returns:
|
|
List of matching drugs with id and name
|
|
"""
|
|
root = self._get_root()
|
|
results = []
|
|
search_term = name.lower()
|
|
|
|
for drug in root.findall('db:drug', self.NAMESPACE):
|
|
drug_id = drug.find('db:drugbank-id[@primary="true"]', self.NAMESPACE).text
|
|
drug_name = self._get_text_safe(drug.find('db:name', self.NAMESPACE))
|
|
|
|
if drug_name:
|
|
if exact:
|
|
if drug_name.lower() == search_term:
|
|
results.append({'id': drug_id, 'name': drug_name})
|
|
else:
|
|
if search_term in drug_name.lower():
|
|
results.append({'id': drug_id, 'name': drug_name})
|
|
|
|
return results
|
|
|
|
|
|
# Example usage
|
|
if __name__ == "__main__":
|
|
# Initialize helper
|
|
db = DrugBankHelper()
|
|
|
|
# Example: Get drug information
|
|
print("Example 1: Get drug information")
|
|
drug_info = db.get_drug_info('DB00001')
|
|
print(f"Drug: {drug_info.get('name')}")
|
|
print(f"Type: {drug_info.get('type')}")
|
|
print(f"Indication: {drug_info.get('indication', 'N/A')[:100]}...")
|
|
print()
|
|
|
|
# Example: Get interactions
|
|
print("Example 2: Get drug interactions")
|
|
interactions = db.get_interactions('DB00001')
|
|
print(f"Found {len(interactions)} interactions")
|
|
if interactions:
|
|
print(f"First interaction: {interactions[0]['partner_name']}")
|
|
print()
|
|
|
|
# Example: Get targets
|
|
print("Example 3: Get drug targets")
|
|
targets = db.get_targets('DB00001')
|
|
print(f"Found {len(targets)} targets")
|
|
if targets:
|
|
print(f"First target: {targets[0]['name']}")
|
|
print()
|
|
|
|
# Example: Check drug pair interaction
|
|
print("Example 4: Check specific drug pair")
|
|
interaction = db.check_interaction('DB00001', 'DB00002')
|
|
if interaction:
|
|
print("Interaction found!")
|
|
print(f"Description: {interaction['description'][:100]}...")
|
|
else:
|
|
print("No interaction found")
|
|
print()
|
|
|
|
# Example: Search by name
|
|
print("Example 5: Search drugs by name")
|
|
results = db.search_by_name('aspirin', exact=True)
|
|
if results:
|
|
print(f"Found: {results[0]['id']} - {results[0]['name']}")
|