Sina1138 commited on
Commit ·
73692a4
1
Parent(s): 726e124
Add unified review card builder and format rebuttal as collapsible HTML in Demo.py
Browse files- interface/Demo.py +70 -47
interface/Demo.py
CHANGED
|
@@ -838,6 +838,38 @@ def render_agreement_html(
|
|
| 838 |
return content
|
| 839 |
|
| 840 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 841 |
# Auto-detect the preprocessed dataset CSV
|
| 842 |
def _find_preprocessed_csv() -> Path:
|
| 843 |
"""Find the most recent preprocessed_scored_reviews_*.csv in the data dir."""
|
|
@@ -998,7 +1030,7 @@ POLARITY_PROGRESS_HTML = """
|
|
| 998 |
<div style="padding:10px 16px;background:#f0fff4;border-radius:8px;border:1px solid #bbf7d0;margin:0;">
|
| 999 |
<div style="display:flex;align-items:center;gap:10px;">
|
| 1000 |
<div style="width:18px;height:18px;border:3px solid #dcfce7;border-top:3px solid #16a34a;border-radius:50%;animation:procspin 1s linear infinite;flex-shrink:0;"></div>
|
| 1001 |
-
<span style="font-weight:600;color:#14532d;font-size:0.9em;white-space:nowrap;">Computing polarity & topic
|
| 1002 |
<div style="flex:1;background:#dcfce7;border-radius:4px;height:6px;overflow:hidden;">
|
| 1003 |
<div style="background:linear-gradient(90deg,#4ade80,#16a34a);height:100%;border-radius:4px;animation:agrslide 2s ease-in-out infinite;"></div>
|
| 1004 |
</div>
|
|
@@ -1119,6 +1151,22 @@ _REBUTTAL_GENERAL_STYLE = (
|
|
| 1119 |
)
|
| 1120 |
|
| 1121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1122 |
def format_rebuttal_for_review(rebuttal: str, review_num: int) -> str:
|
| 1123 |
"""Format rebuttals that reply to a specific review number."""
|
| 1124 |
if not rebuttal or not rebuttal.strip():
|
|
@@ -1284,9 +1332,9 @@ def process_interactive_reviews_fast(text1: str, text2: str, text3: str, text4:
|
|
| 1284 |
polar_items = [(s, {"polarity": polarity_map.get(s)}) for s in sentence_lists[i]]
|
| 1285 |
topic_items = [(s, {"topic": topic_map.get(s)}) for s in sentence_lists[i]]
|
| 1286 |
|
| 1287 |
-
none_html
|
| 1288 |
-
polar_html =
|
| 1289 |
-
topic_html =
|
| 1290 |
|
| 1291 |
none_out.append(gr.update(visible=True, value=none_html))
|
| 1292 |
agree_out.append(gr.update(visible=False, value=""))
|
|
@@ -1446,15 +1494,14 @@ def compute_rsa_in_background(rsa_state: Dict, current_focus: str):
|
|
| 1446 |
agree_out = []
|
| 1447 |
for i in range(MAX_INTERACTIVE_REVIEWS):
|
| 1448 |
if i < len(sentence_lists):
|
| 1449 |
-
|
| 1450 |
-
|
| 1451 |
-
sentence_lists[i],
|
|
|
|
| 1452 |
num_reviews=num_reviews,
|
| 1453 |
-
|
|
|
|
| 1454 |
)
|
| 1455 |
-
div = divergent_per_review.get(i, "")
|
| 1456 |
-
reb = rebuttal_htmls[i]
|
| 1457 |
-
html_val = _wrap_review_card(f"Agreement in Review {i + 1}", f"{agreement_content}{div}{reb}", collapsible=True)
|
| 1458 |
agree_out.append(gr.update(visible=show_agreement, value=html_val))
|
| 1459 |
else:
|
| 1460 |
agree_out.append(gr.update(visible=False, value=""))
|
|
@@ -1693,17 +1740,7 @@ with gr.Blocks(
|
|
| 1693 |
rebuttal_html = ""
|
| 1694 |
if isinstance(review_data, dict) and "sentences" in review_data:
|
| 1695 |
review_item = list(review_data["sentences"].items())
|
| 1696 |
-
|
| 1697 |
-
if rebuttal and rebuttal.strip():
|
| 1698 |
-
rebuttal_html = (
|
| 1699 |
-
f'<details style="{_REBUTTAL_PER_REVIEW_STYLE}">'
|
| 1700 |
-
'<summary style="padding:10px 14px;cursor:pointer;font-size:0.75em;color:#92400e;'
|
| 1701 |
-
'font-weight:600;list-style:none;display:flex;align-items:center;gap:6px;">'
|
| 1702 |
-
'<span style="transition:transform 0.2s;">▶</span> Author Response</summary>'
|
| 1703 |
-
'<div style="padding:10px 14px;">'
|
| 1704 |
-
f'<div style="white-space:pre-wrap;color:#1f2937;font-size:0.85em;line-height:1.5;">{rebuttal}</div>'
|
| 1705 |
-
'</div></details>'
|
| 1706 |
-
)
|
| 1707 |
else:
|
| 1708 |
review_item = list(review_data.items())
|
| 1709 |
review_sentence_lists.append([s for s, _ in review_item])
|
|
@@ -1773,34 +1810,22 @@ with gr.Blocks(
|
|
| 1773 |
|
| 1774 |
review_label = f"Review {i + 1}"
|
| 1775 |
if show_consensuality:
|
| 1776 |
-
|
| 1777 |
-
|
| 1778 |
-
|
|
|
|
| 1779 |
listener=prep_listener, speaker=prep_speaker,
|
| 1780 |
num_reviews=number_of_displayed_reviews,
|
| 1781 |
-
|
| 1782 |
-
|
| 1783 |
-
# Append per-review divergent cards (if RSA data available)
|
| 1784 |
-
if i in divergent_per_review:
|
| 1785 |
-
inner_html += divergent_per_review[i]
|
| 1786 |
-
elif show_polarity:
|
| 1787 |
-
inner_html = render_review_html(
|
| 1788 |
-
review_item, mode="polarity", label="",
|
| 1789 |
-
)
|
| 1790 |
-
elif show_topic:
|
| 1791 |
-
inner_html = render_review_html(
|
| 1792 |
-
review_item, mode="topic", label="",
|
| 1793 |
)
|
| 1794 |
else:
|
| 1795 |
-
|
| 1796 |
-
|
|
|
|
|
|
|
| 1797 |
)
|
| 1798 |
|
| 1799 |
-
# Wrap review content + rebuttal in a single collapsible card
|
| 1800 |
-
html_content = _wrap_review_card(
|
| 1801 |
-
review_label, f'{inner_html}{rebuttal_html}', collapsible=True
|
| 1802 |
-
)
|
| 1803 |
-
|
| 1804 |
agreement_updates.append(gr.update(visible=True, value=html_content))
|
| 1805 |
# Rebuttal is now embedded in the review card, so hide the separate component
|
| 1806 |
rebuttal_updates.append(gr.update(visible=False, value=""))
|
|
@@ -2174,9 +2199,7 @@ with gr.Blocks(
|
|
| 2174 |
if t and t.strip():
|
| 2175 |
sentences = [s for s in glimpse_tokenizer(t) if s.strip()]
|
| 2176 |
plain_items = [(s, {}) for s in sentences]
|
| 2177 |
-
|
| 2178 |
-
reb = rebuttal_htmls[idx]
|
| 2179 |
-
html = _wrap_review_card(f"Review {idx + 1}", f"{inner}{reb}", collapsible=True)
|
| 2180 |
none_out.append(gr.update(visible=True, value=html))
|
| 2181 |
else:
|
| 2182 |
none_out.append(gr.update(visible=False, value=""))
|
|
|
|
| 838 |
return content
|
| 839 |
|
| 840 |
|
| 841 |
+
def build_review_card(
|
| 842 |
+
label: str,
|
| 843 |
+
*,
|
| 844 |
+
review_items: list = None,
|
| 845 |
+
mode: str = "plain",
|
| 846 |
+
sentences: List[str] = None,
|
| 847 |
+
uniqueness: Dict = None,
|
| 848 |
+
listener: dict = None,
|
| 849 |
+
speaker: dict = None,
|
| 850 |
+
num_reviews: int = 0,
|
| 851 |
+
divergent_html: str = "",
|
| 852 |
+
rebuttal_html: str = "",
|
| 853 |
+
collapsible: bool = True,
|
| 854 |
+
) -> str:
|
| 855 |
+
"""Unified review card builder — single entry point for both tabs.
|
| 856 |
+
|
| 857 |
+
For plain/polarity/topic: pass review_items + mode.
|
| 858 |
+
For agreement: pass sentences + RSA dicts (uniqueness, listener, speaker, num_reviews).
|
| 859 |
+
Optional divergent_html and rebuttal_html are appended inside the card.
|
| 860 |
+
"""
|
| 861 |
+
if sentences is not None:
|
| 862 |
+
inner = render_agreement_html(
|
| 863 |
+
sentences, uniqueness or {}, listener, speaker,
|
| 864 |
+
num_reviews=num_reviews, label="",
|
| 865 |
+
)
|
| 866 |
+
elif review_items is not None:
|
| 867 |
+
inner = render_review_html(review_items, mode=mode, label="")
|
| 868 |
+
else:
|
| 869 |
+
inner = ""
|
| 870 |
+
return _wrap_review_card(label, f"{inner}{divergent_html}{rebuttal_html}", collapsible=collapsible)
|
| 871 |
+
|
| 872 |
+
|
| 873 |
# Auto-detect the preprocessed dataset CSV
|
| 874 |
def _find_preprocessed_csv() -> Path:
|
| 875 |
"""Find the most recent preprocessed_scored_reviews_*.csv in the data dir."""
|
|
|
|
| 1030 |
<div style="padding:10px 16px;background:#f0fff4;border-radius:8px;border:1px solid #bbf7d0;margin:0;">
|
| 1031 |
<div style="display:flex;align-items:center;gap:10px;">
|
| 1032 |
<div style="width:18px;height:18px;border:3px solid #dcfce7;border-top:3px solid #16a34a;border-radius:50%;animation:procspin 1s linear infinite;flex-shrink:0;"></div>
|
| 1033 |
+
<span style="font-weight:600;color:#14532d;font-size:0.9em;white-space:nowrap;">Computing polarity & topic... </span>
|
| 1034 |
<div style="flex:1;background:#dcfce7;border-radius:4px;height:6px;overflow:hidden;">
|
| 1035 |
<div style="background:linear-gradient(90deg,#4ade80,#16a34a);height:100%;border-radius:4px;animation:agrslide 2s ease-in-out infinite;"></div>
|
| 1036 |
</div>
|
|
|
|
| 1151 |
)
|
| 1152 |
|
| 1153 |
|
| 1154 |
+
def format_rebuttal_plain(text: str) -> str:
|
| 1155 |
+
"""Format a plain text rebuttal as collapsible HTML.
|
| 1156 |
+
For pre-processed data where each review has its own rebuttal string."""
|
| 1157 |
+
if not text or not text.strip():
|
| 1158 |
+
return ""
|
| 1159 |
+
return (
|
| 1160 |
+
f'<details style="{_REBUTTAL_PER_REVIEW_STYLE}">'
|
| 1161 |
+
'<summary style="padding:10px 14px;cursor:pointer;font-size:0.75em;color:#92400e;'
|
| 1162 |
+
'font-weight:600;list-style:none;display:flex;align-items:center;gap:6px;">'
|
| 1163 |
+
'<span style="transition:transform 0.2s;">▶</span> Author Response</summary>'
|
| 1164 |
+
'<div style="padding:10px 14px;">'
|
| 1165 |
+
f'<div style="white-space:pre-wrap;color:#1f2937;font-size:0.85em;line-height:1.5;">{text}</div>'
|
| 1166 |
+
'</div></details>'
|
| 1167 |
+
)
|
| 1168 |
+
|
| 1169 |
+
|
| 1170 |
def format_rebuttal_for_review(rebuttal: str, review_num: int) -> str:
|
| 1171 |
"""Format rebuttals that reply to a specific review number."""
|
| 1172 |
if not rebuttal or not rebuttal.strip():
|
|
|
|
| 1332 |
polar_items = [(s, {"polarity": polarity_map.get(s)}) for s in sentence_lists[i]]
|
| 1333 |
topic_items = [(s, {"topic": topic_map.get(s)}) for s in sentence_lists[i]]
|
| 1334 |
|
| 1335 |
+
none_html = build_review_card(review_label, review_items=plain_items, mode="plain", rebuttal_html=reb)
|
| 1336 |
+
polar_html = build_review_card(review_label, review_items=polar_items, mode="polarity", rebuttal_html=reb)
|
| 1337 |
+
topic_html = build_review_card(review_label, review_items=topic_items, mode="topic", rebuttal_html=reb)
|
| 1338 |
|
| 1339 |
none_out.append(gr.update(visible=True, value=none_html))
|
| 1340 |
agree_out.append(gr.update(visible=False, value=""))
|
|
|
|
| 1494 |
agree_out = []
|
| 1495 |
for i in range(MAX_INTERACTIVE_REVIEWS):
|
| 1496 |
if i < len(sentence_lists):
|
| 1497 |
+
html_val = build_review_card(
|
| 1498 |
+
f"Agreement in Review {i + 1}",
|
| 1499 |
+
sentences=sentence_lists[i],
|
| 1500 |
+
uniqueness=uniqueness, listener=listener, speaker=speaker,
|
| 1501 |
num_reviews=num_reviews,
|
| 1502 |
+
divergent_html=divergent_per_review.get(i, ""),
|
| 1503 |
+
rebuttal_html=rebuttal_htmls[i],
|
| 1504 |
)
|
|
|
|
|
|
|
|
|
|
| 1505 |
agree_out.append(gr.update(visible=show_agreement, value=html_val))
|
| 1506 |
else:
|
| 1507 |
agree_out.append(gr.update(visible=False, value=""))
|
|
|
|
| 1740 |
rebuttal_html = ""
|
| 1741 |
if isinstance(review_data, dict) and "sentences" in review_data:
|
| 1742 |
review_item = list(review_data["sentences"].items())
|
| 1743 |
+
rebuttal_html = format_rebuttal_plain(review_data.get("rebuttal", ""))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1744 |
else:
|
| 1745 |
review_item = list(review_data.items())
|
| 1746 |
review_sentence_lists.append([s for s, _ in review_item])
|
|
|
|
| 1810 |
|
| 1811 |
review_label = f"Review {i + 1}"
|
| 1812 |
if show_consensuality:
|
| 1813 |
+
html_content = build_review_card(
|
| 1814 |
+
review_label,
|
| 1815 |
+
sentences=[s for s, _ in review_item],
|
| 1816 |
+
uniqueness=consensuality_dict,
|
| 1817 |
listener=prep_listener, speaker=prep_speaker,
|
| 1818 |
num_reviews=number_of_displayed_reviews,
|
| 1819 |
+
divergent_html=divergent_per_review.get(i, ""),
|
| 1820 |
+
rebuttal_html=rebuttal_html,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1821 |
)
|
| 1822 |
else:
|
| 1823 |
+
m = "polarity" if show_polarity else ("topic" if show_topic else "plain")
|
| 1824 |
+
html_content = build_review_card(
|
| 1825 |
+
review_label, review_items=review_item, mode=m,
|
| 1826 |
+
rebuttal_html=rebuttal_html,
|
| 1827 |
)
|
| 1828 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1829 |
agreement_updates.append(gr.update(visible=True, value=html_content))
|
| 1830 |
# Rebuttal is now embedded in the review card, so hide the separate component
|
| 1831 |
rebuttal_updates.append(gr.update(visible=False, value=""))
|
|
|
|
| 2199 |
if t and t.strip():
|
| 2200 |
sentences = [s for s in glimpse_tokenizer(t) if s.strip()]
|
| 2201 |
plain_items = [(s, {}) for s in sentences]
|
| 2202 |
+
html = build_review_card(f"Review {idx + 1}", review_items=plain_items, mode="plain", rebuttal_html=rebuttal_htmls[idx])
|
|
|
|
|
|
|
| 2203 |
none_out.append(gr.update(visible=True, value=html))
|
| 2204 |
else:
|
| 2205 |
none_out.append(gr.update(visible=False, value=""))
|