live timing: Show event status #93

Merged
joaj merged 6 commits from live-timing/event-status into master 2026-09-27 16:25:55 +00:00
Owner

Closes #85

The public live timing site now shows each event's status (pending / active / complete) plus start and end times, taken from the admin event lifecycle transitions.

Data flow

Postgres events (status + started_at/ended_at, stamped on transitions)
  ↓ relay: _sync_event_status() pushes snapshot on change (set_event RPC)
  ↓        also re-pushes full state once per relay restart (self-healing)
SQLite timing.db: new `events` table (name PK, status, started_at, ended_at)
  ↓ get_events(): relay metadata ∪ lap-derived legacy events
Flask: status badge + "Started/Ended … UTC" on the events list and event page

Changes

Postgres (libs/timekeeping_persistence)

  • Migration 20260927_0001: nullable started_at / ended_at columns on events (existing rows stay NULL — displayed as unknown)
  • update_event_status() stamps started_at on PENDING→ACTIVE and ended_at on ACTIVE→COMPLETED
  • get_events() returns the new fields

Relay (live_timing_relay)

  • _sync_event_status() compares a per-event (status, started_at, ended_at) snapshot and calls the new set_event RPC only on change (cheap: fires on transitions, not per poll)

Server (live_timing_server)

  • Additive SQLite events table created in init_db() (no migration needed for existing volumes)
  • New set_event(token, name, status, started_at, ended_at) RPC method
  • get_events() now returns {name, status, started_at, ended_at}; events known only through lap traffic (legacy data) get NULL metadata; ordering: ACTIVE → PENDING → COMPLETED → unknown, then most recent activity
  • set_event upserts, so a relay restart heals any transitions the server missed

UI (live_timing_server/templates)

  • Events list: colour-coded badge (green Active / amber Pending / grey Complete) + start/end timestamps per card
  • Event page: status chip in the header + timestamps

Notes

  • Empty DBs (fresh volumes) get the events table automatically; existing volumes get it on next init_db().
  • The admin UI (ui/) already manages PENDING/ACTIVE/COMPLETED transitions — no changes needed there; timestamps are stamped server-side.
  • black 26.1.0 with target-version py314 rewrites except (A, B): into PEP 758 bare form (invalid on the Python 3.12 the containers run) — models.py was kept compatible deliberately; pre-existing formatting churn left untouched since live_timing_server is not part of make check.

Testing

  • 15/15 live_timing_server tests (5 new: legacy-only events, set_event roundtrip/update, empty-name rejection, ACTIVE-first ordering, join dedup)
  • End-to-end smoke: relay _poll_once → real XML-RPC set_event → SQLite → Flask routes render correct status/times; cache suppresses redundant pushes; invalid transitions still rejected
  • make check passes
Closes #85 The public live timing site now shows each event's **status** (pending / active / complete) plus **start and end times**, taken from the admin event lifecycle transitions. ## Data flow ``` Postgres events (status + started_at/ended_at, stamped on transitions) ↓ relay: _sync_event_status() pushes snapshot on change (set_event RPC) ↓ also re-pushes full state once per relay restart (self-healing) SQLite timing.db: new `events` table (name PK, status, started_at, ended_at) ↓ get_events(): relay metadata ∪ lap-derived legacy events Flask: status badge + "Started/Ended … UTC" on the events list and event page ``` ## Changes **Postgres** (`libs/timekeeping_persistence`) - Migration `20260927_0001`: nullable `started_at` / `ended_at` columns on `events` (existing rows stay NULL — displayed as unknown) - `update_event_status()` stamps `started_at` on PENDING→ACTIVE and `ended_at` on ACTIVE→COMPLETED - `get_events()` returns the new fields **Relay** (`live_timing_relay`) - `_sync_event_status()` compares a per-event `(status, started_at, ended_at)` snapshot and calls the new `set_event` RPC only on change (cheap: fires on transitions, not per poll) **Server** (`live_timing_server`) - Additive SQLite `events` table created in `init_db()` (no migration needed for existing volumes) - New `set_event(token, name, status, started_at, ended_at)` RPC method - `get_events()` now returns `{name, status, started_at, ended_at}`; events known only through lap traffic (legacy data) get `NULL` metadata; ordering: ACTIVE → PENDING → COMPLETED → unknown, then most recent activity - `set_event` upserts, so a relay restart heals any transitions the server missed **UI** (`live_timing_server/templates`) - Events list: colour-coded badge (green Active / amber Pending / grey Complete) + start/end timestamps per card - Event page: status chip in the header + timestamps ## Notes - Empty DBs (fresh volumes) get the events table automatically; existing volumes get it on next `init_db()`. - The admin UI (`ui/`) already manages PENDING/ACTIVE/COMPLETED transitions — no changes needed there; timestamps are stamped server-side. - black 26.1.0 with `target-version py314` rewrites `except (A, B):` into PEP 758 bare form (invalid on the Python 3.12 the containers run) — `models.py` was kept compatible deliberately; pre-existing formatting churn left untouched since `live_timing_server` is not part of `make check`. ## Testing - 15/15 `live_timing_server` tests (5 new: legacy-only events, set_event roundtrip/update, empty-name rejection, ACTIVE-first ordering, join dedup) - End-to-end smoke: relay `_poll_once` → real XML-RPC `set_event` → SQLite → Flask routes render correct status/times; cache suppresses redundant pushes; invalid transitions still rejected - `make check` passes
live_timing: show event status with start/end times
All checks were successful
ci/woodpecker/pr/main Pipeline was successful
f7119e112a
Implements #85. The public live timing site now shows each event's
status (pending / active / complete) and the timestamps of the
PENDING->ACTIVE and ACTIVE->COMPLETED transitions.

Postgres: new migration adds nullable started_at/ended_at to events;
update_event_status stamps them on the corresponding transitions and
get_events returns them. Existing rows keep NULL (unknown).

Relay: _sync_event_status pushes (status, started_at, ended_at) to the
new set_event RPC whenever the snapshot changes, and re-pushes the full
state once per relay restart, healing transitions missed while offline.

Server: additive SQLite events table + set_event RPC method; get_events
joins relay metadata with lap-derived events (legacy events without
metadata keep NULL status), sorted ACTIVE, PENDING, COMPLETED, unknown.

UI: status badge and start/end times on the events list and on each
event page header.
live_timing_server: fix Dockerfile for subdir layout, join relay to timekeeping-net
All checks were successful
ci/woodpecker/pr/main Pipeline was successful
2b928aa3ed
The Dockerfile predates the live_timing_server/ directory: it copied the
repo-root requirements.txt (fastapi stack, no Flask/gunicorn) and the
repo root itself, so 'python rpc_server.py' found nothing at /app. Copy
the service's own requirements.txt and sources instead.

Add live_timing_relay to timekeeping-net (same pattern as api) so it can
reach a locally deployed live_timing_rpc service; the default RPC
endpoint remains the production URL.
live_timing: badge labels Live/Upcoming/Finished
All checks were successful
ci/woodpecker/pr/main Pipeline was successful
38c5f7bda5
ACTIVE events are user-facing "Live" (with pulsing dot), PENDING are
"Upcoming", COMPLETED are "Finished" — the internal status values stay
unchanged.
live_timing: scheduled start time for upcoming events
All checks were successful
ci/woodpecker/pr/main Pipeline was successful
55edc4ae48
Add optional scheduled_start_at to events. Set at creation (admin UI
datetime input / POST /events), pushed by the relay alongside status,
and displayed as "Starts …" on Upcoming events of the live timing site
(also visible for Live events without an actual started_at).

Postgres: migration 20260927_0002 adds the nullable column; naive
datetimes are assumed UTC on insert. Relay: snapshot extends to
(status, scheduled, started, ended). Server: additive SQLite column +
set_event/get_events carry it through. UI: datetime-local input in the
create-event form.
live_timing: prominent start/end time chips
Some checks failed
ci/woodpecker/pr/main Pipeline was canceled
e3ff1ef63a
Replace the small muted meta lines with dedicated time chips on the
events list and event pages: bordered pill with uppercase label and a
larger tabular-numeral value. Scheduled starts get the accent colour;
started/ended stay neutral.
live_timing: keep compact time line on the events list
All checks were successful
ci/woodpecker/pr/main Pipeline was successful
b4088f7315
The chip treatment was too heavy for the list view. Restore the single
muted meta line there (scheduled start takes precedence; otherwise
started/ended) and keep the prominent chips on the event page.
joaj merged commit 85583f1268 into master 2026-09-27 16:25:55 +00:00
joaj deleted branch live-timing/event-status 2026-09-27 16:25:56 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
fspt/timekeeping!93
No description provided.