* 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>
147 lines
5.0 KiB
Python
147 lines
5.0 KiB
Python
"""Seed fuer Saison 2026 — abgeleitet aus plan.md §3.2 und §3.4."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from oamc.meisterschaft.berechnung import PLATZHALTER_PUNKTETABELLE
|
|
from oamc.models import Klasse, Meisterschaft, Saison, Verein
|
|
from oamc.models.enums import Kategorie
|
|
|
|
# "Turniere in der Saison" -> "davon gewertet" (Plan §3.4)
|
|
STREICHRESULTATE_2026 = {
|
|
"12": 8, "11": 8, "10": 8, "9": 7, "8": 6, "7": 5, "6": 4, "5": 3, "4": 2,
|
|
}
|
|
|
|
# Jugendklassen: Einteilung strikt nach Jahrgang (Plan §3.2)
|
|
JUGENDKLASSEN = [
|
|
("J 1", [2019, 2018, 2017], 110, 5.5, "Kindermotorrad", 7),
|
|
("J 2", [2016, 2015], 110, 5.5, "Kindermotorrad", None),
|
|
("J 3", [2014, 2013], 125, 11.0, "Mofa / Motorrad / Roller", None),
|
|
("J 4", [2012, 2011], 125, 11.0, "Mofa / Motorrad / Roller", None),
|
|
("J 5", [2010, 2009, 2008], 125, 11.0, "Mofa / Motorrad / Roller (bis 18. Geburtstag)", None),
|
|
]
|
|
|
|
# Erwachsenenklassen: konkrete Fahrzeugzuordnung ist offene Frage O-2.
|
|
STAMMKLASSEN = [f"S {i}" for i in range(1, 10)]
|
|
ANFAENGERKLASSEN = [f"A {i}" for i in range(1, 5)]
|
|
|
|
VEREINE = [
|
|
("OAMC Reinheim e.V. im ADAC", "OAMC Reinheim", "ADAC Hessen-Thueringen"),
|
|
("MSC Hainstadt", "Hainstadt", "ADAC Hessen-Thueringen"),
|
|
("MSC Schotten", "Schotten", "ADAC Hessen-Thueringen"),
|
|
("PMS Kassel", "Kassel", "ADAC Hessen-Thueringen"),
|
|
]
|
|
|
|
|
|
def _get_or_create(db: Session, model, defaults=None, **kwargs):
|
|
obj = db.scalars(select(model).filter_by(**kwargs)).first()
|
|
if obj is not None:
|
|
return obj, False
|
|
obj = model(**kwargs, **(defaults or {}))
|
|
db.add(obj)
|
|
db.flush()
|
|
return obj, True
|
|
|
|
|
|
def seed_saison_2026(db: Session) -> dict[str, int]:
|
|
"""Idempotenter Seed. Rueckgabe: Zaehler der neu angelegten Objekte."""
|
|
|
|
zaehler = {"saison": 0, "verein": 0, "klasse": 0, "meisterschaft": 0}
|
|
|
|
saison, neu = _get_or_create(
|
|
db,
|
|
Saison,
|
|
jahr=2026,
|
|
defaults={
|
|
"turnierordnung_version": "MT-D2025 (zu verifizieren, Plan §10)",
|
|
"streichresultate_tabelle": STREICHRESULTATE_2026,
|
|
"nenngeld_cent": 1000,
|
|
"aktiv": True,
|
|
},
|
|
)
|
|
zaehler["saison"] += int(neu)
|
|
|
|
for name, kurz, gau in VEREINE:
|
|
_, neu = _get_or_create(
|
|
db, Verein, name=name, defaults={"kurzname": kurz, "adac_gau": gau, "aktiv": True}
|
|
)
|
|
zaehler["verein"] += int(neu)
|
|
|
|
sortierung = 0
|
|
for code, jahrgaenge, ccm, kw, typ, mindestalter in JUGENDKLASSEN:
|
|
sortierung += 1
|
|
_, neu = _get_or_create(
|
|
db,
|
|
Klasse,
|
|
saison_jahr=2026,
|
|
code=code,
|
|
defaults={
|
|
"kategorie": Kategorie.JUGEND,
|
|
"bezeichnung": f"Jugend {code} (Jahrgang {'/'.join(map(str, jahrgaenge))})",
|
|
"jahrgaenge": jahrgaenge,
|
|
"ccm_max": ccm,
|
|
"kw_max": kw,
|
|
"fahrzeugtyp": typ,
|
|
"mindestalter_am_veranstaltungstag": mindestalter,
|
|
"sortierung": sortierung,
|
|
},
|
|
)
|
|
zaehler["klasse"] += int(neu)
|
|
|
|
for code in STAMMKLASSEN:
|
|
sortierung += 1
|
|
_, neu = _get_or_create(
|
|
db, Klasse, saison_jahr=2026, code=code,
|
|
defaults={
|
|
"kategorie": Kategorie.STAMM,
|
|
"bezeichnung": f"Stammfahrer {code}",
|
|
"fahrzeugtyp": None,
|
|
"sortierung": sortierung,
|
|
"notiz": "O-2 offen: Fahrzeugtyp/Hubraum/kW noch zuzuordnen",
|
|
},
|
|
)
|
|
zaehler["klasse"] += int(neu)
|
|
|
|
for code in ANFAENGERKLASSEN:
|
|
sortierung += 1
|
|
_, neu = _get_or_create(
|
|
db, Klasse, saison_jahr=2026, code=code,
|
|
defaults={
|
|
"kategorie": Kategorie.ANFAENGER,
|
|
"bezeichnung": f"Anfaenger {code}",
|
|
"fahrzeugtyp": None,
|
|
"sortierung": sortierung,
|
|
"notiz": "O-2 offen: Fahrzeugtyp/Hubraum/kW noch zuzuordnen",
|
|
},
|
|
)
|
|
zaehler["klasse"] += int(neu)
|
|
|
|
platzhalter_regelwerk = {
|
|
"punkte": {str(k): v for k, v in PLATZHALTER_PUNKTETABELLE.items()},
|
|
"_hinweis": "O-1 offen: PLATZHALTER-Punktetabelle, nicht offiziell!",
|
|
}
|
|
|
|
_, neu = _get_or_create(
|
|
db, Meisterschaft, saison_jahr=2026, bezeichnung="HTH Erwachsene 2026",
|
|
defaults={"kategorie": Kategorie.STAMM, "regelwerk": platzhalter_regelwerk},
|
|
)
|
|
zaehler["meisterschaft"] += int(neu)
|
|
|
|
jugendklassen = db.scalars(
|
|
select(Klasse).where(Klasse.saison_jahr == 2026, Klasse.kategorie == Kategorie.JUGEND)
|
|
).all()
|
|
for k in jugendklassen:
|
|
_, neu = _get_or_create(
|
|
db, Meisterschaft, saison_jahr=2026, bezeichnung=f"HTH Jugend {k.code} 2026",
|
|
defaults={
|
|
"kategorie": Kategorie.JUGEND,
|
|
"klasse_id": k.id,
|
|
"regelwerk": platzhalter_regelwerk,
|
|
},
|
|
)
|
|
zaehler["meisterschaft"] += int(neu)
|
|
|
|
return zaehler
|