Phase 2.1: Card mirror system for deckbuilding features

- Created MtgCardMirror and DeckCardLink models in mirror_models.py
- Created card_mirror_service.py with sync_mirrors functionality
- Added sync_mirrors() method to mtgjson_manager.py
- Added mirror_get_db() dependency to database.py
- Added DecklistFile.status column (DRAUGHT/FINAL)
- Updated DeckCreate schema with status field
- Added DeckWithCardsResponse schema with card_count
- Updated deck router to query card mirrors and return card counts
- Added plain text deck content support
This commit is contained in:
2026-07-22 03:35:57 +00:00
parent a01e33eb5e
commit 2c74a107bc
9 changed files with 744 additions and 70 deletions
+47 -10
View File
@@ -1,18 +1,19 @@
"""Deck management router endpoints."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete, update
from sqlalchemy import select, delete, update, func
from typing import Optional, List
from app.core.database import get_db
from app.core.security import get_current_user
from app.models.models import DecklistFile, DecklistFolder
from app.schemas.schemas import DeckCreate, DeckUpdate, DeckResponse, FolderCreate, FolderResponse
from app.models.mirror_models import DeckCardLink
from app.schemas.schemas import DeckCreate, DeckUpdate, DeckResponse, DeckWithCardsResponse, FolderCreate, FolderResponse
router = APIRouter()
@router.get("/", response_model=List[DeckResponse])
@router.get("/", response_model=List[DeckWithCardsResponse])
async def list_decks(
folder_id: Optional[int] = None,
page: int = 1,
@@ -44,10 +45,24 @@ async def list_decks(
result = await db.execute(stmt)
decks = result.scalars().all()
return [DeckResponse.model_validate(deck) for deck in decks]
# Include card counts for each deck
deck_responses = []
for deck in decks:
# Get card count
count_stmt = select(func.count()).select_from(DeckCardLink).where(
DeckCardLink.deck_id == deck.id
)
count_result = await db.execute(count_stmt)
card_count = count_result.scalar() or 0
deck_data = DeckWithCardsResponse.model_validate(deck)
deck_data.card_count = card_count
deck_responses.append(deck_data)
return deck_responses
@router.post("/", response_model=DeckResponse)
@router.post("/", response_model=DeckWithCardsResponse)
async def create_deck(
request: DeckCreate,
db: AsyncSession = Depends(get_db),
@@ -76,11 +91,15 @@ async def create_deck(
name=request.name,
content=request.content,
format=request.format,
status=request.status,
)
db.add(new_deck)
await db.flush()
return DeckResponse.model_validate(new_deck)
# Return with card count (0 for new deck)
result = DeckWithCardsResponse.model_validate(new_deck)
result.card_count = 0
return result
@router.get("/folders", response_model=List[FolderResponse])
@@ -171,7 +190,7 @@ async def delete_folder(
return {"message": "Folder deleted successfully"}
@router.get("/{deck_id}", response_model=DeckResponse)
@router.get("/{deck_id}", response_model=DeckWithCardsResponse)
async def get_deck(
deck_id: int,
db: AsyncSession = Depends(get_db),
@@ -193,10 +212,19 @@ async def get_deck(
detail="Deck not found",
)
return DeckResponse.model_validate(deck)
# Get card count
count_stmt = select(func.count()).select_from(DeckCardLink).where(
DeckCardLink.deck_id == deck_id
)
count_result = await db.execute(count_stmt)
card_count = count_result.scalar() or 0
result = DeckWithCardsResponse.model_validate(deck)
result.card_count = card_count
return result
@router.patch("/{deck_id}", response_model=DeckResponse)
@router.patch("/{deck_id}", response_model=DeckWithCardsResponse)
async def update_deck(
deck_id: int,
request: DeckUpdate,
@@ -235,7 +263,16 @@ async def update_deck(
result = await db.execute(stmt)
updated_deck = result.scalar_one_or_none()
return DeckResponse.model_validate(updated_deck)
# Get card count
count_stmt = select(func.count()).select_from(DeckCardLink).where(
DeckCardLink.deck_id == deck_id
)
count_result = await db.execute(count_stmt)
card_count = count_result.scalar() or 0
result = DeckWithCardsResponse.model_validate(updated_deck)
result.card_count = card_count
return result
@router.delete("/{deck_id}")