QDG Knowledge Base Read-only viewer QWebHub
overview

Overview

Version 2 · Trimmed detailed architecture/DB/API content into dedicated architecture, database, and api-reference pages; overview now summarizes and links out, plus links to new user-guide and faq pages

Stats (Meeting Stats API)

Single-file FastAPI service (trainer_stats_api.py, app title "Meeting Stats API", currently v2.0.0) that computes trainer and jockey performance statistics for horse-racing meetings — win/place rates, impact values by SP price band, meeting averages, top horses — and optionally persists/serves those stats. It also serves a small static dashboard, login page, and interactive API docs/test pages from static/.

There is no separate module structure; the entire backend lives in one file.

Where to look

  • architecture — data flow, external API bypass, endpoint naming split, middleware
  • database — connections, schema, query patterns, a known SQL quirk
  • api-reference — full endpoint table, auth, request/response examples
  • user-guide — setup, running, testing, troubleshooting
  • faq — recurring questions with durable answers
  • changelog — delivered changes over time

Summary

build_entity_payload() is the key architectural seam: pure Python, no DB dependency, drives both trainer and jockey stats from a flat list of race+run row dicts. Rows are fetched from a source DB (read-only: race, run, courses) and results can be returned directly or persisted via POST /save-stats into a target DB (meeting_stats_payloads). An optional external-API bypass lets /trainerStats, /jockeyStats, /meetingStats fetch already-computed stats from a third party instead of the local DB when config.json → external_api.enabled is true.

Configuration

Copy config.example.json → config.json before running (git-ignored). Path can be overridden with DB_CONFIG_PATH; every DB field can also be overridden via env var: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_SOURCE, DB_TARGET. external_api block is optional — omit or set enabled: false to always use the local DB path. Details: database.

Auth

Single shared static bearer token (tds-stats-secure-token-2026), checked by the verify_token dependency. Accepted via Authorization: Bearer, ?token=, or ?sso_token= query param. POST /login with admin/admin123 (hardcoded) simply returns this same static token — there is no per-user session, expiry, or revocation mechanism. This is a shared API key, not real authentication.

The only server-side state is USER_CONTEXT — an in-memory dict keyed by client IP, set on /trainerStats, /jockeyStats, /meetingStats calls, expiring after 5 minutes, read via GET /api/user-context. Purely a UI convenience cache (lets the UI restore "last meeting queried"); not persisted across restarts and not an auth mechanism.

GET /login redirects to an external Appsmith SSO login when the request looks Appsmith-embedded (referer contains appsmith, no ?direct=). POST /api/logout proxies to an external auth service by sessionId.

Frontend

Static files served from static/ (mounted at /static), no build step:

  • index.html → / — main dashboard (searchable trainer/jockey grids, filters, detail panel)
  • login.html → /login fallback — local login form
  • api_docs.html → /api-docs — hand-written interactive endpoint reference with "Try it"
  • api_test.html → /api-test — manual API tester
  • query_executor.html — ad-hoc query tool, not routed

Running

pip install fastapi uvicorn pymysql pydantic python-multipart
copy config.example.json config.json   # then fill in real credentials
uvicorn trainer_stats_api:app --host 0.0.0.0 --port 8006 --reload
# or: python trainer_stats_api.py

Access: http://127.0.0.1:8006/ (dashboard), /docs (Swagger), /redoc (ReDoc), /api-docs (custom reference). Full setup/operating steps: user-guide.

As a persistent Windows service:

.\.venv\Scripts\python.exe run_service.py   # or .\run_service.bat

Install/uninstall as Administrator: install_service.bat / uninstall_service.bat.

Security notes

  • Single shared static token: anyone with it has full access to every endpoint; no per-user scoping, expiry, or revocation short of changing the constant and redeploying.
  • Hardcoded login credentials (admin/admin123) are effectively decorative — /login returns the same static token regardless of who logs in.
  • config.example.json must never contain real credentials — it is the template only.
  • config.json should stay out of version control; prefer env vars in production.
  • DB queries use parameterized queries (PyMySQL %s/%(name)s placeholders) — no string-interpolated SQL found; keep this pattern for new queries.
  • CORS is currently wide open (allow_origins=["*"], allow_credentials=True) — acceptable for an internal token-gated API, worth tightening before wider exposure.

Further reading in this repository

FULL_PROJECT_DOCUMENTATION.md in the repo root has a more exhaustive single-file reference covering similar ground to this KB — useful as source evidence but not itself kept in sync with the KB.

Updated by Claude on Aug. 12, 2026, 9:51 a.m. · Task: updatewiki create project documentation