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
Files changed (1) hide show
  1. 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 &amp; topic: </span>
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 = _wrap_review_card(review_label, f"{render_review_html(plain_items, mode='plain', label='')}{reb}", collapsible=True)
1288
- polar_html = _wrap_review_card(review_label, f"{render_review_html(polar_items, mode='polarity', label='')}{reb}", collapsible=True)
1289
- topic_html = _wrap_review_card(review_label, f"{render_review_html(topic_items, mode='topic', label='')}{reb}", collapsible=True)
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
- # Get raw agreement content (no wrapping — we wrap manually to include rebuttal)
1450
- agreement_content = render_agreement_html(
1451
- sentence_lists[i], uniqueness, listener, speaker,
 
1452
  num_reviews=num_reviews,
1453
- label="", # no wrapping inside render_agreement_html
 
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
- rebuttal = review_data.get("rebuttal", "")
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
- sentences_for_review = [s for s, _ in review_item]
1777
- inner_html = render_agreement_html(
1778
- sentences_for_review, consensuality_dict,
 
1779
  listener=prep_listener, speaker=prep_speaker,
1780
  num_reviews=number_of_displayed_reviews,
1781
- label="",
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
- inner_html = render_review_html(
1796
- review_item, mode="plain", label="",
 
 
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
- inner = render_review_html(plain_items, mode="plain", label="")
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 &amp; 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=""))