feat: complete deckbuilding feature with local card mirror

- Add MtgonlineCard model (local card data mirror in mtgonline DB)
- Create user_deck.py models (UserDeck, UserDeckCard, DeckPrecedent, CardSuggestion)
- Create user_deck_schemas.py with Pydantic schemas
- Update decks.py router to use local MtgonlineCard instead of cross-DB MtgCard
- Add migration 002 (user deck building tables)
- Add migration 003 (mtgonline_cards table)
- All card lookups now use local mirror for fast queries
- No cross-DB joins in deckbuilding endpoints
This commit is contained in:
2026-07-24 04:42:22 +00:00
parent 356c2121b2
commit c23f88cd41
10 changed files with 1348 additions and 226 deletions
+39
View File
@@ -45,6 +45,45 @@ class User(Base):
return f"<User {self.username} (ID: {self.id})>"
class MtgonlineCard(Base):
"""
Local card data mirror for the mtgonline database.
Mirrors data from mtg_cards (mtgdata database) to avoid cross-database
joins during deckbuilding. Maintains source_id to reference the canonical
mtg_cards.id for sync tracking.
"""
__tablename__ = "mtgonline_cards"
id = Column(Integer, primary_key=True, index=True)
source_id = Column(Integer, nullable=True, index=True) # References mtg_cards.id in mtgdata
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
images = Column(Text, nullable=True) # JSON string
image = Column(Text, nullable=True) # Card image URL
set_code = Column(String(10), nullable=True, index=True)
set_name = Column(String(255), nullable=True)
card_parts = Column(Text, nullable=True) # Comma-separated face names
keywords = Column(Text, nullable=True) # Comma-separated keywords
legalities = Column(Text, nullable=True) # JSON of format legality
# Sync tracking
synced_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
created_at = Column(DateTime, server_default=func.now())
def __repr__(self) -> str:
return f"<MtonlineCard {self.name} (ID: {self.id}, source: {self.source_id})>"
class DecklistFolder(Base):
"""User deck folder."""
__tablename__ = "mtgonline_decklist_folders"