Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| from rdkit import Chem | |
| from rdkit.Chem import Descriptors, Draw, AllChem | |
| import cirpy | |
| # RDKit API with multiple endpoints | |
| def _mol_from_smiles(smiles: str): | |
| mol = Chem.MolFromSmiles(smiles) | |
| if mol is None: | |
| raise gr.Error("Invalid SMILES string.") | |
| return mol | |
| def smiles_to_canonical(smiles: str) -> str: | |
| mol = _mol_from_smiles(smiles) | |
| return Chem.MolToSmiles(mol) | |
| def molecular_weight(smiles: str) -> float: | |
| mol = _mol_from_smiles(smiles) | |
| return float(Descriptors.MolWt(mol)) | |
| def logp(smiles: str) -> float: | |
| mol = _mol_from_smiles(smiles) | |
| return float(Descriptors.MolLogP(mol)) | |
| def tpsa(smiles: str) -> float: | |
| mol = _mol_from_smiles(smiles) | |
| return float(Descriptors.TPSA(mol)) | |
| def mol_image(smiles: str): | |
| mol = _mol_from_smiles(smiles) | |
| return Draw.MolToImage(mol) | |
| def name_to_smiles(name: str) -> str: | |
| """Convert chemical name to SMILES using Chemical Identifier Resolver (CIR)""" | |
| try: | |
| smiles = cirpy.resolve(name, 'smiles') | |
| if smiles is None: | |
| raise gr.Error(f"Could not find SMILES for chemical name: {name}") | |
| return smiles | |
| except Exception as e: | |
| raise gr.Error(f"Error converting name to SMILES: {str(e)}") | |
| def name_to_3d_molecule(name: str) -> tuple: | |
| """Convert chemical name to 3D molecule SDF and 2D visualization""" | |
| try: | |
| # Convert name to SMILES | |
| smiles = cirpy.resolve(name, 'smiles') | |
| if smiles is None: | |
| raise gr.Error(f"Could not find SMILES for chemical name: {name}") | |
| # Create molecule from SMILES | |
| mol = Chem.MolFromSmiles(smiles) | |
| if mol is None: | |
| raise gr.Error(f"Could not create molecule from SMILES: {smiles}") | |
| # Add hydrogens for better 3D structure | |
| mol = Chem.AddHs(mol) | |
| # Generate 3D coordinates | |
| success = AllChem.EmbedMolecule(mol, AllChem.ETKDG()) | |
| if success == -1: | |
| raise gr.Error(f"Could not generate 3D coordinates for: {name}") | |
| # Optimize geometry | |
| AllChem.MMFFOptimizeMolecule(mol) | |
| # Generate SDF content | |
| sdf_content = Chem.MolToMolBlock(mol) | |
| # Generate 2D image for preview | |
| mol_2d = Chem.MolFromSmiles(smiles) # Get 2D version | |
| img = Draw.MolToImage(mol_2d, size=(400, 400)) | |
| # Convert image to base64 | |
| import io | |
| import base64 | |
| import json | |
| buffered = io.BytesIO() | |
| img.save(buffered, format="PNG") | |
| img_str = base64.b64encode(buffered.getvalue()).decode() | |
| # Get SMILES string | |
| canonical_smiles = Chem.MolToSmiles(mol_2d) | |
| # Prepare SDF content for JavaScript (escape backticks) | |
| sdf_content_escaped = sdf_content.replace('`', '\\`') | |
| # Create data URI for the 3D viewer HTML | |
| viewer_html = f""" | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <script src="https://3dmol.csb.pitt.edu/build/3Dmol-min.js"></script> | |
| <style> | |
| body {{ margin: 0; padding: 0; background: white; }} | |
| #viewer {{ width: 100%; height: 100vh; }} | |
| </style> | |
| </head> | |
| <body> | |
| <div id="viewer"></div> | |
| <script> | |
| $(function() {{ | |
| let viewer = $3Dmol.createViewer("viewer", {{backgroundColor: 'white'}}); | |
| let sdfData = `{sdf_content_escaped}`; | |
| viewer.addModel(sdfData, "sdf"); | |
| viewer.setStyle({{}}, {{stick: {{radius: 0.15, colorscheme: 'Jmol'}}, sphere: {{radius: 0.4, colorscheme: 'Jmol'}}}}); | |
| viewer.addLabels({{elem: true}}, {{fontSize: 12, fontColor: 'black', backgroundColor: 'white', backgroundOpacity: 0.7}}); | |
| viewer.zoomTo(); | |
| viewer.render(); | |
| }}); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| viewer_data_uri = "data:text/html;base64," + base64.b64encode(viewer_html.encode()).decode() | |
| # Create HTML with 2D and 3D viewers side by side | |
| html_content = f""" | |
| <div style="padding: 20px;"> | |
| <h3 style="text-align: center;">{name}</h3> | |
| <p style="text-align: center;"><strong>SMILES:</strong> {canonical_smiles}</p> | |
| <div style="display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; margin-top: 20px;"> | |
| <div style="text-align: center;"> | |
| <h4>2D Structure</h4> | |
| <img src="data:image/png;base64,{img_str}" style="max-width: 400px; border: 1px solid #ddd; border-radius: 8px; padding: 10px; background: white;"> | |
| </div> | |
| <div style="text-align: center;"> | |
| <h4>3D Interactive Model</h4> | |
| <iframe src="{viewer_data_uri}" style="width: 400px; height: 400px; border: 1px solid #ddd; border-radius: 8px;"></iframe> | |
| <p style="margin-top: 10px; font-size: 12px; color: #666;">Drag to rotate, scroll to zoom</p> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| return html_content, sdf_content | |
| except Exception as e: | |
| raise gr.Error(f"Error creating molecule: {str(e)}") | |
| smiles_interface = gr.Interface( | |
| fn=smiles_to_canonical, | |
| inputs=gr.Textbox(label="SMILES"), | |
| outputs=gr.Textbox(label="Canonical SMILES"), | |
| api_name="smiles_to_mol", | |
| description="Convert an input SMILES string to its canonical form.", | |
| ) | |
| name_interface = gr.Interface( | |
| fn=name_to_smiles, | |
| inputs=gr.Textbox(label="Chemical Name", placeholder="e.g., aspirin, caffeine, benzene"), | |
| outputs=gr.Textbox(label="SMILES"), | |
| api_name="name_to_smiles", | |
| description="Convert a chemical name to SMILES notation.", | |
| examples=[["aspirin"], ["caffeine"], ["benzene"], ["ethanol"]], | |
| ) | |
| mw_interface = gr.Interface( | |
| fn=molecular_weight, | |
| inputs=gr.Textbox(label="SMILES"), | |
| outputs=gr.Number(label="Molecular Weight (g/mol)"), | |
| api_name="molecular_weight", | |
| description="Compute the molecular weight from a SMILES string.", | |
| ) | |
| logp_interface = gr.Interface( | |
| fn=logp, | |
| inputs=gr.Textbox(label="SMILES"), | |
| outputs=gr.Number(label="logP"), | |
| api_name="logp", | |
| description="Calculate the octanol/water partition coefficient (logP).", | |
| ) | |
| tpsa_interface = gr.Interface( | |
| fn=tpsa, | |
| inputs=gr.Textbox(label="SMILES"), | |
| outputs=gr.Number(label="TPSA"), | |
| api_name="tpsa", | |
| description="Calculate the topological polar surface area (TPSA).", | |
| ) | |
| molecule_3d_interface = gr.Interface( | |
| fn=name_to_3d_molecule, | |
| inputs=gr.Textbox(label="Chemical Name", placeholder="e.g., benzene, aspirin, caffeine, glucose"), | |
| outputs=[ | |
| gr.HTML(label="2D and 3D Molecular Viewer"), | |
| gr.Textbox(label="3D SDF Content (optional - for external viewers)", lines=10, max_lines=20, visible=False) | |
| ], | |
| api_name="name_to_molecule", | |
| description="View 2D structure and interactive 3D molecule. Drag to rotate the 3D model, scroll to zoom.", | |
| cache_examples=False, | |
| ) | |
| demo = gr.TabbedInterface( | |
| [name_interface, molecule_3d_interface, smiles_interface, mw_interface, logp_interface, tpsa_interface], | |
| [ | |
| "Name to SMILES", | |
| "Molecule Viewer", | |
| "SMILES to Canonical", | |
| "Molecular Weight", | |
| "LogP", | |
| "TPSA", | |
| ], | |
| title="RDKit API", | |
| css=".gradio-container {max-width: 800px; margin: auto;}", | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue().launch(server_name="0.0.0.0", server_port=7860) |