Nanny7 commited on
Commit
a2aa450
Β·
1 Parent(s): 53f3a69

Fix interactive explorer error handling

Browse files
Files changed (2) hide show
  1. app.py +137 -121
  2. force_rebuild.txt +1 -1
app.py CHANGED
@@ -871,130 +871,146 @@ def analyze_scaffolds(smiles_list: str) -> str:
871
 
872
  def interactive_molecule_explorer(input_text: str, input_type: str):
873
  """Interactive molecule explorer - input name or SMILES, get structure and properties"""
874
- from rdkit.Chem import Lipinski, Crippen, Descriptors
875
- import pandas as pd
876
-
877
- # Parse input
878
- if input_type == "Name":
879
- try:
880
- smiles = cirpy.resolve(input_text, 'smiles')
881
- if smiles is None:
882
- return None, None, "Could not resolve chemical name. Please try a different name or use SMILES.", None
883
- name = input_text
884
- except:
885
- return None, None, "Error resolving chemical name. Please try SMILES input.", None
886
- else: # SMILES
887
- smiles = input_text
888
- # Try to get name
889
- try:
890
- name = cirpy.resolve(smiles, 'name')
891
- if name is None:
892
- name = smiles
893
- except:
894
- name = smiles
895
-
896
- # Create molecule
897
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898
  mol = Chem.MolFromSmiles(smiles)
899
  if mol is None:
900
- return None, None, "Invalid SMILES string", None
901
- except:
902
- return None, None, "Error parsing molecule", None
903
-
904
- # Generate 2D structure image
905
- mol_2d = Draw.MolToImage(mol, size=(400, 400))
906
-
907
- # Calculate comprehensive properties
908
- properties = {
909
- "Molecular Formula": Chem.rdMolDescriptors.CalcMolFormula(mol),
910
- "Molecular Weight": f"{Descriptors.MolWt(mol):.2f} g/mol",
911
- "LogP (Lipophilicity)": f"{Descriptors.MolLogP(mol):.2f}",
912
- "TPSA (Polar Surface Area)": f"{Descriptors.TPSA(mol):.2f} Ε²",
913
- "H-Bond Donors": Lipinski.NumHDonors(mol),
914
- "H-Bond Acceptors": Lipinski.NumHAcceptors(mol),
915
- "Rotatable Bonds": Lipinski.NumRotatableBonds(mol),
916
- "Aromatic Rings": Lipinski.NumAromaticRings(mol),
917
- "Fraction Csp3": f"{Lipinski.FractionCsp3(mol):.2f}",
918
- "Molar Refractivity": f"{Crippen.MolMR(mol):.2f}",
919
- "Heavy Atoms": Lipinski.HeavyAtomCount(mol),
920
- }
921
-
922
- # Create properties visualization
923
- fig = go.Figure()
924
-
925
- # Create a radar chart for key properties
926
- categories = ['MW/100', 'LogP+5', 'TPSA/20', 'HBD*10', 'HBA*5', 'RotBonds*5']
927
- values = [
928
- Descriptors.MolWt(mol) / 100,
929
- Descriptors.MolLogP(mol) + 5,
930
- Descriptors.TPSA(mol) / 20,
931
- Lipinski.NumHDonors(mol) * 10,
932
- Lipinski.NumHAcceptors(mol) * 5,
933
- Lipinski.NumRotatableBonds(mol) * 5
934
- ]
935
-
936
- fig.add_trace(go.Scatterpolar(
937
- r=values,
938
- theta=categories,
939
- fill='toself',
940
- name='Properties',
941
- line_color='rgb(30, 144, 255)'
942
- ))
943
-
944
- fig.update_layout(
945
- polar=dict(
946
- radialaxis=dict(
947
- visible=True,
948
- range=[0, 15]
949
- )
950
- ),
951
- showlegend=False,
952
- title=f"Property Profile: {name}",
953
- height=400
954
- )
955
-
956
- # Create properties text
957
- props_text = f"**Molecule:** {name}\n**SMILES:** {smiles}\n\n"
958
- props_text += "**Properties:**\n"
959
- for key, value in properties.items():
960
- props_text += f"- {key}: {value}\n"
961
-
962
- # Check Lipinski's Rule of 5
963
- lipinski_violations = 0
964
- lipinski_text = "\n**Lipinski's Rule of 5:**\n"
965
-
966
- if Descriptors.MolWt(mol) > 500:
967
- lipinski_violations += 1
968
- lipinski_text += "❌ MW > 500\n"
969
- else:
970
- lipinski_text += "βœ… MW ≀ 500\n"
971
-
972
- if Descriptors.MolLogP(mol) > 5:
973
- lipinski_violations += 1
974
- lipinski_text += "❌ LogP > 5\n"
975
- else:
976
- lipinski_text += "βœ… LogP ≀ 5\n"
977
-
978
- if Lipinski.NumHDonors(mol) > 5:
979
- lipinski_violations += 1
980
- lipinski_text += "❌ HBD > 5\n"
981
- else:
982
- lipinski_text += "βœ… HBD ≀ 5\n"
983
-
984
- if Lipinski.NumHAcceptors(mol) > 10:
985
- lipinski_violations += 1
986
- lipinski_text += "❌ HBA > 10\n"
987
- else:
988
- lipinski_text += "βœ… HBA ≀ 10\n"
989
-
990
- if lipinski_violations <= 1:
991
- lipinski_text += f"\nβœ… **Drug-like** (Violations: {lipinski_violations})"
992
- else:
993
- lipinski_text += f"\n⚠️ **Not drug-like** (Violations: {lipinski_violations})"
994
-
995
- props_text += lipinski_text
996
-
997
- return mol_2d, fig, props_text, smiles
 
 
 
 
 
 
 
 
 
 
 
 
 
 
998
 
999
 
1000
  def generate_3d_interactive(smiles: str):
 
871
 
872
  def interactive_molecule_explorer(input_text: str, input_type: str):
873
  """Interactive molecule explorer - input name or SMILES, get structure and properties"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
874
  try:
875
+ from rdkit.Chem import Lipinski, Crippen, Descriptors
876
+
877
+ if not input_text or not input_text.strip():
878
+ return None, None, "Please enter a molecule name or SMILES", None
879
+
880
+ # Parse input
881
+ if input_type == "Name":
882
+ try:
883
+ smiles = cirpy.resolve(input_text.strip(), 'smiles')
884
+ if smiles is None:
885
+ return None, None, f"❌ Could not resolve '{input_text}'. Try a different name or use SMILES.", None
886
+ name = input_text.strip()
887
+ except Exception as e:
888
+ return None, None, f"❌ Error resolving chemical name: {str(e)}", None
889
+ else: # SMILES
890
+ smiles = input_text.strip()
891
+ # Try to get name
892
+ try:
893
+ name = cirpy.resolve(smiles, 'name')
894
+ if name is None:
895
+ name = smiles
896
+ except:
897
+ name = smiles
898
+
899
+ # Create molecule
900
  mol = Chem.MolFromSmiles(smiles)
901
  if mol is None:
902
+ return None, None, f"❌ Invalid SMILES: {smiles}", None
903
+
904
+ # Generate 2D structure image
905
+ try:
906
+ mol_2d = Draw.MolToImage(mol, size=(400, 400))
907
+ except Exception as e:
908
+ return None, None, f"❌ Error generating 2D image: {str(e)}", None
909
+
910
+ # Calculate comprehensive properties
911
+ properties = {
912
+ "Molecular Formula": Chem.rdMolDescriptors.CalcMolFormula(mol),
913
+ "Molecular Weight": f"{Descriptors.MolWt(mol):.2f} g/mol",
914
+ "LogP (Lipophilicity)": f"{Descriptors.MolLogP(mol):.2f}",
915
+ "TPSA (Polar Surface Area)": f"{Descriptors.TPSA(mol):.2f} Ε²",
916
+ "H-Bond Donors": Lipinski.NumHDonors(mol),
917
+ "H-Bond Acceptors": Lipinski.NumHAcceptors(mol),
918
+ "Rotatable Bonds": Lipinski.NumRotatableBonds(mol),
919
+ "Aromatic Rings": Lipinski.NumAromaticRings(mol),
920
+ "Fraction Csp3": f"{Lipinski.FractionCsp3(mol):.2f}",
921
+ "Molar Refractivity": f"{Crippen.MolMR(mol):.2f}",
922
+ "Heavy Atoms": Lipinski.HeavyAtomCount(mol),
923
+ }
924
+
925
+ # Create properties visualization
926
+ fig = go.Figure()
927
+
928
+ # Create a radar chart for key properties
929
+ categories = ['MW/100', 'LogP+5', 'TPSA/20', 'HBD*10', 'HBA*5', 'RotBonds*5']
930
+ values = [
931
+ min(Descriptors.MolWt(mol) / 100, 15),
932
+ min(max(Descriptors.MolLogP(mol) + 5, 0), 15),
933
+ min(Descriptors.TPSA(mol) / 20, 15),
934
+ min(Lipinski.NumHDonors(mol) * 2, 15),
935
+ min(Lipinski.NumHAcceptors(mol) * 1.5, 15),
936
+ min(Lipinski.NumRotatableBonds(mol) * 2, 15)
937
+ ]
938
+
939
+ fig.add_trace(go.Scatterpolar(
940
+ r=values,
941
+ theta=categories,
942
+ fill='toself',
943
+ name='Properties',
944
+ line_color='rgb(30, 144, 255)',
945
+ fillcolor='rgba(30, 144, 255, 0.3)'
946
+ ))
947
+
948
+ fig.update_layout(
949
+ polar=dict(
950
+ radialaxis=dict(
951
+ visible=True,
952
+ range=[0, 15]
953
+ )
954
+ ),
955
+ showlegend=False,
956
+ title=f"Property Profile: {name[:50]}",
957
+ height=400,
958
+ margin=dict(l=80, r=80, t=100, b=80)
959
+ )
960
+
961
+ # Create properties text
962
+ props_text = f"## **{name}**\n\n"
963
+ props_text += f"**SMILES:** `{smiles}`\n\n"
964
+ props_text += "### **Molecular Properties:**\n\n"
965
+ for key, value in properties.items():
966
+ props_text += f"- **{key}:** {value}\n"
967
+
968
+ # Check Lipinski's Rule of 5
969
+ lipinski_violations = 0
970
+ lipinski_text = "\n### **Lipinski's Rule of 5 (Drug-Likeness):**\n\n"
971
+
972
+ mw = Descriptors.MolWt(mol)
973
+ logp = Descriptors.MolLogP(mol)
974
+ hbd = Lipinski.NumHDonors(mol)
975
+ hba = Lipinski.NumHAcceptors(mol)
976
+
977
+ if mw > 500:
978
+ lipinski_violations += 1
979
+ lipinski_text += "❌ Molecular Weight > 500 Da\n"
980
+ else:
981
+ lipinski_text += "βœ… Molecular Weight ≀ 500 Da\n"
982
+
983
+ if logp > 5:
984
+ lipinski_violations += 1
985
+ lipinski_text += "❌ LogP > 5\n"
986
+ else:
987
+ lipinski_text += "βœ… LogP ≀ 5\n"
988
+
989
+ if hbd > 5:
990
+ lipinski_violations += 1
991
+ lipinski_text += "❌ H-Bond Donors > 5\n"
992
+ else:
993
+ lipinski_text += "βœ… H-Bond Donors ≀ 5\n"
994
+
995
+ if hba > 10:
996
+ lipinski_violations += 1
997
+ lipinski_text += "❌ H-Bond Acceptors > 10\n"
998
+ else:
999
+ lipinski_text += "βœ… H-Bond Acceptors ≀ 10\n"
1000
+
1001
+ if lipinski_violations <= 1:
1002
+ lipinski_text += f"\n### βœ… **DRUG-LIKE** (Violations: {lipinski_violations}/4)"
1003
+ else:
1004
+ lipinski_text += f"\n### ⚠️ **NOT DRUG-LIKE** (Violations: {lipinski_violations}/4)"
1005
+
1006
+ props_text += lipinski_text
1007
+
1008
+ return mol_2d, fig, props_text, smiles
1009
+
1010
+ except Exception as e:
1011
+ import traceback
1012
+ error_msg = f"❌ **Error:** {str(e)}\n\n```\n{traceback.format_exc()}\n```"
1013
+ return None, None, error_msg, None
1014
 
1015
 
1016
  def generate_3d_interactive(smiles: str):
force_rebuild.txt CHANGED
@@ -1,2 +1,2 @@
1
  # Force rebuild
2
- 2025-11-08 v17
 
1
  # Force rebuild
2
+ 2025-11-08 v18