Files
linusandClaude Sonnet 5 03f6fdb0d9 Erstumsetzung: OAMC Turnierauswertung (AP 0–7, 9)
* Datenmodell + Alembic-Initialmigration (Plan §4), Seed Saison 2026
* Wertungs-Engine (Plan §3.3), regelbasiert je Kategorie/Saison
  - Abnahmetest gegen die Ergebnisliste 2026-05-31
* Meisterschaftsberechnung mit Streichresultaten + Tie-Break
  (PLATZHALTER-Punktetabelle, O-1 offen)
* Ingest-API (idempotent, dry_run) inkl. Excel-Leser und Fahrer-Matching/Merge
* Zeitmessungs-Endpoint (Batch, idempotent, unzugeordnet/unplausibel) + SSE-Anzeige
  - Mess-Pi-Client bewusst NICHT enthalten (docs/zeitmessung-endpoint.md)
* Frontend: öffentliche Seiten + internes Backend (Jinja2/HTMX, kein Build)
* Auslieferung: Docker (multi-arch) + docker-compose sowie Debian-Paket/APT-Repo
* 57 Tests grün gegen echte PostgreSQL-Testdatenbank

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 18:49:00 +02:00

139 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OAMC Turnierauswertung
Webanwendung zur Auswertung von **ADAC Motorrad-Turnieren** des OAMC Reinheim e.V.:
Ergebnisse (Fehlerpunkte + Zeit → Platzierung), Fahrerdatenbank mit Historie,
Meisterschaftswertung und ein **Endpoint für die Lichtschranken-Zeitmessung**.
Fachliche Grundlagen, offene Fragen und Architekturbegründung: **[`plan.md`](plan.md)**.
> Der Mess-Pi-Client (GPIO/Lichtschranke) ist **nicht** Teil dieses Repos — die
> Anwendung stellt nur den Endpoint bereit. Contract:
> [`docs/zeitmessung-endpoint.md`](docs/zeitmessung-endpoint.md).
---
## Stack
| Schicht | Wahl |
|---|---|
| DB | PostgreSQL 17 (JSONB, ARRAY, `pg_trgm`) |
| Backend | Python 3.11+ · FastAPI · SQLAlchemy 2 · Alembic · Pydantic v2 |
| Frontend | serverseitig gerendertes Jinja2 + HTMX (kein Node, kein Build) |
| Live | Server-Sent Events (`sse-starlette`) |
| Auslieferung | **Docker-Image** *und* **Debian-Paket** (`.deb` / eigenes APT-Repo) |
## Projektstruktur
```
src/oamc/
models/ ORM (§4 im Plan)
wertung/ Wertungs-Engine — der Kern (§3.3). engine.py + service.py
meisterschaft/ Punkte, Streichresultate, Tie-Break, Endlaufquali (§3.4)
matching/ Fahrer-Dublettenerkennung + Merge (§6.4)
importer/ Bulk-Ingest + Excel-Leser
geraete/ Zeitmessungs-Endpoint-Logik (§3.6)
api/v1/ read.py · ingest.py · geraete.py · admin.py
frontend/ public.py · intern.py + templates/ static/
migrations/ Alembic
tests/ pytest gegen echte PostgreSQL-Testdatenbank
docker/ Dockerfile · nginx.conf · entrypoint.sh
debian/ .deb-Paketierung (venv unter /opt/oamc-turnier)
deploy/ systemd-Unit · nginx-vHost · Backup-Skript (bare metal)
packaging/apt-repo/ reprepro-Konfiguration + Build-Skript
```
## Entwicklung
```sh
python3 -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env # DATABASE_URL anpassen
createdb oamc_dev # PostgreSQL-Rolle mit CREATEDB nötig
alembic upgrade head
oamc-turnier seed # Stammdaten Saison 2026
oamc-turnier benutzer-anlegen admin --rolle admin
oamc-turnier serve --reload # http://127.0.0.1:8000 · /docs
```
### Tests
Brauchen eine erreichbare PostgreSQL-Testdatenbank (`OAMC_DATABASE_URL`, Default
`…/oamc_test`). Das Schema wird pro Lauf frisch migriert, jeder Test läuft in einer
zurückgerollten Transaktion.
```sh
createdb oamc_test
pytest
```
Der **Abnahmetest der Wertungs-Engine** (`tests/test_wertung_regression_2026_05_31.py`)
rechnet die im Plan §3.3 zitierten Zeilen der realen Ergebnisliste vom 31.05.2026 nach.
## Auslieferung 1 — Docker / „docker repo"
```sh
cp .env.example .env # OAMC_SECRET_KEY setzen!
docker compose up -d # db + app + nginx → http://localhost:8080
docker compose exec app oamc-turnier seed
# Image für eine Registry bauen/pushen:
docker build -t <registry>/oamc-turnier:0.1.0 -f docker/Dockerfile .
docker push <registry>/oamc-turnier:0.1.0
```
Multi-Arch für den Raspberry Pi 4:
```sh
docker buildx build --platform linux/arm64,linux/amd64 \
-t <registry>/oamc-turnier:0.1.0 -f docker/Dockerfile --push .
```
Migrationen laufen automatisch beim Containerstart (`docker/entrypoint.sh`).
## Auslieferung 2 — Debian-Paket / eigenes APT-Repo
```sh
sudo apt install build-essential debhelper devscripts python3-venv
./packaging/apt-repo/build-deb.sh # → ../oamc-turnier_0.1.0_*.deb
sudo apt install ../oamc-turnier_0.1.0_*.deb
```
Das Paket bringt ein eigenes virtualenv unter `/opt/oamc-turnier/venv`, eine
systemd-Unit und Beispielkonfiguration mit. Danach:
```sh
sudoedit /etc/oamc-turnier/oamc-turnier.env # DATABASE_URL, SECRET_KEY
sudo systemctl enable --now oamc-turnier
sudo -u oamc /opt/oamc-turnier/venv/bin/oamc-turnier seed
```
Eigenes signiertes APT-Repo aufsetzen: [`packaging/apt-repo/README.md`](packaging/apt-repo/README.md).
## API-Überblick (`/docs` für die vollständige OpenAPI-Doku)
| Bereich | Auth | Beispiele |
|---|---|---|
| **Read** | | `GET /api/v1/turniere/{id}/ergebnisse`, `/api/v1/fahrer?q=`, `/api/v1/meisterschaften/{id}/stand` |
| **Ingest** | `X-API-Key` | `POST /api/v1/turniere/{id}/ergebnisse` (idempotent, `?dry_run=true`), `POST …/import/xlsx` |
| **Geräte** | `Authorization: Bearer <geräte-token>` | `POST /api/v1/zeitmessungen` (Batch, idempotent), SSE `GET /api/v1/turniere/aktiv/anzeige` |
| **Admin** | interne Session | `POST /api/v1/admin/geraete`, `…/zeitmessungen/{id}/zuordnen`, `…/meisterschaften/{id}/berechnen`, `…/fahrer/merge` |
## Bekannte offene Punkte (aus `plan.md` §10)
Diese sind **im Code klar markiert** und blockieren einen offiziellen Produktivgang:
* **O-1** — echte Meisterschafts-Punktetabelle fehlt. Es läuft eine
`PLATZHALTER_PUNKTETABELLE`; jeder berechnete Stand trägt einen Warnhinweis.
* **O-2** — Zuordnung `S 1``S 9` / `A 1``A 4` → Fahrzeugtyp: Klassen sind
angelegt, `fahrzeugtyp` bleibt `NULL`.
* **Wertungsregeln §3.3** sind aus PDFs *abgeleitet* — vor Produktivgang gegen die
ADAC-Turnierordnung 2026 zu verifizieren. Die Engine ist regelbasiert
konfigurierbar (`oamc.wertung.engine.REGELN`), nicht fest verdrahtet.
* **O-13/O-14/O-16** — Details der Zeitmessung (was wird gemessen, Zuordnung zum
Fahrer, Genauigkeit): siehe `docs/zeitmessung-endpoint.md`.
* **§11 Datenschutz** — Fahrerprofile sind `noindex` + `robots.txt`-gesperrt,
Geburts*jahr* statt -datum, Opt-out-Anonymisierung (`fahrer.anzeige_anonymisiert`)
ist umgesetzt; die Vorstandsentscheidung zur öffentlichen Darstellung von
Jugenddaten steht noch aus.