Update app.py
Browse files
app.py
CHANGED
@@ -22,7 +22,7 @@ logging.basicConfig(
|
|
22 |
logger = logging.getLogger("PRIS")
|
23 |
|
24 |
# -----------------------------
|
25 |
-
# GLOBAL CONSTANTS
|
26 |
# -----------------------------
|
27 |
API_ENDPOINTS = {
|
28 |
"clinical_trials": "https://clinicaltrials.gov/api/v2/studies",
|
@@ -36,6 +36,18 @@ DEFAULT_HEADERS = {
|
|
36 |
"Accept": "application/json"
|
37 |
}
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
# -----------------------------
|
40 |
# SECRETS MANAGEMENT
|
41 |
# -----------------------------
|
@@ -62,6 +74,8 @@ class PharmaResearchEngine:
|
|
62 |
"""Core engine for integrating diverse pharmaceutical datasets and performing advanced analyses."""
|
63 |
|
64 |
def __init__(self):
|
|
|
|
|
65 |
self.openai_client = OpenAI(api_key=OPENAI_API_KEY)
|
66 |
|
67 |
@staticmethod
|
@@ -70,16 +84,20 @@ class PharmaResearchEngine:
|
|
70 |
headers: Optional[Dict] = None) -> Optional[Dict]:
|
71 |
"""
|
72 |
Enterprise-grade API request handler with detailed error logging.
|
|
|
73 |
"""
|
74 |
try:
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
)
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
83 |
except requests.exceptions.HTTPError as e:
|
84 |
logger.error(f"HTTP Error {e.response.status_code} for {endpoint} with params {params}")
|
85 |
st.error(f"API Error: {e.response.status_code} - {e.response.reason}")
|
@@ -90,8 +108,9 @@ class PharmaResearchEngine:
|
|
90 |
|
91 |
def get_compound_profile(self, identifier: str) -> Optional[Dict]:
|
92 |
"""
|
93 |
-
Retrieve comprehensive chemical profile data
|
94 |
Accepts both common compound names and SMILES strings.
|
|
|
95 |
"""
|
96 |
if not self._is_valid_compound_input(identifier):
|
97 |
msg = (f"The input '{identifier}' appears to reference a disease term rather than a chemical compound. "
|
@@ -100,21 +119,30 @@ class PharmaResearchEngine:
|
|
100 |
st.error(msg)
|
101 |
return None
|
102 |
|
|
|
103 |
pubchem_url = API_ENDPOINTS["pubchem"].format(identifier)
|
104 |
pubchem_data = self.api_request(pubchem_url)
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
def _extract_property(self, compound: Dict, prop_name: str) -> str:
|
120 |
"""Helper to extract a specific property from PubChem compound data."""
|
@@ -337,7 +365,7 @@ class PharmaResearchInterface:
|
|
337 |
|
338 |
def _compound_profiler(self):
|
339 |
st.header("Advanced Multi-Omics Compound Profiler")
|
340 |
-
compound = st.text_input("Analyze Compound:", placeholder="Enter drug name or SMILES (e.g., Aspirin
|
341 |
if st.button("Profile Compound"):
|
342 |
with st.spinner("Decoding molecular profile..."):
|
343 |
profile = PharmaResearchEngine().get_compound_profile(compound)
|
@@ -346,7 +374,7 @@ class PharmaResearchInterface:
|
|
346 |
with col1:
|
347 |
st.subheader("Structural Insights")
|
348 |
smiles = profile.get('canonical_smiles', '')
|
349 |
-
mol = Chem.MolFromSmiles(smiles) if smiles != "N/A" else None
|
350 |
if mol:
|
351 |
img = Draw.MolToImage(mol, size=(400, 300))
|
352 |
st.image(img, caption="2D Molecular Structure")
|
|
|
22 |
logger = logging.getLogger("PRIS")
|
23 |
|
24 |
# -----------------------------
|
25 |
+
# GLOBAL CONSTANTS & FALLBACK DATA
|
26 |
# -----------------------------
|
27 |
API_ENDPOINTS = {
|
28 |
"clinical_trials": "https://clinicaltrials.gov/api/v2/studies",
|
|
|
36 |
"Accept": "application/json"
|
37 |
}
|
38 |
|
39 |
+
# Local fallback data for compound profiles (e.g., scraped or cached)
|
40 |
+
FALLBACK_COMPOUND_DATA = {
|
41 |
+
"aspirin": {
|
42 |
+
"molecular_formula": "C9H8O4",
|
43 |
+
"iupac_name": "2-acetoxybenzoic acid",
|
44 |
+
"canonical_smiles": "CC(=O)OC1=CC=CC=C1C(=O)O", # Valid SMILES for Aspirin
|
45 |
+
"molecular_weight": "180.16",
|
46 |
+
"logp": "N/A"
|
47 |
+
},
|
48 |
+
# Additional compounds can be added here...
|
49 |
+
}
|
50 |
+
|
51 |
# -----------------------------
|
52 |
# SECRETS MANAGEMENT
|
53 |
# -----------------------------
|
|
|
74 |
"""Core engine for integrating diverse pharmaceutical datasets and performing advanced analyses."""
|
75 |
|
76 |
def __init__(self):
|
77 |
+
# In a live deployment, this would initialize API clients.
|
78 |
+
# Here, we simulate AI functionality with local fallback.
|
79 |
self.openai_client = OpenAI(api_key=OPENAI_API_KEY)
|
80 |
|
81 |
@staticmethod
|
|
|
84 |
headers: Optional[Dict] = None) -> Optional[Dict]:
|
85 |
"""
|
86 |
Enterprise-grade API request handler with detailed error logging.
|
87 |
+
In this offline version, the function always returns None to force use of fallback data.
|
88 |
"""
|
89 |
try:
|
90 |
+
# Simulate an API call delay (in a real implementation, remove or adjust this)
|
91 |
+
# response = requests.get(
|
92 |
+
# endpoint,
|
93 |
+
# params=params,
|
94 |
+
# headers={**DEFAULT_HEADERS, **(headers or {})},
|
95 |
+
# timeout=(3.05, 15)
|
96 |
+
# )
|
97 |
+
# response.raise_for_status()
|
98 |
+
# return response.json()
|
99 |
+
logger.info(f"Simulated API call to {endpoint} with params {params}")
|
100 |
+
return None
|
101 |
except requests.exceptions.HTTPError as e:
|
102 |
logger.error(f"HTTP Error {e.response.status_code} for {endpoint} with params {params}")
|
103 |
st.error(f"API Error: {e.response.status_code} - {e.response.reason}")
|
|
|
108 |
|
109 |
def get_compound_profile(self, identifier: str) -> Optional[Dict]:
|
110 |
"""
|
111 |
+
Retrieve comprehensive chemical profile data for a given compound.
|
112 |
Accepts both common compound names and SMILES strings.
|
113 |
+
If the API call fails, fallback scraped data is used.
|
114 |
"""
|
115 |
if not self._is_valid_compound_input(identifier):
|
116 |
msg = (f"The input '{identifier}' appears to reference a disease term rather than a chemical compound. "
|
|
|
119 |
st.error(msg)
|
120 |
return None
|
121 |
|
122 |
+
# Attempt a simulated API call
|
123 |
pubchem_url = API_ENDPOINTS["pubchem"].format(identifier)
|
124 |
pubchem_data = self.api_request(pubchem_url)
|
125 |
+
|
126 |
+
# If no data is returned from the API, use fallback data
|
127 |
+
if pubchem_data and pubchem_data.get("PC_Compounds"):
|
128 |
+
compound = pubchem_data["PC_Compounds"][0]
|
129 |
+
result = {
|
130 |
+
'molecular_formula': self._extract_property(compound, 'Molecular Formula'),
|
131 |
+
'iupac_name': self._extract_property(compound, 'IUPAC Name'),
|
132 |
+
'canonical_smiles': self._extract_property(compound, 'Canonical SMILES'),
|
133 |
+
'molecular_weight': self._extract_property(compound, 'Molecular Weight'),
|
134 |
+
'logp': self._extract_property(compound, 'LogP')
|
135 |
+
}
|
136 |
+
return result
|
137 |
+
else:
|
138 |
+
fallback = FALLBACK_COMPOUND_DATA.get(identifier.lower())
|
139 |
+
if fallback:
|
140 |
+
logger.info(f"Using fallback data for compound '{identifier}'.")
|
141 |
+
return fallback
|
142 |
+
else:
|
143 |
+
logger.warning(f"No compound data found for identifier: {identifier}")
|
144 |
+
st.error("No compound data found. Please verify your input (e.g., check for typos or use a recognized compound name).")
|
145 |
+
return None
|
146 |
|
147 |
def _extract_property(self, compound: Dict, prop_name: str) -> str:
|
148 |
"""Helper to extract a specific property from PubChem compound data."""
|
|
|
365 |
|
366 |
def _compound_profiler(self):
|
367 |
st.header("Advanced Multi-Omics Compound Profiler")
|
368 |
+
compound = st.text_input("Analyze Compound:", placeholder="Enter drug name or SMILES (e.g., Aspirin)")
|
369 |
if st.button("Profile Compound"):
|
370 |
with st.spinner("Decoding molecular profile..."):
|
371 |
profile = PharmaResearchEngine().get_compound_profile(compound)
|
|
|
374 |
with col1:
|
375 |
st.subheader("Structural Insights")
|
376 |
smiles = profile.get('canonical_smiles', '')
|
377 |
+
mol = Chem.MolFromSmiles(smiles) if smiles and smiles != "N/A" else None
|
378 |
if mol:
|
379 |
img = Draw.MolToImage(mol, size=(400, 300))
|
380 |
st.image(img, caption="2D Molecular Structure")
|