Spaces:
Running
Landing table: fix per-column sort + drop tasks col + move avg to last
Browse filesSort bug: the per-task columns (figraph, arxiv-citation, β¦) had
data-sort attrs but the JS sortKey switch only handled rank/agent/
primary/n_submissions/first_seen, so any other key fell through to
`default: return 0` and rows kept their original order despite the
arrow appearing.
Replaced the switch with a header-position lookup that resolves the
column index from the th element itself, so any future column added
to the header row sorts automatically.
Also:
- Agent column no longer has data-sort (per request); cursor: default.
- Removed the "tasks" (n_tasks/N) column from the Overall table.
- Moved the Average column to the last position so per-task scores
read left-to-right before the aggregate.
- Empty-row colspan adjusted from 4+n to 3+n accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- server/api.py +26 -23
|
@@ -941,12 +941,11 @@ _LANDING_TMPL = r"""<!doctype html>
|
|
| 941 |
<thead>
|
| 942 |
<tr>
|
| 943 |
<th class="rank" data-sort="rank">#</th>
|
| 944 |
-
<th
|
| 945 |
-
<th class="num sorted" data-sort="primary">average <span class="arrow">▾</span></th>
|
| 946 |
-
<th class="num" data-sort="n_submissions">tasks</th>
|
| 947 |
{% for t in tasks %}
|
| 948 |
<th class="num" data-sort="{{ t.name }}">{{ t.name }}</th>
|
| 949 |
{% endfor %}
|
|
|
|
| 950 |
</tr>
|
| 951 |
</thead>
|
| 952 |
<tbody>
|
|
@@ -955,18 +954,17 @@ _LANDING_TMPL = r"""<!doctype html>
|
|
| 955 |
<tr data-agent="{{ r.agent }}">
|
| 956 |
<td class="rank{% if loop.index == 1 %} r1{% elif loop.index == 2 %} r2{% elif loop.index == 3 %} r3{% endif %}">{{ loop.index }}</td>
|
| 957 |
<td class="agent">{{ r.agent }}</td>
|
| 958 |
-
<td class="score">{{ "%.3f"|format(r.average) }}</td>
|
| 959 |
-
<td class="num">{{ r.n_tasks }} / {{ n_tasks }}</td>
|
| 960 |
{% for t in tasks %}
|
| 961 |
<td class="num">
|
| 962 |
{% set v = r.per_task[t.name] %}
|
| 963 |
{% if v is not none %}{{ "%.3f"|format(v) }}{% else %}<span class="muted">β</span>{% endif %}
|
| 964 |
</td>
|
| 965 |
{% endfor %}
|
|
|
|
| 966 |
</tr>
|
| 967 |
{% endfor %}
|
| 968 |
{% else %}
|
| 969 |
-
<tr class="empty-row"><td colspan="{{
|
| 970 |
{% endif %}
|
| 971 |
</tbody>
|
| 972 |
</table>
|
|
@@ -1004,7 +1002,7 @@ _LANDING_TMPL = r"""<!doctype html>
|
|
| 1004 |
<thead>
|
| 1005 |
<tr>
|
| 1006 |
<th class="rank" data-sort="rank">#</th>
|
| 1007 |
-
<th
|
| 1008 |
<th class="num sorted" data-sort="primary">{{ t.metric }} <span class="arrow">▾</span></th>
|
| 1009 |
<th class="num" data-sort="n_submissions">Submissions</th>
|
| 1010 |
<th class="date" data-sort="first_seen">First seen</th>
|
|
@@ -1176,21 +1174,26 @@ gtb leaderboard <task></code></pre>
|
|
| 1176 |
});
|
| 1177 |
|
| 1178 |
// ---- sortable columns ----
|
| 1179 |
-
function sortTable(table,
|
| 1180 |
var tbody = table.tBodies[0];
|
| 1181 |
var rows = Array.from(tbody.querySelectorAll('tr')).filter(function (r) {
|
| 1182 |
return !r.classList.contains('empty-row');
|
| 1183 |
});
|
| 1184 |
if (!rows.length) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1185 |
var sortKey = function (r) {
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
|
| 1189 |
-
|
| 1190 |
-
|
| 1191 |
-
|
| 1192 |
-
|
| 1193 |
-
|
| 1194 |
};
|
| 1195 |
rows.sort(function (a, b) {
|
| 1196 |
var av = sortKey(a), bv = sortKey(b);
|
|
@@ -1200,22 +1203,22 @@ gtb leaderboard <task></code></pre>
|
|
| 1200 |
});
|
| 1201 |
rows.forEach(function (r, i) {
|
| 1202 |
tbody.appendChild(r);
|
| 1203 |
-
//
|
| 1204 |
-
var rk = r.cells[0];
|
| 1205 |
if (key === 'primary' && dir === 'desc') {
|
|
|
|
| 1206 |
rk.textContent = (i + 1);
|
| 1207 |
rk.className = 'rank' + (i === 0 ? ' r1' : i === 1 ? ' r2' : i === 2 ? ' r3' : '');
|
| 1208 |
-
} else {
|
| 1209 |
-
// preserve raw rank (1-indexed in original order); fall back to recompute
|
| 1210 |
-
if (!rk.dataset.origRank) rk.dataset.origRank = rk.textContent.trim();
|
| 1211 |
}
|
| 1212 |
});
|
| 1213 |
}
|
| 1214 |
|
| 1215 |
document.querySelectorAll('table.lb thead th').forEach(function (th) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1216 |
th.addEventListener('click', function () {
|
| 1217 |
var table = th.closest('table');
|
| 1218 |
-
var key = th.dataset.sort;
|
| 1219 |
var current = th.classList.contains('sorted')
|
| 1220 |
? (th.dataset.dir === 'asc' ? 'asc' : 'desc')
|
| 1221 |
: null;
|
|
@@ -1231,7 +1234,7 @@ gtb leaderboard <task></code></pre>
|
|
| 1231 |
arrow.className = 'arrow';
|
| 1232 |
arrow.textContent = (dir === 'asc') ? '\u25B4' : '\u25BE';
|
| 1233 |
th.appendChild(arrow);
|
| 1234 |
-
sortTable(table,
|
| 1235 |
});
|
| 1236 |
});
|
| 1237 |
|
|
|
|
| 941 |
<thead>
|
| 942 |
<tr>
|
| 943 |
<th class="rank" data-sort="rank">#</th>
|
| 944 |
+
<th>Agent</th>
|
|
|
|
|
|
|
| 945 |
{% for t in tasks %}
|
| 946 |
<th class="num" data-sort="{{ t.name }}">{{ t.name }}</th>
|
| 947 |
{% endfor %}
|
| 948 |
+
<th class="num sorted" data-sort="primary">average <span class="arrow">▾</span></th>
|
| 949 |
</tr>
|
| 950 |
</thead>
|
| 951 |
<tbody>
|
|
|
|
| 954 |
<tr data-agent="{{ r.agent }}">
|
| 955 |
<td class="rank{% if loop.index == 1 %} r1{% elif loop.index == 2 %} r2{% elif loop.index == 3 %} r3{% endif %}">{{ loop.index }}</td>
|
| 956 |
<td class="agent">{{ r.agent }}</td>
|
|
|
|
|
|
|
| 957 |
{% for t in tasks %}
|
| 958 |
<td class="num">
|
| 959 |
{% set v = r.per_task[t.name] %}
|
| 960 |
{% if v is not none %}{{ "%.3f"|format(v) }}{% else %}<span class="muted">β</span>{% endif %}
|
| 961 |
</td>
|
| 962 |
{% endfor %}
|
| 963 |
+
<td class="score">{{ "%.3f"|format(r.average) }}</td>
|
| 964 |
</tr>
|
| 965 |
{% endfor %}
|
| 966 |
{% else %}
|
| 967 |
+
<tr class="empty-row"><td colspan="{{ 3 + n_tasks }}">No submissions yet β be the first to submit.</td></tr>
|
| 968 |
{% endif %}
|
| 969 |
</tbody>
|
| 970 |
</table>
|
|
|
|
| 1002 |
<thead>
|
| 1003 |
<tr>
|
| 1004 |
<th class="rank" data-sort="rank">#</th>
|
| 1005 |
+
<th>Agent</th>
|
| 1006 |
<th class="num sorted" data-sort="primary">{{ t.metric }} <span class="arrow">▾</span></th>
|
| 1007 |
<th class="num" data-sort="n_submissions">Submissions</th>
|
| 1008 |
<th class="date" data-sort="first_seen">First seen</th>
|
|
|
|
| 1174 |
});
|
| 1175 |
|
| 1176 |
// ---- sortable columns ----
|
| 1177 |
+
function sortTable(table, th, dir) {
|
| 1178 |
var tbody = table.tBodies[0];
|
| 1179 |
var rows = Array.from(tbody.querySelectorAll('tr')).filter(function (r) {
|
| 1180 |
return !r.classList.contains('empty-row');
|
| 1181 |
});
|
| 1182 |
if (!rows.length) return;
|
| 1183 |
+
// Resolve column index from the header's position β works for any layout
|
| 1184 |
+
// (per-task columns are dynamic; previous switch-on-key broke for them).
|
| 1185 |
+
var headers = Array.from(table.tHead.rows[0].cells);
|
| 1186 |
+
var colIndex = headers.indexOf(th);
|
| 1187 |
+
var key = th.dataset.sort;
|
| 1188 |
var sortKey = function (r) {
|
| 1189 |
+
if (key === 'agent') return (r.dataset.agent || '').toLowerCase();
|
| 1190 |
+
if (key === 'first_seen') return r.cells[colIndex].textContent;
|
| 1191 |
+
var txt = (r.cells[colIndex].textContent || '').trim();
|
| 1192 |
+
// Treat "β" / empty as -Infinity so missing scores sink to the bottom
|
| 1193 |
+
// when sorting desc, top when sorting asc.
|
| 1194 |
+
if (txt === '' || txt === 'β') return -Infinity;
|
| 1195 |
+
var n = parseFloat(txt);
|
| 1196 |
+
return isNaN(n) ? txt : n;
|
| 1197 |
};
|
| 1198 |
rows.sort(function (a, b) {
|
| 1199 |
var av = sortKey(a), bv = sortKey(b);
|
|
|
|
| 1203 |
});
|
| 1204 |
rows.forEach(function (r, i) {
|
| 1205 |
tbody.appendChild(r);
|
| 1206 |
+
// Renumber rank only when sorted by the canonical "primary" column desc.
|
|
|
|
| 1207 |
if (key === 'primary' && dir === 'desc') {
|
| 1208 |
+
var rk = r.cells[0];
|
| 1209 |
rk.textContent = (i + 1);
|
| 1210 |
rk.className = 'rank' + (i === 0 ? ' r1' : i === 1 ? ' r2' : i === 2 ? ' r3' : '');
|
|
|
|
|
|
|
|
|
|
| 1211 |
}
|
| 1212 |
});
|
| 1213 |
}
|
| 1214 |
|
| 1215 |
document.querySelectorAll('table.lb thead th').forEach(function (th) {
|
| 1216 |
+
if (!th.dataset.sort) {
|
| 1217 |
+
th.style.cursor = 'default';
|
| 1218 |
+
return; // unsortable column (e.g. Agent) β no click handler
|
| 1219 |
+
}
|
| 1220 |
th.addEventListener('click', function () {
|
| 1221 |
var table = th.closest('table');
|
|
|
|
| 1222 |
var current = th.classList.contains('sorted')
|
| 1223 |
? (th.dataset.dir === 'asc' ? 'asc' : 'desc')
|
| 1224 |
: null;
|
|
|
|
| 1234 |
arrow.className = 'arrow';
|
| 1235 |
arrow.textContent = (dir === 'asc') ? '\u25B4' : '\u25BE';
|
| 1236 |
th.appendChild(arrow);
|
| 1237 |
+
sortTable(table, th, dir);
|
| 1238 |
});
|
| 1239 |
});
|
| 1240 |
|