Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -633,59 +633,130 @@ def build_app():
|
|
| 633 |
)
|
| 634 |
|
| 635 |
def _rows_to_html(rows):
|
| 636 |
-
"""Convert agent row data β (html_str, json_str)
|
| 637 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 638 |
html = "<p class='rt-empty'>No topics yet β run topic modelling first.</p>"
|
| 639 |
return html, "[]"
|
| 640 |
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 647 |
|
| 648 |
rows_html = ""
|
| 649 |
-
for i, row in enumerate(
|
| 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('"', """)
|
| 654 |
-
reason_esc = str(reasoning).replace("<", "<").replace(">", ">")
|
| 655 |
-
evidence_esc = str(evidence).replace("<", "<").replace(">", ">")
|
| 656 |
-
label_esc = str(label).replace("<", "<").replace(">", ">")
|
| 657 |
rows_html += f"""
|
| 658 |
<tr data-row="{i}">
|
| 659 |
-
<td class="rt-col-num">{num}</td>
|
| 660 |
-
<td class="rt-col-label">{
|
| 661 |
-
<td class="rt-col-evid">{
|
| 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"
|
| 670 |
-
|
|
|
|
|
|
|
| 671 |
</td>
|
| 672 |
<td class="rt-col-reason">
|
| 673 |
<textarea class="rt-textarea" rows="2"
|
| 674 |
-
oninput="rtUpdate({i},7,this.value)"
|
|
|
|
| 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 |
-
|
| 685 |
-
|
| 686 |
-
var
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
setter.call(el, JSON.stringify(window._rtData));
|
| 688 |
-
el.dispatchEvent(new Event('input',{{bubbles:true}}));
|
| 689 |
}};
|
| 690 |
}})();
|
| 691 |
</script>
|
|
|
|
| 633 |
)
|
| 634 |
|
| 635 |
def _rows_to_html(rows):
|
| 636 |
+
"""Convert agent row data β (html_str, json_str).
|
| 637 |
+
Accepts list-of-lists, list-of-dicts, list-of-tuples, or pd.DataFrame.
|
| 638 |
+
"""
|
| 639 |
+
if rows is None:
|
| 640 |
+
rows = []
|
| 641 |
+
|
| 642 |
+
# ββ Normalise to list-of-lists in TABLE_COLS order ββββββββββββββ
|
| 643 |
+
# Alias map: agent may use slightly different key names
|
| 644 |
+
_ALIASES = {
|
| 645 |
+
"top evidence (truncated)": "Top Evidence",
|
| 646 |
+
"top evidence": "Top Evidence",
|
| 647 |
+
"topic label": "Topic Label",
|
| 648 |
+
"rename to": "Rename To",
|
| 649 |
+
"approve": "Approve",
|
| 650 |
+
"reasoning": "Reasoning",
|
| 651 |
+
"sentences": "Sentences",
|
| 652 |
+
"papers": "Papers",
|
| 653 |
+
"#": "#",
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
if isinstance(rows, pd.DataFrame):
|
| 657 |
+
# Normalise column names via alias map
|
| 658 |
+
rows.columns = [_ALIASES.get(c.lower(), c) for c in rows.columns]
|
| 659 |
+
for col in TABLE_COLS:
|
| 660 |
+
if col not in rows.columns:
|
| 661 |
+
rows[col] = ""
|
| 662 |
+
padded = rows[TABLE_COLS].fillna("").values.tolist()
|
| 663 |
+
|
| 664 |
+
elif rows and isinstance(rows[0], dict):
|
| 665 |
+
# Remap keys through alias table then extract in TABLE_COLS order
|
| 666 |
+
normalised = []
|
| 667 |
+
for r in rows:
|
| 668 |
+
remapped = {_ALIASES.get(k.lower(), k): v for k, v in r.items()}
|
| 669 |
+
normalised.append([remapped.get(col, "") for col in TABLE_COLS])
|
| 670 |
+
padded = normalised
|
| 671 |
+
|
| 672 |
+
else:
|
| 673 |
+
# List of lists / tuples β pad / truncate to exactly 8 columns
|
| 674 |
+
padded = []
|
| 675 |
+
for r in rows:
|
| 676 |
+
row = list(r) + [""] * max(0, 8 - len(list(r)))
|
| 677 |
+
padded.append(row[:8])
|
| 678 |
+
|
| 679 |
+
if not padded:
|
| 680 |
html = "<p class='rt-empty'>No topics yet β run topic modelling first.</p>"
|
| 681 |
return html, "[]"
|
| 682 |
|
| 683 |
+
# ββ Serialise to JSON (drives JS state) βββββββββββββββββββββββββ
|
| 684 |
+
# Convert non-serialisable types (numpy bools, NaN, etc.) safely
|
| 685 |
+
def _safe(v):
|
| 686 |
+
if v is None or (isinstance(v, float) and v != v): # NaN check
|
| 687 |
+
return ""
|
| 688 |
+
try:
|
| 689 |
+
import numpy as np
|
| 690 |
+
if isinstance(v, (np.bool_, np.integer, np.floating)):
|
| 691 |
+
return v.item()
|
| 692 |
+
except ImportError:
|
| 693 |
+
pass
|
| 694 |
+
return v
|
| 695 |
+
|
| 696 |
+
safe_padded = [[_safe(c) for c in row] for row in padded]
|
| 697 |
+
json_data = json.dumps(safe_padded, ensure_ascii=False)
|
| 698 |
+
|
| 699 |
+
# ββ Build HTML rows ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 700 |
+
def _esc(v, attr=False):
|
| 701 |
+
s = str(v) if v is not None else ""
|
| 702 |
+
s = s.replace("&", "&").replace("<", "<").replace(">", ">")
|
| 703 |
+
if attr:
|
| 704 |
+
s = s.replace('"', """).replace("'", "'")
|
| 705 |
+
return s
|
| 706 |
|
| 707 |
rows_html = ""
|
| 708 |
+
for i, row in enumerate(safe_padded):
|
| 709 |
num, label, evidence, sentences, papers, approve, rename, reasoning = row
|
| 710 |
checked = "checked" if str(approve).lower() in ("true", "1", "yes", "β") else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
rows_html += f"""
|
| 712 |
<tr data-row="{i}">
|
| 713 |
+
<td class="rt-col-num">{_esc(num)}</td>
|
| 714 |
+
<td class="rt-col-label">{_esc(label)}</td>
|
| 715 |
+
<td class="rt-col-evid">{_esc(evidence)}</td>
|
| 716 |
+
<td class="rt-col-sent">{_esc(sentences)}</td>
|
| 717 |
+
<td class="rt-col-paper">{_esc(papers)}</td>
|
| 718 |
<td class="rt-col-appr">
|
| 719 |
<input type="checkbox" class="rt-check" {checked}
|
| 720 |
onchange="rtUpdate({i},5,this.checked)">
|
| 721 |
</td>
|
| 722 |
<td class="rt-col-rename">
|
| 723 |
+
<input type="text" class="rt-input"
|
| 724 |
+
value="{_esc(rename, attr=True)}"
|
| 725 |
+
oninput="rtUpdate({i},6,this.value)"
|
| 726 |
+
placeholder="new labelβ¦">
|
| 727 |
</td>
|
| 728 |
<td class="rt-col-reason">
|
| 729 |
<textarea class="rt-textarea" rows="2"
|
| 730 |
+
oninput="rtUpdate({i},7,this.value)"
|
| 731 |
+
placeholder="notesβ¦">{_esc(reasoning)}</textarea>
|
| 732 |
</td>
|
| 733 |
</tr>"""
|
| 734 |
|
| 735 |
+
# ββ Assemble full HTML block βββββββββββββββββββββββββββββββββββββ
|
| 736 |
+
# Use a unique suffix so re-renders don't collide with stale globals
|
| 737 |
+
uid = id(safe_padded)
|
| 738 |
html = f"""<div id="review-table-wrap">
|
| 739 |
<script>
|
| 740 |
(function(){{
|
| 741 |
+
// Initialise or refresh the shared row-data store
|
| 742 |
window._rtData = {json_data};
|
| 743 |
+
|
| 744 |
window.rtUpdate = function(idx, col, val) {{
|
| 745 |
+
if (!window._rtData || !window._rtData[idx]) return;
|
| 746 |
window._rtData[idx][col] = val;
|
| 747 |
+
// Push updated JSON into the hidden Gradio textbox
|
| 748 |
+
// Try both possible DOM locations Gradio uses across versions
|
| 749 |
+
var el = (
|
| 750 |
+
document.querySelector('#review-json-state textarea') ||
|
| 751 |
+
document.querySelector('[id*="review-json-state"] textarea') ||
|
| 752 |
+
document.querySelector('textarea[data-testid="textbox"]')
|
| 753 |
+
);
|
| 754 |
+
if (!el) {{ console.warn("rtUpdate: hidden textbox not found"); return; }}
|
| 755 |
+
var setter = Object.getOwnPropertyDescriptor(
|
| 756 |
+
window.HTMLTextAreaElement.prototype, 'value'
|
| 757 |
+
).set;
|
| 758 |
setter.call(el, JSON.stringify(window._rtData));
|
| 759 |
+
el.dispatchEvent(new Event('input', {{bubbles: true}}));
|
| 760 |
}};
|
| 761 |
}})();
|
| 762 |
</script>
|