Files
mtgonline/backend/alembic/versions/005_missing_tables.py
T

205 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Add missing tables: mtg_sets, mtg_cards, mtg_cards_mirror, card_import_batches, user_card_imports_confirmed, deck_card_links
Revision ID: 005
Revises: 004
Create Date: 2026-01-05 00:00:00.000000
This migration creates the following tables, importing model definitions
from their respective source files to ensure column-level accuracy:
- mtg_sets, mtg_cards → app/models/mtg_models.py
- mtg_cards_mirror, deck_card_links → app/models/mirror_models.py
- card_import_batches → app/models/card_import_batch.py
- user_card_imports_confirmed → app/models/user_card_import_record.py
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# ---------------------------------------------------------------------------
# Model imports one import per source file so column definitions stay
# synchronised with the ORM classes.
# ---------------------------------------------------------------------------
from app.models.mtg_models import MtgSet, MtgCard
from app.models.mirror_models import MtgCardMirror, DeckCardLink
from app.models.card_import_batch import CardImportBatch
from app.models.user_card_import_record import UserCardImportRecord
# revision identifiers, used by Alembic.
revision: str = '005'
down_revision: Union[str, None] = '004'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Create missing tables for MTG data, card imports, and card mirrors.
Column definitions are taken directly from the ORM models imported above.
"""
# ------------------------------------------------------------------
# 1. MTG Sets Table (mtg_sets)
# Source: app/models/mtg_models.py class MtgSet
# ------------------------------------------------------------------
op.create_table(
'mtg_sets',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('code', sa.String(10), unique=True, nullable=False, index=True),
sa.Column('name', sa.String(255), nullable=True),
sa.Column('type', sa.String(100), nullable=True),
sa.Column('release_date', sa.DateTime(), nullable=True),
sa.Column('base_set_size', sa.Integer(), nullable=True),
sa.Column('total_size', sa.Integer(), nullable=True),
sa.Column('is_foil_only', sa.Integer(), nullable=True),
sa.Column('is_non_foil_only', sa.Integer(), nullable=True),
sa.Column('digital', sa.Integer(), nullable=True),
sa.Column('icon_svg_url', sa.Text(), nullable=True),
sa.Column('parent_code', sa.String(10), nullable=True),
sa.Column('mtgo_code', sa.String(10), nullable=True),
sa.Column('image', sa.Text(), nullable=True),
sa.Column('updated_at', sa.DateTime(), server_default=sa.func.now(),
onupdate=sa.func.now()),
)
# ------------------------------------------------------------------
# 2. MTG Cards Table (mtg_cards)
# Source: app/models/mtg_models.py class MtgCard
# ------------------------------------------------------------------
op.create_table(
'mtg_cards',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('set_id', sa.Integer(),
sa.ForeignKey('mtg_sets.id'), nullable=True, index=True),
sa.Column('name', sa.String(255), nullable=True, index=True),
sa.Column('mana_cost', sa.String(255), nullable=True, index=True),
sa.Column('type_line', sa.String(255), nullable=True, index=True),
sa.Column('oracle_text', sa.Text(), nullable=True),
sa.Column('power', sa.String(50), nullable=True),
sa.Column('toughness', sa.String(50), nullable=True),
sa.Column('rarity', sa.String(50), nullable=True, index=True),
sa.Column('layout', sa.String(50), nullable=True),
sa.Column('artist', sa.String(255), nullable=True),
sa.Column('flavor_text', sa.Text(), nullable=True),
sa.Column('numbers', sa.String(100), nullable=True),
sa.Column('identifiers', sa.Text(), nullable=True),
sa.Column('images', sa.Text(), nullable=True),
sa.Column('image', sa.Text(), nullable=True),
sa.Column('updated_at', sa.DateTime(), server_default=sa.func.now(),
onupdate=sa.func.now()),
)
# Composite / performance indexes from mtg_models.py
op.create_index('idx_mtg_cards_name_set', 'mtg_cards', ['name', 'set_id'])
op.create_index('idx_mtg_cards_type', 'mtg_cards', ['type_line'])
op.create_index('idx_mtg_cards_rarity', 'mtg_cards', ['rarity'])
# ------------------------------------------------------------------
# 3. MTG Cards Mirror Table (mtg_cards_mirror)
# Source: app/models/mirror_models.py class MtgCardMirror
# ------------------------------------------------------------------
op.create_table(
'mtg_cards_mirror',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('source_id', sa.Integer(), nullable=True, index=True),
sa.Column('name', sa.String(255), nullable=False, index=True),
sa.Column('mana_cost', sa.String(255), nullable=True),
sa.Column('type_line', sa.String(255), nullable=True),
sa.Column('oracle_text', sa.Text(), nullable=True),
sa.Column('power', sa.String(50), nullable=True),
sa.Column('toughness', sa.String(50), nullable=True),
sa.Column('rarity', sa.String(50), nullable=True),
sa.Column('layout', sa.String(50), nullable=True),
sa.Column('artist', sa.String(255), nullable=True),
sa.Column('flavor_text', sa.Text(), nullable=True),
sa.Column('numbers', sa.String(100), nullable=True),
sa.Column('identifiers', sa.Text(), nullable=True),
sa.Column('images', sa.Text(), nullable=True),
sa.Column('image', sa.Text(), nullable=True),
sa.Column('card_parts', sa.Text(), nullable=True),
sa.Column('keywords', sa.Text(), nullable=True),
sa.Column('legalities', sa.Text(), nullable=True),
sa.Column('set_code', sa.String(10), nullable=True, index=True),
sa.Column('set_name', sa.String(255), nullable=True),
sa.Column('synced_at', sa.DateTime(), server_default=sa.func.now(),
onupdate=sa.func.now()),
sa.Column('created_at', sa.DateTime(), server_default=sa.func.now()),
)
# ------------------------------------------------------------------
# 4. Card Import Batches Table (card_import_batches)
# Source: app/models/card_import_batch.py class CardImportBatch
# ------------------------------------------------------------------
op.create_table(
'card_import_batches',
sa.Column('id', sa.Integer(), autoincrement=True, primary_key=True),
sa.Column('user_id', sa.Integer(),
sa.ForeignKey('mtgonline_users.id', ondelete='CASCADE'),
nullable=False, index=True),
sa.Column('filename', sa.String(255), nullable=False),
sa.Column('file_type', sa.String(10), nullable=False),
sa.Column('file_size', sa.Integer(), nullable=False),
sa.Column('status', sa.String(20), nullable=False, default='pending',
index=True),
sa.Column('total_cards', sa.Integer(), default=0),
sa.Column('matched_cards', sa.Integer(), default=0),
sa.Column('unmatched_cards', sa.Integer(), default=0),
sa.Column('match_results', sa.JSON(), nullable=True),
sa.Column('error_message', sa.Text(), nullable=True),
sa.Column('created_at', sa.DateTime(), server_default=sa.func.now()),
sa.Column('updated_at', sa.DateTime(), server_default=sa.func.now(),
onupdate=sa.func.now()),
)
# ------------------------------------------------------------------
# 5. User Card Imports Confirmed Table (user_card_imports_confirmed)
# Source: app/models/user_card_import_record.py
# class UserCardImportRecord
# ------------------------------------------------------------------
op.create_table(
'user_card_imports_confirmed',
sa.Column('id', sa.Integer(), autoincrement=True, primary_key=True),
sa.Column('user_id', sa.Integer(),
sa.ForeignKey('mtgonline_users.id', ondelete='CASCADE'),
nullable=False, index=True),
sa.Column('batch_id', sa.Integer(),
sa.ForeignKey('card_import_batches.id', ondelete='CASCADE'),
nullable=False, index=True),
sa.Column('is_confirmed', sa.Boolean(), nullable=False, default=True),
sa.Column('confirmed_at', sa.DateTime(), server_default=sa.func.now()),
)
# ------------------------------------------------------------------
# 6. Deck Card Links Table (deck_card_links)
# Source: app/models/mirror_models.py class DeckCardLink
# ------------------------------------------------------------------
op.create_table(
'deck_card_links',
sa.Column('id', sa.Integer(), primary_key=True, index=True),
sa.Column('deck_id', sa.Integer(),
sa.ForeignKey('mtgonline_decklist_files.id',
ondelete='CASCADE'),
nullable=False),
sa.Column('card_id', sa.Integer(),
sa.ForeignKey('mtg_cards_mirror.id'), nullable=False),
sa.Column('quantity', sa.Integer(), nullable=False, default=1),
sa.Column('zone', sa.String(20), nullable=False, default='main'),
)
# Unique constraint + indexes from mirror_models.py
op.create_unique_constraint(
'uq_deck_card_link', 'deck_card_links',
['deck_id', 'card_id', 'zone'])
op.create_index('idx_deck_card_deck', 'deck_card_links', ['deck_id'])
op.create_index('idx_deck_card_card', 'deck_card_links', ['card_id'])
def downgrade() -> None:
"""Drop missing tables in reverse dependency order."""
op.drop_table('deck_card_links')
op.drop_table('user_card_imports_confirmed')
op.drop_table('card_import_batches')
op.drop_table('mtg_cards_mirror')
op.drop_table('mtg_cards')
op.drop_table('mtg_sets')