app: row-click detail panel below the leaderboard tables
Browse filesBundle 1+2 C6. Single shared `gr.Markdown` panel below both
Leaderboard widgets; either table's `.select(...)` event populates
it with the clicked row's notes, agent_url, submitted_at, submission
zip + report links (always), and failure_reason (failed rows only).
Spec'd shape: one panel, two select sources.
leaderboard.py:
- LEADERBOARD_COLS + VALIDATED_LEADERBOARD_COLS each grow by three
hidden detail-only fields (submission_id, notes, failure_reason).
They ride in the underlying DataFrame so the select handler can
read them out directly without a parallel state cache.
- New LEADERBOARD_HIDE_COLUMNS constant lists the columns hidden
from view; both Leaderboard widgets in app.py consume it.
- `_project_and_format` now fills missing columns with None (legacy
rows can lack optional fields like notes; the panel needs the
column-set to be stable regardless of source-row shape).
app.py:
- pandas imported (for the helper's pd.isna check).
- New `_format_detail(df, evt)` helper assembles the markdown:
pulls row by `evt.index` from the widget's currently-displayed
DataFrame; skips fields that are empty / null / NaN; preserves
the pre-formatted link cells (`[code](...)`, `[zip](...)`,
`[report](...)`) which markdown renders into clickable anchors.
Failure reason only shows on `status == "failed"` rows; report
link is already restricted to completed rows by leaderboard.py's
`_report_url_md`.
- Detail panel is a `gr.Markdown` initially carrying a placeholder
("Click a row above for details."). Both Leaderboards wire their
.select event to the same handler + the same output.
No new test file: the C6 acceptance is "row-click on either table
populates the detail panel; failed rows show failure_reason," which
is UI behavior verified on the live Space. Existing 9 unit tests
still pass since the column-list grows but the detail-only fields
are filled-or-None automatically by the new `_project_and_format`
fill-missing step. App.py import is clean, hidden cols are
present in the live DataFrame returned by load_leaderboard_split.
- app.py +75 -0
- leaderboard.py +24 -2
|
@@ -10,10 +10,13 @@ import logging
|
|
| 10 |
import gradio as gr
|
| 11 |
from gradio_leaderboard import Leaderboard
|
| 12 |
|
|
|
|
|
|
|
| 13 |
from leaderboard import (
|
| 14 |
HF_DATA_REPO,
|
| 15 |
HF_SUBMISSIONS_REPO,
|
| 16 |
LEADERBOARD_DATATYPES,
|
|
|
|
| 17 |
VALIDATED_LEADERBOARD_DATATYPES,
|
| 18 |
load_leaderboard_split,
|
| 19 |
)
|
|
@@ -45,6 +48,56 @@ correct 3D model.
|
|
| 45 |
- **Code**: [`huggingface/cadgenbench`](https://github.com/huggingface/cadgenbench).
|
| 46 |
"""
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
with gr.Blocks(title="CADGenBench Leaderboard", theme=gr.themes.Soft()) as app:
|
| 49 |
gr.Markdown(
|
| 50 |
"# CADGenBench Leaderboard\n"
|
|
@@ -61,12 +114,14 @@ with gr.Blocks(title="CADGenBench Leaderboard", theme=gr.themes.Soft()) as app:
|
|
| 61 |
value=initial_validated,
|
| 62 |
datatype=VALIDATED_LEADERBOARD_DATATYPES,
|
| 63 |
search_columns=["submission_name", "submitter_name"],
|
|
|
|
| 64 |
label="Validated Leaderboard",
|
| 65 |
)
|
| 66 |
unvalidated_view = Leaderboard(
|
| 67 |
value=initial_unvalidated,
|
| 68 |
datatype=LEADERBOARD_DATATYPES,
|
| 69 |
search_columns=["submission_name", "submitter_name"],
|
|
|
|
| 70 |
label="Unvalidated Leaderboard",
|
| 71 |
)
|
| 72 |
refresh_btn = gr.Button("Refresh", size="sm")
|
|
@@ -75,6 +130,26 @@ with gr.Blocks(title="CADGenBench Leaderboard", theme=gr.themes.Soft()) as app:
|
|
| 75 |
outputs=[validated_view, unvalidated_view],
|
| 76 |
)
|
| 77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
with gr.Tab("Submit"):
|
| 79 |
gr.Markdown(
|
| 80 |
f"""
|
|
|
|
| 10 |
import gradio as gr
|
| 11 |
from gradio_leaderboard import Leaderboard
|
| 12 |
|
| 13 |
+
import pandas as pd
|
| 14 |
+
|
| 15 |
from leaderboard import (
|
| 16 |
HF_DATA_REPO,
|
| 17 |
HF_SUBMISSIONS_REPO,
|
| 18 |
LEADERBOARD_DATATYPES,
|
| 19 |
+
LEADERBOARD_HIDE_COLUMNS,
|
| 20 |
VALIDATED_LEADERBOARD_DATATYPES,
|
| 21 |
load_leaderboard_split,
|
| 22 |
)
|
|
|
|
| 48 |
- **Code**: [`huggingface/cadgenbench`](https://github.com/huggingface/cadgenbench).
|
| 49 |
"""
|
| 50 |
|
| 51 |
+
DETAIL_PLACEHOLDER = "_Click a row above for details._"
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def _has(value) -> bool:
|
| 55 |
+
"""True for values that should show up in the detail panel."""
|
| 56 |
+
if value is None:
|
| 57 |
+
return False
|
| 58 |
+
if isinstance(value, float) and pd.isna(value):
|
| 59 |
+
return False
|
| 60 |
+
return str(value).strip() != ""
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _format_detail(df: pd.DataFrame | None, evt: gr.SelectData) -> str:
|
| 64 |
+
"""Build the row-detail markdown for the clicked submission.
|
| 65 |
+
|
| 66 |
+
Pulls hidden columns (``notes``, ``failure_reason``,
|
| 67 |
+
``submission_id``) that ride in the underlying DataFrame plus
|
| 68 |
+
the visible link cells (already pre-formatted as ``[label](url)``
|
| 69 |
+
by ``leaderboard.py``'s ``_project_and_format``).
|
| 70 |
+
``failure_reason`` only shows on ``failed`` rows;
|
| 71 |
+
``report_url`` is only non-empty for ``completed`` rows.
|
| 72 |
+
"""
|
| 73 |
+
if df is None or len(df) == 0 or evt is None or evt.index is None:
|
| 74 |
+
return DETAIL_PLACEHOLDER
|
| 75 |
+
idx = evt.index[0] if isinstance(evt.index, (list, tuple)) else evt.index
|
| 76 |
+
if idx < 0 or idx >= len(df):
|
| 77 |
+
return DETAIL_PLACEHOLDER
|
| 78 |
+
row = df.iloc[idx]
|
| 79 |
+
|
| 80 |
+
title = row.get("submission_name") or "(unnamed submission)"
|
| 81 |
+
lines = [f"### {title}", ""]
|
| 82 |
+
if _has(row.get("submitter_name")):
|
| 83 |
+
lines.append(f"- **Submitter**: {row['submitter_name']}")
|
| 84 |
+
if _has(row.get("status")):
|
| 85 |
+
lines.append(f"- **Status**: {row['status']}")
|
| 86 |
+
if _has(row.get("submitted_at")):
|
| 87 |
+
lines.append(f"- **Submitted**: {row['submitted_at']}")
|
| 88 |
+
if _has(row.get("notes")):
|
| 89 |
+
lines.append(f"- **Notes**: {row['notes']}")
|
| 90 |
+
if _has(row.get("agent_url")):
|
| 91 |
+
lines.append(f"- **Agent code**: {row['agent_url']}")
|
| 92 |
+
if _has(row.get("submission_blob_url")):
|
| 93 |
+
lines.append(f"- **Submission**: {row['submission_blob_url']}")
|
| 94 |
+
if _has(row.get("report_url")):
|
| 95 |
+
lines.append(f"- **Report**: {row['report_url']}")
|
| 96 |
+
if row.get("status") == "failed" and _has(row.get("failure_reason")):
|
| 97 |
+
lines.append(f"- **Failure reason**: {row['failure_reason']}")
|
| 98 |
+
return "\n".join(lines)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
with gr.Blocks(title="CADGenBench Leaderboard", theme=gr.themes.Soft()) as app:
|
| 102 |
gr.Markdown(
|
| 103 |
"# CADGenBench Leaderboard\n"
|
|
|
|
| 114 |
value=initial_validated,
|
| 115 |
datatype=VALIDATED_LEADERBOARD_DATATYPES,
|
| 116 |
search_columns=["submission_name", "submitter_name"],
|
| 117 |
+
hide_columns=LEADERBOARD_HIDE_COLUMNS,
|
| 118 |
label="Validated Leaderboard",
|
| 119 |
)
|
| 120 |
unvalidated_view = Leaderboard(
|
| 121 |
value=initial_unvalidated,
|
| 122 |
datatype=LEADERBOARD_DATATYPES,
|
| 123 |
search_columns=["submission_name", "submitter_name"],
|
| 124 |
+
hide_columns=LEADERBOARD_HIDE_COLUMNS,
|
| 125 |
label="Unvalidated Leaderboard",
|
| 126 |
)
|
| 127 |
refresh_btn = gr.Button("Refresh", size="sm")
|
|
|
|
| 130 |
outputs=[validated_view, unvalidated_view],
|
| 131 |
)
|
| 132 |
|
| 133 |
+
# Row-click detail panel: a single shared markdown component
|
| 134 |
+
# below the tables, populated by either table's .select()
|
| 135 |
+
# event. Pulls hidden columns (notes, failure_reason,
|
| 136 |
+
# submission_id) plus the visible link cells (already pre-
|
| 137 |
+
# formatted as markdown by leaderboard.py) for the row.
|
| 138 |
+
detail_panel = gr.Markdown(
|
| 139 |
+
value=DETAIL_PLACEHOLDER,
|
| 140 |
+
label="Selected submission",
|
| 141 |
+
)
|
| 142 |
+
validated_view.select(
|
| 143 |
+
fn=_format_detail,
|
| 144 |
+
inputs=validated_view,
|
| 145 |
+
outputs=detail_panel,
|
| 146 |
+
)
|
| 147 |
+
unvalidated_view.select(
|
| 148 |
+
fn=_format_detail,
|
| 149 |
+
inputs=unvalidated_view,
|
| 150 |
+
outputs=detail_panel,
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
with gr.Tab("Submit"):
|
| 154 |
gr.Markdown(
|
| 155 |
f"""
|
|
@@ -29,6 +29,10 @@ LOCAL_RESULTS_PATH = Path(__file__).parent / "results.jsonl"
|
|
| 29 |
RESULTS_FILENAME = "results.jsonl"
|
| 30 |
HUB_FETCH_TIMEOUT_SECONDS = 30
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
LEADERBOARD_COLS = [
|
| 33 |
"status",
|
| 34 |
"submission_name",
|
|
@@ -40,6 +44,10 @@ LEADERBOARD_COLS = [
|
|
| 40 |
"agent_url",
|
| 41 |
"submission_blob_url",
|
| 42 |
"report_url",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
]
|
| 44 |
|
| 45 |
# Validated table additionally exposes `validation_method`; on the
|
|
@@ -57,8 +65,16 @@ VALIDATED_LEADERBOARD_COLS = [
|
|
| 57 |
"agent_url",
|
| 58 |
"submission_blob_url",
|
| 59 |
"report_url",
|
|
|
|
|
|
|
|
|
|
| 60 |
]
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
# Per-column gradio_leaderboard datatypes. Link columns render their
|
| 63 |
# pre-formatted markdown; everything else is plain string (numeric
|
| 64 |
# cells get pending / failed status tags applied by _fmt_pct /
|
|
@@ -252,9 +268,15 @@ def _project_and_format(df: pd.DataFrame, columns: list[str]) -> pd.DataFrame:
|
|
| 252 |
df["report_url"] = df.apply(
|
| 253 |
lambda r: _report_url_md(r["submission_id"], r["status"]), axis=1,
|
| 254 |
)
|
| 255 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
out = (
|
| 257 |
-
df[
|
| 258 |
.sort_values("aggregate_score", ascending=False, na_position="last")
|
| 259 |
.reset_index(drop=True)
|
| 260 |
)
|
|
|
|
| 29 |
RESULTS_FILENAME = "results.jsonl"
|
| 30 |
HUB_FETCH_TIMEOUT_SECONDS = 30
|
| 31 |
|
| 32 |
+
# Columns visible in the rendered table, in left-to-right order, followed
|
| 33 |
+
# by hidden-but-data-present columns the row-click detail panel pulls from.
|
| 34 |
+
# Hidden columns ride along in the DataFrame so `Leaderboard.select(...)`
|
| 35 |
+
# can read them out without a separate state-cache or re-fetch.
|
| 36 |
LEADERBOARD_COLS = [
|
| 37 |
"status",
|
| 38 |
"submission_name",
|
|
|
|
| 44 |
"agent_url",
|
| 45 |
"submission_blob_url",
|
| 46 |
"report_url",
|
| 47 |
+
# Detail-panel-only (hidden via `hide_columns` on the widget):
|
| 48 |
+
"submission_id",
|
| 49 |
+
"notes",
|
| 50 |
+
"failure_reason",
|
| 51 |
]
|
| 52 |
|
| 53 |
# Validated table additionally exposes `validation_method`; on the
|
|
|
|
| 65 |
"agent_url",
|
| 66 |
"submission_blob_url",
|
| 67 |
"report_url",
|
| 68 |
+
"submission_id",
|
| 69 |
+
"notes",
|
| 70 |
+
"failure_reason",
|
| 71 |
]
|
| 72 |
|
| 73 |
+
# Columns to hide from rendering on both tables. These ride in the
|
| 74 |
+
# DataFrame so the row-click detail panel can populate from them; the
|
| 75 |
+
# widget hides them from view.
|
| 76 |
+
LEADERBOARD_HIDE_COLUMNS = ["submission_id", "notes", "failure_reason"]
|
| 77 |
+
|
| 78 |
# Per-column gradio_leaderboard datatypes. Link columns render their
|
| 79 |
# pre-formatted markdown; everything else is plain string (numeric
|
| 80 |
# cells get pending / failed status tags applied by _fmt_pct /
|
|
|
|
| 268 |
df["report_url"] = df.apply(
|
| 269 |
lambda r: _report_url_md(r["submission_id"], r["status"]), axis=1,
|
| 270 |
)
|
| 271 |
+
# Make sure every declared column exists (legacy rows can be
|
| 272 |
+
# missing optional fields). Detail-panel reads expect the
|
| 273 |
+
# column-set to be stable regardless of which source rows had
|
| 274 |
+
# which keys.
|
| 275 |
+
for c in columns:
|
| 276 |
+
if c not in df.columns:
|
| 277 |
+
df[c] = None
|
| 278 |
out = (
|
| 279 |
+
df[columns]
|
| 280 |
.sort_values("aggregate_score", ascending=False, na_position="last")
|
| 281 |
.reset_index(drop=True)
|
| 282 |
)
|