Skip to content

refactor: define every sport once, in a catalogue - #28

Merged
danielededo merged 1 commit into
mainfrom
refactor/sport-catalogue
Oct 11, 2026
Merged

danielededo merged 1 commit into
mainfrom
refactor/sport-catalogue

Conversation

@danielededo

Copy link
Copy Markdown
Owner

Step 1 of supporting more sports, strength first. What differs between sports was spread over seven places:

  • Backend: an alias table in each of the FIT, TCX and GPX parsers.
  • Frontend: the list of pace sports in formatters.js (which also decided the cadence unit), labels made by capitalising the key, colours in theme.js, and the sport order in SportBreakdown.jsx.

Adding a sport meant finding all of them. Now it's one entry.

What changes

Backend

  • New app/services/sports.py: one Sport per sport, with key, label, pace or speed, cadence unit, palette colour slot, and its FIT, TCX and GPX aliases. Aliases are per format, because Strava's GPX 9 means a run and nothing says it does in TCX.
  • The three parsers drop their tables and call sport_from_file(format, declared). An unknown name is still kept as written (rowing stays rowing), and no name at all is other, as before.
  • New GET /api/sports: the catalogue in display order, without the file aliases.
  • New scripts/export_sports.py: writes the catalogue for the frontend tests, no database needed.

Frontend

  • New src/sports.js registry, loaded by App together with the profile before anything renders. If the catalogue can't be loaded, the app shows the same "Cannot reach the API" screen as for the profile.
  • formatters.js (pace or speed, cadence unit, label), theme.js (sportColor) and SportBreakdown (order) read from it. A sport the catalogue doesn't list reads by speed, in the "other" colour, under its own name, which matches the old behaviour.
  • SPORT_ORDER in theme.js is renamed PALETTE_SLOTS, since it orders colour slots, not sports.

Docs: README (API table, a new "Adding a sport" section, layout, the out-of-date test counts) and a new "Sports" section in docs/DESIGN.md.

No behaviour change

  • Parsers: before swapping them, the new lookup was compared with the three old tables for every alias, plus empty, None, padded, mixed-case and unknown values. 0 mismatches out of 56.
  • Frontend: the existing frontend tests pass unchanged, apart from the SPORT_ORDER rename.
  • One deliberate difference: walking used to sort first in the per-sport breakdown, because it wasn't in SPORT_ORDER (indexOf returned -1). It now has its own place after hiking.

Keeping the frontend tests honest

The frontend tests run without a server, so they read a copy of the catalogue (frontend/tests/sports.json). test_the_frontend_tests_use_the_real_catalogue fails as soon as that copy differs from what the API serves, so the copy can't quietly become a second definition. python -m scripts.export_sports regenerates it.

Checks

  • Backend: ruff lint and format, pytest 374 passed (18 new in tests/test_sports.py), coverage 96.7% (catalogue and route 100%).
  • Frontend: eslint, build, vitest 246 passed across two full runs (8 new). One of them is adding a sport > needs nothing but a catalogue entry: a sport defined only in the catalogue gets its label, pace, cadence unit, colour and order. One earlier full run on this machine had a single timeout in DashboardRecords; it passed alone twice and in the next two full runs.

Next

Step 2: strength as the first sport without distance (FIT training / strength_training), using this catalogue.

What differs between sports lived in seven places: an alias table in each
of the three parsers, and the pace list, cadence unit, labels, colours and
order in the frontend. Adding a sport meant finding all of them.

app/services/sports.py now holds one entry per sport: label, pace or speed,
cadence unit, colour slot, and per-format file aliases. The parsers look
sports up there, GET /api/sports serves it, and the dashboard loads it at
startup and reads it everywhere. No behaviour change: the new lookup was
checked against the old tables for every alias. A backend test keeps the
frontend tests' copy of the catalogue in step.
@danielededo
danielededo merged commit 7c04fb3 into main Oct 11, 2026
5 checks passed
@danielededo
danielededo deleted the refactor/sport-catalogue branch October 11, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant