Files
mtgonline/backend/app/schemas/user_card_collection.py
T
akadmin 1df04aea52 Phase 7: End-to-End API Testing - Database setup, migrations, and application fixes
- Switched from psycopg2 to asyncpg for async SQLAlchemy support
- Fixed router registration in main.py - removed duplicate prefixes
- Added user_data export to routers/__init__.py
- Refactored decks router to use DeckManager service layer
- Integrated FuzzyCardMatcher into card_router search endpoints
- Made WishlistCreate.card_id optional for proper schema validation
- Set PostgreSQL password and configured scram-sha-256 auth
- Updated alembic.ini to use local PostgreSQL instead of Docker hostname
- Created generic_schemas.py for reusable schema patterns
- Added test_routers.py and test_schema_validation.py test files
- All 6 Alembic migrations applied successfully (37 tables created)
- Application running on port 8000 with all services connected
2026-08-18 03:35:19 +00:00

158 lines
3.8 KiB
Python

"""
Pydantic schemas for user card collection features.
Covers card collection CRUD operations, wishlist management, and collection statistics.
"""
from pydantic import BaseModel, Field, ConfigDict
from typing import Optional, List, Dict, Any
from datetime import datetime
from enum import Enum
# ===== Enum Types =====
class CardCondition(str, Enum):
"""Card condition ratings."""
NEAR_MINT = "NEAR_MINT"
LIGHTLY_PLAYED = "LIGHTLY_PLAYED"
MODERATELY_PLAYED = "MODERATELY_PLAYED"
HEAVILY_PLAYED = "HEAVILY_PLAYED"
DAMAGED = "DAMAGED"
class AcquisitionMethod(str, Enum):
"""How a card was acquired."""
PACK_OPENING = "PACK_OPENING"
TRADE = "TRADE"
PURCHASE = "PURCHASE"
GIFT = "GIFT"
CONTEST = "CONTEST"
OTHER = "OTHER"
# ===== Card Collection Schemas =====
class CardCollectionCreate(BaseModel):
"""Card collection item creation request."""
card_id: int
quantity: int = Field(1, ge=1)
condition: CardCondition = CardCondition.NEAR_MINT
language: str = Field("EN", max_length=5)
is_foil: bool = False
is_alt_art: bool = False
acquired_date: Optional[datetime] = None
acquisition_method: Optional[AcquisitionMethod] = None
notes: Optional[str] = None
class CardCollectionUpdate(BaseModel):
"""Card collection item update request."""
quantity: Optional[int] = None
condition: Optional[CardCondition] = None
language: Optional[str] = None
is_foil: Optional[bool] = None
is_alt_art: Optional[bool] = None
acquired_date: Optional[datetime] = None
acquisition_method: Optional[AcquisitionMethod] = None
notes: Optional[str] = None
class CardCollectionResponse(BaseModel):
"""Card collection item response."""
id: int
user_id: int
card_id: int
quantity: int
condition: str
language: str
is_foil: bool
is_alt_art: bool
acquired_date: Optional[datetime]
acquisition_method: Optional[str]
notes: Optional[str]
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class CardCollectionListResponse(BaseModel):
"""List of user card collection."""
cards: List[CardCollectionResponse]
total: int
page: int
page_size: int
total_pages: int
# ===== Wishlist Schemas =====
class WishlistCreate(BaseModel):
"""Wishlist item creation request."""
card_id: Optional[int] = None
max_price: Optional[float] = None
notes: Optional[str] = None
class WishlistUpdate(BaseModel):
"""Wishlist item update request."""
max_price: Optional[float] = None
notes: Optional[str] = None
class WishlistResponse(BaseModel):
"""Wishlist item response."""
id: int
user_id: int
card_id: int
max_price: Optional[float]
notes: Optional[str]
created_at: datetime
model_config = ConfigDict(from_attributes=True)
class WishlistListResponse(BaseModel):
"""List of wishlist items."""
items: List[WishlistResponse]
total: int
# ===== Collection Statistics Schemas =====
class CollectionStatistics(BaseModel):
"""User collection statistics summary."""
total_cards: int
unique_cards: int
total_quantity: int
foil_count: int
alt_art_count: int
condition_breakdown: Dict[str, int]
language_breakdown: Dict[str, int]
acquisition_breakdown: Dict[str, int]
class CollectionSummaryResponse(BaseModel):
"""Collection summary with statistics."""
statistics: CollectionStatistics
recent_acquisitions: List[CardCollectionResponse]
top_cards: List[CardCollectionResponse]
# ===== Generic Response Schemas =====
class MessageResponse(BaseModel):
"""Generic message response."""
message: str
class CountResponse(BaseModel):
"""Generic count response."""
count: int
class ErrorDetail(BaseModel):
"""Error detail."""
error: str
detail: str