Vaishnav14220 commited on
Commit
994cda0
·
1 Parent(s): 6df10d2

Add SVG rendering in reaction detail tab

Browse files
Files changed (1) hide show
  1. app.py +49 -23
app.py CHANGED
@@ -326,27 +326,26 @@ def _build_dataset_plot(detail: ReactionDetail) -> go.Figure | None:
326
  return fig
327
 
328
 
329
- def render_reaction_svg(reaction_text: str):
330
- reaction_text = (reaction_text or "").strip()
331
- if not reaction_text:
332
- return gr.update(value=""), "⚠️ Enter a reaction SMILES/SMARTS string (e.g. CH4.O>>CO2)."
333
- if ">>" not in reaction_text:
334
- return gr.update(value=""), "⚠️ Reaction input must include '>>' separating reactants and products."
335
 
336
  try:
337
- reaction = rdChemReactions.ReactionFromSmarts(reaction_text, useSmiles=True)
338
- except Exception as exc: # pragma: no cover - RDKit parsing issues
339
- return gr.update(value=""), f"🚨 Could not parse reaction: {exc}"
340
 
341
  if reaction is None or (reaction.GetNumReactantTemplates() == 0 and reaction.GetNumProductTemplates() == 0):
342
- return gr.update(value=""), "⚠️ RDKit returned an empty reaction."
343
 
344
  try:
345
  svg = Draw.ReactionToImage(reaction, subImgSize=(220, 220), useSVG=True)
346
- except Exception as exc: # pragma: no cover - RDKit drawing issues
347
- return gr.update(value=""), f"🚨 Failed to render reaction: {exc}"
348
 
349
- if isinstance(svg, tuple): # Some RDKit versions return (svg, legend)
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
- svg = f"<pre>{svg}</pre>"
358
 
359
- status = (
360
- f"Rendered reaction with {reaction.GetNumReactantTemplates()} reactant templates "
361
- f"and {reaction.GetNumProductTemplates()} product templates."
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
- return markdown, table, plot_fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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"):