Meisterschaft: eine pro Saison, mit zugeordneten Turnieren
Auf Vorgabe des Auftraggebers umgestellt:
* genau eine Meisterschaft je Saison (unique auf saison_jahr)
* turnier.meisterschaft_id (nullable FK) statt bool zaehlt_meisterschaft;
NULL = "o. M.". Kompat-Property turnier.zaehlt_meisterschaft bleibt.
* Teilwertungen (Erwachsene-Gesamt, Jugend J1-J5) sind (Kategorie, Klasse)-
Sichten auf die eine Meisterschaft, keine eigenen Zeilen mehr
* meisterschaft_stand: kategorie + klasse_id + Unique je Wertung/Fahrer
* berechne_stand(kategorie=, klasse_id=), wertungen_fuer(), berechne_alle_wertungen()
* Read-API: /meisterschaften/{id}/stand[?wertung=], .../endlauf-qualifikation?wertung=
* Ingest: POST /turniere haengt zaehlende Turniere an die Jahres-Meisterschaft
* Initialmigration neu erzeugt (Projekt noch nicht ausgerollt); Seed: eine
"HTH-Meisterschaft <jahr>"
* Tests angepasst/ergaenzt (61 gruen)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+44
-45
@@ -1,8 +1,8 @@
|
||||
"""Initiales Schema
|
||||
|
||||
Revision ID: df5e2dd5a864
|
||||
Revision ID: d7d7d8a2cf90
|
||||
Revises:
|
||||
Create Date: 2026-09-05 18:08:45.999851+02:00
|
||||
Create Date: 2026-09-05 19:48:05.733306+02:00
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -12,17 +12,15 @@ from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
revision: str = 'df5e2dd5a864'
|
||||
revision: str = 'd7d7d8a2cf90'
|
||||
down_revision: str | None = None
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# pg_trgm fuer die unscharfe Fahrersuche beim Import (Plan §6.4)
|
||||
op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm")
|
||||
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.execute("CREATE EXTENSION IF NOT EXISTS pg_trgm")
|
||||
op.create_table('benutzer',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('benutzername', sa.String(length=80), nullable=False),
|
||||
@@ -105,11 +103,11 @@ def upgrade() -> None:
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_fahrer'))
|
||||
)
|
||||
op.create_index(op.f('ix_fahrer_geburtsjahr'), 'fahrer', ['geburtsjahr'], unique=False)
|
||||
op.create_index(op.f('ix_fahrer_verein_id'), 'fahrer', ['verein_id'], unique=False)
|
||||
op.execute(
|
||||
"CREATE INDEX ix_fahrer_name_trgm ON fahrer USING gin "
|
||||
"((lower(nachname || ' ' || vorname)) gin_trgm_ops)"
|
||||
)
|
||||
op.create_index(op.f('ix_fahrer_verein_id'), 'fahrer', ['verein_id'], unique=False)
|
||||
op.create_table('klasse',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('saison_jahr', sa.Integer(), nullable=False),
|
||||
@@ -128,6 +126,16 @@ def upgrade() -> None:
|
||||
sa.UniqueConstraint('saison_jahr', 'code', name='klasse_saison_code')
|
||||
)
|
||||
op.create_index(op.f('ix_klasse_saison_jahr'), 'klasse', ['saison_jahr'], unique=False)
|
||||
op.create_table('meisterschaft',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('saison_jahr', sa.Integer(), nullable=False),
|
||||
sa.Column('bezeichnung', sa.String(length=200), nullable=False),
|
||||
sa.Column('regelwerk', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
|
||||
sa.ForeignKeyConstraint(['saison_jahr'], ['saison.jahr'], name=op.f('fk_meisterschaft_saison_jahr_saison')),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_meisterschaft')),
|
||||
sa.UniqueConstraint('saison_jahr', name='meisterschaft_saison_eindeutig')
|
||||
)
|
||||
op.create_index(op.f('ix_meisterschaft_saison_jahr'), 'meisterschaft', ['saison_jahr'], unique=True)
|
||||
op.create_table('veranstaltung',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('datum', sa.Date(), nullable=False),
|
||||
@@ -145,40 +153,11 @@ def upgrade() -> None:
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_veranstaltung'))
|
||||
)
|
||||
op.create_index(op.f('ix_veranstaltung_datum'), 'veranstaltung', ['datum'], unique=False)
|
||||
op.create_table('meisterschaft',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('saison_jahr', sa.Integer(), nullable=False),
|
||||
sa.Column('bezeichnung', sa.String(length=200), nullable=False),
|
||||
sa.Column('kategorie', sa.Enum('J', 'S', 'A', name='meisterschaft_kategorie'), nullable=False),
|
||||
sa.Column('klasse_id', sa.Integer(), nullable=True),
|
||||
sa.Column('regelwerk', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
|
||||
sa.ForeignKeyConstraint(['klasse_id'], ['klasse.id'], name=op.f('fk_meisterschaft_klasse_id_klasse')),
|
||||
sa.ForeignKeyConstraint(['saison_jahr'], ['saison.jahr'], name=op.f('fk_meisterschaft_saison_jahr_saison')),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_meisterschaft'))
|
||||
)
|
||||
op.create_index(op.f('ix_meisterschaft_saison_jahr'), 'meisterschaft', ['saison_jahr'], unique=False)
|
||||
op.create_table('turnier',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('veranstaltung_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=120), nullable=False),
|
||||
sa.Column('veranstalter_verein_id', sa.Integer(), nullable=True),
|
||||
sa.Column('zaehlt_meisterschaft', sa.Boolean(), nullable=False),
|
||||
sa.Column('ist_aktiv', sa.Boolean(), nullable=False),
|
||||
sa.Column('status', sa.Enum('geplant', 'laufend', 'vorlaeufig', 'final', name='turnier_status'), nullable=False),
|
||||
sa.Column('erstellt_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('geaendert_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['veranstalter_verein_id'], ['verein.id'], name=op.f('fk_turnier_veranstalter_verein_id_verein')),
|
||||
sa.ForeignKeyConstraint(['veranstaltung_id'], ['veranstaltung.id'], name=op.f('fk_turnier_veranstaltung_id_veranstaltung'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_turnier'))
|
||||
)
|
||||
op.create_index(op.f('ix_turnier_veranstaltung_id'), 'turnier', ['veranstaltung_id'], unique=False)
|
||||
op.create_index(
|
||||
'uq_turnier_ist_aktiv', 'turnier', ['ist_aktiv'], unique=True,
|
||||
postgresql_where=sa.text('ist_aktiv'),
|
||||
)
|
||||
op.create_table('meisterschaft_stand',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('meisterschaft_id', sa.Integer(), nullable=False),
|
||||
sa.Column('kategorie', sa.Enum('J', 'S', 'A', name='meisterschaft_stand_kategorie'), nullable=False),
|
||||
sa.Column('klasse_id', sa.Integer(), nullable=True),
|
||||
sa.Column('fahrer_id', sa.Integer(), nullable=False),
|
||||
sa.Column('punkte_gesamt', sa.Numeric(precision=9, scale=2), nullable=False),
|
||||
sa.Column('punkte_je_lauf', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
|
||||
@@ -187,11 +166,31 @@ def upgrade() -> None:
|
||||
sa.Column('platz', sa.Integer(), nullable=True),
|
||||
sa.Column('berechnet_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['fahrer_id'], ['fahrer.id'], name=op.f('fk_meisterschaft_stand_fahrer_id_fahrer')),
|
||||
sa.ForeignKeyConstraint(['klasse_id'], ['klasse.id'], name=op.f('fk_meisterschaft_stand_klasse_id_klasse')),
|
||||
sa.ForeignKeyConstraint(['meisterschaft_id'], ['meisterschaft.id'], name=op.f('fk_meisterschaft_stand_meisterschaft_id_meisterschaft'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_meisterschaft_stand'))
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_meisterschaft_stand')),
|
||||
sa.UniqueConstraint('meisterschaft_id', 'kategorie', 'klasse_id', 'fahrer_id', name='meisterschaft_stand_eindeutig')
|
||||
)
|
||||
op.create_index(op.f('ix_meisterschaft_stand_fahrer_id'), 'meisterschaft_stand', ['fahrer_id'], unique=False)
|
||||
op.create_index(op.f('ix_meisterschaft_stand_meisterschaft_id'), 'meisterschaft_stand', ['meisterschaft_id'], unique=False)
|
||||
op.create_table('turnier',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('veranstaltung_id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=120), nullable=False),
|
||||
sa.Column('veranstalter_verein_id', sa.Integer(), nullable=True),
|
||||
sa.Column('meisterschaft_id', sa.Integer(), nullable=True),
|
||||
sa.Column('ist_aktiv', sa.Boolean(), nullable=False),
|
||||
sa.Column('status', sa.Enum('geplant', 'laufend', 'vorlaeufig', 'final', name='turnier_status'), nullable=False),
|
||||
sa.Column('erstellt_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('geaendert_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['meisterschaft_id'], ['meisterschaft.id'], name=op.f('fk_turnier_meisterschaft_id_meisterschaft'), ondelete='SET NULL'),
|
||||
sa.ForeignKeyConstraint(['veranstalter_verein_id'], ['verein.id'], name=op.f('fk_turnier_veranstalter_verein_id_verein')),
|
||||
sa.ForeignKeyConstraint(['veranstaltung_id'], ['veranstaltung.id'], name=op.f('fk_turnier_veranstaltung_id_veranstaltung'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_turnier'))
|
||||
)
|
||||
op.create_index(op.f('ix_turnier_meisterschaft_id'), 'turnier', ['meisterschaft_id'], unique=False)
|
||||
op.create_index(op.f('ix_turnier_veranstaltung_id'), 'turnier', ['veranstaltung_id'], unique=False)
|
||||
op.create_index('uq_turnier_ist_aktiv', 'turnier', ['ist_aktiv'], unique=True, postgresql_where=sa.text('ist_aktiv'))
|
||||
op.create_table('wertungsklasse',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('turnier_id', sa.Integer(), nullable=False),
|
||||
@@ -275,20 +274,20 @@ def downgrade() -> None:
|
||||
op.drop_table('start')
|
||||
op.drop_index(op.f('ix_wertungsklasse_turnier_id'), table_name='wertungsklasse')
|
||||
op.drop_table('wertungsklasse')
|
||||
op.drop_index('uq_turnier_ist_aktiv', table_name='turnier', postgresql_where=sa.text('ist_aktiv'))
|
||||
op.drop_index(op.f('ix_turnier_veranstaltung_id'), table_name='turnier')
|
||||
op.drop_index(op.f('ix_turnier_meisterschaft_id'), table_name='turnier')
|
||||
op.drop_table('turnier')
|
||||
op.drop_index(op.f('ix_meisterschaft_stand_meisterschaft_id'), table_name='meisterschaft_stand')
|
||||
op.drop_index(op.f('ix_meisterschaft_stand_fahrer_id'), table_name='meisterschaft_stand')
|
||||
op.drop_table('meisterschaft_stand')
|
||||
op.drop_index('uq_turnier_ist_aktiv', table_name='turnier')
|
||||
op.drop_index(op.f('ix_turnier_veranstaltung_id'), table_name='turnier')
|
||||
op.drop_table('turnier')
|
||||
op.drop_index(op.f('ix_meisterschaft_saison_jahr'), table_name='meisterschaft')
|
||||
op.drop_table('meisterschaft')
|
||||
op.drop_index(op.f('ix_veranstaltung_datum'), table_name='veranstaltung')
|
||||
op.drop_table('veranstaltung')
|
||||
op.drop_index(op.f('ix_meisterschaft_saison_jahr'), table_name='meisterschaft')
|
||||
op.drop_table('meisterschaft')
|
||||
op.drop_index(op.f('ix_klasse_saison_jahr'), table_name='klasse')
|
||||
op.drop_table('klasse')
|
||||
op.drop_index(op.f('ix_fahrer_verein_id'), table_name='fahrer')
|
||||
op.drop_index('ix_fahrer_name_trgm', table_name='fahrer', postgresql_using='gin')
|
||||
op.drop_index(op.f('ix_fahrer_geburtsjahr'), table_name='fahrer')
|
||||
op.drop_table('fahrer')
|
||||
op.drop_table('verein')
|
||||
Reference in New Issue
Block a user