Michael Rabinovich commited on
Commit
3112173
·
1 Parent(s): 53de73a

app: row-click detail panel below the leaderboard tables

Browse files

Bundle 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.

Files changed (2) hide show
  1. app.py +75 -0
  2. leaderboard.py +24 -2
app.py CHANGED
@@ -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"""
leaderboard.py CHANGED
@@ -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
- cols = [c for c in columns if c in df.columns]
 
 
 
 
 
 
256
  out = (
257
- df[cols]
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
  )