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,66 @@
|
||||
"""Alembic-Umgebung. DB-URL und Metadaten kommen aus der Anwendung."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
|
||||
from oamc.config import get_settings
|
||||
from oamc.models import Base
|
||||
|
||||
config = context.config
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
config.set_main_option("sqlalchemy.url", get_settings().database_url)
|
||||
target_metadata = Base.metadata
|
||||
|
||||
# Von der Autogenerate-Vergleichung ausgenommen: der Trigramm-Index wird als
|
||||
# Ausdrucksindex per Roh-SQL in der Initialmigration gepflegt.
|
||||
_IGNORE_INDEXES = {"ix_fahrer_name_trgm"}
|
||||
|
||||
|
||||
def include_object(obj, name, type_, reflected, compare_to): # noqa: ANN001
|
||||
if type_ == "index" and name in _IGNORE_INDEXES:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
context.configure(
|
||||
url=get_settings().database_url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
compare_type=True,
|
||||
compare_server_default=True,
|
||||
include_object=include_object,
|
||||
)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online() -> None:
|
||||
connectable = engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
compare_type=True,
|
||||
compare_server_default=True,
|
||||
include_object=include_object,
|
||||
)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
@@ -0,0 +1,26 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
revision: str = ${repr(up_revision)}
|
||||
down_revision: str | None = ${repr(down_revision)}
|
||||
branch_labels: str | Sequence[str] | None = ${repr(branch_labels)}
|
||||
depends_on: str | Sequence[str] | None = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
||||
@@ -0,0 +1,300 @@
|
||||
"""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 ###
|
||||
Reference in New Issue
Block a user