Add a results archive and purge orphaned passing rows on delete
Until now there was no way to find a completed session without already knowing which event it belonged to - navigation was strictly Events -> one event -> its sessions. A new "Resultatarkiv" view lists every finished session across all events, searchable by event/session name, class, driver and date range. Opening a session reuses the existing single-session leaderboard builder for standings and the existing per-competitor lap lookup for lap history - no new results logic - plus a session-wide penalties/ corrections list (resultsBySession[id].adjustments), previously only visible in the live judging action log. Export buttons reuse the existing per-event PDF and race-package JSON export functions rather than adding a new format. Investigated what should happen to raw timing data when a race is deleted. Training statistics and session replay already read from their own independent, deliberately-retained copies that survive deletion by existing design - unaffected either way. The one thing that outlived deletion with no purpose was the raw `passings` SQLite rows (write-only; nothing in the app reads them), growing forever with driver IDs never purged. A new POST /api/passings/purge endpoint now removes exactly the deleted session(s)' rows, called as a best-effort side call from both delete-event and delete-session. Also fixes a real bug found while building this: ensureSessionResult guarded adjustments/splits/pitEvents/pitStops against being missing on an already-existing result, but not passings itself, so viewing lap history for such a result (e.g. an older/imported race package) crashed outright. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
getSessionTiming,
|
||||
isHighPointsTable,
|
||||
isUntimedSession,
|
||||
ensureSessionResult,
|
||||
} from "../src/timing_logic.js";
|
||||
|
||||
function createUid() {
|
||||
@@ -457,3 +458,19 @@ test("paused session timing freezes and resumes without counting the pause", ()
|
||||
assert.equal(resumed.elapsedMs, 60_000);
|
||||
assert.equal(resumed.remainingMs, 240_000);
|
||||
});
|
||||
|
||||
test("ensureSessionResult backfills a missing passings array on an already-existing result", () => {
|
||||
// A result can already exist (loaded from an older export, or a hand-edited/imported race
|
||||
// package) without ever going through the brand-new-session initialization branch, the same
|
||||
// way it can be missing adjustments/splits/pitEvents/pitStops - passings needs the same guard.
|
||||
const state = { resultsBySession: { session_1: { competitors: {} } } };
|
||||
const result = ensureSessionResult("session_1", { state });
|
||||
assert.deepEqual(result.passings, []);
|
||||
assert.deepEqual(result.adjustments, []);
|
||||
assert.deepEqual(result.splits, []);
|
||||
assert.deepEqual(result.pitEvents, []);
|
||||
assert.deepEqual(result.pitStops, []);
|
||||
|
||||
const brandNew = ensureSessionResult("session_2", { state });
|
||||
assert.deepEqual(brandNew, { passings: [], splits: [], pitEvents: [], pitStops: [], competitors: {}, adjustments: [] });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user