ADE-Explorer-MCP / src /openfda_client.py
Jonas
Add initial implementation of ADE Explorer with requirements, plotting, and API client
dd19932
raw
history blame
3.67 kB
import requests
from cachetools import TTLCache, cached
import time
API_BASE_URL = "https://api.fda.gov/drug/event.json"
# Cache with a TTL of 10 minutes (600 seconds)
cache = TTLCache(maxsize=256, ttl=600)
# 240 requests per minute per IP. A 0.25s delay is a simple way to stay under.
REQUEST_DELAY_SECONDS = 0.25
def get_top_adverse_events(drug_name: str, limit: int = 10) -> dict:
"""
Query OpenFDA to get the top adverse events for a given drug.
Args:
drug_name (str): The name of the drug to search for (brand or generic).
limit (int): The maximum number of adverse events to return.
Returns:
dict: The JSON response from the API, or an error dictionary.
"""
if not drug_name:
return {"error": "Drug name cannot be empty."}
drug_name_processed = drug_name.lower().strip()
# Using a simple cache key
cache_key = f"top_events_{drug_name_processed}_{limit}"
if cache_key in cache:
return cache[cache_key]
query = (
f'search=patient.drug.medicinalproduct:"{drug_name_processed}"'
f'&count=patient.reaction.reactionmeddrapt.exact&limit={limit}'
)
try:
# Respect rate limits
time.sleep(REQUEST_DELAY_SECONDS)
response = requests.get(f"{API_BASE_URL}?{query}")
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
data = response.json()
cache[cache_key] = data
return data
except requests.exceptions.HTTPError as http_err:
if response.status_code == 404:
return {"error": f"No data found for drug: '{drug_name}'. It might be misspelled or not in the database."}
return {"error": f"HTTP error occurred: {http_err}"}
except requests.exceptions.RequestException as req_err:
return {"error": f"A network request error occurred: {req_err}"}
except Exception as e:
return {"error": f"An unexpected error occurred: {e}"}
def get_drug_event_pair_frequency(drug_name: str, event_name: str) -> dict:
"""
Query OpenFDA to get the total number of reports for a specific
drug-adverse event pair.
Args:
drug_name (str): The name of the drug.
event_name (str): The name of the adverse event.
Returns:
dict: The JSON response from the API, or an error dictionary.
"""
if not drug_name or not event_name:
return {"error": "Drug name and event name cannot be empty."}
drug_name_processed = drug_name.lower().strip()
event_name_processed = event_name.lower().strip()
cache_key = f"pair_freq_{drug_name_processed}_{event_name_processed}"
if cache_key in cache:
return cache[cache_key]
query = (
f'search=patient.drug.medicinalproduct:"{drug_name_processed}"'
f'+AND+patient.reaction.reactionmeddrapt:"{event_name_processed}"'
)
try:
time.sleep(REQUEST_DELAY_SECONDS)
response = requests.get(f"{API_BASE_URL}?{query}")
response.raise_for_status()
data = response.json()
cache[cache_key] = data
return data
except requests.exceptions.HTTPError as http_err:
if response.status_code == 404:
return {"error": f"No data found for drug '{drug_name}' and event '{event_name}'. They may be misspelled or not in the database."}
return {"error": f"HTTP error occurred: {http_err}"}
except requests.exceptions.RequestException as req_err:
return {"error": f"A network request error occurred: {req_err}"}
except Exception as e:
return {"error": f"An unexpected error occurred: {e}"}