mgbam commited on
Commit
e173f95
·
verified ·
1 Parent(s): 25f3b01

Update mcp/openfda.py

Browse files
Files changed (1) hide show
  1. mcp/openfda.py +74 -22
mcp/openfda.py CHANGED
@@ -1,24 +1,76 @@
1
  # mcp/openfda.py
2
 
3
- import httpx
4
-
5
- OPENFDA_DRUG_EVENT = "https://api.fda.gov/drug/event.json"
6
-
7
- async def fetch_drug_safety(drug: str, max_results: int = 3):
8
- """Fetch recent adverse event reports for the drug from OpenFDA."""
9
- params = {"search": f'patient.drug.medicinalproduct:"{drug}"', "limit": max_results}
10
- async with httpx.AsyncClient() as client:
11
- try:
12
- resp = await client.get(OPENFDA_DRUG_EVENT, params=params)
13
- results = resp.json().get("results", [])
14
- output = []
15
- for ev in results:
16
- output.append({
17
- "safety_report_id": ev.get("safetyreportid"),
18
- "serious": ev.get("serious"),
19
- "reactions": [r["reactionmeddrapt"] for r in ev.get("patient", {}).get("reaction", [])],
20
- "receivedate": ev.get("receivedate"),
21
- })
22
- return output
23
- except Exception:
24
- return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # mcp/openfda.py
2
 
3
+ #!/usr/bin/env python3
4
+ """MedGenesis – **openFDA** drug‑event wrapper (async, cached).
5
+
6
+ Enhancements over the legacy helper
7
+ -----------------------------------
8
+ * Back‑off retry (2×, 4×, 8×) for intermittent 503s.
9
+ * `@lru_cache` (12 h, 512 keys) to spare quota.
10
+ * Returns empty list instead of raising on any service hiccup.
11
+ * Accepts optional `limit` kwarg (capped at 20).
12
+ * Normalises API response to `schemas.DrugSafety`‑compatible dicts.
13
+
14
+ API docs: https://open.fda.gov/apis/drug/event/
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import asyncio, httpx
19
+ from functools import lru_cache
20
+ from typing import List, Dict
21
+
22
+ _URL = "https://api.fda.gov/drug/event.json"
23
+ _TIMEOUT = 15
24
+ _MAX_LIMIT = 20
25
+
26
+ # ---------------------------------------------------------------------
27
+ # Internal helper with retry
28
+ # ---------------------------------------------------------------------
29
+ async def _get(params: Dict, *, retries: int = 3) -> Dict:
30
+ delay = 2
31
+ last = None
32
+ for _ in range(retries):
33
+ async with httpx.AsyncClient(timeout=_TIMEOUT) as cli:
34
+ last = await cli.get(_URL, params=params)
35
+ if last.status_code == 200:
36
+ return last.json()
37
+ await asyncio.sleep(delay)
38
+ delay *= 2
39
+ return last.json() if last else {}
40
+
41
+
42
+ # ---------------------------------------------------------------------
43
+ # Public API – cached
44
+ # ---------------------------------------------------------------------
45
+ @lru_cache(maxsize=512)
46
+ async def fetch_drug_safety(drug: str, *, limit: int = 3) -> List[Dict]:
47
+ """Return recent adverse‑event reports for *drug* (empty list on failure)."""
48
+ limit = max(1, min(limit, _MAX_LIMIT))
49
+ params = {
50
+ "search": f'patient.drug.medicinalproduct:"{drug}"',
51
+ "limit" : limit,
52
+ }
53
+
54
+ try:
55
+ data = await _get(params)
56
+ rows = data.get("results", [])
57
+ out: List[Dict] = []
58
+ for ev in rows:
59
+ out.append({
60
+ "safety_report_id": ev.get("safetyreportid"),
61
+ "serious" : ev.get("serious"),
62
+ "reactions" : [r.get("reactionmeddrapt") for r in ev.get("patient", {}).get("reaction", [])],
63
+ "receivedate" : ev.get("receivedate"),
64
+ })
65
+ return out
66
+ except Exception:
67
+ return []
68
+
69
+
70
+ # Quick test -----------------------------------------------------------------
71
+ if __name__ == "__main__":
72
+ import asyncio, json
73
+ async def _demo():
74
+ res = await fetch_drug_safety("temozolomide", limit=2)
75
+ print(json.dumps(res, indent=2))
76
+ asyncio.run(_demo())