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

Add interactive molecule explorer with visual property analysis and 3D viewer

Browse files
Files changed (2) hide show
  1. app.py +259 -8
  2. force_rebuild.txt +1 -1
app.py CHANGED
@@ -869,6 +869,255 @@ def analyze_scaffolds(smiles_list: str) -> str:
869
  return "\n".join(results)
870
 
871
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
872
  smiles_interface = gr.Interface(
873
  fn=smiles_to_canonical,
874
  inputs=gr.Textbox(label="SMILES"),
@@ -1006,13 +1255,14 @@ scaffold_interface = gr.Interface(
1006
 
1007
  demo = gr.TabbedInterface(
1008
  [
1009
- name_interface,
1010
- molecule_3d_interface,
1011
- chemiscope_interface,
1012
  orbital_interface,
1013
  properties_interface,
1014
  clustering_interface,
1015
  scaffold_interface,
 
 
 
1016
  smiles_interface,
1017
  smiles_to_name_interface,
1018
  mw_interface,
@@ -1020,21 +1270,22 @@ demo = gr.TabbedInterface(
1020
  tpsa_interface,
1021
  ],
1022
  [
1023
- "Name to SMILES",
1024
- "3D Molecule Viewer",
1025
- "Chemiscope Explorer",
1026
  "Molecular Orbitals",
1027
  "Property Calculator",
1028
  "Molecular Clustering",
1029
  "Scaffold Analysis",
 
 
 
1030
  "SMILES to Canonical",
1031
  "SMILES to Name",
1032
  "Molecular Weight",
1033
  "LogP",
1034
  "TPSA",
1035
  ],
1036
- title="RDKit API",
1037
- css=".gradio-container {max-width: 800px; margin: auto;}",
1038
  )
1039
 
1040
 
 
869
  return "\n".join(results)
870
 
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):
1001
+ """Generate interactive 3D molecule viewer"""
1002
+ if not smiles or smiles == "None":
1003
+ return "<p>Please enter a molecule first</p>"
1004
+
1005
+ try:
1006
+ mol = Chem.MolFromSmiles(smiles)
1007
+ if mol is None:
1008
+ return "<p>Invalid SMILES</p>"
1009
+
1010
+ # Add hydrogens and generate 3D coordinates
1011
+ mol_3d = Chem.AddHs(mol)
1012
+ AllChem.EmbedMolecule(mol_3d, randomSeed=42)
1013
+ AllChem.MMFFOptimizeMolecule(mol_3d)
1014
+
1015
+ # Get molecular structure
1016
+ mol_block = Chem.MolToMolBlock(mol_3d)
1017
+
1018
+ # Escape for JavaScript
1019
+ mol_block_escaped = mol_block.replace('`', '\\`').replace('${', '\\${')
1020
+
1021
+ # Create standalone HTML with 3Dmol.js
1022
+ html_content = f"""
1023
+ <!DOCTYPE html>
1024
+ <html>
1025
+ <head>
1026
+ <script src="https://3dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
1027
+ <style>
1028
+ body {{ margin: 0; padding: 0; }}
1029
+ #viewer {{ width: 100%; height: 500px; }}
1030
+ </style>
1031
+ </head>
1032
+ <body>
1033
+ <div id="viewer"></div>
1034
+ <script>
1035
+ var viewer = $3Dmol.createViewer("viewer", {{backgroundColor: 'white'}});
1036
+ var molData = `{mol_block_escaped}`;
1037
+ viewer.addModel(molData, "sdf");
1038
+ viewer.setStyle({{}}, {{stick: {{radius: 0.15}}, sphere: {{scale: 0.3}}}});
1039
+ viewer.zoomTo();
1040
+ viewer.render();
1041
+ </script>
1042
+ </body>
1043
+ </html>
1044
+ """
1045
+
1046
+ iframe_html = f"""
1047
+ <iframe srcdoc="{html_content.replace('"', '&quot;')}"
1048
+ style="width: 100%; height: 520px; border: 2px solid #2196F3; border-radius: 8px;"
1049
+ sandbox="allow-scripts allow-same-origin">
1050
+ </iframe>
1051
+ """
1052
+
1053
+ return iframe_html
1054
+ except Exception as e:
1055
+ return f"<p>Error generating 3D structure: {str(e)}</p>"
1056
+
1057
+
1058
+ # Interactive Molecule Explorer - Main Feature
1059
+ with gr.Blocks(theme=gr.themes.Soft()) as interactive_explorer:
1060
+ gr.Markdown("# πŸ”¬ Interactive Molecule Explorer")
1061
+ gr.Markdown("Enter a molecule name or SMILES to explore its structure and properties")
1062
+
1063
+ with gr.Row():
1064
+ with gr.Column(scale=1):
1065
+ input_text = gr.Textbox(
1066
+ label="Enter Molecule",
1067
+ placeholder="e.g., aspirin, caffeine, or CCO",
1068
+ lines=1
1069
+ )
1070
+ input_type = gr.Radio(
1071
+ choices=["Name", "SMILES"],
1072
+ value="Name",
1073
+ label="Input Type"
1074
+ )
1075
+ analyze_btn = gr.Button("πŸ” Analyze Molecule", variant="primary", size="lg")
1076
+
1077
+ gr.Markdown("### Quick Examples:")
1078
+ gr.Examples(
1079
+ examples=[
1080
+ ["aspirin", "Name"],
1081
+ ["caffeine", "Name"],
1082
+ ["glucose", "Name"],
1083
+ ["c1ccccc1", "SMILES"],
1084
+ ["CCO", "SMILES"],
1085
+ ],
1086
+ inputs=[input_text, input_type],
1087
+ )
1088
+
1089
+ with gr.Column(scale=1):
1090
+ structure_2d = gr.Image(label="2D Structure", type="pil")
1091
+
1092
+ with gr.Row():
1093
+ properties_plot = gr.Plot(label="Property Radar Chart")
1094
+
1095
+ with gr.Row():
1096
+ properties_text = gr.Markdown(label="Detailed Properties")
1097
+
1098
+ gr.Markdown("---")
1099
+ gr.Markdown("## 🧊 Interactive 3D Viewer")
1100
+ gr.Markdown("Click below to generate the interactive 3D molecular structure")
1101
+
1102
+ smiles_state = gr.State(value=None)
1103
+ generate_3d_btn = gr.Button("🎯 Generate 3D Structure", variant="secondary", size="lg")
1104
+ viewer_3d = gr.HTML(label="3D Molecular Viewer")
1105
+
1106
+ # Connect the analyze button
1107
+ analyze_btn.click(
1108
+ fn=interactive_molecule_explorer,
1109
+ inputs=[input_text, input_type],
1110
+ outputs=[structure_2d, properties_plot, properties_text, smiles_state]
1111
+ )
1112
+
1113
+ # Connect the 3D button
1114
+ generate_3d_btn.click(
1115
+ fn=generate_3d_interactive,
1116
+ inputs=[smiles_state],
1117
+ outputs=[viewer_3d]
1118
+ )
1119
+
1120
+
1121
  smiles_interface = gr.Interface(
1122
  fn=smiles_to_canonical,
1123
  inputs=gr.Textbox(label="SMILES"),
 
1255
 
1256
  demo = gr.TabbedInterface(
1257
  [
1258
+ interactive_explorer,
 
 
1259
  orbital_interface,
1260
  properties_interface,
1261
  clustering_interface,
1262
  scaffold_interface,
1263
+ name_interface,
1264
+ molecule_3d_interface,
1265
+ chemiscope_interface,
1266
  smiles_interface,
1267
  smiles_to_name_interface,
1268
  mw_interface,
 
1270
  tpsa_interface,
1271
  ],
1272
  [
1273
+ "πŸ”¬ Interactive Explorer",
 
 
1274
  "Molecular Orbitals",
1275
  "Property Calculator",
1276
  "Molecular Clustering",
1277
  "Scaffold Analysis",
1278
+ "Name to SMILES",
1279
+ "3D Molecule Viewer",
1280
+ "Chemiscope Explorer",
1281
  "SMILES to Canonical",
1282
  "SMILES to Name",
1283
  "Molecular Weight",
1284
  "LogP",
1285
  "TPSA",
1286
  ],
1287
+ title="RDKit API - Interactive Molecular Analysis",
1288
+ css=".gradio-container {max-width: 1200px; margin: auto;}",
1289
  )
1290
 
1291
 
force_rebuild.txt CHANGED
@@ -1,2 +1,2 @@
1
  # Force rebuild
2
- 2025-11-08 v16
 
1
  # Force rebuild
2
+ 2025-11-08 v17