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,54 @@
|
||||
"""Fahrer-Stammdaten — einmalig, nicht pro Turnier (Plan §4).
|
||||
|
||||
Loest die heutige \"BAUSTELLE\" ``m/m-fahrer.htm`` ab. Dublettenerkennung beim
|
||||
Import: siehe ``oamc.matching.fahrer``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
from sqlalchemy import Boolean, Date, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from oamc.models.base import Base, TimestampMixin, sa_enum
|
||||
from oamc.models.enums import Geschlecht
|
||||
|
||||
|
||||
class Fahrer(Base, TimestampMixin):
|
||||
__tablename__ = "fahrer"
|
||||
# Hinweis: der GIN-Trigramm-Index ``ix_fahrer_name_trgm`` (unscharfe Suche beim
|
||||
# Import, Plan §6.4) wird als Ausdrucksindex in der Migration angelegt und in
|
||||
# ``migrations/env.py`` von der Autogenerate-Vergleichung ausgenommen.
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
nachname: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
vorname: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
geburtsjahr: Mapped[int | None] = mapped_column(Integer, index=True)
|
||||
# nullable: nur fuer die J-1-Pruefung "am Veranstaltungstag 7 Jahre" noetig.
|
||||
# Datenminimierung oeffentlich: nur das Jahr wird angezeigt (Plan §11).
|
||||
geburtsdatum: Mapped[date | None] = mapped_column(Date)
|
||||
geschlecht: Mapped[Geschlecht | None] = mapped_column(sa_enum(Geschlecht, "geschlecht"))
|
||||
verein_id: Mapped[int | None] = mapped_column(ForeignKey("verein.id"), index=True)
|
||||
|
||||
adac_mitgliedsnr: Mapped[str | None] = mapped_column(String(40)) # nie oeffentlich
|
||||
jugendausweis_gueltig_bis: Mapped[date | None] = mapped_column(Date)
|
||||
|
||||
# Datenschutz: Opt-out auf Anzeige als "Teilnehmer N" / Kuerzel (Plan §11)
|
||||
anzeige_anonymisiert: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
|
||||
aktiv: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
|
||||
notiz: Mapped[str | None] = mapped_column(Text)
|
||||
|
||||
verein: Mapped[Verein | None] = relationship(back_populates="fahrer") # noqa: F821
|
||||
starts: Mapped[list[Start]] = relationship(back_populates="fahrer") # noqa: F821
|
||||
|
||||
@property
|
||||
def voller_name(self) -> str:
|
||||
return f"{self.nachname} {self.vorname}"
|
||||
|
||||
@property
|
||||
def anzeigename(self) -> str:
|
||||
if self.anzeige_anonymisiert:
|
||||
return f"Teilnehmer/in #{self.id}"
|
||||
return self.voller_name
|
||||
Reference in New Issue
Block a user