Michael Rabinovich commited on
Commit ·
daae24c
1
Parent(s): 30909aa
gallery: refresh after admin changes
Browse files- app.py +15 -13
- 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
|
| 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
|
| 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
|
| 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 |
-
#
|
| 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) == ""
|