mgbam commited on
Commit
0d096e5
·
verified ·
1 Parent(s): e860922

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +264 -446
app.py CHANGED
@@ -1,467 +1,285 @@
 
 
 
 
 
1
  import streamlit as st
2
  import requests
 
 
3
  import pandas as pd
4
  import matplotlib.pyplot as plt
5
  import seaborn as sns
6
- from rdkit import Chem
7
- from rdkit.Chem import Draw
8
- from fpdf import FPDF
9
- import tempfile
10
- import logging
11
- import os
12
- import plotly.graph_objects as go
13
- import networkx as nx
14
- from typing import Optional, Dict, List, Any
15
- from datetime import datetime
16
  from openai import OpenAI
 
17
 
18
- # -------------------------------
19
- # STREAMLIT CONFIGURATION & LOGGING
20
- # -------------------------------
21
- st.set_page_config(page_title="Pharma Research Expert Platform", layout="wide")
22
- logging.basicConfig(level=logging.ERROR)
23
-
24
- # -------------------------------
25
- # API ENDPOINTS (Stable Sources Only)
26
- # -------------------------------
27
- API_ENDPOINTS = {
28
- "clinical_trials": "https://clinicaltrials.gov/api/v2/studies",
29
- "pubchem": "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/{}/JSON",
30
- "pubmed": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi",
31
- "fda_drug_approval": "https://api.fda.gov/drug/label.json",
32
- "faers_adverse_events": "https://api.fda.gov/drug/event.json",
33
- "rxnorm_rxcui": "https://rxnav.nlm.nih.gov/REST/rxcui.json",
34
- "rxnorm_properties": "https://rxnav.nlm.nih.gov/REST/rxcui/{}/properties.json"
35
- # RxClass and EMA endpoints are omitted due to reliability issues.
36
- }
37
-
38
- # -------------------------------
39
- # TRADE-TO-GENERIC MAPPING (FALLBACK)
40
- # -------------------------------
41
- TRADE_TO_GENERIC = {
42
- "tylenol": "acetaminophen",
43
- "panadol": "acetaminophen",
44
- "asprin": "aspirin"
45
- }
46
-
47
- # -------------------------------
48
- # RETRIEVE SECRETS
49
- # -------------------------------
50
- OPENAI_API_KEY = st.secrets.get("OPENAI_API_KEY")
51
- OPENFDA_KEY = st.secrets.get("OPENFDA_KEY")
52
- PUB_EMAIL = st.secrets.get("PUB_EMAIL")
53
 
54
- if not PUB_EMAIL:
55
- st.error("PUB_EMAIL is not configured in secrets.")
56
- if not OPENFDA_KEY:
57
- st.error("OPENFDA_KEY is not configured in secrets.")
58
- if not OPENAI_API_KEY:
59
- st.error("OPENAI_API_KEY is not configured in secrets.")
60
 
61
- # -------------------------------
62
- # INITIALIZE OPENAI (GPT‑4) CLIENT
63
- # -------------------------------
64
- openai_client = OpenAI(api_key=OPENAI_API_KEY)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
- def generate_ai_content(prompt: str) -> str:
67
- """Generate innovative drug insights using GPT‑4."""
68
- try:
69
- response = openai_client.chat.completions.create(
70
- model="gpt-4",
71
- messages=[{"role": "user", "content": prompt}],
72
- max_tokens=300
73
- )
74
- return response.choices[0].message.content.strip()
75
- except Exception as e:
76
- st.error(f"GPT‑4 error: {e}")
77
- logging.error(e)
78
- return "AI content generation failed."
 
 
 
 
 
79
 
80
- # -------------------------------
81
- # UTILITY FUNCTIONS (with caching)
82
- # -------------------------------
83
- @st.cache_data(show_spinner=False)
84
- def query_api(endpoint: str, params: Optional[Dict] = None) -> Optional[Dict]:
85
- """Perform an HTTP GET request with error handling."""
86
- try:
87
- response = requests.get(endpoint, params=params, timeout=15)
88
- response.raise_for_status()
89
- return response.json()
90
- except Exception as e:
91
- logging.error(f"Error querying {endpoint}: {e}")
92
- return None
93
 
94
- @st.cache_data(show_spinner=False)
95
- def get_pubchem_drug_details(drug_name: str) -> Optional[Dict[str, str]]:
96
- """Retrieve drug details (molecular formula, IUPAC name, SMILES) from PubChem."""
97
- url = API_ENDPOINTS["pubchem"].format(drug_name)
98
- data = query_api(url)
99
- details = {}
100
- if data and data.get("PC_Compounds"):
101
- compound = data["PC_Compounds"][0]
102
- for prop in compound.get("props", []):
103
- urn = prop.get("urn", {})
104
- if urn.get("label") == "Molecular Formula":
105
- details["Molecular Formula"] = prop["value"]["sval"]
106
- if urn.get("name") == "Preferred":
107
- details["IUPAC Name"] = prop["value"]["sval"]
108
- if prop.get("name") == "Canonical SMILES":
109
- details["Canonical SMILES"] = prop["value"]["sval"]
110
- return details
111
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- def draw_molecule(smiles: str) -> Optional[Any]:
114
- """Generate and return a 2D molecular structure image using RDKit."""
115
- try:
116
- mol = Chem.MolFromSmiles(smiles)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  if mol:
118
- return Draw.MolToImage(mol)
119
- except Exception as e:
120
- logging.error(f"Error drawing molecule: {e}")
121
- return None
122
-
123
- def save_pdf_report(content: str, filename: str) -> Optional[str]:
124
- """Save a text report as a PDF file using FPDF."""
125
- try:
126
- pdf = FPDF()
127
- pdf.add_page()
128
- pdf.set_font("Arial", size=12)
129
- pdf.multi_cell(0, 10, content)
130
- pdf.output(filename)
131
- return filename
132
- except Exception as e:
133
- logging.error(f"Error saving PDF: {e}")
134
- return None
135
-
136
- @st.cache_data(show_spinner=False)
137
- def get_clinical_trials(query: str) -> Optional[Dict]:
138
- """Query ClinicalTrials.gov using a term or NCT number."""
139
- if query.upper().startswith("NCT") and query[3:].isdigit():
140
- params = {"id": query, "fmt": "json"}
141
- else:
142
- params = {"query.term": query, "retmax": 10, "retmode": "json"}
143
- return query_api(API_ENDPOINTS["clinical_trials"], params)
144
-
145
- @st.cache_data(show_spinner=False)
146
- def get_pubmed(query: str) -> Optional[Dict]:
147
- """Query PubMed using the provided search term."""
148
- params = {"db": "pubmed", "term": query, "retmax": 10, "retmode": "json", "email": PUB_EMAIL}
149
- return query_api(API_ENDPOINTS["pubmed"], params)
150
-
151
- @st.cache_data(show_spinner=False)
152
- def get_fda_approval(drug_name: str) -> Optional[Dict]:
153
- """Retrieve FDA approval data from openFDA."""
154
- query = f'openfda.brand_name:"{drug_name}"'
155
- params = {"api_key": OPENFDA_KEY, "search": query, "limit": 1}
156
- data = query_api(API_ENDPOINTS["fda_drug_approval"], params)
157
- if data and data.get("results"):
158
- return data["results"][0]
159
- return None
160
-
161
- @st.cache_data(show_spinner=False)
162
- def analyze_adverse_events(drug_name: str, limit: int = 5) -> Optional[Dict]:
163
- """Retrieve adverse events from FAERS."""
164
- query = f'patient.drug.medicinalproduct:"{drug_name}"'
165
- params = {"api_key": OPENFDA_KEY, "search": query, "limit": limit}
166
- return query_api(API_ENDPOINTS["faers_adverse_events"], params)
167
-
168
- @st.cache_data(show_spinner=False)
169
- def get_rxnorm_rxcui(drug_name: str) -> Optional[str]:
170
- """Retrieve RxCUI for a given drug using RxNorm."""
171
- url = f"{API_ENDPOINTS['rxnorm_rxcui']}?name={drug_name}"
172
- data = query_api(url)
173
- if data and "idGroup" in data and data["idGroup"].get("rxnormId"):
174
- return data["idGroup"]["rxnormId"][0]
175
- return None
176
-
177
- @st.cache_data(show_spinner=False)
178
- def get_rxnorm_properties(rxcui: str) -> Optional[Dict]:
179
- """Retrieve RxNorm properties for a given RxCUI."""
180
- url = API_ENDPOINTS["rxnorm_properties"].format(rxcui)
181
- return query_api(url)
182
-
183
- # -------------------------------
184
- # AI-DRIVEN DRUG INSIGHTS
185
- # -------------------------------
186
- def generate_drug_insights(drug_name: str) -> str:
187
- """
188
- Gather FDA, PubChem, and RxNorm data (using trade-to-generic fallback) and build a GPT‑4 prompt
189
- for an innovative drug analysis report.
190
- """
191
- query_name = TRADE_TO_GENERIC.get(drug_name.lower(), drug_name)
192
- fda_info = get_fda_approval(query_name)
193
- fda_status = "Not Available"
194
- if fda_info and fda_info.get("openfda", {}).get("brand_name"):
195
- fda_status = ", ".join(fda_info["openfda"]["brand_name"])
196
- pubchem_details = get_pubchem_drug_details(query_name)
197
- if pubchem_details:
198
- formula = pubchem_details.get("Molecular Formula", "N/A")
199
- iupac = pubchem_details.get("IUPAC Name", "N/A")
200
- canon_smiles = pubchem_details.get("Canonical SMILES", "N/A")
201
- else:
202
- formula = iupac = canon_smiles = "Not Available"
203
- rxnorm_id = get_rxnorm_rxcui(query_name)
204
- if rxnorm_id:
205
- rx_props = get_rxnorm_properties(rxnorm_id)
206
- rxnorm_info = f"RxCUI: {rxnorm_id}\nProperties: {rx_props}"
207
- else:
208
- rxnorm_info = "No RxNorm data available."
209
- prompt = (
210
- f"Provide an innovative, advanced analysis for '{drug_name}' (generic: {query_name}).\n\n"
211
- f"**FDA Approval Status:** {fda_status}\n\n"
212
- f"**PubChem Details:**\n"
213
- f"- Molecular Formula: {formula}\n"
214
- f"- IUPAC Name: {iupac}\n"
215
- f"- Canonical SMILES: {canon_smiles}\n\n"
216
- f"**RxNorm Info:** {rxnorm_info}\n\n"
217
- f"Include bullet points on:\n"
218
- f"- Pharmacogenomic considerations (e.g., genetic variations impacting drug metabolism or toxicity)\n"
219
- f"- Potential repurposing opportunities based on drug mechanisms\n"
220
- f"- Regulatory insights and challenges for personalized medicine\n"
221
- f"- Recommendations for future research and data integration\n"
222
- )
223
- return generate_ai_content(prompt)
224
-
225
- # -------------------------------
226
- # STREAMLIT APP LAYOUT
227
- # -------------------------------
228
- tabs = st.tabs([
229
- "💊 Drug Development",
230
- "📊 Trial Analytics",
231
- "🧬 Molecular Profiling",
232
- "📜 Regulatory Intelligence",
233
- "📚 Literature Search",
234
- "📈 Dashboard",
235
- "🧪 Drug Data Integration",
236
- "🤖 AI Insights"
237
- ])
238
-
239
- # ----- Tab 1: Drug Development -----
240
- with tabs[0]:
241
- st.header("AI‑Driven Drug Development Strategy")
242
- target = st.text_input("Target Disease/Pathway:", placeholder="Enter disease mechanism or target")
243
- target_gene = st.text_input("Target Gene (PharmGKB Accession):", placeholder="e.g., PA1234")
244
- strategy = st.selectbox("Development Strategy:", ["First-in-class", "Me-too", "Repurposing", "Biologic"])
245
 
246
- if st.button("Generate Development Plan"):
247
- with st.spinner("Generating comprehensive development plan..."):
248
- plan_prompt = (
249
- f"Develop a detailed drug development plan for treating {target} using a {strategy} strategy. "
250
- "Include sections on target validation, lead optimization, preclinical testing, clinical trial design, "
251
- "regulatory strategy, market analysis, competitive landscape, and pharmacogenomic considerations."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  )
253
- plan = generate_ai_content(plan_prompt)
254
- st.subheader("Comprehensive Development Plan")
255
- st.markdown(plan)
 
 
 
256
 
257
- st.subheader("FDA Regulatory Insights")
258
- if target:
259
- fda_data = get_fda_approval(target.split()[0])
260
- if fda_data:
261
- st.json(fda_data)
262
- else:
263
- st.write("No FDA data found for the given target.")
 
 
 
 
 
 
 
 
 
 
264
 
265
- st.subheader("Pharmacogenomic Considerations")
266
- if target_gene:
267
- if not target_gene.startswith("PA"):
268
- st.warning("Enter a valid PharmGKB accession (e.g., PA1234).")
269
- else:
270
- # For demonstration, use dummy variant data.
271
- variants = ["rsDummy1", "rsDummy2"]
272
- sample_annots = {"rsDummy1": ["DrugA", "DrugB"], "rsDummy2": ["DrugC"]}
273
- try:
274
- net_fig = create_variant_network(target_gene, variants, sample_annots)
275
- st.plotly_chart(net_fig, use_container_width=True)
276
- except Exception as e:
277
- st.error(f"Network graph error: {e}")
278
- else:
279
- st.write("Provide a PharmGKB gene accession to retrieve pharmacogenomic data.")
280
-
281
- # ----- Tab 2: Clinical Trial Analytics -----
282
- with tabs[1]:
283
- st.header("Clinical Trial Landscape Analytics")
284
- trial_query = st.text_input("Search Clinical Trials:", placeholder="Enter condition, intervention, or NCT number")
285
- if st.button("Analyze Trial Landscape"):
286
- with st.spinner("Fetching trial data..."):
287
- trials = get_clinical_trials(trial_query)
288
- if trials and trials.get("studies"):
289
- trial_data = []
290
- for study in trials["studies"][:5]:
291
- trial_data.append({
292
- "Title": study.get("protocolSection", {}).get("identificationModule", {}).get("briefTitle", "N/A"),
293
- "Status": study.get("protocolSection", {}).get("statusModule", {}).get("overallStatus", "N/A"),
294
- "Phase": study.get("protocolSection", {}).get("designModule", {}).get("phases", ["Not Available"])[0],
295
- "Enrollment": study.get("protocolSection", {}).get("designModule", {}).get("enrollmentInfo", {}).get("count", "N/A")
296
- })
297
- df_trials = pd.DataFrame(trial_data)
298
- st.dataframe(df_trials)
299
- else:
300
- st.warning("No clinical trials found for the query.")
301
 
302
- ae_data = analyze_adverse_events(trial_query)
303
- if ae_data and ae_data.get("results"):
304
- st.subheader("Adverse Event Profile (Top 5)")
305
- ae_results = ae_data["results"][:5]
306
- ae_df = pd.json_normalize(ae_results)
307
- st.dataframe(ae_df)
308
- if "patient.reaction.reactionmeddrapt" in ae_df.columns:
309
- try:
310
- reactions = ae_df["patient.reaction.reactionmeddrapt"].explode().dropna()
311
- top_reactions = reactions.value_counts().nlargest(10)
312
- fig, ax = plt.subplots(figsize=(10, 6))
313
- sns.barplot(x=top_reactions.values, y=top_reactions.index, ax=ax)
314
- ax.set_title("Top Adverse Reactions")
315
- ax.set_xlabel("Frequency")
316
- ax.set_ylabel("Reaction")
317
- st.pyplot(fig)
318
- except Exception as e:
319
- st.error(f"Error visualizing adverse events: {e}")
320
- else:
321
- st.write("No adverse event data available.")
322
-
323
- # ----- Tab 3: Advanced Molecular Profiling -----
324
- with tabs[2]:
325
- st.header("Advanced Molecular Profiling")
326
- compound_input = st.text_input("Compound Identifier:", placeholder="Enter drug name, SMILES, or INN")
327
- if st.button("Analyze Compound"):
328
- with st.spinner("Fetching molecular structure and properties from PubChem..."):
329
- query_compound = TRADE_TO_GENERIC.get(compound_input.lower(), compound_input)
330
- pubchem_info = get_pubchem_drug_details(query_compound)
331
- if pubchem_info:
332
- smiles = pubchem_info.get("Canonical SMILES", "N/A")
333
- if smiles != "N/A":
334
- mol_image = draw_molecule(smiles)
335
- if mol_image:
336
- st.image(mol_image, caption="2D Molecular Structure")
337
  else:
338
- st.error("Canonical SMILES not available for this compound.")
339
- st.subheader("Physicochemical Properties")
340
- st.write(f"**Molecular Formula:** {pubchem_info.get('Molecular Formula', 'N/A')}")
341
- st.write(f"**IUPAC Name:** {pubchem_info.get('IUPAC Name', 'N/A')}")
342
- st.write(f"**Canonical SMILES:** {pubchem_info.get('Canonical SMILES', 'N/A')}")
343
- else:
344
- st.error("PubChem details not available for the given compound.")
345
-
346
- # ----- Tab 4: Global Regulatory Monitoring -----
347
- with tabs[3]:
348
- st.header("Global Regulatory Monitoring")
349
- st.markdown("**Note:** This section focuses on FDA data and PubChem drug details due to API limitations for EMA/WHO/DailyMed.")
350
- drug_prod = st.text_input("Drug Product:", placeholder="Enter generic or brand name")
351
- if st.button("Generate Regulatory Report"):
352
- with st.spinner("Compiling regulatory data..."):
353
- fda_data = get_fda_approval(drug_prod)
354
- fda_status = "Not Available"
355
- if fda_data and fda_data.get("openfda", {}).get("brand_name"):
356
- fda_status = ", ".join(fda_data["openfda"]["brand_name"])
357
- pubchem_details = get_pubchem_drug_details(drug_prod)
358
- if pubchem_details:
359
- formula = pubchem_details.get("Molecular Formula", "N/A")
360
- iupac = pubchem_details.get("IUPAC Name", "N/A")
361
- canon_smiles = pubchem_details.get("Canonical SMILES", "N/A")
362
- else:
363
- formula = iupac = canon_smiles = "Not Available"
364
- col1, col2 = st.columns(2)
365
- with col1:
366
- st.markdown("**FDA Status**")
367
- st.write(fda_status)
368
- with col2:
369
- st.markdown("**Drug Details (PubChem)**")
370
- st.write(f"**Molecular Formula:** {formula}")
371
- st.write(f"**IUPAC Name:** {iupac}")
372
- st.write(f"**Canonical SMILES:** {canon_smiles}")
373
- report_text = (
374
- f"### Regulatory Report for {drug_prod}\n\n"
375
- f"**FDA Status:** {fda_status}\n\n"
376
- f"**Molecular Formula:** {formula}\n\n"
377
- f"**IUPAC Name:** {iupac}\n\n"
378
- f"**Canonical SMILES:** {canon_smiles}\n"
379
- )
380
- with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp:
381
- pdf_file = save_pdf_report(report_text, tmp.name)
382
- if pdf_file:
383
- with open(pdf_file, "rb") as f:
384
- st.download_button("Download Regulatory Report (PDF)", data=f, file_name=f"{drug_prod}_report.pdf", mime="application/pdf")
385
- os.remove(pdf_file)
386
-
387
- # ----- Tab 5: Literature Search -----
388
- with tabs[4]:
389
- st.header("Literature Search")
390
- lit_query = st.text_input("Enter search query for PubMed:", placeholder="e.g., Alzheimer's disease genetics")
391
- if st.button("Search PubMed"):
392
- with st.spinner("Searching PubMed..."):
393
- pubmed_results = get_pubmed(lit_query)
394
- if pubmed_results and pubmed_results.get("esearchresult", {}).get("idlist"):
395
- id_list = pubmed_results["esearchresult"]["idlist"]
396
- st.subheader(f"Found {len(id_list)} PubMed Results")
397
- for pmid in id_list:
398
- st.markdown(f"- [PMID: {pmid}](https://pubmed.ncbi.nlm.nih.gov/{pmid}/)")
399
- else:
400
- st.write("No PubMed results found.")
401
-
402
- # ----- Tab 6: Comprehensive Dashboard -----
403
- with tabs[5]:
404
- st.header("Comprehensive Dashboard")
405
- kpi_fda = 5000
406
- kpi_trials = 12000
407
- kpi_pubs = 250000
408
- col1, col2, col3 = st.columns(3)
409
- col1.metric("FDA Approved Drugs", kpi_fda)
410
- col2.metric("Ongoing Trials", kpi_trials)
411
- col3.metric("Publications", kpi_pubs)
412
- st.subheader("Trend Analysis")
413
- years = list(range(2000, 2026))
414
- approvals = [kpi_fda // len(years)] * len(years)
415
- fig_trend, ax_trend = plt.subplots(figsize=(10, 6))
416
- sns.lineplot(x=years, y=approvals, marker="o", ax=ax_trend)
417
- ax_trend.set_title("FDA Approvals Over Time")
418
- ax_trend.set_xlabel("Year")
419
- ax_trend.set_ylabel("Number of Approvals")
420
- st.pyplot(fig_trend)
421
- st.subheader("Gene-Variant-Drug Network (Sample)")
422
- sample_gene = "CYP2C19"
423
- sample_variants = ["rs4244285", "rs12248560"]
424
- sample_annots = {"rs4244285": ["Clopidogrel", "Omeprazole"], "rs12248560": ["Sertraline"]}
425
- try:
426
- net_fig = create_variant_network(sample_gene, sample_variants, sample_annots)
427
- st.plotly_chart(net_fig, use_container_width=True)
428
- except Exception as e:
429
- st.error(f"Network graph error: {e}")
430
-
431
- # ----- Tab 7: Drug Data Integration -----
432
- with tabs[6]:
433
- st.header("🧪 Drug Data Integration")
434
- drug_integration = st.text_input("Enter Drug Name for API Integration:", placeholder="e.g., aspirin")
435
- if st.button("Retrieve Drug Data"):
436
- with st.spinner("Fetching drug data from multiple sources..."):
437
- query_drug = TRADE_TO_GENERIC.get(drug_integration.lower(), drug_integration)
438
- rxnorm_id = get_rxnorm_rxcui(query_drug)
439
- if rxnorm_id:
440
- rx_props = get_rxnorm_properties(rxnorm_id)
441
- st.subheader("RxNorm Data")
442
- st.write(f"RxCUI for {drug_integration}: {rxnorm_id}")
443
- st.json(rx_props if rx_props else {"message": "No RxNorm properties found."})
444
- else:
445
- st.write("No RxCUI found for the given drug name.")
446
- st.subheader("PubChem Drug Details")
447
- pubchem_info = get_pubchem_drug_details(query_drug)
448
- if pubchem_info:
449
- st.write(f"**Molecular Formula:** {pubchem_info.get('Molecular Formula', 'N/A')}")
450
- st.write(f"**IUPAC Name:** {pubchem_info.get('IUPAC Name', 'N/A')}")
451
- st.write(f"**Canonical SMILES:** {pubchem_info.get('Canonical SMILES', 'N/A')}")
452
- else:
453
- st.write("No PubChem details found.")
454
- st.subheader("RxClass Information")
455
- st.write("RxClass data is currently unavailable via API. Consider alternative sources for drug class information.")
456
-
457
- # ----- Tab 8: AI Insights -----
458
- with tabs[7]:
459
- st.header("🤖 AI Insights")
460
- ai_drug = st.text_input("Enter Drug Name for AI-Driven Analysis:", placeholder="e.g., tylenol")
461
- if st.button("Generate AI Insights"):
462
- with st.spinner("Generating AI-driven insights..."):
463
- query_ai_drug = TRADE_TO_GENERIC.get(ai_drug.lower(), ai_drug)
464
- insights_text = generate_drug_insights(query_ai_drug)
465
- st.subheader("AI‑Driven Drug Analysis")
466
- st.markdown(insights_text)
467
 
 
 
 
 
 
 
 
1
+ """
2
+ Pharma Research Intelligence Suite (PRIS) v2.0
3
+ Enterprise-Grade Drug Development Platform with Robust UI
4
+ """
5
+
6
  import streamlit as st
7
  import requests
8
+ from rdkit import Chem
9
+ from rdkit.Chem import Draw
10
  import pandas as pd
11
  import matplotlib.pyplot as plt
12
  import seaborn as sns
 
 
 
 
 
 
 
 
 
 
13
  from openai import OpenAI
14
+ from typing import Optional, Dict, List, Any
15
 
16
+ # ======================
17
+ # Configuration
18
+ # ======================
19
+ st.set_page_config(
20
+ page_title="PRIS 2.0 - Pharma Research Platform",
21
+ page_icon="🧬",
22
+ layout="wide"
23
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ # ======================
26
+ # API Configuration
27
+ # ======================
28
+ CLINICAL_TRIALS_API = "https://clinicaltrials.gov/api/v2/studies"
29
+ FDA_API = "https://api.fda.gov/drug/label.json"
30
+ PUBCHEM_API = "https://pubchem.ncbi.nlm.nih.gov/rest/pug/compound/name/{}/JSON"
31
 
32
+ # ======================
33
+ # Core Services
34
+ # ======================
35
+ class PharmaResearchService:
36
+ """Enterprise API integration layer with advanced resilience"""
37
+
38
+ def __init__(self):
39
+ self.session = requests.Session()
40
+ self.session.headers.update({
41
+ "User-Agent": "PRIS/2.0 (Enterprise Pharma Analytics)",
42
+ "Accept": "application/json"
43
+ })
44
+
45
+ def get_clinical_trials(self, query: str) -> List[Dict]:
46
+ """Get clinical trials with robust error handling"""
47
+ try:
48
+ response = self.session.get(
49
+ CLINICAL_TRIALS_API,
50
+ params={"query": query, "format": "json", "limit": 5},
51
+ timeout=10
52
+ )
53
+ response.raise_for_status()
54
+ data = response.json()
55
+ return data.get("studies", [])[:5]
56
+ except Exception as e:
57
+ st.error(f"Clinical trials API error: {str(e)}")
58
+ return []
59
 
60
+ def get_fda_approval(self, drug_name: str) -> Optional[Dict]:
61
+ """Retrieve FDA approval information"""
62
+ try:
63
+ response = self.session.get(
64
+ FDA_API,
65
+ params={
66
+ "api_key": st.secrets["OPENFDA_KEY"],
67
+ "search": f'openfda.brand_name:"{drug_name}"',
68
+ "limit": 1
69
+ },
70
+ timeout=10
71
+ )
72
+ response.raise_for_status()
73
+ data = response.json()
74
+ return data.get("results", [None])[0]
75
+ except Exception as e:
76
+ st.error(f"FDA API error: {str(e)}")
77
+ return None
78
 
79
+ def get_compound_data(self, name: str) -> Optional[Dict]:
80
+ """Get PubChem compound data"""
81
+ try:
82
+ response = self.session.get(
83
+ PUBCHEM_API.format(name),
84
+ timeout=10
85
+ )
86
+ response.raise_for_status()
87
+ data = response.json()
88
+ return data.get("PC_Compounds", [{}])[0]
89
+ except Exception as e:
90
+ st.error(f"PubChem API error: {str(e)}")
91
+ return None
92
 
93
+ # ======================
94
+ # UI Components
95
+ # ======================
96
+ class PharmaDashboard:
97
+ """Professional pharmaceutical research dashboard"""
98
+
99
+ def __init__(self):
100
+ self.service = PharmaResearchService()
101
+ self._configure_styles()
102
+
103
+ def _configure_styles(self):
104
+ """Inject custom CSS for professional appearance"""
105
+ st.markdown("""
106
+ <style>
107
+ .main {background-color: #f8f9fa;}
108
+ .block-container {padding-top: 2rem;}
109
+ .header {color: #2c3e50; border-bottom: 2px solid #3498db;}
110
+ .metric {background-color: white; padding: 1.5rem; border-radius: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);}
111
+ .plot-container {background-color: white; padding: 1rem; border-radius: 10px; margin-top: 1rem;}
112
+ </style>
113
+ """, unsafe_allow_html=True)
114
+
115
+ def render(self):
116
+ """Main application interface"""
117
+ st.markdown("<h1 class='header'>PRIS 2.0 - Pharma Research Platform</h1>", unsafe_allow_html=True)
118
+
119
+ tab1, tab2, tab3 = st.tabs([
120
+ "🧪 Compound Analyzer",
121
+ "📊 Clinical Trials",
122
+ "📜 Regulatory Info"
123
+ ])
124
+
125
+ with tab1:
126
+ self._render_compound_analyzer()
127
+ with tab2:
128
+ self._render_clinical_trials()
129
+ with tab3:
130
+ self._render_regulatory_info()
131
+
132
+ def _render_compound_analyzer(self):
133
+ """Compound analysis section"""
134
+ st.subheader("Molecular Analysis")
135
+ col1, col2 = st.columns([1, 2])
136
+
137
+ with col1:
138
+ compound = st.text_input("Enter compound name:", "Aspirin")
139
+ if st.button("Analyze"):
140
+ with st.spinner("Analyzing compound..."):
141
+ data = self.service.get_compound_data(compound)
142
+ if data:
143
+ self._display_compound_info(data)
144
+ else:
145
+ st.warning("Compound not found")
146
 
147
+ with col2:
148
+ if "mol_image" in st.session_state:
149
+ st.image(st.session_state.mol_image, caption="Molecular Structure")
150
+
151
+ def _display_compound_info(self, data: Dict):
152
+ """Show compound information"""
153
+ properties = self._extract_properties(data)
154
+
155
+ col1, col2, col3 = st.columns(3)
156
+ with col1:
157
+ st.markdown(f"**Molecular Formula** \n{properties['formula']}")
158
+ with col2:
159
+ st.markdown(f"**Molecular Weight** \n{properties['weight']}")
160
+ with col3:
161
+ st.markdown(f"**IUPAC Name** \n{properties['iupac']}")
162
+
163
+ # Generate molecular structure
164
+ mol = Chem.MolFromSmiles(properties['smiles'])
165
  if mol:
166
+ img = Draw.MolToImage(mol, size=(400, 300))
167
+ st.session_state.mol_image = img
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
+ def _extract_properties(self, data: Dict) -> Dict:
170
+ """Extract chemical properties from PubChem data"""
171
+ return {
172
+ "formula": next(
173
+ (p["value"]["sval"] for p in data.get("props", [])
174
+ if p.get("urn", {}).get("label") == "Molecular Formula"),
175
+ "N/A"
176
+ ),
177
+ "weight": next(
178
+ (p["value"]["sval"] for p in data.get("props", [])
179
+ if p.get("urn", {}).get("label") == "Molecular Weight"),
180
+ "N/A"
181
+ ),
182
+ "iupac": next(
183
+ (p["value"]["sval"] for p in data.get("props", [])
184
+ if p.get("urn", {}).get("name") == "Preferred"),
185
+ "N/A"
186
+ ),
187
+ "smiles": next(
188
+ (p["value"]["sval"] for p in data.get("props", [])
189
+ if p.get("name") == "Canonical SMILES"),
190
+ "N/A"
191
  )
192
+ }
193
+
194
+ def _render_clinical_trials(self):
195
+ """Clinical trials analysis section"""
196
+ st.subheader("Clinical Trial Explorer")
197
+ query = st.text_input("Search for trials:", "Diabetes")
198
 
199
+ if st.button("Search Trials"):
200
+ with st.spinner("Searching clinical trials..."):
201
+ trials = self.service.get_clinical_trials(query)
202
+ if trials:
203
+ self._display_trial_results(trials)
204
+ else:
205
+ st.warning("No trials found for this query")
206
+
207
+ def _display_trial_results(self, trials: List[Dict]):
208
+ """Display clinical trial results"""
209
+ df = pd.DataFrame([{
210
+ "NCT ID": t.get("protocolSection", {}).get("identificationModule", {}).get("nctId", "N/A"),
211
+ "Title": t.get("protocolSection", {}).get("identificationModule", {}).get("briefTitle", "N/A"),
212
+ "Status": t.get("protocolSection", {}).get("statusModule", {}).get("overallStatus", "N/A"),
213
+ "Phase": self._parse_phase(t),
214
+ "Enrollment": t.get("protocolSection", {}).get("designModule", {}).get("enrollmentInfo", {}).get("count", "N/A")
215
+ } for t in trials])
216
 
217
+ st.dataframe(
218
+ df,
219
+ use_container_width=True,
220
+ hide_index=True,
221
+ column_config={
222
+ "NCT ID": st.column_config.TextColumn(width="medium"),
223
+ "Title": st.column_config.TextColumn(width="large"),
224
+ "Status": st.column_config.TextColumn(width="small"),
225
+ "Phase": st.column_config.TextColumn(width="small"),
226
+ "Enrollment": st.column_config.NumberColumn(format="%d")
227
+ }
228
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
+ # Phase distribution visualization
231
+ st.subheader("Trial Phase Distribution")
232
+ self._plot_phase_distribution(df)
233
+
234
+ def _parse_phase(self, trial: Dict) -> str:
235
+ """Extract and format trial phase"""
236
+ phases = trial.get("protocolSection", {}).get("designModule", {}).get("phases", [])
237
+ return phases[0] if phases else "N/A"
238
+
239
+ def _plot_phase_distribution(self, df: pd.DataFrame):
240
+ """Create phase distribution visualization"""
241
+ fig, ax = plt.subplots(figsize=(8, 4))
242
+ sns.countplot(
243
+ data=df,
244
+ x="Phase",
245
+ order=sorted(df["Phase"].unique()),
246
+ palette="Blues_d",
247
+ ax=ax
248
+ )
249
+ ax.set_title("Clinical Trial Phases")
250
+ ax.set_xlabel("")
251
+ ax.set_ylabel("Number of Trials")
252
+ st.pyplot(fig)
253
+
254
+ def _render_regulatory_info(self):
255
+ """Regulatory information section"""
256
+ st.subheader("Regulatory Intelligence")
257
+ drug_name = st.text_input("Enter drug name:", "Aspirin")
258
+
259
+ if st.button("Search Regulatory Data"):
260
+ with st.spinner("Retrieving FDA information..."):
261
+ fda_data = self.service.get_fda_approval(drug_name)
262
+ if fda_data:
263
+ self._display_fda_info(fda_data)
 
264
  else:
265
+ st.warning("No FDA data found for this drug")
266
+
267
+ def _display_fda_info(self, data: Dict):
268
+ """Display FDA approval information"""
269
+ openfda = data.get("openfda", {})
270
+ st.markdown(f"""
271
+ **Brand Name**: {', '.join(openfda.get('brand_name', ['N/A']))}
272
+ **Manufacturer**: {', '.join(openfda.get('manufacturer_name', ['N/A']))}
273
+ **Approval Date**: {data.get('effective_time', 'N/A')}
274
+ **Dosage Form**: {', '.join(openfda.get('dosage_form', ['N/A']))}
275
+ """)
276
+
277
+ st.subheader("Indications and Usage")
278
+ st.write(data.get("indications_and_usage", ["No information available"])[0])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
 
280
+ # ======================
281
+ # Application Entry
282
+ # ======================
283
+ if __name__ == "__main__":
284
+ app = PharmaDashboard()
285
+ app.render()