Michael Rabinovich commited on
Commit
daae24c
·
1 Parent(s): 30909aa

gallery: refresh after admin changes

Browse files
Files changed (2) hide show
  1. app.py +15 -13
  2. tests/test_proxy.py +35 -0
app.py CHANGED
@@ -352,8 +352,8 @@ def _admin_promote(
352
  table_df: pd.DataFrame | None,
353
  method: str | None,
354
  profile: gr.OAuthProfile | None,
355
- ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
356
- """Promote every ticked row, then refresh the admin table + both tiers.
357
 
358
  Re-checks :func:`admin.is_admin` server-side so a tampered client
359
  that re-enables the button still can't write.
@@ -372,14 +372,14 @@ def _admin_promote(
372
  gr.Info(f"Promoted {len(ids)} row(s) to validated ({method}).")
373
  validated, unvalidated, _ = _safe_load_split()
374
  admin_df, _ = _safe_load_admin()
375
- return admin_df, validated, unvalidated
376
 
377
 
378
  def _admin_demote(
379
  table_df: pd.DataFrame | None,
380
  profile: gr.OAuthProfile | None,
381
- ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
382
- """Demote every ticked row, then refresh the admin table + both tiers."""
383
  if not is_admin(profile):
384
  raise gr.Error("You are not in the admin set.")
385
  ids = _selected_ids(table_df)
@@ -392,15 +392,15 @@ def _admin_demote(
392
  gr.Info(f"Demoted {len(ids)} row(s) to unvalidated.")
393
  validated, unvalidated, _ = _safe_load_split()
394
  admin_df, _ = _safe_load_admin()
395
- return admin_df, validated, unvalidated
396
 
397
 
398
  def _admin_delete(
399
  table_df: pd.DataFrame | None,
400
  confirm: bool,
401
  profile: gr.OAuthProfile | None,
402
- ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, gr.Checkbox, gr.Button]:
403
- """Delete every ticked row (artifacts + row), then refresh and disarm.
404
 
405
  Resets the confirm checkbox and re-disables the delete button on
406
  the way out so the next deletion needs a fresh, deliberate confirm.
@@ -423,6 +423,7 @@ def _admin_delete(
423
  admin_df,
424
  validated,
425
  unvalidated,
 
426
  gr.Checkbox(value=False),
427
  gr.Button(interactive=False),
428
  )
@@ -641,8 +642,8 @@ with gr.Blocks(title="CADGenBench Leaderboard", theme=gr.themes.Soft()) as block
641
  # self-contained HTML doc inlined into an iframe `srcdoc` with
642
  # base64 thumbnails, so it keeps its own style context and makes
643
  # no second HTTP request (works on the private Space, where
644
- # HF's edge 404s in-browser custom-route fetches). Built at boot;
645
- # the Refresh button rebuilds it after a promotion/new result.
646
  gallery_html = gr.HTML(value=_gallery_iframe_html())
647
  gallery_refresh_btn = gr.Button("Refresh gallery", size="sm")
648
  gallery_refresh_btn.click(
@@ -863,12 +864,12 @@ to publish the resulting row on the public leaderboard.
863
  promote_btn.click(
864
  fn=_admin_promote,
865
  inputs=[admin_table, admin_method_radio],
866
- outputs=[admin_table, validated_view, unvalidated_view],
867
  )
868
  demote_btn.click(
869
  fn=_admin_demote,
870
  inputs=[admin_table],
871
- outputs=[admin_table, validated_view, unvalidated_view],
872
  )
873
  delete_confirm.change(
874
  fn=_arm_delete, inputs=[delete_confirm], outputs=delete_btn,
@@ -877,7 +878,7 @@ to publish the resulting row on the public leaderboard.
877
  fn=_admin_delete,
878
  inputs=[admin_table, delete_confirm],
879
  outputs=[
880
- admin_table, validated_view, unvalidated_view,
881
  delete_confirm, delete_btn,
882
  ],
883
  )
@@ -901,6 +902,7 @@ to publish the resulting row on the public leaderboard.
901
  # per page load; LoginButton clicks also re-trigger this through
902
  # Gradio's auth-event plumbing.
903
  blocks.load(fn=_enable_submit_when_logged_in, outputs=submit_btn)
 
904
 
905
  # Same per-load OAuth read, gating the Admin tab's controls on
906
  # membership in the CADGENBENCH_ADMINS set. Logged-out / non-admin
 
352
  table_df: pd.DataFrame | None,
353
  method: str | None,
354
  profile: gr.OAuthProfile | None,
355
+ ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
356
+ """Promote ticked rows, then refresh admin, leaderboard, and gallery.
357
 
358
  Re-checks :func:`admin.is_admin` server-side so a tampered client
359
  that re-enables the button still can't write.
 
372
  gr.Info(f"Promoted {len(ids)} row(s) to validated ({method}).")
373
  validated, unvalidated, _ = _safe_load_split()
374
  admin_df, _ = _safe_load_admin()
375
+ return admin_df, validated, unvalidated, _gallery_iframe_html()
376
 
377
 
378
  def _admin_demote(
379
  table_df: pd.DataFrame | None,
380
  profile: gr.OAuthProfile | None,
381
+ ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
382
+ """Demote ticked rows, then refresh admin, leaderboard, and gallery."""
383
  if not is_admin(profile):
384
  raise gr.Error("You are not in the admin set.")
385
  ids = _selected_ids(table_df)
 
392
  gr.Info(f"Demoted {len(ids)} row(s) to unvalidated.")
393
  validated, unvalidated, _ = _safe_load_split()
394
  admin_df, _ = _safe_load_admin()
395
+ return admin_df, validated, unvalidated, _gallery_iframe_html()
396
 
397
 
398
  def _admin_delete(
399
  table_df: pd.DataFrame | None,
400
  confirm: bool,
401
  profile: gr.OAuthProfile | None,
402
+ ) -> tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, str, gr.Checkbox, gr.Button]:
403
+ """Delete ticked rows, then refresh admin, leaderboard, gallery, and disarm.
404
 
405
  Resets the confirm checkbox and re-disables the delete button on
406
  the way out so the next deletion needs a fresh, deliberate confirm.
 
423
  admin_df,
424
  validated,
425
  unvalidated,
426
+ _gallery_iframe_html(),
427
  gr.Checkbox(value=False),
428
  gr.Button(interactive=False),
429
  )
 
642
  # self-contained HTML doc inlined into an iframe `srcdoc` with
643
  # base64 thumbnails, so it keeps its own style context and makes
644
  # no second HTTP request (works on the private Space, where
645
+ # HF's edge 404s in-browser custom-route fetches). Built at boot,
646
+ # rebuilt on page load, and refreshed after admin actions.
647
  gallery_html = gr.HTML(value=_gallery_iframe_html())
648
  gallery_refresh_btn = gr.Button("Refresh gallery", size="sm")
649
  gallery_refresh_btn.click(
 
864
  promote_btn.click(
865
  fn=_admin_promote,
866
  inputs=[admin_table, admin_method_radio],
867
+ outputs=[admin_table, validated_view, unvalidated_view, gallery_html],
868
  )
869
  demote_btn.click(
870
  fn=_admin_demote,
871
  inputs=[admin_table],
872
+ outputs=[admin_table, validated_view, unvalidated_view, gallery_html],
873
  )
874
  delete_confirm.change(
875
  fn=_arm_delete, inputs=[delete_confirm], outputs=delete_btn,
 
878
  fn=_admin_delete,
879
  inputs=[admin_table, delete_confirm],
880
  outputs=[
881
+ admin_table, validated_view, unvalidated_view, gallery_html,
882
  delete_confirm, delete_btn,
883
  ],
884
  )
 
902
  # per page load; LoginButton clicks also re-trigger this through
903
  # Gradio's auth-event plumbing.
904
  blocks.load(fn=_enable_submit_when_logged_in, outputs=submit_btn)
905
+ blocks.load(fn=_gallery_iframe_html, outputs=gallery_html)
906
 
907
  # Same per-load OAuth read, gating the Admin tab's controls on
908
  # membership in the CADGENBENCH_ADMINS set. Logged-out / non-admin
tests/test_proxy.py CHANGED
@@ -239,6 +239,41 @@ def test_gate_admin_controls_refreshes_live_table(monkeypatch):
239
  assert table_update.interactive is True
240
 
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  def test_data_error_banner_md_present_on_error_empty_otherwise():
243
  """Banner markdown is non-empty (and names the cause) only on error."""
244
  assert app._data_error_banner_md(None) == ""
 
239
  assert table_update.interactive is True
240
 
241
 
242
+ def test_admin_delete_refreshes_gallery(monkeypatch):
243
+ """Deleting rows also replaces the Gallery iframe srcdoc."""
244
+ table_df = pd.DataFrame(
245
+ [
246
+ {
247
+ "select": True,
248
+ "submission_id": "validated-old-row",
249
+ }
250
+ ]
251
+ )
252
+ empty_admin = pd.DataFrame(columns=leaderboard.ADMIN_COLUMNS)
253
+ empty_validated = pd.DataFrame(columns=leaderboard.VALIDATED_LEADERBOARD_COLS)
254
+ empty_unvalidated = pd.DataFrame(columns=leaderboard.LEADERBOARD_COLS)
255
+ deleted: list[list[str]] = []
256
+
257
+ monkeypatch.setattr(app, "is_admin", lambda profile: True)
258
+ monkeypatch.setattr(app, "delete_rows", lambda ids: deleted.append(ids))
259
+ monkeypatch.setattr(
260
+ app, "_safe_load_split", lambda: (empty_validated, empty_unvalidated, None)
261
+ )
262
+ monkeypatch.setattr(app, "_safe_load_admin", lambda: (empty_admin, None))
263
+ monkeypatch.setattr(app, "_gallery_iframe_html", lambda: "<iframe>empty</iframe>")
264
+ monkeypatch.setattr(app.gr, "Info", lambda *a, **k: None)
265
+
266
+ admin_df, validated, unvalidated, gallery_html, _confirm, _delete_btn = (
267
+ app._admin_delete(table_df, True, types.SimpleNamespace(username="michaelr27"))
268
+ )
269
+
270
+ assert deleted == [["validated-old-row"]]
271
+ assert admin_df is empty_admin
272
+ assert validated is empty_validated
273
+ assert unvalidated is empty_unvalidated
274
+ assert gallery_html == "<iframe>empty</iframe>"
275
+
276
+
277
  def test_data_error_banner_md_present_on_error_empty_otherwise():
278
  """Banner markdown is non-empty (and names the cause) only on error."""
279
  assert app._data_error_banner_md(None) == ""