tttjjj commited on
Commit
b431be7
·
1 Parent(s): afc5741

Fix PDF lag: isolate questions editor in an st.fragment

Browse files

Editing the form previously triggered a full-app rerun, re-sending the ~3MB
base64 PDF each time (very laggy). The questions editor + actions now run inside
an st.fragment, so edits there rerun only that fragment — the PDF panel (outside
the fragment) is not re-rendered. Falls back to a no-op decorator on Streamlit
versions without st.fragment.

Files changed (1) hide show
  1. app.py +69 -54
app.py CHANGED
@@ -37,6 +37,13 @@ st.set_page_config(page_title="TDB Intake", page_icon="🔬", layout="wide")
37
 
38
  SOURCE_REPO = "trialdesignbench/source"
39
 
 
 
 
 
 
 
 
40
  _STATUS_EMOJI = {"pending": "🟡", "reviewed": "🟢", "needs_fix": "🔴"}
41
 
42
 
@@ -350,60 +357,10 @@ def render_pdf_panel() -> None:
350
 
351
  # ------------- form ------------------------------------------------------
352
 
353
- def render_form() -> None:
354
- # top fields
355
- c1, c2 = st.columns(2)
356
- with c1:
357
- st.text_input("trial_id", key="trial_id", placeholder="e.g., NCT01234567")
358
- with c2:
359
- st.text_input("username", key="username", placeholder="e.g., jdoe")
360
-
361
- st.button(
362
- "Find versions",
363
- on_click=_find_versions,
364
- help="List all previously submitted versions for this trial_id + username.",
365
- )
366
-
367
- versions = st.session_state.versions
368
- if versions:
369
- options = [v["submissionId"] for v in versions]
370
-
371
- def _ver_label(sid: str) -> str:
372
- v = next((x for x in versions if x["submissionId"] == sid), None)
373
- if not v:
374
- return sid
375
- emoji = _STATUS_EMOJI.get(v.get("status", "pending"), "⚪")
376
- rc = v.get("review_count", 0)
377
- rtag = f"{rc} review(s)" if rc else "no reviews"
378
- return (
379
- f"{v['submittedAt']} · {v['num_questions']} Q · "
380
- f"{emoji} {v.get('status','pending')} ({rtag})"
381
- )
382
-
383
- vc1, vc2 = st.columns([3, 1])
384
- with vc1:
385
- st.selectbox(
386
- "Select a version to load",
387
- options=options,
388
- format_func=_ver_label,
389
- key="version_select",
390
- )
391
- with vc2:
392
- st.write("")
393
- st.write("")
394
- st.button("Load selected version", on_click=_load_selected, use_container_width=True)
395
-
396
- # Overall reviewer feedback across all versions (per-question feedback is
397
- # shown inside each question block below).
398
- overall_history = [r for r in st.session_state.pair_reviews if not r.get("question_id")]
399
- if overall_history:
400
- with st.container(border=True):
401
- st.markdown(f"**Overall reviewer feedback — all versions ({len(overall_history)})**")
402
- _render_review_lines(overall_history)
403
-
404
- st.divider()
405
-
406
- # questions list
407
  st.subheader("Questions")
408
  if not st.session_state.questions:
409
  st.caption('No questions yet. Click "Add question" below to begin.')
@@ -517,6 +474,64 @@ def render_form() -> None:
517
  )
518
 
519
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  # ------------- layout ----------------------------------------------------
521
 
522
  st.title("Trial Design Benchmark")
 
37
 
38
  SOURCE_REPO = "trialdesignbench/source"
39
 
40
+ # st.fragment (Streamlit >=1.37) isolates reruns; fall back to a no-op on older
41
+ # versions so the app still runs (just without the perf isolation).
42
+ fragment = getattr(st, "fragment", None) or getattr(st, "experimental_fragment", None)
43
+ if fragment is None:
44
+ def fragment(func): # type: ignore
45
+ return func
46
+
47
  _STATUS_EMOJI = {"pending": "🟡", "reviewed": "🟢", "needs_fix": "🔴"}
48
 
49
 
 
357
 
358
  # ------------- form ------------------------------------------------------
359
 
360
+ @fragment
361
+ def _questions_fragment() -> None:
362
+ """The questions editor + actions. Runs as a fragment so frequent edits
363
+ here don't trigger a full-app rerun (which would re-send the PDF)."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  st.subheader("Questions")
365
  if not st.session_state.questions:
366
  st.caption('No questions yet. Click "Add question" below to begin.')
 
474
  )
475
 
476
 
477
+ def render_form() -> None:
478
+ # top fields
479
+ c1, c2 = st.columns(2)
480
+ with c1:
481
+ st.text_input("trial_id", key="trial_id", placeholder="e.g., NCT01234567")
482
+ with c2:
483
+ st.text_input("username", key="username", placeholder="e.g., jdoe")
484
+
485
+ st.button(
486
+ "Find versions",
487
+ on_click=_find_versions,
488
+ help="List all previously submitted versions for this trial_id + username.",
489
+ )
490
+
491
+ versions = st.session_state.versions
492
+ if versions:
493
+ options = [v["submissionId"] for v in versions]
494
+
495
+ def _ver_label(sid: str) -> str:
496
+ v = next((x for x in versions if x["submissionId"] == sid), None)
497
+ if not v:
498
+ return sid
499
+ emoji = _STATUS_EMOJI.get(v.get("status", "pending"), "⚪")
500
+ rc = v.get("review_count", 0)
501
+ rtag = f"{rc} review(s)" if rc else "no reviews"
502
+ return (
503
+ f"{v['submittedAt']} · {v['num_questions']} Q · "
504
+ f"{emoji} {v.get('status','pending')} ({rtag})"
505
+ )
506
+
507
+ vc1, vc2 = st.columns([3, 1])
508
+ with vc1:
509
+ st.selectbox(
510
+ "Select a version to load",
511
+ options=options,
512
+ format_func=_ver_label,
513
+ key="version_select",
514
+ )
515
+ with vc2:
516
+ st.write("")
517
+ st.write("")
518
+ st.button("Load selected version", on_click=_load_selected, use_container_width=True)
519
+
520
+ # Overall reviewer feedback across all versions (per-question feedback is
521
+ # shown inside each question block below).
522
+ overall_history = [r for r in st.session_state.pair_reviews if not r.get("question_id")]
523
+ if overall_history:
524
+ with st.container(border=True):
525
+ st.markdown(f"**Overall reviewer feedback — all versions ({len(overall_history)})**")
526
+ _render_review_lines(overall_history)
527
+
528
+ st.divider()
529
+
530
+ # Questions + actions live in a fragment so editing them reruns only this
531
+ # part — NOT the heavy PDF panel on the left (avoids re-sending the PDF).
532
+ _questions_fragment()
533
+
534
+
535
  # ------------- layout ----------------------------------------------------
536
 
537
  st.title("Trial Design Benchmark")