Skip to content

Commit a82da89

Browse files
author
Nils Bars
committed
Drop empty teams from the points-over-time chart
best_sum.computeChartScoresOverTime() used to inject a synthetic {time: now, score: 0} point for any team present in the submission map but without any events inside an assignment window. That placeholder rendered as a stray zero data point in the chart. Omit such teams from the result instead. Also refresh docs/SCOREBOARD.md to describe the current ECharts-based rendering, the theme-token color wiring, the assignment-boundary label layout, and the x-axis range expansion that keeps boundary markers in the viewport.
1 parent 178238d commit a82da89

2 files changed

Lines changed: 27 additions & 10 deletions

File tree

docs/SCOREBOARD.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ Ranking is computed client-side by
9696
is their highest in-window submission score, and the ranking score is
9797
the sum of those bests across challenges.
9898

99+
`computeChartScoresOverTime()` emits cumulative points per team over
100+
time for the points-over-time chart. Only submission events that fall
101+
inside an assignment's `[start, end]` window are considered; teams with
102+
no in-window events are omitted from the chart data entirely.
103+
99104
## API Endpoints
100105

101106
Both endpoints live in `webapp/ref/frontend_api/scoreboard.py`, are
@@ -166,17 +171,30 @@ endpoints and hands the data to the components under
166171

167172
- `RankingTable.vue` — sorted points table with earned badge icons.
168173
- `HighscoreCard.vue` — per-assignment top-score card.
169-
- `PointsOverTimeChart.vue` — cumulative points line chart with
170-
assignment-boundary annotations.
171-
- `ChallengePlot.vue` — per-challenge scatter of best-ever improvements
172-
(regressions are filtered out).
174+
- `PointsOverTimeChart.vue` — cumulative points line chart with dashed
175+
vertical markLines at each assignment boundary. The boundary labels
176+
("Assignment N") are rotated 90° and sit at the vertical midpoint of
177+
each line.
178+
- `ChallengePlot.vue` — per-challenge line chart of each team's
179+
monotonically best score over time (regressions are filtered out).
180+
When any task has a `baseline`, a horizontal dashed markLine at the
181+
sum of per-task baselines is drawn with a centered "baseline" label.
173182
- `Countdown.vue` — timer for the currently-running assignment's deadline.
174183

175184
All charts use Apache ECharts with native `dataZoom` on the time axis.
176-
The default interaction model is wheel/pinch zoom plus drag-to-pan on the
177-
x-axis, with a slider scrubber below the chart for coarse navigation.
178-
The x-axis is capped at the earliest data point so users can't pan into
179-
empty pre-data space.
185+
The default interaction model is wheel/pinch zoom plus drag-to-pan on
186+
the x-axis, with a slider scrubber below the chart for coarse
187+
navigation. The x-axis range spans the union of submission timestamps
188+
and assignment boundaries (with 2% padding) so every boundary marker
189+
stays in the viewport even when no data straddles it.
190+
191+
Chart colors (axes, grid, legend, tooltip, data palette, markLine) are
192+
read from the active Vuetify `--v-theme-*` tokens in
193+
`spa-frontend/src/components/scoreboard/chartSetup.ts`. A
194+
`MutationObserver` on `document.body`'s class list watches for theme
195+
toggles and triggers each mounted chart to re-render with the new
196+
tokens, so the light and dark themes each get their own high-contrast
197+
palette without a page reload.
180198

181199
Badges are a visual consequence of crossing a scoring threshold — no
182200
dedicated backend. Badge assets are static SVG files at

spa-frontend/src/ranking/best_sum.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,8 @@ export function computeChartScoresOverTime(
109109
out[ev.team].push({ time: ev.ts.getTime(), score: totals[ev.team] });
110110
}
111111

112-
const nowMs = Date.now();
113112
for (const team of teamSet) {
114-
if (out[team].length === 0) out[team].push({ time: nowMs, score: 0 });
113+
if (out[team].length === 0) delete out[team];
115114
}
116115
return out;
117116
}

0 commit comments

Comments
 (0)