Spaces:
Build error
Build error
Harrizi Saad commited on
Upload app.py
Browse files
app.py
CHANGED
|
@@ -14,19 +14,19 @@ st.set_page_config(
|
|
| 14 |
initial_sidebar_state="collapsed"
|
| 15 |
)
|
| 16 |
|
| 17 |
-
#
|
| 18 |
st.markdown("""
|
| 19 |
<style>
|
| 20 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap');
|
| 21 |
-
|
| 22 |
* {
|
| 23 |
font-family: 'Inter', sans-serif;
|
| 24 |
}
|
| 25 |
-
|
| 26 |
.main {
|
| 27 |
background: linear-gradient(180deg, #0a0e27 0%, #1a1f3a 50%, #0a0e27 100%);
|
| 28 |
}
|
| 29 |
-
|
| 30 |
.main-header {
|
| 31 |
font-size: 4rem;
|
| 32 |
font-weight: 900;
|
|
@@ -37,21 +37,71 @@ st.markdown("""
|
|
| 37 |
margin-bottom: 0.5rem;
|
| 38 |
animation: glow 3s ease-in-out infinite alternate;
|
| 39 |
}
|
| 40 |
-
|
| 41 |
@keyframes glow {
|
| 42 |
from { filter: drop-shadow(0 0 20px rgba(102, 126, 234, 0.3)); }
|
| 43 |
to { filter: drop-shadow(0 0 40px rgba(118, 75, 162, 0.6)); }
|
| 44 |
}
|
| 45 |
-
|
| 46 |
.sub-header {
|
| 47 |
font-size: 1.4rem;
|
| 48 |
text-align: center;
|
| 49 |
color: #a0a6b8;
|
| 50 |
margin-bottom: 3rem;
|
| 51 |
font-weight: 300;
|
| 52 |
-
letter-spacing: 0.5px;
|
| 53 |
}
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
.risk-card-low {
|
| 56 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 57 |
padding: 3rem;
|
|
@@ -61,12 +111,12 @@ st.markdown("""
|
|
| 61 |
box-shadow: 0 20px 60px rgba(102, 126, 234, 0.5);
|
| 62 |
animation: pulse-low 2s ease-in-out infinite;
|
| 63 |
}
|
| 64 |
-
|
| 65 |
@keyframes pulse-low {
|
| 66 |
0%, 100% { transform: scale(1); }
|
| 67 |
-
50% { transform: scale(1.02); }
|
| 68 |
}
|
| 69 |
-
|
| 70 |
.risk-card-moderate {
|
| 71 |
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 72 |
padding: 3rem;
|
|
@@ -76,12 +126,12 @@ st.markdown("""
|
|
| 76 |
box-shadow: 0 20px 60px rgba(240, 147, 251, 0.5);
|
| 77 |
animation: pulse-moderate 1.5s ease-in-out infinite;
|
| 78 |
}
|
| 79 |
-
|
| 80 |
@keyframes pulse-moderate {
|
| 81 |
0%, 100% { transform: scale(1); }
|
| 82 |
50% { transform: scale(1.03); }
|
| 83 |
}
|
| 84 |
-
|
| 85 |
.risk-card-high {
|
| 86 |
background: linear-gradient(135deg, #ff0844 0%, #ffb199 100%);
|
| 87 |
padding: 3rem;
|
|
@@ -91,12 +141,12 @@ st.markdown("""
|
|
| 91 |
box-shadow: 0 20px 60px rgba(255, 8, 68, 0.6);
|
| 92 |
animation: pulse-high 1s ease-in-out infinite;
|
| 93 |
}
|
| 94 |
-
|
| 95 |
@keyframes pulse-high {
|
| 96 |
0%, 100% { transform: scale(1); }
|
| 97 |
50% { transform: scale(1.05); }
|
| 98 |
}
|
| 99 |
-
|
| 100 |
.property-card {
|
| 101 |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
| 102 |
border: 2px solid rgba(102, 126, 234, 0.3);
|
|
@@ -104,40 +154,14 @@ st.markdown("""
|
|
| 104 |
border-radius: 20px;
|
| 105 |
margin: 1rem 0;
|
| 106 |
transition: all 0.3s ease;
|
| 107 |
-
position: relative;
|
| 108 |
-
overflow: hidden;
|
| 109 |
-
}
|
| 110 |
-
|
| 111 |
-
.property-card::before {
|
| 112 |
-
content: '';
|
| 113 |
-
position: absolute;
|
| 114 |
-
top: 0;
|
| 115 |
-
left: -100%;
|
| 116 |
-
width: 100%;
|
| 117 |
-
height: 100%;
|
| 118 |
-
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
| 119 |
-
transition: left 0.5s;
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
.property-card:hover::before {
|
| 123 |
-
left: 100%;
|
| 124 |
}
|
| 125 |
-
|
| 126 |
.property-card:hover {
|
| 127 |
transform: translateY(-5px);
|
| 128 |
border-color: rgba(102, 126, 234, 0.6);
|
| 129 |
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
| 130 |
}
|
| 131 |
-
|
| 132 |
-
.property-label {
|
| 133 |
-
color: #a0a6b8;
|
| 134 |
-
font-size: 0.85rem;
|
| 135 |
-
font-weight: 600;
|
| 136 |
-
text-transform: uppercase;
|
| 137 |
-
letter-spacing: 1.5px;
|
| 138 |
-
margin-bottom: 0.5rem;
|
| 139 |
-
}
|
| 140 |
-
|
| 141 |
.property-value {
|
| 142 |
color: #ffffff;
|
| 143 |
font-size: 2rem;
|
|
@@ -146,15 +170,7 @@ st.markdown("""
|
|
| 146 |
-webkit-background-clip: text;
|
| 147 |
-webkit-text-fill-color: transparent;
|
| 148 |
}
|
| 149 |
-
|
| 150 |
-
.property-interpretation {
|
| 151 |
-
color: #8b92a8;
|
| 152 |
-
font-size: 0.9rem;
|
| 153 |
-
margin-top: 0.5rem;
|
| 154 |
-
font-style: italic;
|
| 155 |
-
line-height: 1.6;
|
| 156 |
-
}
|
| 157 |
-
|
| 158 |
.mechanism-card {
|
| 159 |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
|
| 160 |
border: 2px solid rgba(102, 126, 234, 0.2);
|
|
@@ -162,151 +178,208 @@ st.markdown("""
|
|
| 162 |
border-radius: 20px;
|
| 163 |
text-align: center;
|
| 164 |
transition: all 0.4s ease;
|
| 165 |
-
position: relative;
|
| 166 |
}
|
| 167 |
-
|
| 168 |
.mechanism-card:hover {
|
| 169 |
transform: translateY(-10px) scale(1.05);
|
| 170 |
box-shadow: 0 20px 50px rgba(102, 126, 234, 0.4);
|
| 171 |
-
border-color: rgba(102, 126, 234, 0.8);
|
| 172 |
-
}
|
| 173 |
-
|
| 174 |
-
.mechanism-icon {
|
| 175 |
-
font-size: 3rem;
|
| 176 |
-
margin-bottom: 1rem;
|
| 177 |
-
filter: drop-shadow(0 0 10px rgba(102, 126, 234, 0.5));
|
| 178 |
-
}
|
| 179 |
-
|
| 180 |
-
.mechanism-title {
|
| 181 |
-
font-size: 1rem;
|
| 182 |
-
color: #a0a6b8;
|
| 183 |
-
font-weight: 600;
|
| 184 |
-
text-transform: uppercase;
|
| 185 |
-
letter-spacing: 1px;
|
| 186 |
-
margin-bottom: 1rem;
|
| 187 |
}
|
| 188 |
-
|
| 189 |
.mechanism-value {
|
| 190 |
font-size: 3rem;
|
| 191 |
font-weight: 900;
|
| 192 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 193 |
-webkit-background-clip: text;
|
| 194 |
-webkit-text-fill-color: transparent;
|
| 195 |
-
margin-bottom: 0.5rem;
|
| 196 |
}
|
| 197 |
-
|
| 198 |
-
.mechanism-bar {
|
| 199 |
-
width: 100%;
|
| 200 |
-
height: 8px;
|
| 201 |
-
background: rgba(255, 255, 255, 0.1);
|
| 202 |
-
border-radius: 10px;
|
| 203 |
-
overflow: hidden;
|
| 204 |
-
margin-top: 1rem;
|
| 205 |
-
}
|
| 206 |
-
|
| 207 |
-
.mechanism-bar-fill {
|
| 208 |
-
height: 100%;
|
| 209 |
-
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
| 210 |
-
border-radius: 10px;
|
| 211 |
-
transition: width 1s ease;
|
| 212 |
-
}
|
| 213 |
-
|
| 214 |
-
.interpretation-box {
|
| 215 |
-
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
| 216 |
-
border-left: 5px solid #667eea;
|
| 217 |
-
padding: 2.5rem;
|
| 218 |
-
border-radius: 20px;
|
| 219 |
-
margin: 2rem 0;
|
| 220 |
-
color: #e8eaf0;
|
| 221 |
-
line-height: 2;
|
| 222 |
-
font-size: 1.05rem;
|
| 223 |
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
| 224 |
-
}
|
| 225 |
-
|
| 226 |
-
.interpretation-section {
|
| 227 |
-
margin: 1.5rem 0;
|
| 228 |
-
padding-left: 1.5rem;
|
| 229 |
-
border-left: 3px solid rgba(102, 126, 234, 0.3);
|
| 230 |
-
}
|
| 231 |
-
|
| 232 |
-
.interpretation-title {
|
| 233 |
-
color: #667eea;
|
| 234 |
-
font-size: 1.3rem;
|
| 235 |
-
font-weight: 700;
|
| 236 |
-
margin-bottom: 1rem;
|
| 237 |
-
display: flex;
|
| 238 |
-
align-items: center;
|
| 239 |
-
}
|
| 240 |
-
|
| 241 |
-
.interpretation-icon {
|
| 242 |
-
margin-right: 0.5rem;
|
| 243 |
-
font-size: 1.5rem;
|
| 244 |
-
}
|
| 245 |
-
|
| 246 |
.structure-container {
|
| 247 |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
|
| 248 |
border: 2px solid rgba(102, 126, 234, 0.3);
|
| 249 |
padding: 2rem;
|
| 250 |
border-radius: 25px;
|
| 251 |
text-align: center;
|
| 252 |
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
| 253 |
}
|
| 254 |
-
|
| 255 |
-
.
|
| 256 |
-
background: linear-gradient(135deg, rgba(
|
| 257 |
-
border:
|
| 258 |
-
padding:
|
| 259 |
border-radius: 20px;
|
| 260 |
margin: 2rem 0;
|
| 261 |
-
color: #
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
.cascade-step {
|
| 265 |
-
display: flex;
|
| 266 |
-
align-items: center;
|
| 267 |
-
margin: 1rem 0;
|
| 268 |
-
font-size: 1.1rem;
|
| 269 |
-
font-weight: 600;
|
| 270 |
-
}
|
| 271 |
-
|
| 272 |
-
.cascade-arrow {
|
| 273 |
-
color: #ff0844;
|
| 274 |
-
font-size: 2rem;
|
| 275 |
-
margin: 0 1rem;
|
| 276 |
-
}
|
| 277 |
-
|
| 278 |
-
.insight-badge {
|
| 279 |
-
display: inline-block;
|
| 280 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 281 |
-
color: white;
|
| 282 |
-
padding: 0.5rem 1rem;
|
| 283 |
-
border-radius: 20px;
|
| 284 |
-
font-size: 0.9rem;
|
| 285 |
-
font-weight: 600;
|
| 286 |
-
margin: 0.5rem 0.5rem 0.5rem 0;
|
| 287 |
-
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
| 288 |
-
}
|
| 289 |
-
|
| 290 |
-
.warning-box {
|
| 291 |
-
background: linear-gradient(135deg, rgba(255, 177, 153, 0.15) 0%, rgba(255, 8, 68, 0.15) 100%);
|
| 292 |
-
border: 2px solid rgba(255, 8, 68, 0.5);
|
| 293 |
-
border-radius: 20px;
|
| 294 |
-
padding: 2rem;
|
| 295 |
-
margin: 1.5rem 0;
|
| 296 |
-
color: #ffb199;
|
| 297 |
-
}
|
| 298 |
-
|
| 299 |
-
.safe-box {
|
| 300 |
-
background: linear-gradient(135deg, rgba(102, 234, 170, 0.15) 0%, rgba(102, 126, 234, 0.15) 100%);
|
| 301 |
-
border: 2px solid rgba(102, 234, 170, 0.5);
|
| 302 |
-
border-radius: 20px;
|
| 303 |
-
padding: 2rem;
|
| 304 |
-
margin: 1.5rem 0;
|
| 305 |
-
color: #66eaaa;
|
| 306 |
}
|
| 307 |
</style>
|
| 308 |
""", unsafe_allow_html=True)
|
| 309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
@st.cache_resource
|
| 311 |
def load_models():
|
| 312 |
"""Load models"""
|
|
@@ -324,243 +397,13 @@ def load_models():
|
|
| 324 |
st.error(f"Error: {str(e)}")
|
| 325 |
return None
|
| 326 |
|
| 327 |
-
def get_property_interpretation(prop_name, value, mol):
|
| 328 |
-
"""Deep interpretation of each property"""
|
| 329 |
-
interpretations = {
|
| 330 |
-
'MW': {
|
| 331 |
-
'value': value,
|
| 332 |
-
'status': '✅ Optimal' if 250 <= value <= 400 else ('⚠️ High' if value > 400 else '⚠️ Low'),
|
| 333 |
-
'meaning': f"""
|
| 334 |
-
**What it means:** Molecular weight of {value:.1f} Da.
|
| 335 |
-
{
|
| 336 |
-
'Perfect size for cellular uptake and distribution. Like a key fitting a lock.' if 250 <= value <= 400 else
|
| 337 |
-
f'Large molecules (>400 Da) accumulate in cells like cargo ships too big for the harbor. They burden protein degradation systems, leading to cellular stress. Your compound is {value-400:.0f} Da above the safe threshold.' if value > 400 else
|
| 338 |
-
'Very small molecules may lack specificity and be rapidly cleared.'
|
| 339 |
-
}
|
| 340 |
-
"""
|
| 341 |
-
},
|
| 342 |
-
'LogP': {
|
| 343 |
-
'value': value,
|
| 344 |
-
'status': '✅ Optimal' if 0.5 <= value <= 2.5 else ('🔴 Very High' if value > 4 else ('⚠️ High' if value > 2.5 else '⚠️ Low')),
|
| 345 |
-
'meaning': f"""
|
| 346 |
-
**What it means:** LogP of {value:.2f} (lipophilicity = fat-loving tendency).
|
| 347 |
-
{
|
| 348 |
-
'Perfect balance! Can cross membranes but won't get trapped. Like a passport that works everywhere.' if 0.5 <= value <= 2.5 else
|
| 349 |
-
f'🚨 CRITICAL: LogP 4-6 is the "danger zone"! Your compound ({value:.2f}) will accumulate 3-5x in mitochondria compared to cytoplasm. Think of it like oil droplets concentrating in the engine - they disrupt the machinery. This causes membrane potential collapse and triggers the entire toxicity cascade.' if 4 <= value <= 6 else
|
| 350 |
-
f'High lipophilicity ({value:.2f}) means your compound loves fat more than water. It will get trapped in membranes and mitochondria, unable to escape. This is why many drugs fail - they become "membrane prisoners".' if value > 2.5 else
|
| 351 |
-
'Too water-loving. May have poor membrane permeability.'
|
| 352 |
-
}
|
| 353 |
-
"""
|
| 354 |
-
},
|
| 355 |
-
'TPSA': {
|
| 356 |
-
'value': value,
|
| 357 |
-
'status': '✅ Optimal' if 50 <= value <= 90 else ('⚠️ Outside optimal' if 40 <= value <= 140 else '🔴 Problematic'),
|
| 358 |
-
'meaning': f"""
|
| 359 |
-
**What it means:** {value:.1f} Ų of polar surface area.
|
| 360 |
-
{
|
| 361 |
-
'Goldilocks zone! Can cross membranes AND reach intracellular targets.' if 50 <= value <= 90 else
|
| 362 |
-
f'TPSA of {value:.1f} is outside the sweet spot (50-90 Ų). ' + (
|
| 363 |
-
'Too polar - struggles to cross lipid membranes. Like trying to push a water balloon through oil.' if value > 140 else
|
| 364 |
-
f'In the accessible range (40-140 Ų) where compounds can reach cytoplasmic stress pathways. This is why we see ARE activation at this TPSA.' if 40 <= value <= 140 else
|
| 365 |
-
'Very low polarity - will partition heavily into membranes, potentially disrupting them.'
|
| 366 |
-
)
|
| 367 |
-
}
|
| 368 |
-
"""
|
| 369 |
-
},
|
| 370 |
-
'AromaticRings': {
|
| 371 |
-
'value': int(value),
|
| 372 |
-
'status': '✅ Safe' if value <= 2 else ('⚠️ Moderate' if value == 3 else '🔴 High Risk'),
|
| 373 |
-
'meaning': f"""
|
| 374 |
-
**What it means:** {int(value)} aromatic ring(s) - flat, electron-rich structures.
|
| 375 |
-
{
|
| 376 |
-
'Low aromatic content = lower toxicity risk. Aromatic rings are like flat plates that can slide between biological membranes.' if value <= 2 else
|
| 377 |
-
f'🚨 WARNING: {int(value)} aromatic rings! Our data shows ≥3 rings cause 3.5x higher mitochondrial toxicity. Why? Aromatic systems can π-stack (like plates stacking) with membrane lipids and intercalate into DNA. They physically disrupt the delicate architecture of mitochondrial cristae where ATP is made. Think of it like putting cardboard sheets between the pages of a book - it disrupts the structure.' if value >= 3 else
|
| 378 |
-
'Moderate aromatic content. Monitor for membrane interactions.'
|
| 379 |
-
}
|
| 380 |
-
"""
|
| 381 |
-
}
|
| 382 |
-
}
|
| 383 |
-
|
| 384 |
-
return interpretations.get(prop_name, {'value': value, 'status': 'N/A', 'meaning': ''})
|
| 385 |
-
|
| 386 |
-
def generate_deep_interpretation(result, props):
|
| 387 |
-
"""Generate deep, mechanistic interpretation"""
|
| 388 |
-
overall = result['overall_toxicity']
|
| 389 |
-
prob_are = result['oxidative_stress']['probability']
|
| 390 |
-
prob_mmp = result['mitochondrial_dysfunction']['probability']
|
| 391 |
-
prob_p53 = result['dna_damage']['probability']
|
| 392 |
-
|
| 393 |
-
# Determine primary mechanism
|
| 394 |
-
mechanisms = [
|
| 395 |
-
('Mitochondrial Dysfunction', prob_mmp, '⚡'),
|
| 396 |
-
('Oxidative Stress', prob_are, '🔥'),
|
| 397 |
-
('DNA Damage', prob_p53, '🧬')
|
| 398 |
-
]
|
| 399 |
-
mechanisms.sort(key=lambda x: x[1], reverse=True)
|
| 400 |
-
primary_mech = mechanisms[0]
|
| 401 |
-
|
| 402 |
-
if overall['risk_level'] == 'LOW':
|
| 403 |
-
return f"""
|
| 404 |
-
<div class="safe-box">
|
| 405 |
-
<div class="interpretation-title">
|
| 406 |
-
<span class="interpretation-icon">✅</span>
|
| 407 |
-
Safe Chemical Space
|
| 408 |
-
</div>
|
| 409 |
-
|
| 410 |
-
<p><strong>Your compound sits in the safe zone.</strong> It avoids the major toxicity triggers we identified in 11,306 compounds.</p>
|
| 411 |
-
|
| 412 |
-
<div class="interpretation-section">
|
| 413 |
-
<strong>Why it's safe:</strong>
|
| 414 |
-
<ul>
|
| 415 |
-
<li>{'✅ Optimal molecular weight (250-400 Da) - perfect for cellular handling' if 250 <= props['MW'] <= 400 else '✅ Manageable size'}</li>
|
| 416 |
-
<li>{'✅ Balanced lipophilicity (LogP 0.5-2.5) - can travel without getting trapped' if 0.5 <= props['LogP'] <= 2.5 else '✅ Acceptable lipophilicity'}</li>
|
| 417 |
-
<li>{'✅ Low aromatic content (≤2 rings) - won't disrupt membranes' if props['AromaticRings'] <= 2 else '✅ Moderate aromatic content'}</li>
|
| 418 |
-
</ul>
|
| 419 |
-
</div>
|
| 420 |
-
|
| 421 |
-
<p><strong>Next steps:</strong> This computational prediction is promising, but remember - even "safe" compounds need experimental validation. Test in relevant cell lines, check for off-target effects, and validate the therapeutic window.</p>
|
| 422 |
-
</div>
|
| 423 |
-
"""
|
| 424 |
-
|
| 425 |
-
elif overall['risk_level'] == 'MODERATE':
|
| 426 |
-
return f"""
|
| 427 |
-
<div class="interpretation-box">
|
| 428 |
-
<div class="interpretation-title">
|
| 429 |
-
<span class="interpretation-icon">⚠️</span>
|
| 430 |
-
Moderate Concerns: Mechanistic Analysis
|
| 431 |
-
</div>
|
| 432 |
-
|
| 433 |
-
<p><strong>Primary concern: {primary_mech[0]} ({primary_mech[1]:.0%})</strong></p>
|
| 434 |
-
|
| 435 |
-
<div class="interpretation-section">
|
| 436 |
-
<strong>🔬 What's happening at the molecular level:</strong>
|
| 437 |
-
<br><br>
|
| 438 |
-
{
|
| 439 |
-
f"Your compound's LogP of {props['LogP']:.2f} suggests moderate membrane partitioning. While not in the critical 4-6 range, it may still accumulate in lipid-rich compartments over time. Combined with {int(props['AromaticRings'])} aromatic rings, there's potential for membrane perturbation." if prob_mmp > prob_are and prob_mmp > prob_p53 else
|
| 440 |
-
f"Oxidative stress activation ({prob_are:.0%}) suggests your compound can generate or is susceptible to ROS. With {int(props['Heteroatoms'])} heteroatoms, there may be redox-active centers that cycle between oxidized/reduced states, producing superoxide as a byproduct." if prob_are > prob_mmp and prob_are > prob_p53 else
|
| 441 |
-
f"DNA damage signaling ({prob_p53:.0%}) with {int(props['RotatableBonds'])} rotatable bonds suggests potential for DNA intercalation or formation of reactive intermediates that alkylate DNA bases."
|
| 442 |
-
}
|
| 443 |
-
</div>
|
| 444 |
-
|
| 445 |
-
<div class="interpretation-section">
|
| 446 |
-
<strong>💡 Medicinal chemistry strategies:</strong>
|
| 447 |
-
<ul>
|
| 448 |
-
{f'<li>Reduce LogP: Add polar groups (OH, NH2, carboxylic acid) to decrease membrane accumulation</li>' if props['LogP'] > 3 else ''}
|
| 449 |
-
{f'<li>Reduce aromatic content: Replace one aromatic ring with a saturated heterocycle (piperidine, tetrahydropyran)</li>' if props['AromaticRings'] >= 3 else ''}
|
| 450 |
-
{f'<li>Reduce molecular weight: Remove non-essential substituents. Each 50 Da reduction decreases toxicity risk</li>' if props['MW'] > 400 else ''}
|
| 451 |
-
{f'<li>Add rigidity: Reduce rotatable bonds with cyclic constraints to prevent DNA intercalation</li>' if props['RotatableBonds'] > 7 else ''}
|
| 452 |
-
<li>Consider prodrug strategy: Mask toxic features until metabolic activation at target site</li>
|
| 453 |
-
</ul>
|
| 454 |
-
</div>
|
| 455 |
-
|
| 456 |
-
<p><strong>Clinical perspective:</strong> Moderate-risk compounds can sometimes be developed successfully if the therapeutic index is favorable. Focus on: (1) Identifying the therapeutic window, (2) Optimizing PK to minimize tissue accumulation, (3) Considering alternate dosing regimens.</p>
|
| 457 |
-
</div>
|
| 458 |
-
"""
|
| 459 |
-
|
| 460 |
-
else: # HIGH RISK
|
| 461 |
-
# Identify the cascade
|
| 462 |
-
cascade_active = []
|
| 463 |
-
if prob_mmp > 0.6:
|
| 464 |
-
cascade_active.append('Mitochondrial Damage')
|
| 465 |
-
if prob_are > 0.6:
|
| 466 |
-
cascade_active.append('Oxidative Stress')
|
| 467 |
-
if prob_p53 > 0.6:
|
| 468 |
-
cascade_active.append('DNA Damage')
|
| 469 |
-
|
| 470 |
-
is_full_cascade = len(cascade_active) >= 2
|
| 471 |
-
|
| 472 |
-
# Build the cascade content separately
|
| 473 |
-
cascade_html = ""
|
| 474 |
-
if is_full_cascade:
|
| 475 |
-
cascade_html = f"""
|
| 476 |
-
<div class="cascade-box">
|
| 477 |
-
<p><strong>⚠️ FULL TOXICITY CASCADE DETECTED</strong></p>
|
| 478 |
-
<p>Your compound triggers multiple mechanisms in sequence, exactly as our model predicted:</p>
|
| 479 |
-
<div class="cascade-step">
|
| 480 |
-
<span>Lipophilic Compound (LogP: {props["LogP"]:.2f})</span>
|
| 481 |
-
<span class="cascade-arrow">→</span>
|
| 482 |
-
<span>Mitochondrial Accumulation</span>
|
| 483 |
-
</div>
|
| 484 |
-
<div class="cascade-step">
|
| 485 |
-
<span class="cascade-arrow">→</span>
|
| 486 |
-
<span>Membrane Disruption (MMP: {prob_mmp:.0%})</span>
|
| 487 |
-
</div>
|
| 488 |
-
<div class="cascade-step">
|
| 489 |
-
<span class="cascade-arrow">→</span>
|
| 490 |
-
<span>ROS Production</span>
|
| 491 |
-
</div>
|
| 492 |
-
<div class="cascade-step">
|
| 493 |
-
<span class="cascade-arrow">→</span>
|
| 494 |
-
<span>Oxidative Stress (ARE: {prob_are:.0%})</span>
|
| 495 |
-
</div>
|
| 496 |
-
<div class="cascade-step">
|
| 497 |
-
<span class="cascade-arrow">→</span>
|
| 498 |
-
<span>DNA Damage (p53: {prob_p53:.0%})</span>
|
| 499 |
-
</div>
|
| 500 |
-
<p>This is the signature of compounds that cause systemic cellular failure.</p>
|
| 501 |
-
</div>
|
| 502 |
-
"""
|
| 503 |
-
else:
|
| 504 |
-
cascade_html = f"<p><strong>Primary mechanism: {primary_mech[0]} at {primary_mech[1]:.0%}</strong></p>"
|
| 505 |
-
|
| 506 |
-
return f"""
|
| 507 |
-
<div class="warning-box">
|
| 508 |
-
<div class="interpretation-title">
|
| 509 |
-
<span class="interpretation-icon">🔴</span>
|
| 510 |
-
High Toxicity Risk: The Complete Mechanistic Story
|
| 511 |
-
</div>
|
| 512 |
-
|
| 513 |
-
{cascade_html}
|
| 514 |
-
|
| 515 |
-
<div class="interpretation-section">
|
| 516 |
-
<strong>🔬 Root causes identified:</strong>
|
| 517 |
-
<ul>
|
| 518 |
-
{f'<li><strong>Critical LogP ({props["LogP"]:.2f}):</strong> In the 4-6 "danger zone". This causes 3-5x mitochondrial accumulation. Your compound will concentrate in the powerhouse of the cell and disrupt electron transport. LogP 4-6 compounds show 10x higher toxicity in our dataset.</li>' if 4 <= props['LogP'] <= 6 else ''}
|
| 519 |
-
{f'<li><strong>High LogP ({props["LogP"]:.2f}):</strong> Extremely lipophilic. Will partition heavily into membranes and organelles, unable to distribute normally.</li>' if props['LogP'] > 6 else ''}
|
| 520 |
-
{f'<li><strong>Multiple aromatic rings ({int(props["AromaticRings"])}):</strong> Each ring is a flat, electron-rich plate. ≥3 rings can stack (π-π interactions) with membrane lipids and intercalate between DNA bases. Our data: 3.5x higher mitochondrial toxicity with ≥3 rings.</li>' if props['AromaticRings'] >= 3 else ''}
|
| 521 |
-
{f'<li><strong>Large molecular weight ({props["MW"]:.0f} Da):</strong> Exceeds the 400 Da safety threshold by {props["MW"]-400:.0f} Da. Large molecules accumulate because cellular machinery can't efficiently process them. They burden proteasomes and autophagy systems.</li>' if props['MW'] > 400 else ''}
|
| 522 |
-
{f'<li><strong>High flexibility ({int(props["RotatableBonds"])} rotatable bonds):</strong> Can adopt conformations that fit between DNA base pairs, causing intercalation and strand breaks.</li>' if props['RotatableBonds'] > 7 else ''}
|
| 523 |
-
{f'<li><strong>Heteroatom-rich ({int(props["Heteroatoms"])} heteroatoms):</strong> N, O, S atoms can undergo redox cycling, generating superoxide (O2•−) and other ROS. This is metabolic activation of toxicity.</li>' if props['Heteroatoms'] > 7 else ''}
|
| 524 |
-
</ul>
|
| 525 |
-
</div>
|
| 526 |
-
|
| 527 |
-
<div class="interpretation-section">
|
| 528 |
-
<strong>⚠️ Why this matters clinically:</strong>
|
| 529 |
-
<p>Compounds with this profile typically:</p>
|
| 530 |
-
<ul>
|
| 531 |
-
<li>Show hepatotoxicity in preclinical models (liver has high mitochondrial density)</li>
|
| 532 |
-
<li>Cause cardiotoxicity (heart relies 100% on mitochondrial ATP)</li>
|
| 533 |
-
<li>Trigger idiosyncratic drug reactions (immune system recognizes damaged cells)</li>
|
| 534 |
-
<li>Fail Phase I/II trials due to dose-limiting toxicities</li>
|
| 535 |
-
<li>May carry black box warnings if approved (e.g., mitochondrial toxins like linezolid)</li>
|
| 536 |
-
</ul>
|
| 537 |
-
</div>
|
| 538 |
-
|
| 539 |
-
<div class="interpretation-section">
|
| 540 |
-
<strong>🔧 Rescue strategies (if therapeutic target is compelling):</strong>
|
| 541 |
-
<ol>
|
| 542 |
-
<li><strong>Dramatic LogP reduction:</strong> Target LogP <3. Add multiple polar groups. Consider zwitterions.</li>
|
| 543 |
-
<li><strong>De-aromatization:</strong> Replace aromatic rings with saturated rings (cyclohexane, piperidine). Breaks π-stacking.</li>
|
| 544 |
-
<li><strong>Size reduction:</strong> Remove ALL non-essential atoms. Target MW <400 Da. Use fragment-based approach.</li>
|
| 545 |
-
<li><strong>Prodrug masking:</strong> Hide toxic features until enzymatic activation at target site. Converts systemic toxin into local therapeutic.</li>
|
| 546 |
-
<li><strong>Targeted delivery:</strong> Nanoparticle, antibody-drug conjugate, or cell-penetrating peptide to restrict distribution.</li>
|
| 547 |
-
</ol>
|
| 548 |
-
</div>
|
| 549 |
-
|
| 550 |
-
<p><strong>Honest assessment:</strong> {
|
| 551 |
-
'This compound would likely fail preclinical safety studies. Unless the therapeutic target is unprecedented (e.g., treating a fatal disease with no alternatives), recommend exploring alternative chemical series.' if is_full_cascade else
|
| 552 |
-
'This compound shows significant safety concerns. Consider whether the therapeutic benefit could justify the risk, or if alternative approaches exist.'
|
| 553 |
-
}</p>
|
| 554 |
-
</div>
|
| 555 |
-
"""
|
| 556 |
-
|
| 557 |
def compute_features(smiles, feature_names):
|
| 558 |
"""Compute features"""
|
| 559 |
try:
|
| 560 |
mol = Chem.MolFromSmiles(smiles)
|
| 561 |
if mol is None:
|
| 562 |
return None, "Invalid SMILES", None
|
| 563 |
-
|
| 564 |
features = {
|
| 565 |
'MW': Descriptors.MolWt(mol),
|
| 566 |
'LogP': Descriptors.MolLogP(mol),
|
|
@@ -586,10 +429,10 @@ def compute_features(smiles, feature_names):
|
|
| 586 |
'NumAliphaticRings': Lipinski.NumAliphaticRings(mol),
|
| 587 |
'FractionCsp3': Descriptors.FractionCsp3(mol) if hasattr(Descriptors, 'FractionCsp3') else 0.0,
|
| 588 |
}
|
| 589 |
-
|
| 590 |
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
|
| 591 |
fp_array = np.array(fp)
|
| 592 |
-
|
| 593 |
feature_vector = []
|
| 594 |
for fname in feature_names:
|
| 595 |
if fname.startswith('Morgan_'):
|
|
@@ -597,30 +440,30 @@ def compute_features(smiles, feature_names):
|
|
| 597 |
feature_vector.append(fp_array[bit_idx])
|
| 598 |
else:
|
| 599 |
feature_vector.append(features.get(fname, 0))
|
| 600 |
-
|
| 601 |
return np.array(feature_vector).reshape(1, -1), None, features
|
| 602 |
-
|
| 603 |
except Exception as e:
|
| 604 |
return None, f"Error: {str(e)}", None
|
| 605 |
|
| 606 |
def predict_toxicity(smiles, models):
|
| 607 |
"""Predict toxicity"""
|
| 608 |
X, error, raw_features = compute_features(smiles, models['feature_names'])
|
| 609 |
-
|
| 610 |
if error:
|
| 611 |
return {'error': error}
|
| 612 |
-
|
| 613 |
try:
|
| 614 |
X_are = models['scaler_are'].transform(X)
|
| 615 |
X_mmp = models['scaler_mmp'].transform(X)
|
| 616 |
X_p53 = models['scaler_p53'].transform(X)
|
| 617 |
-
|
| 618 |
prob_are = float(models['model_are'].predict_proba(X_are)[0, 1])
|
| 619 |
prob_mmp = float(models['model_mmp'].predict_proba(X_mmp)[0, 1])
|
| 620 |
prob_p53 = float(models['model_p53'].predict_proba(X_p53)[0, 1])
|
| 621 |
-
|
| 622 |
overall_prob = max(prob_are, prob_mmp, prob_p53)
|
| 623 |
-
|
| 624 |
if overall_prob < 0.35:
|
| 625 |
risk = "LOW"
|
| 626 |
prediction = "NON-TOXIC"
|
|
@@ -630,7 +473,7 @@ def predict_toxicity(smiles, models):
|
|
| 630 |
else:
|
| 631 |
risk = "HIGH"
|
| 632 |
prediction = "TOXIC"
|
| 633 |
-
|
| 634 |
return {
|
| 635 |
'overall_toxicity': {
|
| 636 |
'prediction': prediction,
|
|
@@ -652,50 +495,50 @@ if models is None:
|
|
| 652 |
|
| 653 |
# Header
|
| 654 |
st.markdown('<p class="main-header">🧪 Multi-Endpoint Toxicity Predictor</p>', unsafe_allow_html=True)
|
| 655 |
-
st.markdown('<p class="sub-header">
|
| 656 |
|
| 657 |
# Tabs
|
| 658 |
-
tab1, tab2
|
| 659 |
|
| 660 |
with tab1:
|
| 661 |
-
st.markdown("### Enter SMILES")
|
| 662 |
-
|
| 663 |
smiles = st.text_input(
|
| 664 |
"SMILES:",
|
| 665 |
placeholder="e.g., CC(=O)Oc1ccccc1C(=O)O",
|
| 666 |
label_visibility="collapsed"
|
| 667 |
)
|
| 668 |
-
|
| 669 |
st.markdown("**Examples:**")
|
| 670 |
-
|
| 671 |
examples = {
|
| 672 |
"Aspirin (Safe)": "CC(=O)Oc1ccccc1C(=O)O",
|
| 673 |
-
"Caffeine (Safe)": "CN1C=NC2=C1C(=O)N(C(=O)N2C)C",
|
| 674 |
"Doxorubicin (Toxic)": "COc1cccc2c1C(=O)c1c(O)c3c(c(O)c1C2=O)C[C@@](O)(C(=O)CO)C[C@@H]3O[C@H]1C[C@H](N)[C@H](O)[C@H](C)O1",
|
| 675 |
-
"Tamoxifen (Toxic)": "CCC(=C(c1ccccc1)c1ccc(OCCN(C)C)cc1)c1ccccc1"
|
|
|
|
| 676 |
}
|
| 677 |
-
|
| 678 |
for name, smi in examples.items():
|
| 679 |
st.code(smi, language=None)
|
| 680 |
st.caption(name)
|
| 681 |
-
|
| 682 |
-
if st.button("
|
| 683 |
if not smiles:
|
| 684 |
st.warning("⚠️ Please enter a SMILES string")
|
| 685 |
else:
|
| 686 |
mol = Chem.MolFromSmiles(smiles)
|
| 687 |
-
|
| 688 |
if mol is None:
|
| 689 |
st.error("❌ Invalid SMILES")
|
| 690 |
else:
|
| 691 |
-
with st.spinner("
|
| 692 |
result = predict_toxicity(smiles, models)
|
| 693 |
-
|
| 694 |
if 'error' in result:
|
| 695 |
st.error(f"❌ {result['error']}")
|
| 696 |
else:
|
| 697 |
st.markdown("---")
|
| 698 |
-
|
| 699 |
# Structure
|
| 700 |
st.markdown("### 🧬 Molecular Structure")
|
| 701 |
col_struct = st.columns([1, 2, 1])
|
|
@@ -704,234 +547,147 @@ with tab1:
|
|
| 704 |
img = Draw.MolToImage(mol, size=(500, 500))
|
| 705 |
st.image(img, use_column_width=True)
|
| 706 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 707 |
-
|
| 708 |
st.markdown("---")
|
| 709 |
-
|
| 710 |
-
#
|
| 711 |
overall = result['overall_toxicity']
|
| 712 |
-
st.markdown("### 🎯 Overall Toxicity Assessment")
|
| 713 |
-
|
| 714 |
-
col_risk = st.columns([1, 2, 1])
|
| 715 |
-
with col_risk[1]:
|
| 716 |
-
if overall['risk_level'] == 'LOW':
|
| 717 |
-
st.markdown(f"""
|
| 718 |
-
<div class="risk-card-low">
|
| 719 |
-
<div style="font-size: 4rem;">✅</div>
|
| 720 |
-
<div style="font-size: 2.5rem; font-weight: 900; margin: 1rem 0;">{overall['prediction']}</div>
|
| 721 |
-
<div style="font-size: 1.3rem; opacity: 0.9;">Risk Level: {overall['risk_level']}</div>
|
| 722 |
-
<div style="font-size: 3rem; font-weight: 900; margin-top: 1.5rem;">{overall['probability']:.1%}</div>
|
| 723 |
-
<div style="font-size: 1rem; opacity: 0.8; margin-top: 0.5rem;">Confidence Score</div>
|
| 724 |
-
</div>
|
| 725 |
-
""", unsafe_allow_html=True)
|
| 726 |
-
elif overall['risk_level'] == 'MODERATE':
|
| 727 |
-
st.markdown(f"""
|
| 728 |
-
<div class="risk-card-moderate">
|
| 729 |
-
<div style="font-size: 4rem;">⚠️</div>
|
| 730 |
-
<div style="font-size: 2.5rem; font-weight: 900; margin: 1rem 0;">{overall['prediction']}</div>
|
| 731 |
-
<div style="font-size: 1.3rem; opacity: 0.9;">Risk Level: {overall['risk_level']}</div>
|
| 732 |
-
<div style="font-size: 3rem; font-weight: 900; margin-top: 1.5rem;">{overall['probability']:.1%}</div>
|
| 733 |
-
<div style="font-size: 1rem; opacity: 0.8; margin-top: 0.5rem;">Confidence Score</div>
|
| 734 |
-
</div>
|
| 735 |
-
""", unsafe_allow_html=True)
|
| 736 |
-
else:
|
| 737 |
-
st.markdown(f"""
|
| 738 |
-
<div class="risk-card-high">
|
| 739 |
-
<div style="font-size: 4rem;">🔴</div>
|
| 740 |
-
<div style="font-size: 2.5rem; font-weight: 900; margin: 1rem 0;">{overall['prediction']}</div>
|
| 741 |
-
<div style="font-size: 1.3rem; opacity: 0.9;">Risk Level: {overall['risk_level']}</div>
|
| 742 |
-
<div style="font-size: 3rem; font-weight: 900; margin-top: 1.5rem;">{overall['probability']:.1%}</div>
|
| 743 |
-
<div style="font-size: 1rem; opacity: 0.8; margin-top: 0.5rem;">Confidence Score</div>
|
| 744 |
-
</div>
|
| 745 |
-
""", unsafe_allow_html=True)
|
| 746 |
-
|
| 747 |
-
st.markdown("---")
|
| 748 |
-
|
| 749 |
-
# Mechanism breakdown
|
| 750 |
-
st.markdown("### 📊 Mechanism-Specific Analysis")
|
| 751 |
-
|
| 752 |
prob_are = result['oxidative_stress']['probability']
|
| 753 |
prob_mmp = result['mitochondrial_dysfunction']['probability']
|
| 754 |
prob_p53 = result['dna_damage']['probability']
|
| 755 |
-
|
| 756 |
-
col1, col2, col3 = st.columns(
|
| 757 |
-
|
| 758 |
with col1:
|
| 759 |
-
st.
|
| 760 |
-
|
| 761 |
-
<div class="mechanism-icon">🔥</div>
|
| 762 |
-
<div class="mechanism-title">Oxidative Stress</div>
|
| 763 |
-
<div class="mechanism-value">{prob_are:.0%}</div>
|
| 764 |
-
<div style="color: #a0a6b8; font-size: 0.9rem;">ARE/Nrf2 Activation</div>
|
| 765 |
-
<div class="mechanism-bar">
|
| 766 |
-
<div class="mechanism-bar-fill" style="width: {prob_are*100}%"></div>
|
| 767 |
-
</div>
|
| 768 |
-
</div>
|
| 769 |
-
""", unsafe_allow_html=True)
|
| 770 |
-
|
| 771 |
with col2:
|
| 772 |
-
st.
|
| 773 |
-
<div class="mechanism-card">
|
| 774 |
-
<div class="mechanism-icon">⚡</div>
|
| 775 |
-
<div class="mechanism-title">Mitochondrial</div>
|
| 776 |
-
<div class="mechanism-value">{prob_mmp:.0%}</div>
|
| 777 |
-
<div style="color: #a0a6b8; font-size: 0.9rem;">Membrane Potential Loss</div>
|
| 778 |
-
<div class="mechanism-bar">
|
| 779 |
-
<div class="mechanism-bar-fill" style="width: {prob_mmp*100}%"></div>
|
| 780 |
-
</div>
|
| 781 |
-
</div>
|
| 782 |
-
""", unsafe_allow_html=True)
|
| 783 |
-
|
| 784 |
with col3:
|
| 785 |
-
st.
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
<div class="mechanism-value">{prob_p53:.0%}</div>
|
| 790 |
-
<div style="color: #a0a6b8; font-size: 0.9rem;">p53 Pathway Activation</div>
|
| 791 |
-
<div class="mechanism-bar">
|
| 792 |
-
<div class="mechanism-bar-fill" style="width: {prob_p53*100}%"></div>
|
| 793 |
-
</div>
|
| 794 |
-
</div>
|
| 795 |
-
""", unsafe_allow_html=True)
|
| 796 |
-
|
| 797 |
st.markdown("---")
|
| 798 |
-
|
| 799 |
-
#
|
| 800 |
-
st.markdown("### 🔬
|
| 801 |
-
|
| 802 |
props = result['molecular_properties']
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
st.markdown(f"""
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 814 |
</div>
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 822 |
|
| 823 |
with tab2:
|
| 824 |
-
st.markdown("### 🔬 The Science Behind the Predictions")
|
| 825 |
-
|
| 826 |
-
st.markdown("""
|
| 827 |
-
## The Toxicity Cascade
|
| 828 |
-
|
| 829 |
-
Our analysis of 11,306 compounds revealed that drug toxicity follows a predictable cascade through cellular compartments:
|
| 830 |
-
""")
|
| 831 |
-
|
| 832 |
-
st.markdown("""
|
| 833 |
-
<div class="cascade-box">
|
| 834 |
-
<div class="cascade-step">
|
| 835 |
-
<span>🧪 Lipophilic Compound (LogP 4-6)</span>
|
| 836 |
-
<span class="cascade-arrow">→</span>
|
| 837 |
-
<span>Passive diffusion into mitochondria</span>
|
| 838 |
-
</div>
|
| 839 |
-
<div class="cascade-step">
|
| 840 |
-
<span class="cascade-arrow">→</span>
|
| 841 |
-
<span>⚡ Membrane Disruption</span>
|
| 842 |
-
<span class="cascade-arrow">→</span>
|
| 843 |
-
<span>3-5x accumulation in cristae</span>
|
| 844 |
-
</div>
|
| 845 |
-
<div class="cascade-step">
|
| 846 |
-
<span class="cascade-arrow">→</span>
|
| 847 |
-
<span>💥 Electron Transport Chain Inhibition</span>
|
| 848 |
-
<span class="cascade-arrow">→</span>
|
| 849 |
-
<span>Electrons leak</span>
|
| 850 |
-
</div>
|
| 851 |
-
<div class="cascade-step">
|
| 852 |
-
<span class="cascade-arrow">→</span>
|
| 853 |
-
<span>🔥 Massive ROS Production</span>
|
| 854 |
-
<span class="cascade-arrow">→</span>
|
| 855 |
-
<span>Superoxide, hydrogen peroxide</span>
|
| 856 |
-
</div>
|
| 857 |
-
<div class="cascade-step">
|
| 858 |
-
<span class="cascade-arrow">→</span>
|
| 859 |
-
<span>📢 Oxidative Stress (ARE Activation)</span>
|
| 860 |
-
<span class="cascade-arrow">→</span>
|
| 861 |
-
<span>Nrf2 translocates to nucleus</span>
|
| 862 |
-
</div>
|
| 863 |
-
<div class="cascade-step">
|
| 864 |
-
<span class="cascade-arrow">→</span>
|
| 865 |
-
<span>🧬 DNA Damage (p53 Activation)</span>
|
| 866 |
-
<span class="cascade-arrow">→</span>
|
| 867 |
-
<span>ROS attacks DNA bases</span>
|
| 868 |
-
</div>
|
| 869 |
-
<div class="cascade-step">
|
| 870 |
-
<span class="cascade-arrow">→</span>
|
| 871 |
-
<span>💀 Cell Death</span>
|
| 872 |
-
<span class="cascade-arrow">→</span>
|
| 873 |
-
<span>Apoptosis or necrosis</span>
|
| 874 |
-
</div>
|
| 875 |
-
</div>
|
| 876 |
-
""", unsafe_allow_html=True)
|
| 877 |
-
|
| 878 |
-
st.markdown("""
|
| 879 |
-
## Quantitative Evidence
|
| 880 |
-
|
| 881 |
-
<div class="interpretation-box">
|
| 882 |
-
<strong>Co-occurrence Analysis:</strong>
|
| 883 |
-
<ul>
|
| 884 |
-
<li><strong>57% of MMP+ compounds</strong> also show ARE activation (2.4x enrichment, p < 0.001)</li>
|
| 885 |
-
<li><strong>53% of MMP+ compounds</strong> also show p53 activation (3.0x enrichment, p < 0.001)</li>
|
| 886 |
-
<li>This is NOT random - it's a biological cascade</li>
|
| 887 |
-
</ul>
|
| 888 |
-
|
| 889 |
-
<strong>Chemical Property Thresholds:</strong>
|
| 890 |
-
<ul>
|
| 891 |
-
<li><strong>LogP 4-6:</strong> 10x higher overall toxicity vs LogP 0-2</li>
|
| 892 |
-
<li><strong>≥3 Aromatic Rings:</strong> 3.5x higher mitochondrial toxicity</li>
|
| 893 |
-
<li><strong>MW >400 Da:</strong> 2.4x higher DNA damage</li>
|
| 894 |
-
<li><strong>>7 Rotatable Bonds:</strong> 1.6x higher p53 activation</li>
|
| 895 |
-
</ul>
|
| 896 |
-
</div>
|
| 897 |
-
""", unsafe_allow_html=True)
|
| 898 |
-
|
| 899 |
-
st.markdown("""
|
| 900 |
-
## Why This Matters
|
| 901 |
-
|
| 902 |
-
Traditional QSAR models treat toxicity as a black box. We don't.
|
| 903 |
-
|
| 904 |
-
**Our approach:**
|
| 905 |
-
1. ✅ **Mechanistic**: We know *why* compounds are toxic
|
| 906 |
-
2. ✅ **Actionable**: We can suggest *how* to fix them
|
| 907 |
-
3. ✅ **Validated**: 100% accuracy on known compounds
|
| 908 |
-
4. ✅ **Interpretable**: SHAP values explain every prediction
|
| 909 |
-
|
| 910 |
-
This isn't just a model - it's a mechanistic framework validated on over 11,000 compounds.
|
| 911 |
-
""")
|
| 912 |
-
|
| 913 |
-
with tab3:
|
| 914 |
st.markdown("""
|
| 915 |
### 📖 About This Tool
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
**
|
| 920 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 921 |
**Endpoints:**
|
| 922 |
-
- 🔥
|
| 923 |
-
- ⚡
|
| 924 |
-
- 🧬
|
| 925 |
-
|
| 926 |
-
**⚠️ Disclaimer:** For research only.
|
| 927 |
-
|
| 928 |
-
**
|
| 929 |
-
|
| 930 |
-
Multi-Endpoint Toxicity Predictor: A Mechanistic Framework
|
| 931 |
-
EPA ToxCast Database (2024)
|
| 932 |
-
https://huggingface.co/spaces/MlchaeI/Toxicity_2
|
| 933 |
-
```
|
| 934 |
""")
|
| 935 |
|
| 936 |
st.markdown("---")
|
| 937 |
-
st.markdown('<p style="text-align: center; color: #8b92a8;
|
|
|
|
| 14 |
initial_sidebar_state="collapsed"
|
| 15 |
)
|
| 16 |
|
| 17 |
+
# [Keep all the beautiful CSS from before]
|
| 18 |
st.markdown("""
|
| 19 |
<style>
|
| 20 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap');
|
| 21 |
+
|
| 22 |
* {
|
| 23 |
font-family: 'Inter', sans-serif;
|
| 24 |
}
|
| 25 |
+
|
| 26 |
.main {
|
| 27 |
background: linear-gradient(180deg, #0a0e27 0%, #1a1f3a 50%, #0a0e27 100%);
|
| 28 |
}
|
| 29 |
+
|
| 30 |
.main-header {
|
| 31 |
font-size: 4rem;
|
| 32 |
font-weight: 900;
|
|
|
|
| 37 |
margin-bottom: 0.5rem;
|
| 38 |
animation: glow 3s ease-in-out infinite alternate;
|
| 39 |
}
|
| 40 |
+
|
| 41 |
@keyframes glow {
|
| 42 |
from { filter: drop-shadow(0 0 20px rgba(102, 126, 234, 0.3)); }
|
| 43 |
to { filter: drop-shadow(0 0 40px rgba(118, 75, 162, 0.6)); }
|
| 44 |
}
|
| 45 |
+
|
| 46 |
.sub-header {
|
| 47 |
font-size: 1.4rem;
|
| 48 |
text-align: center;
|
| 49 |
color: #a0a6b8;
|
| 50 |
margin-bottom: 3rem;
|
| 51 |
font-weight: 300;
|
|
|
|
| 52 |
}
|
| 53 |
+
|
| 54 |
+
.hypothesis-box {
|
| 55 |
+
background: linear-gradient(135deg, rgba(102, 234, 170, 0.1) 0%, rgba(102, 126, 234, 0.1) 100%);
|
| 56 |
+
border: 2px solid rgba(102, 234, 170, 0.4);
|
| 57 |
+
border-radius: 20px;
|
| 58 |
+
padding: 2rem;
|
| 59 |
+
margin: 1.5rem 0;
|
| 60 |
+
color: #e8eaf0;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
.hypothesis-title {
|
| 64 |
+
color: #66eaaa;
|
| 65 |
+
font-size: 1.5rem;
|
| 66 |
+
font-weight: 800;
|
| 67 |
+
margin-bottom: 1rem;
|
| 68 |
+
display: flex;
|
| 69 |
+
align-items: center;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
.experiment-box {
|
| 73 |
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(240, 147, 251, 0.1) 100%);
|
| 74 |
+
border: 2px solid rgba(102, 126, 234, 0.3);
|
| 75 |
+
border-radius: 15px;
|
| 76 |
+
padding: 1.5rem;
|
| 77 |
+
margin: 1rem 0;
|
| 78 |
+
transition: all 0.3s ease;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.experiment-box:hover {
|
| 82 |
+
transform: translateX(10px);
|
| 83 |
+
border-color: rgba(102, 126, 234, 0.6);
|
| 84 |
+
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
.experiment-title {
|
| 88 |
+
color: #667eea;
|
| 89 |
+
font-size: 1.2rem;
|
| 90 |
+
font-weight: 700;
|
| 91 |
+
margin-bottom: 0.5rem;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.prediction-badge {
|
| 95 |
+
display: inline-block;
|
| 96 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 97 |
+
color: white;
|
| 98 |
+
padding: 0.4rem 1rem;
|
| 99 |
+
border-radius: 15px;
|
| 100 |
+
font-size: 0.85rem;
|
| 101 |
+
font-weight: 700;
|
| 102 |
+
margin: 0.5rem 0.5rem 0.5rem 0;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
.risk-card-low {
|
| 106 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 107 |
padding: 3rem;
|
|
|
|
| 111 |
box-shadow: 0 20px 60px rgba(102, 126, 234, 0.5);
|
| 112 |
animation: pulse-low 2s ease-in-out infinite;
|
| 113 |
}
|
| 114 |
+
|
| 115 |
@keyframes pulse-low {
|
| 116 |
0%, 100% { transform: scale(1); }
|
| 117 |
+
50% { transform: scale(1.02); }
|
| 118 |
}
|
| 119 |
+
|
| 120 |
.risk-card-moderate {
|
| 121 |
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
| 122 |
padding: 3rem;
|
|
|
|
| 126 |
box-shadow: 0 20px 60px rgba(240, 147, 251, 0.5);
|
| 127 |
animation: pulse-moderate 1.5s ease-in-out infinite;
|
| 128 |
}
|
| 129 |
+
|
| 130 |
@keyframes pulse-moderate {
|
| 131 |
0%, 100% { transform: scale(1); }
|
| 132 |
50% { transform: scale(1.03); }
|
| 133 |
}
|
| 134 |
+
|
| 135 |
.risk-card-high {
|
| 136 |
background: linear-gradient(135deg, #ff0844 0%, #ffb199 100%);
|
| 137 |
padding: 3rem;
|
|
|
|
| 141 |
box-shadow: 0 20px 60px rgba(255, 8, 68, 0.6);
|
| 142 |
animation: pulse-high 1s ease-in-out infinite;
|
| 143 |
}
|
| 144 |
+
|
| 145 |
@keyframes pulse-high {
|
| 146 |
0%, 100% { transform: scale(1); }
|
| 147 |
50% { transform: scale(1.05); }
|
| 148 |
}
|
| 149 |
+
|
| 150 |
.property-card {
|
| 151 |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
| 152 |
border: 2px solid rgba(102, 126, 234, 0.3);
|
|
|
|
| 154 |
border-radius: 20px;
|
| 155 |
margin: 1rem 0;
|
| 156 |
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
}
|
| 158 |
+
|
| 159 |
.property-card:hover {
|
| 160 |
transform: translateY(-5px);
|
| 161 |
border-color: rgba(102, 126, 234, 0.6);
|
| 162 |
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
| 163 |
}
|
| 164 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
.property-value {
|
| 166 |
color: #ffffff;
|
| 167 |
font-size: 2rem;
|
|
|
|
| 170 |
-webkit-background-clip: text;
|
| 171 |
-webkit-text-fill-color: transparent;
|
| 172 |
}
|
| 173 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
.mechanism-card {
|
| 175 |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
|
| 176 |
border: 2px solid rgba(102, 126, 234, 0.2);
|
|
|
|
| 178 |
border-radius: 20px;
|
| 179 |
text-align: center;
|
| 180 |
transition: all 0.4s ease;
|
|
|
|
| 181 |
}
|
| 182 |
+
|
| 183 |
.mechanism-card:hover {
|
| 184 |
transform: translateY(-10px) scale(1.05);
|
| 185 |
box-shadow: 0 20px 50px rgba(102, 126, 234, 0.4);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
}
|
| 187 |
+
|
| 188 |
.mechanism-value {
|
| 189 |
font-size: 3rem;
|
| 190 |
font-weight: 900;
|
| 191 |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 192 |
-webkit-background-clip: text;
|
| 193 |
-webkit-text-fill-color: transparent;
|
|
|
|
| 194 |
}
|
| 195 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
.structure-container {
|
| 197 |
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
|
| 198 |
border: 2px solid rgba(102, 126, 234, 0.3);
|
| 199 |
padding: 2rem;
|
| 200 |
border-radius: 25px;
|
| 201 |
text-align: center;
|
|
|
|
| 202 |
}
|
| 203 |
+
|
| 204 |
+
.interpretation-box {
|
| 205 |
+
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
| 206 |
+
border-left: 5px solid #667eea;
|
| 207 |
+
padding: 2.5rem;
|
| 208 |
border-radius: 20px;
|
| 209 |
margin: 2rem 0;
|
| 210 |
+
color: #e8eaf0;
|
| 211 |
+
line-height: 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
}
|
| 213 |
</style>
|
| 214 |
""", unsafe_allow_html=True)
|
| 215 |
|
| 216 |
+
def generate_testable_hypotheses(result, props, mol):
|
| 217 |
+
"""Generate specific, testable hypotheses with experimental protocols"""
|
| 218 |
+
|
| 219 |
+
prob_are = result['oxidative_stress']['probability']
|
| 220 |
+
prob_mmp = result['mitochondrial_dysfunction']['probability']
|
| 221 |
+
prob_p53 = result['dna_damage']['probability']
|
| 222 |
+
|
| 223 |
+
hypotheses = []
|
| 224 |
+
|
| 225 |
+
# HYPOTHESIS 1: Mitochondrial Accumulation (if high LogP)
|
| 226 |
+
if props['LogP'] > 3:
|
| 227 |
+
hypotheses.append({
|
| 228 |
+
'title': f"🎯 Hypothesis 1: Mitochondrial Accumulation via Lipophilicity",
|
| 229 |
+
'hypothesis': f"Your compound (LogP = {props['LogP']:.2f}) will accumulate preferentially in mitochondria at 3-5x higher concentration than cytoplasm due to lipophilic partitioning into the inner mitochondrial membrane.",
|
| 230 |
+
'rationale': f"Compounds with LogP {props['LogP']:.2f} partition heavily into lipid bilayers. Mitochondria have a highly negative membrane potential (-180 mV), creating an electrochemical gradient that traps lipophilic cations. This is the Nernst equation in action.",
|
| 231 |
+
'experiments': [
|
| 232 |
+
{
|
| 233 |
+
'name': "MitoTracker Colocalization",
|
| 234 |
+
'protocol': "• Treat HepG2 cells with 10 µM compound for 2h\n• Co-stain with MitoTracker Red (100 nM)\n• Use confocal microscopy to measure Pearson correlation\n• Compare to positive control (rhodamine 123)",
|
| 235 |
+
'readout': "Pearson coefficient >0.7 = strong mitochondrial accumulation",
|
| 236 |
+
'expected': f"Predict >0.75 correlation based on LogP {props['LogP']:.2f}",
|
| 237 |
+
'timeline': "2-3 days",
|
| 238 |
+
'cost': "$500-800"
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
'name': "Mitochondrial Isolation + LC-MS",
|
| 242 |
+
'protocol': "• Treat cells with compound (10 µM, 4h)\n• Isolate mitochondria via differential centrifugation\n• Extract compound from mitochondrial vs cytosolic fractions\n• Quantify by LC-MS/MS",
|
| 243 |
+
'readout': "Mitochondrial/cytosolic concentration ratio",
|
| 244 |
+
'expected': f"Predict {3 if props['LogP'] < 5 else 5}-fold enrichment in mitochondria",
|
| 245 |
+
'timeline': "1 week",
|
| 246 |
+
'cost': "$2,000-3,000"
|
| 247 |
+
}
|
| 248 |
+
]
|
| 249 |
+
})
|
| 250 |
+
|
| 251 |
+
# HYPOTHESIS 2: Direct Membrane Disruption (if aromatic)
|
| 252 |
+
if props['AromaticRings'] >= 3:
|
| 253 |
+
hypotheses.append({
|
| 254 |
+
'title': f"🎯 Hypothesis 2: Direct Membrane Disruption via π-π Stacking",
|
| 255 |
+
'hypothesis': f"Your compound ({int(props['AromaticRings'])} aromatic rings) will directly disrupt mitochondrial membranes through π-π interactions with membrane lipids and respiratory chain complexes.",
|
| 256 |
+
'rationale': f"Aromatic rings are planar and electron-rich. With {int(props['AromaticRings'])} rings, your compound can intercalate between lipid acyl chains and stack with aromatic residues in Complex I and III. This is direct physical disruption, not just accumulation.",
|
| 257 |
+
'experiments': [
|
| 258 |
+
{
|
| 259 |
+
'name': "Isolated Mitochondria MMP Assay",
|
| 260 |
+
'protocol': "• Isolate mitochondria from rat liver\n• Measure membrane potential with TMRM fluorescence\n• Add compound directly to isolated mitochondria (no cells)\n• Monitor depolarization in real-time",
|
| 261 |
+
'readout': "% depolarization vs vehicle control",
|
| 262 |
+
'expected': f"Predict {30 if props['AromaticRings'] == 3 else 50}% depolarization at 10 µM within 30 min",
|
| 263 |
+
'timeline': "3-4 days",
|
| 264 |
+
'cost': "$800-1,200"
|
| 265 |
+
},
|
| 266 |
+
{
|
| 267 |
+
'name': "Liposome Permeability Assay",
|
| 268 |
+
'protocol': "• Prepare cardiolipin-enriched liposomes (mimics inner mitochondrial membrane)\n• Load with calcein dye\n• Add compound and measure dye leakage\n• Compare to non-aromatic control",
|
| 269 |
+
'readout': "Calcein release rate",
|
| 270 |
+
'expected': f"{int(props['AromaticRings'])}x faster than non-aromatic analog",
|
| 271 |
+
'timeline': "2-3 days",
|
| 272 |
+
'cost': "$400-600"
|
| 273 |
+
}
|
| 274 |
+
]
|
| 275 |
+
})
|
| 276 |
+
|
| 277 |
+
# HYPOTHESIS 3: ROS Generation (if high ARE + high MMP)
|
| 278 |
+
if prob_are > 0.5 and prob_mmp > 0.5:
|
| 279 |
+
hypotheses.append({
|
| 280 |
+
'title': f"🎯 Hypothesis 3: ROS-Mediated Toxicity Cascade",
|
| 281 |
+
'hypothesis': f"Your compound will cause mitochondrial dysfunction (predicted {prob_mmp:.0%}), leading to ROS production that activates the ARE/Nrf2 pathway (predicted {prob_are:.0%}). ROS is the mechanistic link.",
|
| 282 |
+
'rationale': f"When electron transport is disrupted, electrons leak from Complex I and III, reducing O₂ to superoxide (O₂•⁻). This triggers Nrf2 nuclear translocation. Your high scores on both pathways suggest this cascade is active.",
|
| 283 |
+
'experiments': [
|
| 284 |
+
{
|
| 285 |
+
'name': "ROS Temporal Analysis",
|
| 286 |
+
'protocol': "• Treat HepG2 cells with compound (10 µM)\n• Measure ROS with MitoSOX (mitochondrial O₂•⁻) every 30 min for 4h\n• Simultaneously measure ARE-luciferase reporter\n• Test if ROS peaks BEFORE ARE activation",
|
| 287 |
+
'readout': "Time course: ROS peak → ARE activation",
|
| 288 |
+
'expected': "ROS peaks at 1-2h, ARE activation at 3-4h (causal sequence)",
|
| 289 |
+
'timeline': "1 week",
|
| 290 |
+
'cost': "$1,500-2,000"
|
| 291 |
+
},
|
| 292 |
+
{
|
| 293 |
+
'name': "ROS Scavenger Rescue",
|
| 294 |
+
'protocol': "• Pre-treat cells with N-acetylcysteine (NAC, 5 mM)\n• Add compound + measure ARE activation\n• If ROS is causal, NAC should block ARE",
|
| 295 |
+
'readout': "% reduction in ARE activation with NAC",
|
| 296 |
+
'expected': "Predict >70% reduction (proves ROS causality)",
|
| 297 |
+
'timeline': "3-4 days",
|
| 298 |
+
'cost': "$600-800"
|
| 299 |
+
}
|
| 300 |
+
]
|
| 301 |
+
})
|
| 302 |
+
|
| 303 |
+
# HYPOTHESIS 4: DNA Intercalation (if flexible + high p53)
|
| 304 |
+
if props['RotatableBonds'] > 7 and prob_p53 > 0.5:
|
| 305 |
+
hypotheses.append({
|
| 306 |
+
'title': f"🎯 Hypothesis 4: DNA Intercalation via Molecular Flexibility",
|
| 307 |
+
'hypothesis': f"Your compound ({int(props['RotatableBonds'])} rotatable bonds) will intercalate into DNA, causing double-strand breaks and p53 activation (predicted {prob_p53:.0%}).",
|
| 308 |
+
'rationale': f"Flexible molecules can adopt planar conformations that fit between DNA base pairs. Classical intercalators like doxorubicin have 5-10 rotatable bonds. Your compound's flexibility suggests it can contort to fit.",
|
| 309 |
+
'experiments': [
|
| 310 |
+
{
|
| 311 |
+
'name': "Ethidium Bromide Displacement",
|
| 312 |
+
'protocol': "• Incubate calf thymus DNA with ethidium bromide\n• Add increasing concentrations of your compound\n• Measure fluorescence quenching (EtBr displacement)\n• Calculate IC₅₀ for displacement",
|
| 313 |
+
'readout': "IC₅₀ for EtBr displacement",
|
| 314 |
+
'expected': "IC₅₀ < 50 µM indicates intercalation",
|
| 315 |
+
'timeline': "2 days",
|
| 316 |
+
'cost': "$300-400"
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
'name': "γH2AX Foci Formation",
|
| 320 |
+
'protocol': "• Treat cells with compound (2-10 µM, 24h)\n• Fix and stain for γH2AX (DNA double-strand break marker)\n• Count nuclear foci per cell\n• Compare to etoposide (positive control)",
|
| 321 |
+
'readout': "Average foci per nucleus",
|
| 322 |
+
'expected': f"Predict {5 if prob_p53 < 0.7 else 10}+ foci per cell",
|
| 323 |
+
'timeline': "3-4 days",
|
| 324 |
+
'cost': "$800-1,000"
|
| 325 |
+
}
|
| 326 |
+
]
|
| 327 |
+
})
|
| 328 |
+
|
| 329 |
+
# HYPOTHESIS 5: Complex I Inhibition (if specific structural features)
|
| 330 |
+
if prob_mmp > 0.7 and props['LogP'] > 4:
|
| 331 |
+
hypotheses.append({
|
| 332 |
+
'title': f"🎯 Hypothesis 5: Specific Complex I Inhibition",
|
| 333 |
+
'hypothesis': f"Your compound will specifically inhibit mitochondrial Complex I (NADH dehydrogenase), similar to rotenone, due to structural complementarity with the ubiquinone binding site.",
|
| 334 |
+
'rationale': f"LogP {props['LogP']:.2f} + aromatic character = structural similarity to known Complex I inhibitors (rotenone, MPP+). The ubiquinone binding pocket is hydrophobic and aromatic-rich.",
|
| 335 |
+
'experiments': [
|
| 336 |
+
{
|
| 337 |
+
'name': "Seahorse XF Complex I Stress Test",
|
| 338 |
+
'protocol': "• Measure oxygen consumption rate (OCR) in HepG2\n• Sequential injection: Compound → Oligomycin → FCCP → Rotenone/Antimycin A\n• Quantify Complex I-dependent respiration",
|
| 339 |
+
'readout': "% inhibition of Complex I vs total respiration",
|
| 340 |
+
'expected': "Predict >50% Complex I-specific inhibition",
|
| 341 |
+
'timeline': "1 week",
|
| 342 |
+
'cost': "$2,000-2,500"
|
| 343 |
+
},
|
| 344 |
+
{
|
| 345 |
+
'name': "Isolated Complex I Activity Assay",
|
| 346 |
+
'protocol': "• Isolate Complex I from bovine heart mitochondria\n• Measure NADH:ubiquinone oxidoreductase activity\n• Add compound at 1-100 µM\n• Calculate IC₅₀",
|
| 347 |
+
'readout': "IC₅₀ for Complex I inhibition",
|
| 348 |
+
'expected': f"IC₅₀ {5 if props['LogP'] > 5 else 20} µM",
|
| 349 |
+
'timeline': "1 week",
|
| 350 |
+
'cost': "$1,500-2,000"
|
| 351 |
+
}
|
| 352 |
+
]
|
| 353 |
+
})
|
| 354 |
+
|
| 355 |
+
# HYPOTHESIS 6: Metabolic Activation (if has oxidizable groups)
|
| 356 |
+
if props['NumN'] + props['NumO'] + props['NumS'] > 5:
|
| 357 |
+
hypotheses.append({
|
| 358 |
+
'title': f"🎯 Hypothesis 6: Metabolic Activation to Reactive Metabolites",
|
| 359 |
+
'hypothesis': f"Your compound contains {int(props['NumN'] + props['NumO'] + props['NumS'])} heteroatoms (N, O, S) that may be metabolically activated by CYP450s to reactive electrophiles or redox-cycling quinones.",
|
| 360 |
+
'rationale': f"Heteroatoms are CYP450 substrates. Oxidation can generate reactive intermediates (epoxides, quinones, iminium ions) that covalently modify proteins or redox cycle to generate ROS. This is bioactivation toxicity.",
|
| 361 |
+
'experiments': [
|
| 362 |
+
{
|
| 363 |
+
'name': "Microsomal Stability + Metabolite ID",
|
| 364 |
+
'protocol': "• Incubate compound with human liver microsomes + NADPH\n• Sample at 0, 15, 30, 60 min\n• Analyze by LC-MS/MS for parent disappearance + metabolite formation\n• Identify oxidative metabolites",
|
| 365 |
+
'readout': "t₁/₂ + metabolite structures",
|
| 366 |
+
'expected': "Predict t₁/₂ < 30 min (rapid metabolism) + N/O oxidation products",
|
| 367 |
+
'timeline': "1-2 weeks",
|
| 368 |
+
'cost': "$3,000-4,000"
|
| 369 |
+
},
|
| 370 |
+
{
|
| 371 |
+
'name': "CYP450 Inhibitor Rescue",
|
| 372 |
+
'protocol': "• Pre-treat cells with ketoconazole (pan-CYP inhibitor, 1 µM)\n• Add compound + measure toxicity (MTT assay)\n• If metabolism is required, ketoconazole will rescue",
|
| 373 |
+
'readout': "% rescue by CYP inhibition",
|
| 374 |
+
'expected': "Predict >50% rescue (proves bioactivation)",
|
| 375 |
+
'timeline': "3-4 days",
|
| 376 |
+
'cost': "$500-700"
|
| 377 |
+
}
|
| 378 |
+
]
|
| 379 |
+
})
|
| 380 |
+
|
| 381 |
+
return hypotheses
|
| 382 |
+
|
| 383 |
@st.cache_resource
|
| 384 |
def load_models():
|
| 385 |
"""Load models"""
|
|
|
|
| 397 |
st.error(f"Error: {str(e)}")
|
| 398 |
return None
|
| 399 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
def compute_features(smiles, feature_names):
|
| 401 |
"""Compute features"""
|
| 402 |
try:
|
| 403 |
mol = Chem.MolFromSmiles(smiles)
|
| 404 |
if mol is None:
|
| 405 |
return None, "Invalid SMILES", None
|
| 406 |
+
|
| 407 |
features = {
|
| 408 |
'MW': Descriptors.MolWt(mol),
|
| 409 |
'LogP': Descriptors.MolLogP(mol),
|
|
|
|
| 429 |
'NumAliphaticRings': Lipinski.NumAliphaticRings(mol),
|
| 430 |
'FractionCsp3': Descriptors.FractionCsp3(mol) if hasattr(Descriptors, 'FractionCsp3') else 0.0,
|
| 431 |
}
|
| 432 |
+
|
| 433 |
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)
|
| 434 |
fp_array = np.array(fp)
|
| 435 |
+
|
| 436 |
feature_vector = []
|
| 437 |
for fname in feature_names:
|
| 438 |
if fname.startswith('Morgan_'):
|
|
|
|
| 440 |
feature_vector.append(fp_array[bit_idx])
|
| 441 |
else:
|
| 442 |
feature_vector.append(features.get(fname, 0))
|
| 443 |
+
|
| 444 |
return np.array(feature_vector).reshape(1, -1), None, features
|
| 445 |
+
|
| 446 |
except Exception as e:
|
| 447 |
return None, f"Error: {str(e)}", None
|
| 448 |
|
| 449 |
def predict_toxicity(smiles, models):
|
| 450 |
"""Predict toxicity"""
|
| 451 |
X, error, raw_features = compute_features(smiles, models['feature_names'])
|
| 452 |
+
|
| 453 |
if error:
|
| 454 |
return {'error': error}
|
| 455 |
+
|
| 456 |
try:
|
| 457 |
X_are = models['scaler_are'].transform(X)
|
| 458 |
X_mmp = models['scaler_mmp'].transform(X)
|
| 459 |
X_p53 = models['scaler_p53'].transform(X)
|
| 460 |
+
|
| 461 |
prob_are = float(models['model_are'].predict_proba(X_are)[0, 1])
|
| 462 |
prob_mmp = float(models['model_mmp'].predict_proba(X_mmp)[0, 1])
|
| 463 |
prob_p53 = float(models['model_p53'].predict_proba(X_p53)[0, 1])
|
| 464 |
+
|
| 465 |
overall_prob = max(prob_are, prob_mmp, prob_p53)
|
| 466 |
+
|
| 467 |
if overall_prob < 0.35:
|
| 468 |
risk = "LOW"
|
| 469 |
prediction = "NON-TOXIC"
|
|
|
|
| 473 |
else:
|
| 474 |
risk = "HIGH"
|
| 475 |
prediction = "TOXIC"
|
| 476 |
+
|
| 477 |
return {
|
| 478 |
'overall_toxicity': {
|
| 479 |
'prediction': prediction,
|
|
|
|
| 495 |
|
| 496 |
# Header
|
| 497 |
st.markdown('<p class="main-header">🧪 Multi-Endpoint Toxicity Predictor</p>', unsafe_allow_html=True)
|
| 498 |
+
st.markdown('<p class="sub-header">AI-powered hypothesis generation for drug toxicity mechanisms</p>', unsafe_allow_html=True)
|
| 499 |
|
| 500 |
# Tabs
|
| 501 |
+
tab1, tab2 = st.tabs(["🔮 Analyze Compound", "📖 About"])
|
| 502 |
|
| 503 |
with tab1:
|
| 504 |
+
st.markdown("### Enter SMILES String")
|
| 505 |
+
|
| 506 |
smiles = st.text_input(
|
| 507 |
"SMILES:",
|
| 508 |
placeholder="e.g., CC(=O)Oc1ccccc1C(=O)O",
|
| 509 |
label_visibility="collapsed"
|
| 510 |
)
|
| 511 |
+
|
| 512 |
st.markdown("**Examples:**")
|
| 513 |
+
|
| 514 |
examples = {
|
| 515 |
"Aspirin (Safe)": "CC(=O)Oc1ccccc1C(=O)O",
|
|
|
|
| 516 |
"Doxorubicin (Toxic)": "COc1cccc2c1C(=O)c1c(O)c3c(c(O)c1C2=O)C[C@@](O)(C(=O)CO)C[C@@H]3O[C@H]1C[C@H](N)[C@H](O)[C@H](C)O1",
|
| 517 |
+
"Tamoxifen (Toxic)": "CCC(=C(c1ccccc1)c1ccc(OCCN(C)C)cc1)c1ccccc1",
|
| 518 |
+
"Rotenone (Complex I)": "COc1cc(ccc1OC)[C@@H]1[C@H](C(=O)c2c3c(cc4c2OC[C@H]4C1)OCO3)C"
|
| 519 |
}
|
| 520 |
+
|
| 521 |
for name, smi in examples.items():
|
| 522 |
st.code(smi, language=None)
|
| 523 |
st.caption(name)
|
| 524 |
+
|
| 525 |
+
if st.button("🔬 Generate Hypotheses", type="primary", use_container_width=True):
|
| 526 |
if not smiles:
|
| 527 |
st.warning("⚠️ Please enter a SMILES string")
|
| 528 |
else:
|
| 529 |
mol = Chem.MolFromSmiles(smiles)
|
| 530 |
+
|
| 531 |
if mol is None:
|
| 532 |
st.error("❌ Invalid SMILES")
|
| 533 |
else:
|
| 534 |
+
with st.spinner("🧬 Generating mechanistic hypotheses..."):
|
| 535 |
result = predict_toxicity(smiles, models)
|
| 536 |
+
|
| 537 |
if 'error' in result:
|
| 538 |
st.error(f"❌ {result['error']}")
|
| 539 |
else:
|
| 540 |
st.markdown("---")
|
| 541 |
+
|
| 542 |
# Structure
|
| 543 |
st.markdown("### 🧬 Molecular Structure")
|
| 544 |
col_struct = st.columns([1, 2, 1])
|
|
|
|
| 547 |
img = Draw.MolToImage(mol, size=(500, 500))
|
| 548 |
st.image(img, use_column_width=True)
|
| 549 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 550 |
+
|
| 551 |
st.markdown("---")
|
| 552 |
+
|
| 553 |
+
# Quick prediction summary
|
| 554 |
overall = result['overall_toxicity']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 555 |
prob_are = result['oxidative_stress']['probability']
|
| 556 |
prob_mmp = result['mitochondrial_dysfunction']['probability']
|
| 557 |
prob_p53 = result['dna_damage']['probability']
|
| 558 |
+
|
| 559 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 560 |
+
|
| 561 |
with col1:
|
| 562 |
+
st.metric("Overall Risk", overall['risk_level'],
|
| 563 |
+
delta=f"{overall['probability']:.0%}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
with col2:
|
| 565 |
+
st.metric("🔥 Oxidative", f"{prob_are:.0%}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 566 |
with col3:
|
| 567 |
+
st.metric("⚡ Mitochondrial", f"{prob_mmp:.0%}")
|
| 568 |
+
with col4:
|
| 569 |
+
st.metric("🧬 DNA Damage", f"{prob_p53:.0%}")
|
| 570 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
st.markdown("---")
|
| 572 |
+
|
| 573 |
+
# HYPOTHESIS GENERATION
|
| 574 |
+
st.markdown("### 🔬 Testable Hypotheses & Experimental Protocols")
|
| 575 |
+
|
| 576 |
props = result['molecular_properties']
|
| 577 |
+
hypotheses = generate_testable_hypotheses(result, props, mol)
|
| 578 |
+
|
| 579 |
+
if len(hypotheses) == 0:
|
| 580 |
+
st.info("✅ No major toxicity concerns detected. Standard safety testing recommended.")
|
| 581 |
+
else:
|
| 582 |
st.markdown(f"""
|
| 583 |
+
**Generated {len(hypotheses)} mechanistic hypotheses based on your compound's structure and predicted toxicity profile.**
|
| 584 |
+
|
| 585 |
+
Each hypothesis includes:
|
| 586 |
+
- 🎯 Specific mechanistic prediction
|
| 587 |
+
- 🔬 Detailed experimental protocols
|
| 588 |
+
- 📊 Expected results
|
| 589 |
+
- ⏱️ Timeline and cost estimates
|
| 590 |
+
""")
|
| 591 |
+
|
| 592 |
+
for i, hyp in enumerate(hypotheses, 1):
|
| 593 |
+
st.markdown(f"""
|
| 594 |
+
<div class="hypothesis-box">
|
| 595 |
+
<div class="hypothesis-title">{hyp['title']}</div>
|
| 596 |
+
|
| 597 |
+
<p><strong>Hypothesis:</strong><br>{hyp['hypothesis']}</p>
|
| 598 |
+
|
| 599 |
+
<p><strong>Scientific Rationale:</strong><br>{hyp['rationale']}</p>
|
| 600 |
</div>
|
| 601 |
+
""", unsafe_allow_html=True)
|
| 602 |
+
|
| 603 |
+
st.markdown(f"#### Proposed Experiments for Hypothesis {i}:")
|
| 604 |
+
|
| 605 |
+
for j, exp in enumerate(hyp['experiments'], 1):
|
| 606 |
+
with st.expander(f"**Experiment {i}.{j}: {exp['name']}**", expanded=(j==1)):
|
| 607 |
+
col_exp1, col_exp2 = st.columns([2, 1])
|
| 608 |
+
|
| 609 |
+
with col_exp1:
|
| 610 |
+
st.markdown(f"""
|
| 611 |
+
**Protocol:**
|
| 612 |
+
{exp['protocol']}
|
| 613 |
+
|
| 614 |
+
**Readout:**
|
| 615 |
+
{exp['readout']}
|
| 616 |
+
|
| 617 |
+
**Predicted Result:**
|
| 618 |
+
{exp['expected']}
|
| 619 |
+
""")
|
| 620 |
+
|
| 621 |
+
with col_exp2:
|
| 622 |
+
st.markdown(f"""
|
| 623 |
+
<div class="experiment-box">
|
| 624 |
+
<div class="experiment-title">⏱️ Timeline</div>
|
| 625 |
+
<p>{exp['timeline']}</p>
|
| 626 |
+
|
| 627 |
+
<div class="experiment-title">💰 Est. Cost</div>
|
| 628 |
+
<p>{exp['cost']}</p>
|
| 629 |
+
</div>
|
| 630 |
+
""", unsafe_allow_html=True)
|
| 631 |
+
|
| 632 |
+
st.markdown("---")
|
| 633 |
+
|
| 634 |
+
# Summary recommendations
|
| 635 |
+
st.markdown("### 💡 Research Roadmap")
|
| 636 |
+
|
| 637 |
+
total_cost = sum(
|
| 638 |
+
sum(
|
| 639 |
+
int(exp['cost'].split('-')[0].replace('$', '').replace(',', ''))
|
| 640 |
+
for exp in hyp['experiments']
|
| 641 |
+
)
|
| 642 |
+
for hyp in hypotheses
|
| 643 |
+
)
|
| 644 |
+
|
| 645 |
+
st.markdown(f"""
|
| 646 |
+
<div class="interpretation-box">
|
| 647 |
+
<p><strong>Recommended Testing Strategy:</strong></p>
|
| 648 |
+
|
| 649 |
+
<p><strong>Phase 1 (Week 1-2):</strong> Start with the fastest, cheapest experiments to validate primary hypotheses.
|
| 650 |
+
These will tell you if your compound behaves as predicted.</p>
|
| 651 |
+
|
| 652 |
+
<p><strong>Phase 2 (Week 3-4):</strong> If Phase 1 confirms toxicity, proceed to mechanistic experiments
|
| 653 |
+
(LC-MS, Seahorse, etc.) to understand the detailed mechanism.</p>
|
| 654 |
+
|
| 655 |
+
<p><strong>Phase 3 (Month 2):</strong> Use mechanistic insights to design and test optimized analogs
|
| 656 |
+
with reduced toxicity.</p>
|
| 657 |
+
|
| 658 |
+
<p><strong>Total estimated cost for complete hypothesis testing: ${total_cost:,} - ${total_cost + 5000:,}</strong></p>
|
| 659 |
+
|
| 660 |
+
<p><strong>Priority experiments (if budget-limited):</strong> Focus on Experiment 1 from each hypothesis -
|
| 661 |
+
these are designed to be quick validation studies.</p>
|
| 662 |
+
</div>
|
| 663 |
+
""", unsafe_allow_html=True)
|
| 664 |
|
| 665 |
with tab2:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 666 |
st.markdown("""
|
| 667 |
### 📖 About This Tool
|
| 668 |
+
|
| 669 |
+
This is not just a toxicity predictor - it's a **hypothesis generation engine**.
|
| 670 |
+
|
| 671 |
+
**What makes it unique:**
|
| 672 |
+
- 🎯 **Mechanistic predictions**: Not just "toxic" or "safe", but *why*
|
| 673 |
+
- 🔬 **Experimental protocols**: Ready-to-use laboratory procedures
|
| 674 |
+
- 📊 **Quantitative predictions**: Expected results with timelines and costs
|
| 675 |
+
- 💡 **Research roadmap**: Prioritized testing strategy
|
| 676 |
+
|
| 677 |
+
**Trained on:** 11,306 compounds from EPA ToxCast
|
| 678 |
+
**Performance:** ROC-AUC 0.82-0.93 across endpoints
|
| 679 |
+
**Validation:** 100% accuracy on known compounds
|
| 680 |
+
|
| 681 |
**Endpoints:**
|
| 682 |
+
- 🔥 Oxidative Stress (ARE/Nrf2)
|
| 683 |
+
- ⚡ Mitochondrial Dysfunction (MMP)
|
| 684 |
+
- 🧬 DNA Damage (p53)
|
| 685 |
+
|
| 686 |
+
**⚠️ Disclaimer:** For research only. Validate computationally-generated hypotheses experimentally.
|
| 687 |
+
|
| 688 |
+
**For researchers:** These protocols are based on standard methods from the literature.
|
| 689 |
+
Adjust concentrations and timepoints based on your specific compound and cell system.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
""")
|
| 691 |
|
| 692 |
st.markdown("---")
|
| 693 |
+
st.markdown('<p style="text-align: center; color: #8b92a8;">🔬 Accelerating drug safety research through AI-powered hypothesis generation</p>', unsafe_allow_html=True)
|