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>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""Meisterschaftsberechnung (AP 3) — mit PLATZHALTER-Punktetabelle (O-1 offen)."""
|
||||
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from oamc.meisterschaft.berechnung import (
|
||||
PLATZHALTER_PUNKTETABELLE,
|
||||
berechne_stand,
|
||||
gewertete_laeufe,
|
||||
punkte_fuer_platz,
|
||||
)
|
||||
from oamc.models import Meisterschaft
|
||||
from oamc.models.enums import VeranstaltungStatus
|
||||
from tests import factories as f
|
||||
|
||||
|
||||
def test_punkte_fuer_platz_platzhalter():
|
||||
assert punkte_fuer_platz(1) == Decimal(25)
|
||||
assert punkte_fuer_platz(10) == Decimal(6)
|
||||
assert punkte_fuer_platz(99) == Decimal(1)
|
||||
assert punkte_fuer_platz(None) == Decimal(0)
|
||||
|
||||
|
||||
def test_streichresultate_tabelle():
|
||||
tab = {"12": 8, "9": 7, "4": 2}
|
||||
assert gewertete_laeufe(12, tab) == 8
|
||||
assert gewertete_laeufe(10, tab) == 7 # naechstkleinerer Schluessel (9)
|
||||
assert gewertete_laeufe(3, tab) == 3 # unterhalb der Tabelle: kein Streichresultat
|
||||
|
||||
|
||||
def _lauf(db, k, ergebnisse, *, datum, zaehlt=True):
|
||||
"""ergebnisse = [(fahrer, platz), ...]"""
|
||||
v = f.veranstaltung(db, datum=datum, status=VeranstaltungStatus.FINAL)
|
||||
t = f.turnier(db, v, zaehlt_meisterschaft=zaehlt)
|
||||
for i, (fahrer, platz) in enumerate(ergebnisse, start=1):
|
||||
s = f.start(db, t, fahrer, k, startnummer=i)
|
||||
e = f.ergebnis(db, s, zeit=Decimal("100"), fehler=0)
|
||||
e.platzierung = platz
|
||||
db.flush()
|
||||
return t
|
||||
|
||||
|
||||
def test_stand_summiert_punkte_und_zaehlt_siege(seed_2026):
|
||||
db = seed_2026
|
||||
k = f.klasse(db, "J 3")
|
||||
a = f.fahrer(db, "Sieger", "Sina", 2014)
|
||||
b = f.fahrer(db, "Zweite", "Zoe", 2014)
|
||||
_lauf(db, k, [(a, 1), (b, 2)], datum=date(2026, 4, 1))
|
||||
_lauf(db, k, [(a, 1), (b, 2)], datum=date(2026, 5, 1))
|
||||
m = db.scalars(
|
||||
select(Meisterschaft).where(Meisterschaft.bezeichnung == "HTH Jugend J 3 2026")
|
||||
).one()
|
||||
bericht = berechne_stand(db, m.id)
|
||||
zeilen = {z.fahrer_id: z for z in bericht.zeilen}
|
||||
assert zeilen[a.id].platz == 1
|
||||
assert zeilen[a.id].punkte_gesamt == Decimal(50) # 2x Platz 1 = 2x25
|
||||
assert zeilen[a.id].anzahl_siege == 2
|
||||
assert bericht.hinweise # O-1 Platzhalter-Warnung
|
||||
|
||||
|
||||
def test_tie_break_majoritaet_der_siege(seed_2026):
|
||||
db = seed_2026
|
||||
k = f.klasse(db, "J 3")
|
||||
a = f.fahrer(db, "Aaa", "A", 2014)
|
||||
b = f.fahrer(db, "Bbb", "B", 2014)
|
||||
# beide sammeln gleich viele Punkte, aber A hat mehr Siege
|
||||
_lauf(db, k, [(a, 1), (b, 6)], datum=date(2026, 4, 1)) # A:25 B:10
|
||||
_lauf(db, k, [(b, 1), (a, 6)], datum=date(2026, 5, 1)) # B:25 A:10
|
||||
_lauf(db, k, [(a, 1), (b, 1)], datum=date(2026, 6, 1)) # beide 25, beide Sieg
|
||||
_lauf(db, k, [(a, 1), (b, 4)], datum=date(2026, 6, 15)) # A:25 (Sieg) B:13
|
||||
m = db.scalars(
|
||||
select(Meisterschaft).where(Meisterschaft.bezeichnung == "HTH Jugend J 3 2026")
|
||||
).one()
|
||||
bericht = berechne_stand(db, m.id)
|
||||
assert bericht.zeilen[0].fahrer_id == a.id
|
||||
assert bericht.zeilen[0].anzahl_siege > bericht.zeilen[1].anzahl_siege
|
||||
|
||||
|
||||
def test_laeufe_ohne_meisterschaft_zaehlen_nicht(seed_2026):
|
||||
db = seed_2026
|
||||
k = f.klasse(db, "J 3")
|
||||
a = f.fahrer(db, "Solo", "S", 2014)
|
||||
_lauf(db, k, [(a, 1)], datum=date(2026, 4, 1), zaehlt=False)
|
||||
m = db.scalars(
|
||||
select(Meisterschaft).where(Meisterschaft.bezeichnung == "HTH Jugend J 3 2026")
|
||||
).one()
|
||||
bericht = berechne_stand(db, m.id)
|
||||
assert bericht.laeufe_gesamt == 0
|
||||
assert bericht.zeilen == []
|
||||
Reference in New Issue
Block a user