Files
mtgonline/backend/app/services/card_search_service.py
T
akadmin 900f51af3a Complete architecture simplification: 54+ files → 37 files
- Removed 11 duplicate router files (auth.py, users.py, decks.py, card_router.py, interactions.py, user_data.py, rooms.py, games/router.py, refresh.py, card_import.py, ws.py)
- Removed duplicate core files (logging_config.py, settings.py)
- Removed unused services (card_mirror_service.py, mtgjson_loader.py, mtgjson_uploader.py)
- Removed unused schemas (proto_messages.py, protocol_constants.py)
- Created run.py startup script for simple execution
- Created app/core/config.py for unified configuration management
- Created app/core/logging.py for logging configuration
- Consolidated routers into 6 focused files (auth_router.py, users_router.py, decks_router.py, cards_router.py, user_data_router.py, games_router.py)
- Updated all imports to use consolidated model paths
- Added backward compatibility aliases in database.py
- Created SIMPLIFICATION_SUMMARY.md documentation

Architecture improvements:
- Single unified database engine with auto-detection (SQLite/PostgreSQL)
- Consolidated models into 2 files (models.py, mtg_models.py)
- Clean router structure with one file per domain
- Simplified configuration with sensible defaults
- SQLite by default for development, PostgreSQL for production

Testing:
- All imports verified working
- App loads successfully
- Ready for development and testing
2026-08-26 15:15:36 +00:00

190 lines
5.5 KiB
Python

"""Card search service with filters."""
from typing import List, Dict, Any, Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, or_, and_
from sqlalchemy.orm import selectinload
from app.models.mtg_models import MtgCard, MtgSet, MtgCardMirror
class CardSearchService:
"""Card search service with filters."""
@staticmethod
async def search_cards(
db: AsyncSession,
query: str,
card_type: Optional[str] = None,
set_code: Optional[str] = None,
color: Optional[str] = None,
limit: int = 100,
offset: int = 0,
) -> Dict[str, Any]:
"""
Search cards with filters.
Args:
db: Database session
query: Search query (name, type, mana cost)
card_type: Filter by card type
set_code: Filter by set code
color: Filter by card color
limit: Maximum results
offset: Number of results to skip
Returns:
Dictionary with search results and metadata
"""
# Build conditions
conditions = [
or_(
MtgCard.name.ilike(f"%{query}%"),
MtgCard.type_line.ilike(f"%{query}%"),
MtgCard.mana_cost.ilike(f"%{query}%"),
)
]
if card_type:
conditions.append(MtgCard.type_line.ilike(f"%{card_type}%"))
if set_code:
conditions.append(MtgCard.set_code == set_code)
if color:
# Parse color string (e.g., "WU" for white-blue)
colors = [c.strip() for c in color.upper().split(",")]
for c in colors:
if c in ["W", "U", "B", "R", "G"]:
conditions.append(MtgCard.colors.ilike(f"%{c}%"))
# Count total results
count_stmt = select(MtgCard).where(*conditions)
total_result = await db.execute(count_stmt)
total = len(total_result.scalars().all())
# Fetch results with pagination
stmt = select(MtgCard).where(*conditions).offset(offset).limit(limit)
result = await db.execute(stmt)
cards = result.scalars().all()
# Format results
card_list = []
for card in cards:
card_data = {
"id": card.id,
"name": card.name,
"mana_cost": card.mana_cost,
"type_line": card.type_line,
"oracle_text": card.oracle_text,
"power": card.power,
"toughness": card.toughness,
"rarity": card.rarity,
"layout": card.layout,
"colors": card.colors,
"set_code": card.set_code,
"set_name": card.set_name,
}
card_list.append(card_data)
return {
"cards": card_list,
"total": total,
"page": offset // limit + 1,
"page_size": limit,
"total_pages": (total + limit - 1) // limit,
}
@staticmethod
async def get_card_by_id(db: AsyncSession, card_id: int) -> Optional[Dict[str, Any]]:
"""
Get a card by its ID.
Args:
db: Database session
card_id: Card ID
Returns:
Card data dictionary or None
"""
stmt = select(MtgCard).where(MtgCard.id == card_id)
result = await db.execute(stmt)
card = result.scalar_one_or_none()
if not card:
return None
return {
"id": card.id,
"name": card.name,
"mana_cost": card.mana_cost,
"type_line": card.type_line,
"oracle_text": card.oracle_text,
"power": card.power,
"toughness": card.toughness,
"rarity": card.rarity,
"layout": card.layout,
"colors": card.colors,
"set_code": card.set_code,
"set_name": card.set_name,
"identifiers": card.identifiers,
"images": card.images,
}
@staticmethod
async def get_sets(db: AsyncSession) -> List[Dict[str, Any]]:
"""
Get all available sets.
Args:
db: Database session
Returns:
List of set data dictionaries
"""
stmt = select(MtgSet).order_by(MtgSet.name)
result = await db.execute(stmt)
sets = result.scalars().all()
return [
{
"id": s.id,
"name": s.name,
"code": s.code,
"release_date": s.release_date,
"card_count": s.card_count,
}
for s in sets
]
@staticmethod
async def get_card_types(db: AsyncSession) -> List[str]:
"""
Get all unique card types.
Args:
db: Database session
Returns:
List of unique card types
"""
stmt = select(MtgCard.type_line).distinct()
result = await db.execute(stmt)
types = result.scalars().all()
return list(types)
@staticmethod
async def get_card_rarities(db: AsyncSession) -> List[str]:
"""
Get all unique card rarities.
Args:
db: Database session
Returns:
List of unique rarities
"""
stmt = select(MtgCard.rarity).distinct()
result = await db.execute(stmt)
rarities = result.scalars().all()
return list(rarities)