""" SQLAlchemy ORM models for card mirrors and deck-card links. Mirrored card data lives in mtgo_platform for fast deckbuilding queries. The MTGJSON database remains the canonical source of truth. """ from sqlalchemy import ( Column, Integer, String, Text, DateTime, ForeignKey, Index, UniqueConstraint, Boolean ) from sqlalchemy.orm import relationship from sqlalchemy.sql import func from app.core.database import Base class MtgCardMirror(Base): """ Mirrored card data for user decks. Contains all relevant card fields copied from mtg_cards (mtg_data) to avoid cross-DB joins during deckbuilding. Back-references source_id to the canonical mtg_cards.id for sync tracking. """ __tablename__ = "mtg_cards_mirror" id = Column(Integer, primary_key=True, index=True) source_id = Column( Integer, nullable=True, index=True, comment="Logical reference to mtg_cards.id in mtg_data database (cross-DB reference, no FK constraint)" ) name = Column(String(255), nullable=False, index=True) mana_cost = Column(String(255), nullable=True) type_line = Column(String(255), nullable=True) oracle_text = Column(Text, nullable=True) power = Column(String(50), nullable=True) toughness = Column(String(50), nullable=True) rarity = Column(String(50), nullable=True) layout = Column(String(50), nullable=True) artist = Column(String(255), nullable=True) flavor_text = Column(Text, nullable=True) numbers = Column(String(100), nullable=True) identifiers = Column(Text, nullable=True) # JSON string of all identifiers images = Column(Text, nullable=True) # JSON string of image URLs image = Column(Text, nullable=True) # Card image URL from MTGJSON card_parts = Column(Text, nullable=True) # Comma-separated list of face names keywords = Column(Text, nullable=True) # Comma-separated keywords legalities = Column(Text, nullable=True) # JSON of format legality set_code = Column(String(10), nullable=True, index=True) set_name = Column(String(255), nullable=True) # Sync tracking synced_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) created_at = Column(DateTime, server_default=func.now()) # Relationships deck_links = relationship( "DeckCardLink", back_populates="card", cascade="all, delete-orphan" ) def __repr__(self) -> str: return f"" class DeckCardLink(Base): """ Junction table linking user decks to mirrored cards. Represents a specific quantity of a mirrored card in a specific zone (main deck or sideboard) of a user's deck. """ __tablename__ = "deck_card_links" id = Column(Integer, primary_key=True, index=True) deck_id = Column(Integer, ForeignKey("mtgonline_decklist_files.id", ondelete="CASCADE"), nullable=False) card_id = Column(Integer, ForeignKey("mtg_cards_mirror.id"), nullable=False) quantity = Column(Integer, nullable=False, default=1) zone = Column(String(20), nullable=False, default="main") # 'main' or 'sideboard' # Composite unique: a card can only appear once per zone in a deck __table_args__ = ( UniqueConstraint('deck_id', 'card_id', 'zone', name='uq_deck_card_link'), Index('idx_deck_card_deck', 'deck_id'), Index('idx_deck_card_card', 'card_id'), ) # Relationships deck = relationship("DecklistFile", back_populates="card_links") card = relationship("MtgCardMirror", back_populates="deck_links") def __repr__(self) -> str: return ( f"" )