"""Initiales Schema Revision ID: df5e2dd5a864 Revises: Create Date: 2026-09-05 18:08:45.999851+02:00 """ from __future__ import annotations from collections.abc import Sequence from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql revision: str = 'df5e2dd5a864' 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.create_table('benutzer', sa.Column('id', sa.Integer(), nullable=False), sa.Column('benutzername', sa.String(length=80), nullable=False), sa.Column('anzeigename', sa.String(length=120), nullable=False), sa.Column('passwort_hash', sa.String(length=255), nullable=False), sa.Column('rolle', sa.Enum('admin', 'auswerter', 'lesend', name='benutzer_rolle'), nullable=False), sa.Column('aktiv', sa.Boolean(), nullable=False), sa.Column('letzter_login_am', sa.DateTime(timezone=True), nullable=True), 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.PrimaryKeyConstraint('id', name=op.f('pk_benutzer')), sa.UniqueConstraint('benutzername', name=op.f('uq_benutzer_benutzername')) ) op.create_table('geraet', sa.Column('id', sa.Integer(), nullable=False), sa.Column('bezeichnung', sa.String(length=120), nullable=False), sa.Column('typ', sa.Enum('lichtschranke', 'anzeige', name='geraet_typ'), nullable=False), sa.Column('token_hash', sa.String(length=255), nullable=False), sa.Column('aktiv', sa.Boolean(), nullable=False), sa.Column('letzte_meldung_am', sa.DateTime(timezone=True), nullable=True), sa.Column('firmware_version', sa.String(length=60), nullable=True), sa.Column('queue_laenge_gemeldet', sa.Integer(), nullable=True), sa.Column('notiz', sa.Text(), nullable=True), sa.Column('konfiguration', postgresql.JSONB(astext_type=sa.Text()), 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.PrimaryKeyConstraint('id', name=op.f('pk_geraet')), sa.UniqueConstraint('bezeichnung', name=op.f('uq_geraet_bezeichnung')) ) op.create_table('import_job', sa.Column('id', sa.Integer(), nullable=False), sa.Column('quelle', sa.String(length=120), nullable=False), sa.Column('idempotenz_schluessel', sa.String(length=200), nullable=True), sa.Column('dateiname', sa.String(length=300), nullable=True), sa.Column('roh_payload', postgresql.JSONB(astext_type=sa.Text()), nullable=True), sa.Column('ergebnis_bericht', postgresql.JSONB(astext_type=sa.Text()), nullable=True), sa.Column('status', sa.Enum('angenommen', 'vorschau', 'fehler', 'teilweise', name='import_status'), nullable=False), sa.Column('fehler', sa.Text(), nullable=True), sa.Column('angelegt_von', sa.String(length=120), nullable=True), sa.Column('angelegt_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), sa.PrimaryKeyConstraint('id', name=op.f('pk_import_job')) ) op.create_index(op.f('ix_import_job_idempotenz_schluessel'), 'import_job', ['idempotenz_schluessel'], unique=False) op.create_table('saison', sa.Column('jahr', sa.Integer(), autoincrement=False, nullable=False), sa.Column('turnierordnung_version', sa.String(length=60), nullable=True), sa.Column('streichresultate_tabelle', postgresql.JSONB(astext_type=sa.Text()), nullable=False), sa.Column('nenngeld_cent', sa.Integer(), nullable=False), sa.Column('aktiv', sa.Boolean(), nullable=False), sa.PrimaryKeyConstraint('jahr', name=op.f('pk_saison')) ) op.create_table('verein', sa.Column('id', sa.Integer(), nullable=False), sa.Column('name', sa.String(length=200), nullable=False), sa.Column('kurzname', sa.String(length=60), nullable=True), sa.Column('adac_gau', sa.String(length=120), nullable=True), sa.Column('aktiv', sa.Boolean(), nullable=False), sa.Column('notiz', sa.Text(), nullable=True), 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.PrimaryKeyConstraint('id', name=op.f('pk_verein')), sa.UniqueConstraint('name', name=op.f('uq_verein_name')) ) op.create_table('fahrer', sa.Column('id', sa.Integer(), nullable=False), sa.Column('nachname', sa.String(length=120), nullable=False), sa.Column('vorname', sa.String(length=120), nullable=False), sa.Column('geburtsjahr', sa.Integer(), nullable=True), sa.Column('geburtsdatum', sa.Date(), nullable=True), sa.Column('geschlecht', sa.Enum('M', 'W', 'D', name='geschlecht'), nullable=True), sa.Column('verein_id', sa.Integer(), nullable=True), sa.Column('adac_mitgliedsnr', sa.String(length=40), nullable=True), sa.Column('jugendausweis_gueltig_bis', sa.Date(), nullable=True), sa.Column('anzeige_anonymisiert', sa.Boolean(), nullable=False), sa.Column('aktiv', sa.Boolean(), nullable=False), sa.Column('notiz', sa.Text(), nullable=True), 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(['verein_id'], ['verein.id'], name=op.f('fk_fahrer_verein_id_verein')), sa.PrimaryKeyConstraint('id', name=op.f('pk_fahrer')) ) op.create_index(op.f('ix_fahrer_geburtsjahr'), 'fahrer', ['geburtsjahr'], 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), sa.Column('code', sa.String(length=10), nullable=False), sa.Column('kategorie', sa.Enum('J', 'S', 'A', name='kategorie'), nullable=False), sa.Column('bezeichnung', sa.String(length=200), nullable=False), sa.Column('jahrgaenge', postgresql.ARRAY(sa.Integer()), nullable=True), sa.Column('ccm_max', sa.Integer(), nullable=True), sa.Column('kw_max', sa.Numeric(precision=5, scale=2), nullable=True), sa.Column('fahrzeugtyp', sa.String(length=200), nullable=True), sa.Column('mindestalter_am_veranstaltungstag', sa.Integer(), nullable=True), sa.Column('sortierung', sa.Integer(), nullable=False), sa.Column('notiz', sa.Text(), nullable=True), sa.ForeignKeyConstraint(['saison_jahr'], ['saison.jahr'], name=op.f('fk_klasse_saison_jahr_saison'), ondelete='CASCADE'), sa.PrimaryKeyConstraint('id', name=op.f('pk_klasse')), 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('veranstaltung', sa.Column('id', sa.Integer(), nullable=False), sa.Column('datum', sa.Date(), nullable=False), sa.Column('titel', sa.String(length=300), nullable=False), sa.Column('ort', sa.String(length=200), nullable=True), sa.Column('ausrichter_verein_id', sa.Integer(), nullable=True), sa.Column('ist_doppelturnier', sa.Boolean(), nullable=False), sa.Column('veranstaltungsleiter', sa.String(length=200), nullable=True), sa.Column('turnierleiter', sa.String(length=200), nullable=True), sa.Column('status', sa.Enum('geplant', 'laufend', 'vorlaeufig', 'final', name='veranstaltung_status'), nullable=False), sa.Column('notiz', sa.Text(), nullable=True), 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(['ausrichter_verein_id'], ['verein.id'], name=op.f('fk_veranstaltung_ausrichter_verein_id_verein')), 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('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), sa.Column('gestrichene_laeufe', postgresql.JSONB(astext_type=sa.Text()), nullable=False), sa.Column('anzahl_siege', sa.Integer(), nullable=False), 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(['meisterschaft_id'], ['meisterschaft.id'], name=op.f('fk_meisterschaft_stand_meisterschaft_id_meisterschaft'), ondelete='CASCADE'), sa.PrimaryKeyConstraint('id', name=op.f('pk_meisterschaft_stand')) ) 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('wertungsklasse', sa.Column('id', sa.Integer(), nullable=False), sa.Column('turnier_id', sa.Integer(), nullable=False), sa.Column('bezeichnung', sa.String(length=200), nullable=False), sa.Column('klasse_ids', postgresql.ARRAY(sa.Integer()), nullable=False), sa.Column('sortierung', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['turnier_id'], ['turnier.id'], name=op.f('fk_wertungsklasse_turnier_id_turnier'), ondelete='CASCADE'), sa.PrimaryKeyConstraint('id', name=op.f('pk_wertungsklasse')) ) op.create_index(op.f('ix_wertungsklasse_turnier_id'), 'wertungsklasse', ['turnier_id'], unique=False) op.create_table('start', sa.Column('id', sa.Integer(), nullable=False), sa.Column('turnier_id', sa.Integer(), nullable=False), sa.Column('fahrer_id', sa.Integer(), nullable=False), sa.Column('klasse_id', sa.Integer(), nullable=False), sa.Column('wertungsklasse_id', sa.Integer(), nullable=True), sa.Column('startnummer', sa.Integer(), nullable=False), sa.Column('fahrzeug', sa.String(length=200), nullable=True), sa.Column('nenngeld_bezahlt', sa.Boolean(), nullable=False), sa.Column('status', sa.Enum('genannt', 'gestartet', 'DNF', 'DNS', 'DSQ', name='start_status'), nullable=False), sa.Column('nennreihenfolge', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['fahrer_id'], ['fahrer.id'], name=op.f('fk_start_fahrer_id_fahrer')), sa.ForeignKeyConstraint(['klasse_id'], ['klasse.id'], name=op.f('fk_start_klasse_id_klasse')), sa.ForeignKeyConstraint(['turnier_id'], ['turnier.id'], name=op.f('fk_start_turnier_id_turnier'), ondelete='CASCADE'), sa.ForeignKeyConstraint(['wertungsklasse_id'], ['wertungsklasse.id'], name=op.f('fk_start_wertungsklasse_id_wertungsklasse'), ondelete='SET NULL'), sa.PrimaryKeyConstraint('id', name=op.f('pk_start')), sa.UniqueConstraint('turnier_id', 'startnummer', name='start_turnier_startnummer') ) op.create_index(op.f('ix_start_fahrer_id'), 'start', ['fahrer_id'], unique=False) op.create_index(op.f('ix_start_turnier_id'), 'start', ['turnier_id'], unique=False) op.create_index(op.f('ix_start_wertungsklasse_id'), 'start', ['wertungsklasse_id'], unique=False) op.create_table('ergebnis', sa.Column('start_id', sa.Integer(), nullable=False), sa.Column('summe_zeit', sa.Numeric(precision=7, scale=2), nullable=True), sa.Column('summe_fehler', sa.Integer(), nullable=True), sa.Column('gesamt', sa.Numeric(precision=9, scale=2), nullable=True), sa.Column('platzierung', sa.Integer(), nullable=True), sa.Column('zeit_vollstaendig', sa.Boolean(), nullable=False), sa.Column('fehler_vollstaendig', sa.Boolean(), nullable=False), sa.Column('erfasst_von', sa.String(length=120), nullable=True), sa.Column('erfasst_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), sa.Column('korrigiert_am', sa.DateTime(timezone=True), nullable=True), sa.Column('korrektur_grund', sa.Text(), nullable=True), sa.ForeignKeyConstraint(['start_id'], ['start.id'], name=op.f('fk_ergebnis_start_id_start'), ondelete='CASCADE'), sa.PrimaryKeyConstraint('start_id', name=op.f('pk_ergebnis')) ) op.create_table('zeitmessung', sa.Column('id', sa.UUID(), nullable=False), sa.Column('geraet_id', sa.Integer(), nullable=False), sa.Column('turnier_id', sa.Integer(), nullable=False), sa.Column('start_id', sa.Integer(), nullable=True), sa.Column('startnummer_gemeldet', sa.Integer(), nullable=True), sa.Column('messpunkt', sa.String(length=80), nullable=False), sa.Column('dauer_sekunden', sa.Numeric(precision=7, scale=2), nullable=False), sa.Column('gemessen_am', sa.DateTime(timezone=True), nullable=False), sa.Column('monotonic_ns', sa.BigInteger(), nullable=True), sa.Column('empfangen_am', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False), sa.Column('nachgeliefert', sa.Boolean(), nullable=False), sa.Column('status', sa.Enum('gueltig', 'verworfen', 'unzugeordnet', 'unplausibel', name='zeitmessung_status'), nullable=False), sa.Column('verworfen_grund', sa.Text(), nullable=True), sa.Column('roh', postgresql.JSONB(astext_type=sa.Text()), nullable=True), sa.ForeignKeyConstraint(['geraet_id'], ['geraet.id'], name=op.f('fk_zeitmessung_geraet_id_geraet')), sa.ForeignKeyConstraint(['start_id'], ['start.id'], name=op.f('fk_zeitmessung_start_id_start'), ondelete='SET NULL'), sa.ForeignKeyConstraint(['turnier_id'], ['turnier.id'], name=op.f('fk_zeitmessung_turnier_id_turnier'), ondelete='CASCADE'), sa.PrimaryKeyConstraint('id', name=op.f('pk_zeitmessung')) ) op.create_index('ix_zeitmessung_start', 'zeitmessung', ['start_id'], unique=False) op.create_index('ix_zeitmessung_turnier_status', 'zeitmessung', ['turnier_id', 'status'], unique=False) # ### end Alembic commands ### def downgrade() -> None: # ### commands auto generated by Alembic - please adjust! ### op.drop_index('ix_zeitmessung_turnier_status', table_name='zeitmessung') op.drop_index('ix_zeitmessung_start', table_name='zeitmessung') op.drop_table('zeitmessung') op.drop_table('ergebnis') op.drop_index(op.f('ix_start_wertungsklasse_id'), table_name='start') op.drop_index(op.f('ix_start_turnier_id'), table_name='start') op.drop_index(op.f('ix_start_fahrer_id'), table_name='start') op.drop_table('start') op.drop_index(op.f('ix_wertungsklasse_turnier_id'), table_name='wertungsklasse') op.drop_table('wertungsklasse') 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_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') op.drop_table('saison') op.drop_index(op.f('ix_import_job_idempotenz_schluessel'), table_name='import_job') op.drop_table('import_job') op.drop_table('geraet') op.drop_table('benutzer') # ### end Alembic commands ###