alidenewade commited on
Commit
dfd281b
Β·
verified Β·
1 Parent(s): 3b2df55

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -97
app.py CHANGED
@@ -43,7 +43,7 @@ def apply_custom_styling():
43
  <style>
44
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
45
 
46
- html, body, [class*=\"st-\"] {
47
  font-family: 'Roboto', sans-serif;
48
  }
49
 
@@ -53,11 +53,11 @@ def apply_custom_styling():
53
  }
54
 
55
  /* Tab styles */
56
- .stTabs [data-baseweb=\"tab-list\"] {
57
  gap: 24px;
58
  }
59
 
60
- .stTabs [data-baseweb=\"tab\"] {
61
  height: 50px;
62
  white-space: pre-wrap;
63
  background: none;
@@ -67,12 +67,12 @@ def apply_custom_styling():
67
  color: #AAA;
68
  }
69
 
70
- .stTabs [data-baseweb=\"tab\"]:hover {
71
  background: #222;
72
  color: #FFF;
73
  }
74
 
75
- .stTabs [aria-selected=\"true\"] {
76
  border-bottom: 2px solid #00A0FF; /* Highlight color for active tab */
77
  color: #FFF;
78
  }
@@ -154,7 +154,6 @@ def visualize_protein_3d(pdb_data: str, title="Protein 3D Structure"):
154
  return None, "Cannot generate 3D view: No PDB data provided."
155
  try:
156
  viewer = py3Dmol.view(width='100%', height=600)
157
- # MODIFIED: Changed background color
158
  viewer.setBackgroundColor('#1C1C1C')
159
  viewer.addModel(pdb_data, "pdb")
160
  viewer.setStyle({'cartoon': {'color': 'spectrum', 'thickness': 0.8}})
@@ -206,28 +205,46 @@ def calculate_molecular_properties(smiles_list: list):
206
 
207
  def assess_drug_likeness(df: pd.DataFrame):
208
  """
209
- Assesses drug-likeness based on Lipinski's Rule of Five.
 
210
  """
211
  if df.empty:
212
- return pd.DataFrame(), "Cannot assess drug-likeness: No properties data." [cite: 26]
213
- df_copy = df.copy()
214
- df_copy['MW_OK'] = df_copy['MW'] <= 500
215
- df_copy['LogP_OK'] = df_copy['LogP'] <= 5
216
- df_copy['HBD_OK'] = df_copy['HBD'] <= 5
217
- df_copy['HBA_OK'] = df_copy['HBA'] <= 10
218
- df_copy['Lipinski_Violations'] = (~df_copy[['MW_OK', 'LogP_OK', 'HBD_OK', 'HBA_OK']]).sum(axis=1)
219
- # Fixed: Use proper colored emojis instead of boolean values
220
- df_copy['Drug_Like'] = df_copy['Lipinski_Violations'].apply(lambda x: 'βœ… Yes' if x <= 1 else '❌ No')
221
- log = "βœ… Assessed drug-likeness using Lipinski's Rule of Five.\n" [cite: 27]
222
- return df_copy, log [cite: 27]
223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  def plot_properties_dashboard(df: pd.DataFrame):
225
  """
226
- Creates a 2x2 dashboard of molecular property visualizations.
 
227
  """
228
- if df.empty:
229
- return None, "Cannot plot: No analysis data." [cite: 28]
230
 
 
 
 
 
231
  plt.style.use('dark_background')
232
  fig, axes = plt.subplots(2, 2, figsize=(12, 10))
233
  fig.suptitle("Molecular Properties Analysis", fontsize=16)
@@ -237,17 +254,18 @@ def plot_properties_dashboard(df: pd.DataFrame):
237
  for ax in ax_row:
238
  ax.set_facecolor('none')
239
 
240
- # FIXED: Correctly map string values to colors
241
- axes[0,0].scatter(df['MW'], df['LogP'], c=df['Drug_Like'].map({'βœ… Yes': 'green', '❌ No': 'red'}), s=80, alpha=0.7)
 
 
242
  axes[0,0].set_title('Molecular Weight vs LogP')
243
- axes[0,0].set_xlabel('Molecular Weight (Da)') [cite: 29]
244
- axes[0,0].set_ylabel('LogP') [cite: 29]
245
- axes[0,0].axvline(500, color='r', linestyle='--', alpha=0.6, label='MW < 500') [cite: 29]
246
- axes[0,0].axhline(5, color='r', linestyle='--', alpha=0.6, label='LogP < 5') [cite: 29]
247
  axes[0,0].legend()
248
 
249
- # FIXED: Correctly map string values to colors
250
- axes[0,1].scatter(df['HBD'], df['HBA'], c=df['Drug_Like'].map({'βœ… Yes': 'green', '❌ No': 'red'}), s=80, alpha=0.7)
251
  axes[0,1].set_title('Hydrogen Bonding Properties')
252
  axes[0,1].set_xlabel('Hydrogen Bond Donors')
253
  axes[0,1].set_ylabel('Hydrogen Bond Acceptors')
@@ -255,22 +273,20 @@ def plot_properties_dashboard(df: pd.DataFrame):
255
  axes[0,1].axhline(10, color='r', linestyle='--', alpha=0.6, label='HBA < 10')
256
  axes[0,1].legend()
257
 
258
- # FIXED: Correctly map string values to colors
259
- axes[1,0].scatter(df['TPSA'], df['RotBonds'], c=df['Drug_Like'].map({'βœ… Yes': 'green', '❌ No': 'red'}), s=80, alpha=0.7)
260
- axes[1,0].set_title('TPSA vs Flexibility') [cite: 30]
261
- axes[1,0].set_xlabel('Topological Polar Surface Area (Γ…Β²)') [cite: 30]
262
- axes[1,0].set_ylabel('Rotatable Bonds') [cite: 30]
263
 
264
  drug_like_counts = df['Drug_Like'].value_counts()
265
- # FIXED: Correctly map string values to labels and colors
266
- labels = drug_like_counts.index
267
- colors = ['green' if i == 'βœ… Yes' else 'red' for i in drug_like_counts.index]
268
  axes[1,1].pie(drug_like_counts.values, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
269
  axes[1,1].set_title('Drug-likeness Distribution')
270
 
271
  plt.tight_layout(rect=[0, 0, 1, 0.96])
272
  return fig, "βœ… Generated properties dashboard."
273
-
274
  # ===== Phase 2 Functions =====
275
  def get_phase2_molecules():
276
  return {
@@ -312,21 +328,16 @@ def visualize_molecule_2d_3d(smiles: str, name: str):
312
  if not mol: return f"<p>Invalid SMILES for {name}</p>", f"❌ Invalid SMILES for {name}"
313
 
314
  drawer = Draw.rdMolDraw2D.MolDraw2DSVG(400, 300)
315
- # Set dark theme colors for 2D drawing
316
  drawer.drawOptions().clearBackground = False
317
  drawer.drawOptions().addStereoAnnotation = True
318
  drawer.drawOptions().baseFontSize = 0.8
319
  drawer.drawOptions().circleAtoms = False
320
- drawer.drawOptions().highlightColour = (1, 0.5, 0) # Orange for highlights
321
-
322
- # Set colors for dark background visibility
323
- drawer.drawOptions().backgroundColour = (0.11, 0.11, 0.11) # Dark background
324
- drawer.drawOptions().symbolColour = (1, 1, 1) # White symbols
325
- drawer.drawOptions().defaultColour = (1, 1, 1) # White default color
326
-
327
- # Try to set annotation color (this might help with (R)/(S) labels)
328
  try:
329
- drawer.drawOptions().annotationColour = (1, 1, 1) # White annotations
330
  except:
331
  pass
332
 
@@ -334,52 +345,13 @@ def visualize_molecule_2d_3d(smiles: str, name: str):
334
  drawer.FinishDrawing()
335
  svg_2d = drawer.GetDrawingText().replace('svg:', '')
336
 
337
- # More aggressive SVG text color fixes - target all possible black text variations
338
  import re
339
-
340
- # First, comprehensive string replacements
341
  svg_2d = svg_2d.replace('stroke="black"', 'stroke="white"')
342
  svg_2d = svg_2d.replace('fill="black"', 'fill="white"')
343
  svg_2d = svg_2d.replace('stroke="#000000"', 'stroke="#FFFFFF"')
344
  svg_2d = svg_2d.replace('fill="#000000"', 'fill="#FFFFFF"')
345
- svg_2d = svg_2d.replace('stroke="#000"', 'stroke="#FFF"')
346
- svg_2d = svg_2d.replace('fill="#000"', 'fill="#FFF"')
347
- svg_2d = svg_2d.replace('stroke:black', 'stroke:white')
348
- svg_2d = svg_2d.replace('fill:black', 'fill:white')
349
- svg_2d = svg_2d.replace('stroke:#000000', 'stroke:#FFFFFF')
350
- svg_2d = svg_2d.replace('fill:#000000', 'fill:#FFFFFF')
351
- svg_2d = svg_2d.replace('stroke:#000', 'stroke:#FFF')
352
- svg_2d = svg_2d.replace('fill:#000', 'fill:#FFF')
353
- svg_2d = svg_2d.replace('stroke="rgb(0,0,0)"', 'stroke="rgb(255,255,255)"')
354
- svg_2d = svg_2d.replace('fill="rgb(0,0,0)"', 'fill="rgb(255,255,255)"')
355
- svg_2d = svg_2d.replace('stroke:rgb(0,0,0)', 'stroke:rgb(255,255,255)')
356
- svg_2d = svg_2d.replace('fill:rgb(0,0,0)', 'fill:rgb(255,255,255)')
357
- svg_2d = svg_2d.replace('color="black"', 'color="white"')
358
- svg_2d = svg_2d.replace('color:#000000', 'color:#FFFFFF')
359
- svg_2d = svg_2d.replace('color:#000', 'color:#FFF')
360
-
361
- # Aggressive regex-based fixes for all text elements
362
- # Remove any existing fill attributes from text elements and add white fill
363
- svg_2d = re.sub(r'<text([^>]*?)\s+fill="[^"]*"([^>]*?)>', r'<text\1\2 fill="white">', svg_2d)
364
- svg_2d = re.sub(r'<text([^>]*?)(?<!fill="white")>', r'<text\1 fill="white">', svg_2d)
365
-
366
- # Fix style attributes in text elements
367
- svg_2d = re.sub(r'<text([^>]*?)style="([^"]*?)fill:\s*(?:black|#000000|#000|rgb\(0,0,0\))([^"]*?)"([^>]*?)>',
368
- r'<text\1style="\2fill:white\3"\4>', svg_2d)
369
-
370
- # If text elements don't have any fill specified, ensure they get white
371
  svg_2d = re.sub(r'<text(?![^>]*fill=)([^>]*?)>', r'<text fill="white"\1>', svg_2d)
372
 
373
- # Clean up any duplicate fill attributes
374
- svg_2d = re.sub(r'fill="white"\s+fill="white"', 'fill="white"', svg_2d)
375
-
376
- # Final catch-all: replace any remaining black in the entire SVG
377
- svg_2d = re.sub(r'\bblack\b', 'white', svg_2d)
378
- svg_2d = re.sub(r'#000000', '#FFFFFF', svg_2d)
379
- svg_2d = re.sub(r'#000\b', '#FFF', svg_2d)
380
- svg_2d = re.sub(r'rgb\(0,\s*0,\s*0\)', 'rgb(255,255,255)', svg_2d)
381
-
382
- # Embed the SVG within a div with a dark background for consistency
383
  svg_2d = f'<div style="background-color: #1C1C1C; padding: 10px; border-radius: 5px;">{svg_2d}</div>'
384
 
385
  mol_3d = Chem.AddHs(mol)
@@ -416,7 +388,6 @@ def visualize_protein_ligand_interaction(pdb_data: str, pdb_id: str, ligand_resn
416
  if not pdb_data: return None, "Cannot generate view: No PDB data provided."
417
  try:
418
  viewer = py3Dmol.view(width='100%', height=700)
419
- # MODIFIED: Changed background color
420
  viewer.setBackgroundColor('#1C1C1C')
421
  viewer.addModel(pdb_data, "pdb")
422
  viewer.setStyle({'cartoon': {'color': 'spectrum', 'thickness': 0.8}})
@@ -453,7 +424,6 @@ def calculate_comprehensive_properties(smiles_dict: dict):
453
  'TPSA': Descriptors.TPSA(mol), 'Rotatable_Bonds': Descriptors.NumRotatableBonds(mol),
454
  'Aromatic_Rings': Descriptors.NumAromaticRings(mol),
455
  'Lipinski_Violations': violations,
456
- # Fixed: Use proper colored emojis instead of text
457
  'Drug_Like': 'βœ… Yes' if violations <= 1 else '❌ No'})
458
  df = pd.DataFrame(analysis).round(2)
459
  log += f"βœ… Calculated comprehensive properties for {len(df)} compounds.\n"
@@ -478,7 +448,6 @@ def predict_toxicity(properties_df: pd.DataFrame):
478
  toxicity_prob = rf_model.predict_proba(X_pred)[:, 1]
479
  results_df = properties_df[['Compound']].copy()
480
  results_df['Toxicity_Probability'] = np.round(toxicity_prob, 3)
481
- # Fixed: Use proper colored emojis that will display correctly
482
  results_df['Predicted_Risk'] = ["🟒 LOW" if p < 0.3 else "🟑 MODERATE" if p < 0.7 else "πŸ”΄ HIGH" for p in toxicity_prob]
483
  return results_df, "βœ… Predicted toxicity using a pre-trained simulation model.\n"
484
 
@@ -576,15 +545,21 @@ with tab1:
576
  smiles_list = [s.strip() for s in smiles_input_p1.split('\n') if s.strip()]
577
  props_df, log_props = calculate_molecular_properties(smiles_list)
578
  full_log += log_props
579
- analysis_df, log_lipinski = assess_drug_likeness(props_df)
 
 
 
580
  full_log += log_lipinski
 
 
581
  props_plot, log_plot = plot_properties_dashboard(analysis_df)
582
  full_log += log_plot
583
  full_log += "\n--- Phase 1 Analysis Complete ---"
584
  st.session_state.log_p1 = full_log
585
 
 
586
  lipinski_cols = ['Molecule', 'MW', 'LogP', 'HBD', 'HBA', 'Lipinski_Violations', 'Drug_Like']
587
- lipinski_subset_df = analysis_df[lipinski_cols] if not analysis_df.empty else pd.DataFrame(columns=lipinski_cols)
588
 
589
  st.session_state.results_p1 = {
590
  'protein_view_html': protein_view_html,
@@ -601,7 +576,8 @@ with tab1:
601
  p1_tabs = st.tabs(["Protein Information", "Molecule Analysis", "Analysis Plots"])
602
  with p1_tabs[0]:
603
  st.subheader("Protein 3D Structure (Interactive)")
604
- st.components.v1.html(res1.get('protein_view_html', '<p>No data</p>'), height=600, scrolling=False)
 
605
  st.subheader("FASTA Sequence Information")
606
  st.text_area("", res1.get('fasta_log', 'No data'), height=200, key="fasta_info_area")
607
  with p1_tabs[1]:
@@ -613,6 +589,8 @@ with tab1:
613
  st.subheader("Molecular Properties Dashboard")
614
  if res1.get('props_plot'):
615
  st.pyplot(res1['props_plot'])
 
 
616
 
617
  # ===== TAB 2: LEAD GENERATION & OPTIMIZATION =====
618
  with tab2:
@@ -668,10 +646,12 @@ with tab2:
668
  st.dataframe(res2.get('admet_df', pd.DataFrame()), use_container_width=True)
669
  with p2_tabs[1]:
670
  st.subheader("Interactive 2D and 3D views of candidate molecules.")
671
- st.components.v1.html(res2.get('combined_viz_html', '<p>No data</p>'), height=700, scrolling=True)
 
672
  with p2_tabs[2]:
673
  st.subheader("Detailed view of the top candidate binding to the protein.")
674
- st.components.v1.html(res2.get('interaction_html', '<p>No data</p>'), height=700, scrolling=False)
 
675
 
676
 
677
  # ===== TAB 3: PRECLINICAL DEVELOPMENT =====
@@ -717,7 +697,8 @@ with tab3:
717
  st.dataframe(res3.get('tox_df', pd.DataFrame()), use_container_width=True)
718
  with p3_tabs[1]:
719
  st.subheader("Interactive 3D gallery of the compounds under analysis.")
720
- st.components.v1.html(res3.get('combined_viz_html', '<p>No data</p>'), height=1000, scrolling=True)
 
721
 
722
 
723
  # ===== TAB 4: POST-MARKET SURVEILLANCE =====
@@ -756,7 +737,8 @@ with tab4:
756
  p4_tabs = st.tabs(["Pharmacovigilance Analysis", "Regulatory & Ethical Frameworks"])
757
  with p4_tabs[0]:
758
  st.subheader("Simulated Adverse Event Analysis")
759
- st.pyplot(res4['plot_bar'])
 
760
  st.dataframe(res4['rwd_df'], use_container_width=True)
761
 
762
  with p4_tabs[1]:
@@ -767,3 +749,4 @@ with tab4:
767
  with col2:
768
  st.subheader("Ethical Framework for AI in Healthcare")
769
  st.dataframe(res4.get('eth_df', pd.DataFrame()), use_container_width=True)
 
 
43
  <style>
44
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
45
 
46
+ html, body, [class*="st-"] {
47
  font-family: 'Roboto', sans-serif;
48
  }
49
 
 
53
  }
54
 
55
  /* Tab styles */
56
+ .stTabs [data-baseweb="tab-list"] {
57
  gap: 24px;
58
  }
59
 
60
+ .stTabs [data-baseweb="tab"] {
61
  height: 50px;
62
  white-space: pre-wrap;
63
  background: none;
 
67
  color: #AAA;
68
  }
69
 
70
+ .stTabs [data-baseweb="tab"]:hover {
71
  background: #222;
72
  color: #FFF;
73
  }
74
 
75
+ .stTabs [aria-selected="true"] {
76
  border-bottom: 2px solid #00A0FF; /* Highlight color for active tab */
77
  color: #FFF;
78
  }
 
154
  return None, "Cannot generate 3D view: No PDB data provided."
155
  try:
156
  viewer = py3Dmol.view(width='100%', height=600)
 
157
  viewer.setBackgroundColor('#1C1C1C')
158
  viewer.addModel(pdb_data, "pdb")
159
  viewer.setStyle({'cartoon': {'color': 'spectrum', 'thickness': 0.8}})
 
205
 
206
  def assess_drug_likeness(df: pd.DataFrame):
207
  """
208
+ Assesses drug-likeness based on Lipinski's Rule of Five.
209
+ This version returns a boolean for plotting and a formatted string for display.
210
  """
211
  if df.empty:
212
+ return pd.DataFrame(), pd.DataFrame(), "Cannot assess drug-likeness: No properties data."
 
 
 
 
 
 
 
 
 
 
213
 
214
+ # Create a copy for analysis to avoid modifying the original dataframe
215
+ analysis_df = df.copy()
216
+ analysis_df['MW_OK'] = analysis_df['MW'] <= 500
217
+ analysis_df['LogP_OK'] = analysis_df['LogP'] <= 5
218
+ analysis_df['HBD_OK'] = analysis_df['HBD'] <= 5
219
+ analysis_df['HBA_OK'] = analysis_df['HBA'] <= 10
220
+ analysis_df['Lipinski_Violations'] = (~analysis_df[['MW_OK', 'LogP_OK', 'HBD_OK', 'HBA_OK']]).sum(axis=1)
221
+
222
+ # This boolean column is for the plotting function
223
+ analysis_df['Drug_Like'] = analysis_df['Lipinski_Violations'] <= 1
224
+
225
+ # Create a separate DataFrame for display purposes with emojis
226
+ display_df = df.copy()
227
+ display_df['Lipinski_Violations'] = analysis_df['Lipinski_Violations']
228
+ display_df['Drug_Like'] = analysis_df['Drug_Like'].apply(lambda x: 'βœ… Yes' if x else '❌ No')
229
+
230
+ log = "βœ… Assessed drug-likeness using Lipinski's Rule of Five.\n"
231
+
232
+ # Return both the analysis_df (for plotting) and display_df (for tables)
233
+ return analysis_df, display_df, log
234
+
235
+
236
  def plot_properties_dashboard(df: pd.DataFrame):
237
  """
238
+ Creates a 2x2 dashboard of molecular property visualizations.
239
+ This version expects a boolean 'Drug_Like' column.
240
  """
241
+ if df.empty or 'Drug_Like' not in df.columns:
242
+ return None, "Cannot plot: No analysis data or 'Drug_Like' column missing."
243
 
244
+ # Ensure 'Drug_Like' is boolean for correct mapping
245
+ if df['Drug_Like'].dtype != bool:
246
+ return None, f"Cannot plot: 'Drug_Like' column must be boolean, but it is {df['Drug_Like'].dtype}."
247
+
248
  plt.style.use('dark_background')
249
  fig, axes = plt.subplots(2, 2, figsize=(12, 10))
250
  fig.suptitle("Molecular Properties Analysis", fontsize=16)
 
254
  for ax in ax_row:
255
  ax.set_facecolor('none')
256
 
257
+ # Color mapping now correctly uses boolean values
258
+ color_map = {True: 'green', False: 'red'}
259
+
260
+ axes[0,0].scatter(df['MW'], df['LogP'], c=df['Drug_Like'].map(color_map), s=80, alpha=0.7)
261
  axes[0,0].set_title('Molecular Weight vs LogP')
262
+ axes[0,0].set_xlabel('Molecular Weight (Da)')
263
+ axes[0,0].set_ylabel('LogP')
264
+ axes[0,0].axvline(500, color='r', linestyle='--', alpha=0.6, label='MW < 500')
265
+ axes[0,0].axhline(5, color='r', linestyle='--', alpha=0.6, label='LogP < 5')
266
  axes[0,0].legend()
267
 
268
+ axes[0,1].scatter(df['HBD'], df['HBA'], c=df['Drug_Like'].map(color_map), s=80, alpha=0.7)
 
269
  axes[0,1].set_title('Hydrogen Bonding Properties')
270
  axes[0,1].set_xlabel('Hydrogen Bond Donors')
271
  axes[0,1].set_ylabel('Hydrogen Bond Acceptors')
 
273
  axes[0,1].axhline(10, color='r', linestyle='--', alpha=0.6, label='HBA < 10')
274
  axes[0,1].legend()
275
 
276
+ axes[1,0].scatter(df['TPSA'], df['RotBonds'], c=df['Drug_Like'].map(color_map), s=80, alpha=0.7)
277
+ axes[1,0].set_title('TPSA vs Flexibility')
278
+ axes[1,0].set_xlabel('Topological Polar Surface Area (Γ…Β²)')
279
+ axes[1,0].set_ylabel('Rotatable Bonds')
 
280
 
281
  drug_like_counts = df['Drug_Like'].value_counts()
282
+ labels = ['Drug-like' if i else 'Non-drug-like' for i in drug_like_counts.index]
283
+ colors = ['green' if i else 'red' for i in drug_like_counts.index]
 
284
  axes[1,1].pie(drug_like_counts.values, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
285
  axes[1,1].set_title('Drug-likeness Distribution')
286
 
287
  plt.tight_layout(rect=[0, 0, 1, 0.96])
288
  return fig, "βœ… Generated properties dashboard."
289
+
290
  # ===== Phase 2 Functions =====
291
  def get_phase2_molecules():
292
  return {
 
328
  if not mol: return f"<p>Invalid SMILES for {name}</p>", f"❌ Invalid SMILES for {name}"
329
 
330
  drawer = Draw.rdMolDraw2D.MolDraw2DSVG(400, 300)
 
331
  drawer.drawOptions().clearBackground = False
332
  drawer.drawOptions().addStereoAnnotation = True
333
  drawer.drawOptions().baseFontSize = 0.8
334
  drawer.drawOptions().circleAtoms = False
335
+ drawer.drawOptions().highlightColour = (1, 0.5, 0)
336
+ drawer.drawOptions().backgroundColour = (0.11, 0.11, 0.11)
337
+ drawer.drawOptions().symbolColour = (1, 1, 1)
338
+ drawer.drawOptions().defaultColour = (1, 1, 1)
 
 
 
 
339
  try:
340
+ drawer.drawOptions().annotationColour = (1, 1, 1)
341
  except:
342
  pass
343
 
 
345
  drawer.FinishDrawing()
346
  svg_2d = drawer.GetDrawingText().replace('svg:', '')
347
 
 
348
  import re
 
 
349
  svg_2d = svg_2d.replace('stroke="black"', 'stroke="white"')
350
  svg_2d = svg_2d.replace('fill="black"', 'fill="white"')
351
  svg_2d = svg_2d.replace('stroke="#000000"', 'stroke="#FFFFFF"')
352
  svg_2d = svg_2d.replace('fill="#000000"', 'fill="#FFFFFF"')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  svg_2d = re.sub(r'<text(?![^>]*fill=)([^>]*?)>', r'<text fill="white"\1>', svg_2d)
354
 
 
 
 
 
 
 
 
 
 
 
355
  svg_2d = f'<div style="background-color: #1C1C1C; padding: 10px; border-radius: 5px;">{svg_2d}</div>'
356
 
357
  mol_3d = Chem.AddHs(mol)
 
388
  if not pdb_data: return None, "Cannot generate view: No PDB data provided."
389
  try:
390
  viewer = py3Dmol.view(width='100%', height=700)
 
391
  viewer.setBackgroundColor('#1C1C1C')
392
  viewer.addModel(pdb_data, "pdb")
393
  viewer.setStyle({'cartoon': {'color': 'spectrum', 'thickness': 0.8}})
 
424
  'TPSA': Descriptors.TPSA(mol), 'Rotatable_Bonds': Descriptors.NumRotatableBonds(mol),
425
  'Aromatic_Rings': Descriptors.NumAromaticRings(mol),
426
  'Lipinski_Violations': violations,
 
427
  'Drug_Like': 'βœ… Yes' if violations <= 1 else '❌ No'})
428
  df = pd.DataFrame(analysis).round(2)
429
  log += f"βœ… Calculated comprehensive properties for {len(df)} compounds.\n"
 
448
  toxicity_prob = rf_model.predict_proba(X_pred)[:, 1]
449
  results_df = properties_df[['Compound']].copy()
450
  results_df['Toxicity_Probability'] = np.round(toxicity_prob, 3)
 
451
  results_df['Predicted_Risk'] = ["🟒 LOW" if p < 0.3 else "🟑 MODERATE" if p < 0.7 else "πŸ”΄ HIGH" for p in toxicity_prob]
452
  return results_df, "βœ… Predicted toxicity using a pre-trained simulation model.\n"
453
 
 
545
  smiles_list = [s.strip() for s in smiles_input_p1.split('\n') if s.strip()]
546
  props_df, log_props = calculate_molecular_properties(smiles_list)
547
  full_log += log_props
548
+
549
+ # --- MODIFIED LOGIC ---
550
+ # assess_drug_likeness now returns two dataframes: one for analysis/plotting, one for display
551
+ analysis_df, display_df, log_lipinski = assess_drug_likeness(props_df)
552
  full_log += log_lipinski
553
+
554
+ # The plotting function uses the 'analysis_df' with the boolean 'Drug_Like' column
555
  props_plot, log_plot = plot_properties_dashboard(analysis_df)
556
  full_log += log_plot
557
  full_log += "\n--- Phase 1 Analysis Complete ---"
558
  st.session_state.log_p1 = full_log
559
 
560
+ # The display dataframe has the pretty emojis
561
  lipinski_cols = ['Molecule', 'MW', 'LogP', 'HBD', 'HBA', 'Lipinski_Violations', 'Drug_Like']
562
+ lipinski_subset_df = display_df[lipinski_cols] if not display_df.empty else pd.DataFrame(columns=lipinski_cols)
563
 
564
  st.session_state.results_p1 = {
565
  'protein_view_html': protein_view_html,
 
576
  p1_tabs = st.tabs(["Protein Information", "Molecule Analysis", "Analysis Plots"])
577
  with p1_tabs[0]:
578
  st.subheader("Protein 3D Structure (Interactive)")
579
+ if res1.get('protein_view_html'):
580
+ st.components.v1.html(res1['protein_view_html'], height=600, scrolling=False)
581
  st.subheader("FASTA Sequence Information")
582
  st.text_area("", res1.get('fasta_log', 'No data'), height=200, key="fasta_info_area")
583
  with p1_tabs[1]:
 
589
  st.subheader("Molecular Properties Dashboard")
590
  if res1.get('props_plot'):
591
  st.pyplot(res1['props_plot'])
592
+ else:
593
+ st.warning("Could not generate plots. Please check the logs for more details.")
594
 
595
  # ===== TAB 2: LEAD GENERATION & OPTIMIZATION =====
596
  with tab2:
 
646
  st.dataframe(res2.get('admet_df', pd.DataFrame()), use_container_width=True)
647
  with p2_tabs[1]:
648
  st.subheader("Interactive 2D and 3D views of candidate molecules.")
649
+ if res2.get('combined_viz_html'):
650
+ st.components.v1.html(res2.get('combined_viz_html'), height=700, scrolling=True)
651
  with p2_tabs[2]:
652
  st.subheader("Detailed view of the top candidate binding to the protein.")
653
+ if res2.get('interaction_html'):
654
+ st.components.v1.html(res2.get('interaction_html'), height=700, scrolling=False)
655
 
656
 
657
  # ===== TAB 3: PRECLINICAL DEVELOPMENT =====
 
697
  st.dataframe(res3.get('tox_df', pd.DataFrame()), use_container_width=True)
698
  with p3_tabs[1]:
699
  st.subheader("Interactive 3D gallery of the compounds under analysis.")
700
+ if res3.get('combined_viz_html'):
701
+ st.components.v1.html(res3.get('combined_viz_html'), height=1000, scrolling=True)
702
 
703
 
704
  # ===== TAB 4: POST-MARKET SURVEILLANCE =====
 
737
  p4_tabs = st.tabs(["Pharmacovigilance Analysis", "Regulatory & Ethical Frameworks"])
738
  with p4_tabs[0]:
739
  st.subheader("Simulated Adverse Event Analysis")
740
+ if res4.get('plot_bar'):
741
+ st.pyplot(res4['plot_bar'])
742
  st.dataframe(res4['rwd_df'], use_container_width=True)
743
 
744
  with p4_tabs[1]:
 
749
  with col2:
750
  st.subheader("Ethical Framework for AI in Healthcare")
751
  st.dataframe(res4.get('eth_df', pd.DataFrame()), use_container_width=True)
752
+