Zhu Jiajun (jz28583) Claude Opus 4.7 (1M context) commited on
Commit
bf48fd7
Β·
1 Parent(s): f819f00

Landing table: fix per-column sort + drop tasks col + move avg to last

Browse files

Sort 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>

Files changed (1) hide show
  1. server/api.py +26 -23
server/api.py CHANGED
@@ -941,12 +941,11 @@ _LANDING_TMPL = r"""<!doctype html>
941
  <thead>
942
  <tr>
943
  <th class="rank" data-sort="rank">#</th>
944
- <th data-sort="agent">Agent</th>
945
- <th class="num sorted" data-sort="primary">average <span class="arrow">&#9662;</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="{{ 4 + n_tasks }}">No submissions yet β€” be the first to submit.</td></tr>
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 data-sort="agent">Agent</th>
1008
  <th class="num sorted" data-sort="primary">{{ t.metric }} <span class="arrow">&#9662;</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 &lt;task&gt;</code></pre>
1176
  });
1177
 
1178
  // ---- sortable columns ----
1179
- function sortTable(table, key, dir) {
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
- switch (key) {
1187
- case 'rank': return parseInt(r.cells[0].textContent, 10) || 0;
1188
- case 'agent': return (r.dataset.agent || '').toLowerCase();
1189
- case 'primary': return parseFloat(r.cells[2].textContent) || 0;
1190
- case 'n_submissions': return parseInt(r.cells[3].textContent, 10) || 0;
1191
- case 'first_seen': return r.cells[4].textContent;
1192
- default: return 0;
1193
- }
1194
  };
1195
  rows.sort(function (a, b) {
1196
  var av = sortKey(a), bv = sortKey(b);
@@ -1200,22 +1203,22 @@ gtb leaderboard &lt;task&gt;</code></pre>
1200
  });
1201
  rows.forEach(function (r, i) {
1202
  tbody.appendChild(r);
1203
- // recompute rank cell only when sorted by primary desc (i.e. canonical order)
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 &lt;task&gt;</code></pre>
1231
  arrow.className = 'arrow';
1232
  arrow.textContent = (dir === 'asc') ? '\u25B4' : '\u25BE';
1233
  th.appendChild(arrow);
1234
- sortTable(table, key, dir);
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">&#9662;</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">&#9662;</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