Files
game-marathon/backend/alembic/versions/029_add_widget_tokens.py
mamonov.ep f78eacb1a5 Добавлен Skip with Exile, модерация марафонов и выдача предметов
## Skip with Exile (новый расходник)
- Новая модель ExiledGame для хранения изгнанных игр
- Расходник skip_exile: пропуск без штрафа + игра исключается из пула навсегда
- Фильтрация изгнанных игр при выдаче заданий
- UI кнопка в PlayPage для использования skip_exile

## Модерация марафонов (для организаторов)
- Эндпоинты: skip-assignment, exiled-games, restore-exiled-game
- UI в LeaderboardPage: кнопка скипа у каждого участника
- Выбор типа скипа (обычный/с изгнанием) + причина
- Telegram уведомления о модерации

## Админская выдача предметов
- Эндпоинты: admin grant/remove items, get user inventory
- Новая страница AdminGrantItemPage (как магазин)
- Telegram уведомление при получении подарка

## Исправления миграций
- Миграции 029/030 теперь идемпотентны (проверка существования таблиц)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-10 23:02:37 +03:00

47 lines
1.4 KiB
Python

"""Add widget tokens
Revision ID: 029
Revises: 028
Create Date: 2025-01-09
"""
from alembic import op
from sqlalchemy import inspect
import sqlalchemy as sa
revision = '029_add_widget_tokens'
down_revision = '028_add_promo_codes'
branch_labels = None
depends_on = None
def table_exists(table_name: str) -> bool:
bind = op.get_bind()
inspector = inspect(bind)
return table_name in inspector.get_table_names()
def upgrade():
if table_exists('widget_tokens'):
return
op.create_table(
'widget_tokens',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('token', sa.String(64), nullable=False),
sa.Column('participant_id', sa.Integer(), nullable=False),
sa.Column('marathon_id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=True),
sa.Column('is_active', sa.Boolean(), nullable=False, server_default='true'),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['participant_id'], ['participants.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['marathon_id'], ['marathons.id'], ondelete='CASCADE'),
)
op.create_index('ix_widget_tokens_token', 'widget_tokens', ['token'], unique=True)
def downgrade():
op.drop_index('ix_widget_tokens_token', table_name='widget_tokens')
op.drop_table('widget_tokens')