Spaces:
Sleeping
Sleeping
Update api_clients/clinicaltrials_client.py
Browse files
api_clients/clinicaltrials_client.py
CHANGED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# api_clients/clinicaltrials_client.py
|
2 |
+
"""
|
3 |
+
Client for the ClinicalTrials.gov API (Version 2).
|
4 |
+
This client is optimized to find active, recruiting studies for a given condition,
|
5 |
+
which is the most relevant information for patients and caregivers.
|
6 |
+
"""
|
7 |
+
import aiohttp
|
8 |
+
from urllib.parse import quote_plus
|
9 |
+
from .config import CLINICALTRIALS_BASE_URL, REQUEST_HEADERS
|
10 |
+
|
11 |
+
async def find_trials(session: aiohttp.ClientSession, condition_query: str, max_results: int = 5) -> list[dict]:
|
12 |
+
"""
|
13 |
+
Searches ClinicalTrials.gov for active, recruiting studies related to a condition.
|
14 |
+
|
15 |
+
Args:
|
16 |
+
session (aiohttp.ClientSession): The active HTTP session.
|
17 |
+
condition_query (str): The medical condition to search for (e.g., "Type 2 Diabetes").
|
18 |
+
max_results (int): The maximum number of trial results to return.
|
19 |
+
|
20 |
+
Returns:
|
21 |
+
list[dict]: A list of dictionaries, where each dictionary represents a clinical trial.
|
22 |
+
Returns an empty list if no trials are found or an error occurs.
|
23 |
+
"""
|
24 |
+
if not condition_query:
|
25 |
+
return []
|
26 |
+
|
27 |
+
# The API is powerful. We will specifically query for trials that are currently recruiting,
|
28 |
+
# as this is the most actionable information for a user.
|
29 |
+
# The `quote_plus` function ensures the query is safely encoded for the URL.
|
30 |
+
formatted_query = quote_plus(condition_query)
|
31 |
+
|
32 |
+
# Construct the query parameters
|
33 |
+
params = {
|
34 |
+
'query.term': formatted_query,
|
35 |
+
'filter.overallStatus': 'RECRUITING', # The "wow" factor: finding ACTIONABLE trials
|
36 |
+
'pageSize': max_results,
|
37 |
+
'format': 'json'
|
38 |
+
}
|
39 |
+
|
40 |
+
url = f"{CLINICALTRIALS_BASE_URL}/studies"
|
41 |
+
|
42 |
+
try:
|
43 |
+
async with session.get(url, params=params, headers=REQUEST_HEADERS, timeout=15) as resp:
|
44 |
+
# Raise an exception for bad status codes (4xx or 5xx)
|
45 |
+
resp.raise_for_status()
|
46 |
+
data = await resp.json()
|
47 |
+
|
48 |
+
studies_list = data.get('studies', [])
|
49 |
+
if not studies_list:
|
50 |
+
return []
|
51 |
+
|
52 |
+
# Parse the complex response into a simple, clean list of dictionaries
|
53 |
+
parsed_trials = []
|
54 |
+
for study in studies_list:
|
55 |
+
# Use nested .get() calls to safely access potentially missing keys
|
56 |
+
protocol = study.get('protocolSection', {})
|
57 |
+
id_module = protocol.get('identificationModule', {})
|
58 |
+
status_module = protocol.get('statusModule', {})
|
59 |
+
|
60 |
+
nct_id = id_module.get('nctId', '')
|
61 |
+
if not nct_id:
|
62 |
+
continue # Skip if the trial has no ID
|
63 |
+
|
64 |
+
parsed_trials.append({
|
65 |
+
"title": id_module.get('briefTitle', 'Title Not Available'),
|
66 |
+
"nct_id": nct_id,
|
67 |
+
"status": status_module.get('overallStatus', 'Status Unknown'),
|
68 |
+
"url": f"https://clinicaltrials.gov/study/{nct_id}",
|
69 |
+
"condition": ", ".join(protocol.get('conditionsModule', {}).get('conditions', ['N/A'])),
|
70 |
+
"intervention": ", ".join(i.get('name', 'N/A') for i in protocol.get('armsAndInterventionsModule', {}).get('interventions', []))
|
71 |
+
})
|
72 |
+
|
73 |
+
return parsed_trials
|
74 |
+
|
75 |
+
except aiohttp.ClientError as e:
|
76 |
+
print(f"An error occurred while fetching data from ClinicalTrials.gov: {e}")
|
77 |
+
return []
|
78 |
+
except Exception as e:
|
79 |
+
print(f"A general error occurred in the clinicaltrials_client: {e}")
|
80 |
+
return []
|