mgbam commited on
Commit
c4afc76
·
verified ·
1 Parent(s): 047750f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +138 -130
app.py CHANGED
@@ -21,21 +21,20 @@ st.set_page_config(page_title="Pharma Research Expert Platform", layout="wide")
21
  logging.basicConfig(level=logging.ERROR)
22
 
23
  # -------------------------------
24
- # API ENDPOINTS (Stable Sources Only)
25
  # -------------------------------
26
  API_ENDPOINTS = {
27
- "clinical_trials": "https://clinicaltrials.gov/api/v2/studies",
28
  "pubchem": "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/{}/JSON",
29
  "pubmed": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi",
30
  "fda_drug_approval": "https://api.fda.gov/drug/label.json",
31
  "faers_adverse_events": "https://api.fda.gov/drug/event.json",
32
- # PharmGKB endpoints for gene variants (if available)
33
  "pharmgkb_gene_variants": "https://api.pharmgkb.org/v1/data/gene/{}/variants",
34
- # BioPortal for ontology searches
35
- "bioportal_search": "https://data.bioontology.org/search",
36
- # RxNorm & RxClass endpoints
37
  "rxnorm_rxcui": "https://rxnav.nlm.nih.gov/REST/rxcui.json",
38
  "rxnorm_properties": "https://rxnav.nlm.nih.gov/REST/rxcui/{}/properties.json",
 
39
  "rxclass_by_drug": "https://rxnav.nlm.nih.gov/REST/class/byDrugName.json"
40
  }
41
 
@@ -44,22 +43,20 @@ API_ENDPOINTS = {
44
  # -------------------------------
45
  TRADE_TO_GENERIC = {
46
  "tylenol": "acetaminophen",
 
47
  "advil": "ibuprofen",
48
- # Extend with additional mappings as desired
49
  }
50
 
51
  # -------------------------------
52
- # SECRETS RETRIEVAL
53
  # -------------------------------
54
  OPENAI_API_KEY = st.secrets.get("OPENAI_API_KEY")
55
- BIOPORTAL_API_KEY = st.secrets.get("BIOPORTAL_API_KEY")
56
- PUB_EMAIL = st.secrets.get("PUB_EMAIL")
57
  OPENFDA_KEY = st.secrets.get("OPENFDA_KEY")
 
58
 
59
  if not PUB_EMAIL:
60
  st.error("PUB_EMAIL is not configured in secrets.")
61
- if not BIOPORTAL_API_KEY:
62
- st.error("BIOPORTAL_API_KEY is not configured in secrets.")
63
  if not OPENFDA_KEY:
64
  st.error("OPENFDA_KEY is not configured in secrets.")
65
  if not OPENAI_API_KEY:
@@ -72,7 +69,7 @@ from openai import OpenAI
72
  openai_client = OpenAI(api_key=OPENAI_API_KEY)
73
 
74
  def generate_ai_content(prompt: str) -> str:
75
- """Call GPT-4 to generate innovative insights."""
76
  try:
77
  response = openai_client.chat.completions.create(
78
  model="gpt-4",
@@ -81,52 +78,28 @@ def generate_ai_content(prompt: str) -> str:
81
  )
82
  return response.choices[0].message.content.strip()
83
  except Exception as e:
84
- st.error(f"GPT-4 generation error: {e}")
85
  logging.error(e)
86
  return "AI content generation failed."
87
 
88
  # -------------------------------
89
- # UTILITY FUNCTIONS WITH CACHING
90
  # -------------------------------
91
  @st.cache_data(show_spinner=False)
92
  def query_api(endpoint: str, params: Optional[Dict] = None, headers: Optional[Dict] = None) -> Optional[Dict]:
93
- """Wrapper for HTTP GET requests with error handling."""
94
  try:
95
  response = requests.get(endpoint, params=params, headers=headers, timeout=15)
96
  response.raise_for_status()
97
  return response.json()
98
  except Exception as e:
99
  st.error(f"API error for {endpoint}: {e}")
100
- logging.error(f"API error for {endpoint}: {e}")
101
- return None
102
-
103
- @st.cache_data(show_spinner=False)
104
- def get_pubchem_smiles(drug_name: str) -> Optional[str]:
105
- """Retrieve canonical SMILES using PubChem."""
106
- url = API_ENDPOINTS["pubchem"].format(drug_name)
107
- data = query_api(url)
108
- if data and data.get("PC_Compounds"):
109
- for prop in data["PC_Compounds"][0].get("props", []):
110
- if prop.get("name") == "Canonical SMILES":
111
- return prop["value"]["sval"]
112
- return None
113
-
114
- def draw_molecule(smiles: str) -> Optional[Any]:
115
- """Generate a 2D molecule image using RDKit."""
116
- try:
117
- mol = Chem.MolFromSmiles(smiles)
118
- if mol:
119
- return Draw.MolToImage(mol)
120
- else:
121
- st.error("Invalid SMILES string provided.")
122
- except Exception as e:
123
- st.error(f"Error drawing molecule: {e}")
124
  logging.error(e)
125
  return None
126
 
127
  @st.cache_data(show_spinner=False)
128
  def get_pubchem_drug_details(drug_name: str) -> Optional[Dict[str, str]]:
129
- """Retrieve drug details from PubChem."""
130
  url = API_ENDPOINTS["pubchem"].format(drug_name)
131
  data = query_api(url)
132
  details = {}
@@ -143,9 +116,23 @@ def get_pubchem_drug_details(drug_name: str) -> Optional[Dict[str, str]]:
143
  return details
144
  return None
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  @st.cache_data(show_spinner=False)
147
  def get_clinical_trials(query: str) -> Optional[Dict]:
148
- """Query ClinicalTrials.gov."""
149
  if query.upper().startswith("NCT") and query[3:].isdigit():
150
  params = {"id": query, "fmt": "json"}
151
  else:
@@ -154,16 +141,13 @@ def get_clinical_trials(query: str) -> Optional[Dict]:
154
 
155
  @st.cache_data(show_spinner=False)
156
  def get_pubmed(query: str) -> Optional[Dict]:
157
- """Query PubMed."""
158
  params = {"db": "pubmed", "term": query, "retmax": 10, "retmode": "json", "email": PUB_EMAIL}
159
  return query_api(API_ENDPOINTS["pubmed"], params)
160
 
161
  @st.cache_data(show_spinner=False)
162
  def get_fda_approval(drug_name: str) -> Optional[Dict]:
163
- """Retrieve FDA approval info using openFDA."""
164
- if not OPENFDA_KEY:
165
- st.error("OpenFDA key not configured.")
166
- return None
167
  query = f'openfda.brand_name:"{drug_name}"'
168
  params = {"api_key": OPENFDA_KEY, "search": query, "limit": 1}
169
  data = query_api(API_ENDPOINTS["fda_drug_approval"], params)
@@ -173,30 +157,14 @@ def get_fda_approval(drug_name: str) -> Optional[Dict]:
173
 
174
  @st.cache_data(show_spinner=False)
175
  def analyze_adverse_events(drug_name: str, limit: int = 5) -> Optional[Dict]:
176
- """Retrieve FAERS adverse events."""
177
- if not OPENFDA_KEY:
178
- st.error("OpenFDA key not configured.")
179
- return None
180
  query = f'patient.drug.medicinalproduct:"{drug_name}"'
181
  params = {"api_key": OPENFDA_KEY, "search": query, "limit": limit}
182
  return query_api(API_ENDPOINTS["faers_adverse_events"], params)
183
 
184
- @st.cache_data(show_spinner=False)
185
- def get_pharmgkb_variants_for_gene(pharmgkb_gene_id: str) -> Optional[List[str]]:
186
- """Return variant IDs for a PharmGKB gene accession."""
187
- if not pharmgkb_gene_id.startswith("PA"):
188
- st.warning("Enter a valid PharmGKB gene accession (e.g., PA1234).")
189
- return None
190
- endpoint = API_ENDPOINTS["pharmgkb_gene_variants"].format(pharmgkb_gene_id)
191
- data = query_api(endpoint)
192
- if data and data.get("data"):
193
- return [variant["id"] for variant in data["data"]]
194
- st.warning(f"No variants found for PharmGKB gene {pharmgkb_gene_id}.")
195
- return None
196
-
197
  @st.cache_data(show_spinner=False)
198
  def get_rxnorm_rxcui(drug_name: str) -> Optional[str]:
199
- """Return RxCUI for a drug."""
200
  url = f"{API_ENDPOINTS['rxnorm_rxcui']}?name={drug_name}"
201
  data = query_api(url)
202
  if data and "idGroup" in data and data["idGroup"].get("rxnormId"):
@@ -206,36 +174,79 @@ def get_rxnorm_rxcui(drug_name: str) -> Optional[str]:
206
 
207
  @st.cache_data(show_spinner=False)
208
  def get_rxnorm_properties(rxcui: str) -> Optional[Dict]:
209
- """Return RxNorm properties for a given RxCUI."""
210
  url = API_ENDPOINTS["rxnorm_properties"].format(rxcui)
211
  return query_api(url)
212
 
213
  @st.cache_data(show_spinner=False)
214
  def get_rxclass_by_drug_name(drug_name: str) -> Optional[Dict]:
215
- """Return RxClass information for a drug."""
216
  url = f"{API_ENDPOINTS['rxclass_by_drug']}?drugName={drug_name}"
217
  data = query_api(url)
218
- if data and "classMember" in data:
219
- return data
220
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
  # -------------------------------
223
  # AI-DRIVEN DRUG INSIGHTS
224
  # -------------------------------
225
  def generate_drug_insights(drug_name: str) -> str:
226
  """
227
- Gather FDA, PubChem, RxNorm, and RxClass data (using generic fallback) and build a GPT‑4 prompt
228
  for an innovative, bullet‑point drug analysis.
229
  """
230
  query_name = TRADE_TO_GENERIC.get(drug_name.lower(), drug_name)
231
 
232
- # Get FDA data
233
  fda_info = get_fda_approval(query_name)
234
  fda_status = "Not Approved"
235
  if fda_info and fda_info.get("openfda", {}).get("brand_name"):
236
  fda_status = ", ".join(fda_info["openfda"]["brand_name"])
237
 
238
- # Get PubChem details
239
  pubchem_details = get_pubchem_drug_details(query_name)
240
  if pubchem_details:
241
  formula = pubchem_details.get("Molecular Formula", "N/A")
@@ -244,7 +255,7 @@ def generate_drug_insights(drug_name: str) -> str:
244
  else:
245
  formula = iupac = canon_smiles = "Not Available"
246
 
247
- # RxNorm and RxClass
248
  rxnorm_id = get_rxnorm_rxcui(query_name)
249
  if rxnorm_id:
250
  rx_props = get_rxnorm_properties(rxnorm_id)
@@ -252,22 +263,25 @@ def generate_drug_insights(drug_name: str) -> str:
252
  else:
253
  rxnorm_info = "No RxNorm data available."
254
 
 
255
  rxclass_data = get_rxclass_by_drug_name(query_name)
256
  rxclass_info = rxclass_data if rxclass_data else "No RxClass data available."
257
 
258
- # Construct prompt for GPT-4
259
  prompt = (
260
- f"Please provide an advanced, innovative drug analysis report for '{drug_name}' "
261
- f"(generic: {query_name}).\n\n"
262
- f"**FDA Approval Status:** {fda_status}\n"
263
- f"**PubChem Details:** Molecular Formula: {formula}, IUPAC Name: {iupac}, Canonical SMILES: {canon_smiles}\n"
264
- f"**RxNorm Info:** {rxnorm_info}\n"
 
 
265
  f"**RxClass Info:** {rxclass_info}\n\n"
266
- f"Include the following in bullet points:\n"
267
- f"- Pharmacogenomic considerations (including genetic variants that might affect metabolism and toxicity).\n"
268
- f"- Potential repurposing opportunities based on drug mechanism.\n"
269
- f"- Regulatory insights and challenges, particularly for expanding indications or personalized medicine.\n"
270
- f"- Innovative suggestions for future research and data integration approaches.\n"
271
  )
272
  return generate_ai_content(prompt)
273
 
@@ -287,7 +301,7 @@ tabs = st.tabs([
287
 
288
  # ----- Tab 1: Drug Development -----
289
  with tabs[0]:
290
- st.header("AI-Driven Drug Development Strategy")
291
  target = st.text_input("Target Disease/Pathway:", placeholder="Enter disease mechanism or target")
292
  target_gene = st.text_input("Target Gene (PharmGKB Accession):", placeholder="e.g., PA1234")
293
  strategy = st.selectbox("Development Strategy:", ["First-in-class", "Me-too", "Repurposing", "Biologic"])
@@ -297,7 +311,7 @@ with tabs[0]:
297
  plan_prompt = (
298
  f"Develop a detailed drug development plan for treating {target} using a {strategy} strategy. "
299
  "Include sections on target validation, lead optimization, preclinical testing, clinical trial design, "
300
- "regulatory strategy, market analysis, competitive landscape, and relevant pharmacogenomic considerations."
301
  )
302
  plan = generate_ai_content(plan_prompt)
303
  st.subheader("Comprehensive Development Plan")
@@ -320,10 +334,17 @@ with tabs[0]:
320
  if variants:
321
  st.write("PharmGKB Variants:")
322
  st.write(variants)
 
 
323
  for vid in variants[:3]:
324
- annotations = _get_pharmgkb_clinical_annotations(vid)
325
- st.write(f"Annotations for Variant {vid}:")
326
- st.json(annotations if annotations else {"message": "No annotations found."})
 
 
 
 
 
327
  else:
328
  st.write("No variants found for the specified PharmGKB gene accession.")
329
  else:
@@ -345,7 +366,8 @@ with tabs[1]:
345
  "Phase": study.get("protocolSection", {}).get("designModule", {}).get("phases", ["Not Available"])[0],
346
  "Enrollment": study.get("protocolSection", {}).get("designModule", {}).get("enrollmentInfo", {}).get("count", "N/A")
347
  })
348
- _display_dataframe(trial_data, list(trial_data[0].keys()))
 
349
  else:
350
  st.warning("No clinical trials found for the query.")
351
 
@@ -370,35 +392,34 @@ with tabs[1]:
370
  else:
371
  st.write("No adverse event data available.")
372
 
373
- # ----- Tab 3: Molecular Profiling -----
374
  with tabs[2]:
375
  st.header("Advanced Molecular Profiling")
376
  compound_input = st.text_input("Compound Identifier:", placeholder="Enter drug name, SMILES, or INN")
377
  if st.button("Analyze Compound"):
378
- with st.spinner("Querying PubChem for molecular structure..."):
 
379
  query_compound = TRADE_TO_GENERIC.get(compound_input.lower(), compound_input)
380
- smiles = _get_pubchem_smiles(query_compound)
381
- if smiles:
382
- mol_image = draw_molecule(smiles)
383
- if mol_image:
384
- st.image(mol_image, caption="2D Molecular Structure")
 
 
 
 
 
 
 
 
385
  else:
386
- st.error("Molecular structure not found. Try a more specific compound name.")
387
- pubchem_data = query_api(API_ENDPOINTS["pubchem"].format(query_compound))
388
- if pubchem_data and pubchem_data.get("PC_Compounds"):
389
- st.subheader("Physicochemical Properties")
390
- props = pubchem_data["PC_Compounds"][0].get("props", [])
391
- mw = next((prop["value"]["sval"] for prop in props if prop.get("name") == "Molecular Weight"), "N/A")
392
- logp = next((prop["value"]["sval"] for prop in props if prop.get("name") == "LogP"), "N/A")
393
- st.write(f"**Molecular Weight:** {mw}")
394
- st.write(f"**LogP:** {logp}")
395
- else:
396
- st.error("Physicochemical properties not available.")
397
 
398
- # ----- Tab 4: Regulatory Intelligence -----
399
  with tabs[3]:
400
  st.header("Global Regulatory Monitoring")
401
- st.markdown("**Note:** Due to persistent issues with EMA/WHO/DailyMed APIs, this section focuses on FDA data and PubChem drug details.")
402
  drug_prod = st.text_input("Drug Product:", placeholder="Enter generic or brand name")
403
  if st.button("Generate Regulatory Report"):
404
  with st.spinner("Compiling regulatory data..."):
@@ -430,7 +451,7 @@ with tabs[3]:
430
  f"**Canonical SMILES:** {canon_smiles}\n"
431
  )
432
  with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
433
- pdf_file = _save_pdf_report(report_text, tmp.name)
434
  if pdf_file:
435
  with open(pdf_file, "rb") as f:
436
  st.download_button("Download Regulatory Report (PDF)", data=f, file_name=f"{drug_prod}_report.pdf", mime="application/pdf")
@@ -450,25 +471,12 @@ with tabs[4]:
450
  st.markdown(f"- [PMID: {pmid}](https://pubmed.ncbi.nlm.nih.gov/{pmid}/)")
451
  else:
452
  st.write("No PubMed results found.")
453
- st.header("Ontology Search")
454
- ont_query = st.text_input("Enter search query for Ontology:", placeholder="e.g., Alzheimer's disease")
455
- ont_select = st.selectbox("Select Ontology", ["MESH", "NCIT", "GO", "SNOMEDCT"])
456
- if st.button("Search BioPortal"):
457
- with st.spinner("Searching BioPortal..."):
458
- bioportal_results = _get_bioportal_data(ont_select, ont_query)
459
- if bioportal_results and bioportal_results.get("collection"):
460
- st.subheader(f"BioPortal Results for {ont_select}")
461
- for item in bioportal_results["collection"]:
462
- label = item.get("prefLabel", "N/A")
463
- ont_id = item.get("@id", "N/A")
464
- st.markdown(f"- **{label}** ({ont_id})")
465
- else:
466
- st.write("No ontology results found.")
467
 
468
  # ----- Tab 6: Comprehensive Dashboard -----
469
  with tabs[5]:
470
  st.header("Comprehensive Dashboard")
471
- # Example KPIs (these could later be replaced by dynamic queries)
472
  kpi_fda = 5000
473
  kpi_trials = 12000
474
  kpi_pubs = 250000
@@ -486,11 +494,12 @@ with tabs[5]:
486
  ax_trend.set_ylabel("Number of Approvals")
487
  st.pyplot(fig_trend)
488
  st.subheader("Gene-Variant-Drug Network (Sample)")
 
489
  sample_gene = "CYP2C19"
490
  sample_variants = ["rs4244285", "rs12248560"]
491
  sample_annots = {"rs4244285": ["Clopidogrel", "Omeprazole"], "rs12248560": ["Sertraline"]}
492
  try:
493
- net_fig = _create_variant_network(sample_gene, sample_variants, sample_annots)
494
  st.plotly_chart(net_fig, use_container_width=True)
495
  except Exception as e:
496
  st.error(f"Network graph error: {e}")
@@ -533,6 +542,5 @@ with tabs[7]:
533
  with st.spinner("Generating AI-driven insights..."):
534
  query_ai_drug = TRADE_TO_GENERIC.get(ai_drug.lower(), ai_drug)
535
  insights_text = generate_drug_insights(query_ai_drug)
536
- st.subheader("AI-Driven Drug Analysis")
537
  st.markdown(insights_text)
538
-
 
21
  logging.basicConfig(level=logging.ERROR)
22
 
23
  # -------------------------------
24
+ # API ENDPOINTS (Using only Stable Sources)
25
  # -------------------------------
26
  API_ENDPOINTS = {
27
+ "clinical_trials": "https://clinicaltrials.gov/api/v2/studies", # No email required now
28
  "pubchem": "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/{}/JSON",
29
  "pubmed": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi",
30
  "fda_drug_approval": "https://api.fda.gov/drug/label.json",
31
  "faers_adverse_events": "https://api.fda.gov/drug/event.json",
32
+ # PharmGKB endpoint for gene variants (if available)
33
  "pharmgkb_gene_variants": "https://api.pharmgkb.org/v1/data/gene/{}/variants",
34
+ # RxNorm endpoints
 
 
35
  "rxnorm_rxcui": "https://rxnav.nlm.nih.gov/REST/rxcui.json",
36
  "rxnorm_properties": "https://rxnav.nlm.nih.gov/REST/rxcui/{}/properties.json",
37
+ # RxClass endpoint (may return no data, so we provide a fallback message)
38
  "rxclass_by_drug": "https://rxnav.nlm.nih.gov/REST/class/byDrugName.json"
39
  }
40
 
 
43
  # -------------------------------
44
  TRADE_TO_GENERIC = {
45
  "tylenol": "acetaminophen",
46
+ "panadol": "acetaminophen",
47
  "advil": "ibuprofen",
48
+ # Add additional mappings as needed
49
  }
50
 
51
  # -------------------------------
52
+ # RETRIEVE SECRETS
53
  # -------------------------------
54
  OPENAI_API_KEY = st.secrets.get("OPENAI_API_KEY")
 
 
55
  OPENFDA_KEY = st.secrets.get("OPENFDA_KEY")
56
+ PUB_EMAIL = st.secrets.get("PUB_EMAIL")
57
 
58
  if not PUB_EMAIL:
59
  st.error("PUB_EMAIL is not configured in secrets.")
 
 
60
  if not OPENFDA_KEY:
61
  st.error("OPENFDA_KEY is not configured in secrets.")
62
  if not OPENAI_API_KEY:
 
69
  openai_client = OpenAI(api_key=OPENAI_API_KEY)
70
 
71
  def generate_ai_content(prompt: str) -> str:
72
+ """Generate innovative insights using GPT‑4."""
73
  try:
74
  response = openai_client.chat.completions.create(
75
  model="gpt-4",
 
78
  )
79
  return response.choices[0].message.content.strip()
80
  except Exception as e:
81
+ st.error(f"GPT4 generation error: {e}")
82
  logging.error(e)
83
  return "AI content generation failed."
84
 
85
  # -------------------------------
86
+ # UTILITY FUNCTIONS (with caching)
87
  # -------------------------------
88
  @st.cache_data(show_spinner=False)
89
  def query_api(endpoint: str, params: Optional[Dict] = None, headers: Optional[Dict] = None) -> Optional[Dict]:
90
+ """HTTP GET with error handling and caching."""
91
  try:
92
  response = requests.get(endpoint, params=params, headers=headers, timeout=15)
93
  response.raise_for_status()
94
  return response.json()
95
  except Exception as e:
96
  st.error(f"API error for {endpoint}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  logging.error(e)
98
  return None
99
 
100
  @st.cache_data(show_spinner=False)
101
  def get_pubchem_drug_details(drug_name: str) -> Optional[Dict[str, str]]:
102
+ """Retrieve drug details (including molecular formula, IUPAC name, and SMILES) from PubChem."""
103
  url = API_ENDPOINTS["pubchem"].format(drug_name)
104
  data = query_api(url)
105
  details = {}
 
116
  return details
117
  return None
118
 
119
+ def save_pdf_report(report_content: str, filename: str) -> Optional[str]:
120
+ """Save a text report as a PDF file using FPDF."""
121
+ try:
122
+ pdf = FPDF()
123
+ pdf.add_page()
124
+ pdf.set_font("Arial", size=12)
125
+ pdf.multi_cell(0, 10, report_content)
126
+ pdf.output(filename)
127
+ return filename
128
+ except Exception as e:
129
+ st.error(f"Error saving PDF: {e}")
130
+ logging.error(e)
131
+ return None
132
+
133
  @st.cache_data(show_spinner=False)
134
  def get_clinical_trials(query: str) -> Optional[Dict]:
135
+ """Query ClinicalTrials.gov (NCT number or term search)."""
136
  if query.upper().startswith("NCT") and query[3:].isdigit():
137
  params = {"id": query, "fmt": "json"}
138
  else:
 
141
 
142
  @st.cache_data(show_spinner=False)
143
  def get_pubmed(query: str) -> Optional[Dict]:
144
+ """Query PubMed using the given search term."""
145
  params = {"db": "pubmed", "term": query, "retmax": 10, "retmode": "json", "email": PUB_EMAIL}
146
  return query_api(API_ENDPOINTS["pubmed"], params)
147
 
148
  @st.cache_data(show_spinner=False)
149
  def get_fda_approval(drug_name: str) -> Optional[Dict]:
150
+ """Retrieve FDA drug approval data using openFDA."""
 
 
 
151
  query = f'openfda.brand_name:"{drug_name}"'
152
  params = {"api_key": OPENFDA_KEY, "search": query, "limit": 1}
153
  data = query_api(API_ENDPOINTS["fda_drug_approval"], params)
 
157
 
158
  @st.cache_data(show_spinner=False)
159
  def analyze_adverse_events(drug_name: str, limit: int = 5) -> Optional[Dict]:
160
+ """Retrieve adverse event data from FAERS."""
 
 
 
161
  query = f'patient.drug.medicinalproduct:"{drug_name}"'
162
  params = {"api_key": OPENFDA_KEY, "search": query, "limit": limit}
163
  return query_api(API_ENDPOINTS["faers_adverse_events"], params)
164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  @st.cache_data(show_spinner=False)
166
  def get_rxnorm_rxcui(drug_name: str) -> Optional[str]:
167
+ """Retrieve the RxCUI for a drug from RxNorm."""
168
  url = f"{API_ENDPOINTS['rxnorm_rxcui']}?name={drug_name}"
169
  data = query_api(url)
170
  if data and "idGroup" in data and data["idGroup"].get("rxnormId"):
 
174
 
175
  @st.cache_data(show_spinner=False)
176
  def get_rxnorm_properties(rxcui: str) -> Optional[Dict]:
177
+ """Retrieve RxNorm properties for a given RxCUI."""
178
  url = API_ENDPOINTS["rxnorm_properties"].format(rxcui)
179
  return query_api(url)
180
 
181
  @st.cache_data(show_spinner=False)
182
  def get_rxclass_by_drug_name(drug_name: str) -> Optional[Dict]:
183
+ """Query RxClass for drug classification info. (Fallback if no data is returned.)"""
184
  url = f"{API_ENDPOINTS['rxclass_by_drug']}?drugName={drug_name}"
185
  data = query_api(url)
186
+ return data # May return None if no data is found
187
+
188
+ def create_variant_network(gene: str, variants: List[str], annotations: Dict[str, List[str]]) -> go.Figure:
189
+ """Generate a gene-variant-drug network graph using NetworkX and Plotly."""
190
+ G = nx.Graph()
191
+ G.add_node(gene, color="lightblue")
192
+ for variant in variants:
193
+ G.add_node(variant, color="lightgreen")
194
+ G.add_edge(gene, variant)
195
+ for drug in annotations.get(variant, []):
196
+ if drug and drug != "N/A":
197
+ G.add_node(drug, color="lightcoral")
198
+ G.add_edge(variant, drug)
199
+ pos = nx.spring_layout(G)
200
+ edge_x, edge_y = [], []
201
+ for edge in G.edges():
202
+ x0, y0 = pos[edge[0]]
203
+ x1, y1 = pos[edge[1]]
204
+ edge_x.extend([x0, x1, None])
205
+ edge_y.extend([y0, y1, None])
206
+ edge_trace = go.Scatter(
207
+ x=edge_x, y=edge_y, line=dict(width=1, color="#888"),
208
+ hoverinfo="none", mode="lines"
209
+ )
210
+ node_x, node_y, node_text, node_color = [], [], [], []
211
+ for node in G.nodes():
212
+ x, y = pos[node]
213
+ node_x.append(x)
214
+ node_y.append(y)
215
+ node_text.append(node)
216
+ node_color.append(G.nodes[node].get("color", "gray"))
217
+ node_trace = go.Scatter(
218
+ x=node_x, y=node_y, mode="markers+text", hoverinfo="text",
219
+ text=node_text, textposition="bottom center",
220
+ marker=dict(color=node_color, size=12, line_width=2)
221
+ )
222
+ fig = go.Figure(data=[edge_trace, node_trace],
223
+ layout=go.Layout(
224
+ title=dict(text="Gene-Variant-Drug Network", font=dict(size=16)),
225
+ showlegend=False,
226
+ hovermode="closest",
227
+ margin=dict(b=20, l=5, r=5, t=40),
228
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
229
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
230
+ ))
231
+ return fig
232
 
233
  # -------------------------------
234
  # AI-DRIVEN DRUG INSIGHTS
235
  # -------------------------------
236
  def generate_drug_insights(drug_name: str) -> str:
237
  """
238
+ Gather data from FDA, PubChem, RxNorm, and RxClass (using generic fallback) and build a GPT‑4 prompt
239
  for an innovative, bullet‑point drug analysis.
240
  """
241
  query_name = TRADE_TO_GENERIC.get(drug_name.lower(), drug_name)
242
 
243
+ # FDA Data
244
  fda_info = get_fda_approval(query_name)
245
  fda_status = "Not Approved"
246
  if fda_info and fda_info.get("openfda", {}).get("brand_name"):
247
  fda_status = ", ".join(fda_info["openfda"]["brand_name"])
248
 
249
+ # PubChem Data
250
  pubchem_details = get_pubchem_drug_details(query_name)
251
  if pubchem_details:
252
  formula = pubchem_details.get("Molecular Formula", "N/A")
 
255
  else:
256
  formula = iupac = canon_smiles = "Not Available"
257
 
258
+ # RxNorm Data
259
  rxnorm_id = get_rxnorm_rxcui(query_name)
260
  if rxnorm_id:
261
  rx_props = get_rxnorm_properties(rxnorm_id)
 
263
  else:
264
  rxnorm_info = "No RxNorm data available."
265
 
266
+ # RxClass Data
267
  rxclass_data = get_rxclass_by_drug_name(query_name)
268
  rxclass_info = rxclass_data if rxclass_data else "No RxClass data available."
269
 
270
+ # Construct prompt for GPT4
271
  prompt = (
272
+ f"Provide an innovative, advanced drug analysis for '{drug_name}' (generic: {query_name}).\n\n"
273
+ f"**FDA Approval Status:** {fda_status}\n\n"
274
+ f"**PubChem Details:**\n"
275
+ f"- Molecular Formula: {formula}\n"
276
+ f"- IUPAC Name: {iupac}\n"
277
+ f"- Canonical SMILES: {canon_smiles}\n\n"
278
+ f"**RxNorm Info:** {rxnorm_info}\n\n"
279
  f"**RxClass Info:** {rxclass_info}\n\n"
280
+ f"Include in bullet points:\n"
281
+ f"- Pharmacogenomic considerations (e.g. genetic variants impacting metabolism or toxicity)\n"
282
+ f"- Potential repurposing opportunities and innovative therapeutic insights\n"
283
+ f"- Regulatory challenges and suggestions for personalized medicine approaches\n"
284
+ f"- Forward‑looking recommendations for future research and integration of diverse data sources\n"
285
  )
286
  return generate_ai_content(prompt)
287
 
 
301
 
302
  # ----- Tab 1: Drug Development -----
303
  with tabs[0]:
304
+ st.header("AIDriven Drug Development Strategy")
305
  target = st.text_input("Target Disease/Pathway:", placeholder="Enter disease mechanism or target")
306
  target_gene = st.text_input("Target Gene (PharmGKB Accession):", placeholder="e.g., PA1234")
307
  strategy = st.selectbox("Development Strategy:", ["First-in-class", "Me-too", "Repurposing", "Biologic"])
 
311
  plan_prompt = (
312
  f"Develop a detailed drug development plan for treating {target} using a {strategy} strategy. "
313
  "Include sections on target validation, lead optimization, preclinical testing, clinical trial design, "
314
+ "regulatory strategy, market analysis, competitive landscape, and pharmacogenomic considerations."
315
  )
316
  plan = generate_ai_content(plan_prompt)
317
  st.subheader("Comprehensive Development Plan")
 
334
  if variants:
335
  st.write("PharmGKB Variants:")
336
  st.write(variants)
337
+ # Optionally, display network graph if variant annotations are available.
338
+ sample_annots = {}
339
  for vid in variants[:3]:
340
+ # Here you would normally fetch annotations.
341
+ # For demonstration, we set a dummy list:
342
+ sample_annots[vid] = ["DrugA", "DrugB"]
343
+ try:
344
+ net_fig = create_variant_network(target_gene, variants[:3], sample_annots)
345
+ st.plotly_chart(net_fig, use_container_width=True)
346
+ except Exception as e:
347
+ st.error(f"Network graph error: {e}")
348
  else:
349
  st.write("No variants found for the specified PharmGKB gene accession.")
350
  else:
 
366
  "Phase": study.get("protocolSection", {}).get("designModule", {}).get("phases", ["Not Available"])[0],
367
  "Enrollment": study.get("protocolSection", {}).get("designModule", {}).get("enrollmentInfo", {}).get("count", "N/A")
368
  })
369
+ df_trials = pd.DataFrame(trial_data)
370
+ st.dataframe(df_trials)
371
  else:
372
  st.warning("No clinical trials found for the query.")
373
 
 
392
  else:
393
  st.write("No adverse event data available.")
394
 
395
+ # ----- Tab 3: Advanced Molecular Profiling -----
396
  with tabs[2]:
397
  st.header("Advanced Molecular Profiling")
398
  compound_input = st.text_input("Compound Identifier:", placeholder="Enter drug name, SMILES, or INN")
399
  if st.button("Analyze Compound"):
400
+ with st.spinner("Querying PubChem for molecular structure and properties..."):
401
+ # Use trade-to-generic mapping
402
  query_compound = TRADE_TO_GENERIC.get(compound_input.lower(), compound_input)
403
+ pubchem_info = get_pubchem_drug_details(query_compound)
404
+ if pubchem_info:
405
+ smiles = pubchem_info.get("Canonical SMILES")
406
+ if smiles and smiles != "N/A":
407
+ mol_image = draw_molecule(smiles)
408
+ if mol_image:
409
+ st.image(mol_image, caption="2D Molecular Structure")
410
+ else:
411
+ st.error("Canonical SMILES not found for this compound.")
412
+ st.subheader("Physicochemical Properties")
413
+ st.write(f"**Molecular Formula:** {pubchem_info.get('Molecular Formula', 'N/A')}")
414
+ st.write(f"**IUPAC Name:** {pubchem_info.get('IUPAC Name', 'N/A')}")
415
+ st.write(f"**Canonical SMILES:** {pubchem_info.get('Canonical SMILES', 'N/A')}")
416
  else:
417
+ st.error("PubChem details not available for the given compound.")
 
 
 
 
 
 
 
 
 
 
418
 
419
+ # ----- Tab 4: Global Regulatory Monitoring -----
420
  with tabs[3]:
421
  st.header("Global Regulatory Monitoring")
422
+ st.markdown("**Note:** This section focuses on FDA data and PubChem drug details due to limitations with EMA/WHO/DailyMed APIs.")
423
  drug_prod = st.text_input("Drug Product:", placeholder="Enter generic or brand name")
424
  if st.button("Generate Regulatory Report"):
425
  with st.spinner("Compiling regulatory data..."):
 
451
  f"**Canonical SMILES:** {canon_smiles}\n"
452
  )
453
  with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
454
+ pdf_file = save_pdf_report(report_text, tmp.name)
455
  if pdf_file:
456
  with open(pdf_file, "rb") as f:
457
  st.download_button("Download Regulatory Report (PDF)", data=f, file_name=f"{drug_prod}_report.pdf", mime="application/pdf")
 
471
  st.markdown(f"- [PMID: {pmid}](https://pubmed.ncbi.nlm.nih.gov/{pmid}/)")
472
  else:
473
  st.write("No PubMed results found.")
474
+ # (Ontology search removed due to unreliable endpoints)
 
 
 
 
 
 
 
 
 
 
 
 
 
475
 
476
  # ----- Tab 6: Comprehensive Dashboard -----
477
  with tabs[5]:
478
  st.header("Comprehensive Dashboard")
479
+ # Static sample KPIs these can be replaced with dynamic aggregated data in the future
480
  kpi_fda = 5000
481
  kpi_trials = 12000
482
  kpi_pubs = 250000
 
494
  ax_trend.set_ylabel("Number of Approvals")
495
  st.pyplot(fig_trend)
496
  st.subheader("Gene-Variant-Drug Network (Sample)")
497
+ # Sample network using dummy data
498
  sample_gene = "CYP2C19"
499
  sample_variants = ["rs4244285", "rs12248560"]
500
  sample_annots = {"rs4244285": ["Clopidogrel", "Omeprazole"], "rs12248560": ["Sertraline"]}
501
  try:
502
+ net_fig = create_variant_network(sample_gene, sample_variants, sample_annots)
503
  st.plotly_chart(net_fig, use_container_width=True)
504
  except Exception as e:
505
  st.error(f"Network graph error: {e}")
 
542
  with st.spinner("Generating AI-driven insights..."):
543
  query_ai_drug = TRADE_TO_GENERIC.get(ai_drug.lower(), ai_drug)
544
  insights_text = generate_drug_insights(query_ai_drug)
545
+ st.subheader("AIDriven Drug Analysis")
546
  st.markdown(insights_text)