Initial commit
This commit is contained in:
350
skills/drugbank-database/scripts/drugbank_helper.py
Normal file
350
skills/drugbank-database/scripts/drugbank_helper.py
Normal file
@@ -0,0 +1,350 @@
|
||||
#!/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']}")
|
||||
Reference in New Issue
Block a user