Spaces:
Runtime error
Runtime error
Vaishnav14220 commited on
Commit ·
994cda0
1
Parent(s): 6df10d2
Add SVG rendering in reaction detail tab
Browse files
app.py
CHANGED
|
@@ -326,27 +326,26 @@ def _build_dataset_plot(detail: ReactionDetail) -> go.Figure | None:
|
|
| 326 |
return fig
|
| 327 |
|
| 328 |
|
| 329 |
-
def
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
return gr.update(value=""), "⚠️ Reaction input must include '>>' separating reactants and products."
|
| 335 |
|
| 336 |
try:
|
| 337 |
-
reaction = rdChemReactions.ReactionFromSmarts(
|
| 338 |
-
except Exception
|
| 339 |
-
return
|
| 340 |
|
| 341 |
if reaction is None or (reaction.GetNumReactantTemplates() == 0 and reaction.GetNumProductTemplates() == 0):
|
| 342 |
-
return
|
| 343 |
|
| 344 |
try:
|
| 345 |
svg = Draw.ReactionToImage(reaction, subImgSize=(220, 220), useSVG=True)
|
| 346 |
-
except Exception
|
| 347 |
-
return
|
| 348 |
|
| 349 |
-
if isinstance(svg, tuple):
|
| 350 |
svg = svg[0]
|
| 351 |
if hasattr(svg, "data"):
|
| 352 |
svg = svg.data
|
|
@@ -354,33 +353,59 @@ def render_reaction_svg(reaction_text: str):
|
|
| 354 |
svg = svg.decode("utf-8", errors="ignore")
|
| 355 |
|
| 356 |
if not isinstance(svg, str) or "<svg" not in svg:
|
| 357 |
-
|
| 358 |
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
return svg, status
|
| 364 |
|
| 365 |
|
| 366 |
def fetch_detail(selected_url: str, manual_url: str):
|
| 367 |
detail_url = (manual_url or "").strip() or (selected_url or "").strip()
|
| 368 |
if not detail_url:
|
| 369 |
-
return "ℹ️ Select a reaction above or paste a detail URL.", [], None
|
| 370 |
|
| 371 |
try:
|
| 372 |
detail = client.fetch_reaction_detail(detail_url)
|
| 373 |
except Exception as exc: # pragma: no cover - network/parsing issues
|
| 374 |
-
return f"🚨 Could not load detail: {exc}", [], None
|
| 375 |
|
| 376 |
markdown = _format_detail_markdown(detail, detail_url)
|
| 377 |
table = _datasets_to_table(detail)
|
| 378 |
if not table:
|
| 379 |
markdown += "\n\n_No kinetics datasets were returned for this reaction._"
|
| 380 |
-
return markdown, table, None
|
| 381 |
|
| 382 |
plot_fig = _build_dataset_plot(detail)
|
| 383 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 384 |
|
| 385 |
|
| 386 |
def _parse_points(text: str) -> Tuple[List[float], List[float], List[str]]:
|
|
@@ -542,6 +567,7 @@ def build_interface() -> gr.Blocks:
|
|
| 542 |
wrap=True,
|
| 543 |
)
|
| 544 |
reaction_plot = gr.Plot()
|
|
|
|
| 545 |
|
| 546 |
search_button.click(
|
| 547 |
fn=perform_search,
|
|
@@ -552,7 +578,7 @@ def build_interface() -> gr.Blocks:
|
|
| 552 |
detail_button.click(
|
| 553 |
fn=fetch_detail,
|
| 554 |
inputs=[selection, manual_url],
|
| 555 |
-
outputs=[detail_markdown, dataset_table, reaction_plot],
|
| 556 |
)
|
| 557 |
|
| 558 |
with gr.Tab("Reaction SVG"):
|
|
|
|
| 326 |
return fig
|
| 327 |
|
| 328 |
|
| 329 |
+
def _render_smiles_to_svg(smiles_text: str) -> str | None:
|
| 330 |
+
"""Helper to render a SMILES/SMARTS reaction string to SVG."""
|
| 331 |
+
smiles_text = (smiles_text or "").strip()
|
| 332 |
+
if not smiles_text or ">>" not in smiles_text:
|
| 333 |
+
return None
|
|
|
|
| 334 |
|
| 335 |
try:
|
| 336 |
+
reaction = rdChemReactions.ReactionFromSmarts(smiles_text, useSmiles=True)
|
| 337 |
+
except Exception:
|
| 338 |
+
return None
|
| 339 |
|
| 340 |
if reaction is None or (reaction.GetNumReactantTemplates() == 0 and reaction.GetNumProductTemplates() == 0):
|
| 341 |
+
return None
|
| 342 |
|
| 343 |
try:
|
| 344 |
svg = Draw.ReactionToImage(reaction, subImgSize=(220, 220), useSVG=True)
|
| 345 |
+
except Exception:
|
| 346 |
+
return None
|
| 347 |
|
| 348 |
+
if isinstance(svg, tuple):
|
| 349 |
svg = svg[0]
|
| 350 |
if hasattr(svg, "data"):
|
| 351 |
svg = svg.data
|
|
|
|
| 353 |
svg = svg.decode("utf-8", errors="ignore")
|
| 354 |
|
| 355 |
if not isinstance(svg, str) or "<svg" not in svg:
|
| 356 |
+
return None
|
| 357 |
|
| 358 |
+
return svg
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
def render_reaction_svg(reaction_text: str):
|
| 362 |
+
reaction_text = (reaction_text or "").strip()
|
| 363 |
+
if not reaction_text:
|
| 364 |
+
return "", "⚠️ Enter a reaction SMILES/SMARTS string (e.g. CH4.O>>CO2)."
|
| 365 |
+
if ">>" not in reaction_text:
|
| 366 |
+
return "", "⚠️ Reaction input must include '>>' separating reactants and products."
|
| 367 |
+
|
| 368 |
+
svg = _render_smiles_to_svg(reaction_text)
|
| 369 |
+
if not svg:
|
| 370 |
+
return "", "🚨 Could not parse or render the reaction."
|
| 371 |
+
|
| 372 |
+
status = "✅ Reaction rendered successfully."
|
| 373 |
return svg, status
|
| 374 |
|
| 375 |
|
| 376 |
def fetch_detail(selected_url: str, manual_url: str):
|
| 377 |
detail_url = (manual_url or "").strip() or (selected_url or "").strip()
|
| 378 |
if not detail_url:
|
| 379 |
+
return "ℹ️ Select a reaction above or paste a detail URL.", [], None, ""
|
| 380 |
|
| 381 |
try:
|
| 382 |
detail = client.fetch_reaction_detail(detail_url)
|
| 383 |
except Exception as exc: # pragma: no cover - network/parsing issues
|
| 384 |
+
return f"🚨 Could not load detail: {exc}", [], None, ""
|
| 385 |
|
| 386 |
markdown = _format_detail_markdown(detail, detail_url)
|
| 387 |
table = _datasets_to_table(detail)
|
| 388 |
if not table:
|
| 389 |
markdown += "\n\n_No kinetics datasets were returned for this reaction._"
|
| 390 |
+
return markdown, table, None, ""
|
| 391 |
|
| 392 |
plot_fig = _build_dataset_plot(detail)
|
| 393 |
+
|
| 394 |
+
# Try to render the reaction title as SVG
|
| 395 |
+
reaction_svg = ""
|
| 396 |
+
if detail.title:
|
| 397 |
+
# Extract SMILES-like patterns from title (heuristic)
|
| 398 |
+
title = detail.title.strip()
|
| 399 |
+
# Look for patterns like "A + B → C" or "A >> B"
|
| 400 |
+
if " → " in title:
|
| 401 |
+
parts = title.split(" → ")
|
| 402 |
+
if len(parts) == 2:
|
| 403 |
+
smiles_attempt = parts[0].replace(" + ", ".") + ">>" + parts[1].replace(" + ", ".")
|
| 404 |
+
svg = _render_smiles_to_svg(smiles_attempt)
|
| 405 |
+
if svg:
|
| 406 |
+
reaction_svg = svg
|
| 407 |
+
|
| 408 |
+
return markdown, table, plot_fig, reaction_svg
|
| 409 |
|
| 410 |
|
| 411 |
def _parse_points(text: str) -> Tuple[List[float], List[float], List[str]]:
|
|
|
|
| 567 |
wrap=True,
|
| 568 |
)
|
| 569 |
reaction_plot = gr.Plot()
|
| 570 |
+
reaction_svg = gr.HTML()
|
| 571 |
|
| 572 |
search_button.click(
|
| 573 |
fn=perform_search,
|
|
|
|
| 578 |
detail_button.click(
|
| 579 |
fn=fetch_detail,
|
| 580 |
inputs=[selection, manual_url],
|
| 581 |
+
outputs=[detail_markdown, dataset_table, reaction_plot, reaction_svg],
|
| 582 |
)
|
| 583 |
|
| 584 |
with gr.Tab("Reaction SVG"):
|