CodeChamp95 commited on
Commit
f2d554c
Β·
verified Β·
1 Parent(s): d89add7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +164 -31
app.py CHANGED
@@ -338,20 +338,73 @@ body, .gradio-container {
338
  border-bottom-color: var(--accent) !important;
339
  }
340
 
341
- /* ── Dataframe ── */
342
- .dataframe-wrap table {
343
- font-family: var(--mono) !important;
344
- font-size: .78rem !important;
 
 
345
  }
346
- .dataframe-wrap th {
347
- background: #1a1f2e !important;
348
- color: var(--accent) !important;
349
- font-family: var(--display) !important;
350
- font-size: .7rem !important;
351
- letter-spacing: .08em !important;
352
- text-transform: uppercase !important;
353
  }
354
- .dataframe-wrap td { border-color: var(--border) !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
  /* ── File upload ── */
357
  .file-drop {
@@ -477,14 +530,20 @@ def build_app():
477
 
478
  # ── Tab A: Review Table ──────────────────────────────────────
479
  with gr.Tab("πŸ“‹ Review Table"):
480
- review_table = gr.Dataframe(
481
- value=EMPTY_TABLE,
482
- headers=TABLE_COLS,
483
- datatype=["number", "str", "str", "number", "number", "str", "str", "str"],
484
- interactive=True,
485
- wrap=True,
486
- elem_classes=["dataframe-wrap"],
487
- label="Topic review β€” edit Approve / Rename To / Reasoning, then click Submit",
 
 
 
 
 
 
488
  )
489
  submit_review_btn = gr.Button(
490
  "Submit Review βœ“",
@@ -533,7 +592,7 @@ def build_app():
533
  def on_send(message, history, csv_path, charts_state, downloads_state):
534
  """Stream agent responses and update all UI elements."""
535
  if not message.strip():
536
- yield history, "", phase_html("idle"), [], None, charts_state, downloads_state
537
  return
538
 
539
  # Accumulate streamed updates
@@ -548,38 +607,109 @@ def build_app():
548
  (last_history, last_phase,
549
  last_charts, last_downloads, new_topics) = result
550
 
 
551
  yield (
552
  last_history,
553
  "", # clear input
554
  phase_html(last_phase),
555
- _rows_to_df(new_topics) if new_topics else gr.update(),
 
556
  gr.update(), # downloads updated below
557
  last_charts,
558
  last_downloads,
559
  )
560
 
561
  # Final yield with downloads
 
562
  yield (
563
  last_history,
564
  "",
565
  phase_html(last_phase),
566
- _rows_to_df(new_topics) if new_topics else gr.update(),
 
567
  last_downloads if last_downloads else None,
568
  last_charts,
569
  last_downloads,
570
  )
571
 
572
- def _rows_to_df(rows):
 
573
  if not rows:
574
- return EMPTY_TABLE
575
- df = pd.DataFrame(rows, columns=TABLE_COLS)
576
- return df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
577
 
578
  send_outputs = [
579
  chatbot,
580
  user_input,
581
  phase_display,
582
- review_table,
 
583
  download_files,
584
  state_charts,
585
  state_downloads,
@@ -597,9 +727,12 @@ def build_app():
597
  )
598
 
599
  # ── Submit review ─────────────────────────────────────────────────
600
- def on_submit_review(table_df, history, charts_state, downloads_state):
601
  """Pass edited table back to the agent as a review submission."""
602
- rows = table_df.values.tolist() if isinstance(table_df, pd.DataFrame) else table_df or []
 
 
 
603
 
604
  last_history = history
605
  last_phase = "review"
@@ -628,7 +761,7 @@ def build_app():
628
 
629
  submit_review_btn.click(
630
  fn=on_submit_review,
631
- inputs=[review_table, chatbot, state_charts, state_downloads],
632
  outputs=[chatbot, phase_display, download_files, state_charts, state_downloads],
633
  )
634
 
 
338
  border-bottom-color: var(--accent) !important;
339
  }
340
 
341
+ /* ── Review Table (custom HTML β€” replaces gr.Dataframe) ── */
342
+ #review-table-wrap {
343
+ overflow-x: auto;
344
+ width: 100%;
345
+ border-radius: var(--radius);
346
+ border: 1px solid var(--border);
347
  }
348
+ .rt-table {
349
+ width: 100%;
350
+ min-width: 960px;
351
+ border-collapse: collapse;
352
+ font-family: var(--mono);
353
+ font-size: .78rem;
 
354
  }
355
+ .rt-table th {
356
+ background: #1a1f2e;
357
+ color: var(--accent);
358
+ font-family: var(--display);
359
+ font-size: .68rem;
360
+ letter-spacing: .1em;
361
+ text-transform: uppercase;
362
+ padding: .65rem .8rem;
363
+ text-align: left;
364
+ border-bottom: 2px solid var(--border);
365
+ white-space: nowrap;
366
+ position: sticky;
367
+ top: 0;
368
+ z-index: 2;
369
+ }
370
+ .rt-table td {
371
+ padding: .55rem .8rem;
372
+ border-bottom: 1px solid var(--border);
373
+ vertical-align: top;
374
+ color: var(--text);
375
+ }
376
+ .rt-table tr:nth-child(even) td { background: rgba(255,255,255,.02); }
377
+ .rt-table tr:hover td { background: rgba(79,142,247,.06); transition: background .15s; }
378
+ .rt-col-num { width: 45px; text-align: center; color: var(--muted); }
379
+ .rt-col-label { min-width: 160px; font-weight: 600; }
380
+ .rt-col-evid { min-width: 240px; line-height: 1.6; color: var(--muted); }
381
+ .rt-col-sent { width: 80px; text-align: center; }
382
+ .rt-col-paper { width: 70px; text-align: center; }
383
+ .rt-col-appr { width: 72px; text-align: center; }
384
+ .rt-col-rename { min-width: 140px; }
385
+ .rt-col-reason { min-width: 200px; }
386
+ .rt-check {
387
+ width: 18px; height: 18px; cursor: pointer;
388
+ accent-color: var(--success);
389
+ }
390
+ .rt-input, .rt-textarea {
391
+ width: 100%; box-sizing: border-box;
392
+ padding: .32rem .55rem;
393
+ background: #0d0f14;
394
+ border: 1px solid var(--border);
395
+ color: var(--text);
396
+ border-radius: 5px;
397
+ font-family: var(--sans);
398
+ font-size: .78rem;
399
+ transition: border-color .15s;
400
+ }
401
+ .rt-input:focus, .rt-textarea:focus {
402
+ border-color: var(--accent);
403
+ outline: none;
404
+ box-shadow: 0 0 0 2px rgba(79,142,247,.18);
405
+ }
406
+ .rt-textarea { resize: vertical; min-height: 46px; }
407
+ .rt-empty { color: var(--muted); padding: 1.5rem; font-style: italic; text-align: center; }
408
 
409
  /* ── File upload ── */
410
  .file-drop {
 
530
 
531
  # ── Tab A: Review Table ──────────────────────────────────────
532
  with gr.Tab("πŸ“‹ Review Table"):
533
+ gr.HTML(
534
+ "<p style='color:var(--muted,#7a8299);font-size:.8rem;margin:0 0 .6rem'>"
535
+ "Check βœ“ Approve, optionally fill Rename To / Reasoning, then click Submit.</p>"
536
+ )
537
+ review_table_html = gr.HTML(
538
+ value="<p class='rt-empty' style='color:#7a8299;padding:1rem;font-style:italic'>"
539
+ "No topics yet β€” run topic modelling first.</p>",
540
+ label="",
541
+ )
542
+ # Hidden textbox holds JSON-serialised table rows, kept in sync by JS
543
+ review_json_state = gr.Textbox(
544
+ value="[]",
545
+ visible=False,
546
+ elem_id="review-json-state",
547
  )
548
  submit_review_btn = gr.Button(
549
  "Submit Review βœ“",
 
592
  def on_send(message, history, csv_path, charts_state, downloads_state):
593
  """Stream agent responses and update all UI elements."""
594
  if not message.strip():
595
+ yield history, "", phase_html("idle"), gr.update(), gr.update(), None, charts_state, downloads_state
596
  return
597
 
598
  # Accumulate streamed updates
 
607
  (last_history, last_phase,
608
  last_charts, last_downloads, new_topics) = result
609
 
610
+ _html, _json = _rows_to_html(new_topics) if new_topics else (gr.update(), gr.update())
611
  yield (
612
  last_history,
613
  "", # clear input
614
  phase_html(last_phase),
615
+ _html,
616
+ _json,
617
  gr.update(), # downloads updated below
618
  last_charts,
619
  last_downloads,
620
  )
621
 
622
  # Final yield with downloads
623
+ _html, _json = _rows_to_html(new_topics) if new_topics else (gr.update(), gr.update())
624
  yield (
625
  last_history,
626
  "",
627
  phase_html(last_phase),
628
+ _html,
629
+ _json,
630
  last_downloads if last_downloads else None,
631
  last_charts,
632
  last_downloads,
633
  )
634
 
635
+ def _rows_to_html(rows):
636
+ """Convert agent row data β†’ (html_str, json_str) for the custom table."""
637
  if not rows:
638
+ html = "<p class='rt-empty'>No topics yet β€” run topic modelling first.</p>"
639
+ return html, "[]"
640
+
641
+ padded = []
642
+ for r in rows:
643
+ row = list(r) + [""] * max(0, 8 - len(r))
644
+ padded.append(row[:8])
645
+
646
+ json_data = json.dumps(padded)
647
+
648
+ rows_html = ""
649
+ for i, row in enumerate(padded):
650
+ num, label, evidence, sentences, papers, approve, rename, reasoning = row
651
+ checked = "checked" if str(approve).lower() in ("true", "1", "yes", "βœ“") else ""
652
+ # Escape for HTML attribute safety
653
+ rename_esc = str(rename).replace('"', "&quot;")
654
+ reason_esc = str(reasoning).replace("<", "&lt;").replace(">", "&gt;")
655
+ evidence_esc = str(evidence).replace("<", "&lt;").replace(">", "&gt;")
656
+ label_esc = str(label).replace("<", "&lt;").replace(">", "&gt;")
657
+ rows_html += f"""
658
+ <tr data-row="{i}">
659
+ <td class="rt-col-num">{num}</td>
660
+ <td class="rt-col-label">{label_esc}</td>
661
+ <td class="rt-col-evid">{evidence_esc}</td>
662
+ <td class="rt-col-sent">{sentences}</td>
663
+ <td class="rt-col-paper">{papers}</td>
664
+ <td class="rt-col-appr">
665
+ <input type="checkbox" class="rt-check" {checked}
666
+ onchange="rtUpdate({i},5,this.checked)">
667
+ </td>
668
+ <td class="rt-col-rename">
669
+ <input type="text" class="rt-input" value="{rename_esc}"
670
+ oninput="rtUpdate({i},6,this.value)" placeholder="new label…">
671
+ </td>
672
+ <td class="rt-col-reason">
673
+ <textarea class="rt-textarea" rows="2"
674
+ oninput="rtUpdate({i},7,this.value)" placeholder="notes…">{reason_esc}</textarea>
675
+ </td>
676
+ </tr>"""
677
+
678
+ html = f"""<div id="review-table-wrap">
679
+ <script>
680
+ (function(){{
681
+ window._rtData = {json_data};
682
+ window.rtUpdate = function(idx, col, val) {{
683
+ window._rtData[idx][col] = val;
684
+ var el = document.querySelector('#review-json-state textarea');
685
+ if (!el) return;
686
+ var setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype,'value').set;
687
+ setter.call(el, JSON.stringify(window._rtData));
688
+ el.dispatchEvent(new Event('input',{{bubbles:true}}));
689
+ }};
690
+ }})();
691
+ </script>
692
+ <table class="rt-table">
693
+ <thead><tr>
694
+ <th class="rt-col-num">#</th>
695
+ <th class="rt-col-label">Topic Label</th>
696
+ <th class="rt-col-evid">Top Evidence</th>
697
+ <th class="rt-col-sent">Sentences</th>
698
+ <th class="rt-col-paper">Papers</th>
699
+ <th class="rt-col-appr">Approve</th>
700
+ <th class="rt-col-rename">Rename To</th>
701
+ <th class="rt-col-reason">Reasoning</th>
702
+ </tr></thead>
703
+ <tbody>{rows_html}</tbody>
704
+ </table></div>"""
705
+ return html, json_data
706
 
707
  send_outputs = [
708
  chatbot,
709
  user_input,
710
  phase_display,
711
+ review_table_html,
712
+ review_json_state,
713
  download_files,
714
  state_charts,
715
  state_downloads,
 
727
  )
728
 
729
  # ── Submit review ─────────────────────────────────────────────────
730
+ def on_submit_review(json_str, history, charts_state, downloads_state):
731
  """Pass edited table back to the agent as a review submission."""
732
+ try:
733
+ rows = json.loads(json_str) if json_str and json_str.strip() not in ("", "[]") else []
734
+ except Exception:
735
+ rows = []
736
 
737
  last_history = history
738
  last_phase = "review"
 
761
 
762
  submit_review_btn.click(
763
  fn=on_submit_review,
764
+ inputs=[review_json_state, chatbot, state_charts, state_downloads],
765
  outputs=[chatbot, phase_display, download_files, state_charts, state_downloads],
766
  )
767