User Guide
Version 1 · New setup/operating guide derived from CLAUDE.md, README.md, and repository scripts
User Guide
Audience
Anyone setting up, running, or operating this API for the first time — locally for development, or as a standing Windows service.
Prerequisites
- Python 3.x with
pip - Network access to a MySQL/MariaDB instance holding
race,run,courses(and, if you want to persist stats, write access to a target DB formeeting_stats_payloads) - Real DB credentials — ask Thilina or Ryan; never put real credentials in
config.example.json(see overview → Security notes)
First-time setup
- Install dependencies:
(or, per CLAUDE.md:pip install -r requirements.txtpip install fastapi uvicorn pymysql pydantic python-multipart) - Copy the config template and fill in real values:
Editcopy config.example.json config.jsondatabase.host/port/user/password/source_db/target_db. Leaveexternal_api.enabledasfalseunless you're specifically testing the external-API bypass path (see architecture). - Verify DB connectivity:
python test_db_connection.py
Running for development
uvicorn trainer_stats_api:app --host 0.0.0.0 --port 8006 --reload
or
python trainer_stats_api.py
Then check:
http://127.0.0.1:8006/health→{"status": "ok"}http://127.0.0.1:8006/→ dashboard UIhttp://127.0.0.1:8006/docs→ Swagger UIhttp://127.0.0.1:8006/api-docs→ hand-written reference with "Try it" buttonshttp://127.0.0.1:8006/api-test→ manual API tester
Using the dashboard
/ serves a searchable trainer/jockey grid with filters (search, sort, min starts, min win %,
last-win filter) and a detail panel. It calls the API using the shared static token
(tds-stats-secure-token-2026) client-side. Without a live DB, regenerate_sample.py can
regenerate trainers_complete.json — sample data for exercising the UI without a database.
Calling the API directly
See api-reference for the full endpoint list and request/response shapes. Quick smoke test:
curl "http://127.0.0.1:8006/trainerStats?date=2026-06-03&courseId=116" \
-H "Authorization: Bearer tds-stats-secure-token-2026"
Running as a persistent Windows service
.\.venv\Scripts\python.exe run_service.py
# or: .\run_service.bat
Install/uninstall (run as Administrator):
install_service.bat
uninstall_service.bat
Testing
| Script | Purpose |
|---|---|
test_db_connection.py |
Verifies DB connectivity using config.json |
test_api.py |
Exercises API endpoints |
quick_test.py |
Basic smoke test |
regenerate_sample.py |
Regenerates trainers_complete.json sample data for local UI testing without a DB |
Troubleshooting
RuntimeError: Config file not found: config.json— you haven't copiedconfig.example.json→config.jsonyet (orDB_CONFIG_PATHpoints somewhere wrong).- 401 on any endpoint except
/,/health,/login— missing or wrong token; supplyAuthorization: Bearer tds-stats-secure-token-2026,?token=..., or?sso_token=.... - Empty trainer/jockey list from
/trainer-statswithcourseNameset (nocourseId) — see the known SQL quirk in database before assuming there's no data for that date. /saved-statsalways returns{"status": "not_found"}— either nothing has been saved for that date/course yet, or the target DB'smeeting_stats_payloadstable doesn't exist yet (both cases look identical to the caller — see database).- Server logs —
loggeris configured atINFOlevel to stdout;server_error.login the repo root holds a historical error log from a prior run.
Verification checklist after a config or code change
python test_db_connection.pysucceeds.GET /healthreturns{"status": "ok"}.- A real
GET /trainerStats?date=...&courseId=...call (with a date/course known to have race data) returns a non-emptytrainersarray. - If you changed persistence:
POST /save-statsfollowed by a matchingGET /saved-statsreturns the same payload back.