64 lines
2.8 KiB
Python
64 lines
2.8 KiB
Python
"""Жизненный цикл партии: статус, тайминг, длительность, причина победы; place → nullable.
|
|
|
|
Идемпотентна: на свежей БД (0001 создаёт схему из актуальных моделей через create_all)
|
|
столбцы уже есть → no-op; на существующей БД (ревизия 0002, старая схема) — добавляет
|
|
столбцы и делает place nullable. render_as_batch включён в env.py (нужен для SQLite).
|
|
|
|
Revision ID: 0003_match_lifecycle
|
|
Revises: 0002_seed_reference
|
|
Create Date: 2026-06-15
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from sqlalchemy import inspect
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "0003_match_lifecycle"
|
|
down_revision: Union[str, None] = "0002_seed_reference"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
bind = op.get_bind()
|
|
insp = inspect(bind)
|
|
|
|
match_cols = {c["name"] for c in insp.get_columns("matches")}
|
|
add = []
|
|
if "status" not in match_cols:
|
|
add.append(sa.Column("status", sa.String(16), nullable=False, server_default="finished"))
|
|
if "started_at" not in match_cols:
|
|
add.append(sa.Column("started_at", sa.DateTime(), nullable=True))
|
|
if "finished_at" not in match_cols:
|
|
add.append(sa.Column("finished_at", sa.DateTime(), nullable=True))
|
|
if "duration_minutes" not in match_cols:
|
|
add.append(sa.Column("duration_minutes", sa.Integer(), nullable=True))
|
|
if "win_reason" not in match_cols:
|
|
add.append(sa.Column("win_reason", sa.String(16), nullable=True))
|
|
|
|
if add:
|
|
with op.batch_alter_table("matches") as b:
|
|
for col in add:
|
|
b.add_column(col)
|
|
# Бэкфилл для существующих (уже завершённых) партий.
|
|
op.execute("UPDATE matches SET status = 'finished' WHERE status IS NULL")
|
|
op.execute("UPDATE matches SET started_at = created_at WHERE started_at IS NULL")
|
|
op.execute("UPDATE matches SET finished_at = created_at WHERE finished_at IS NULL")
|
|
|
|
# place → nullable, если ещё NOT NULL.
|
|
place_col = next(c for c in insp.get_columns("match_participants") if c["name"] == "place")
|
|
if not place_col["nullable"]:
|
|
with op.batch_alter_table("match_participants") as b:
|
|
b.alter_column("place", existing_type=sa.Integer(), nullable=True)
|
|
|
|
|
|
def downgrade() -> None:
|
|
with op.batch_alter_table("matches") as b:
|
|
for name in ("win_reason", "duration_minutes", "finished_at", "started_at", "status"):
|
|
try:
|
|
b.drop_column(name)
|
|
except Exception: # noqa: BLE001
|
|
pass
|