Initial commit of mtgonline project
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
"""Card database service for importing and querying card data."""
|
||||
import httpx
|
||||
from typing import List, Dict, Optional
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class CardType(str, Enum):
|
||||
"""Card types from Magic: The Gathering."""
|
||||
CREATURES = "Creature"
|
||||
INSTANT = "Instant"
|
||||
SORCERY = "Sorcery"
|
||||
ENCHANTMENT = "Enchantment"
|
||||
ARTIFACT = "Artifact"
|
||||
PLANE = "Plane"
|
||||
PLANESWALKER = "Planeswalker"
|
||||
LAND = "Land"
|
||||
BATTLE = "Battle"
|
||||
|
||||
|
||||
class CardColor(str, Enum):
|
||||
"""Card colors."""
|
||||
WHITE = "W"
|
||||
BLUE = "U"
|
||||
BLACK = "B"
|
||||
RED = "R"
|
||||
GREEN = "G"
|
||||
COLORLESS = "C"
|
||||
MULTICOLOR = "M"
|
||||
SHARD = "S"
|
||||
WIDGET = "X"
|
||||
|
||||
|
||||
class CardRarity(str, Enum):
|
||||
"""Card rarities."""
|
||||
COMMON = "common"
|
||||
UNCOMMON = "uncommon"
|
||||
RARE = "rare"
|
||||
MYTHIC = "mythic"
|
||||
SPECIAL = "special"
|
||||
|
||||
|
||||
@dataclass
|
||||
class CardData:
|
||||
"""Card information from card database."""
|
||||
id: int
|
||||
name: str
|
||||
types: List[CardType]
|
||||
colors: List[CardColor]
|
||||
rarity: CardRarity
|
||||
set_code: str
|
||||
collector_number: str
|
||||
flavor_text: Optional[str] = None
|
||||
rules_text: Optional[str] = None
|
||||
power: Optional[str] = None
|
||||
toughness: Optional[str] = None
|
||||
artist: Optional[str] = None
|
||||
image_url: Optional[str] = None
|
||||
provider_id: Optional[str] = None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name} ({self.set_code}-{self.collector_number})"
|
||||
|
||||
|
||||
class CardDatabase:
|
||||
"""Card database service for importing and querying card data."""
|
||||
|
||||
def __init__(self):
|
||||
self.cards: Dict[int, CardData] = {}
|
||||
self._next_id = 1
|
||||
|
||||
async def import_from_mtjson(self, url: str = "https://mtjson.xyz/api/5.0.0/") -> List[CardData]:
|
||||
"""Import card data from MTJSON API."""
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
imported_cards = []
|
||||
for card_data in data:
|
||||
card = self._parse_mtjson_card(card_data)
|
||||
self.cards[self._next_id] = card
|
||||
imported_cards.append(card)
|
||||
self._next_id += 1
|
||||
|
||||
return imported_cards
|
||||
|
||||
def _parse_mtjson_card(self, data: dict) -> CardData:
|
||||
"""Parse MTJSON card data into CardData."""
|
||||
card_id = self._next_id
|
||||
|
||||
# Extract types
|
||||
types = []
|
||||
if "types" in data:
|
||||
for type_str in data["types"]:
|
||||
try:
|
||||
types.append(CardType(type_str))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Extract colors
|
||||
colors = []
|
||||
if "colors" in data:
|
||||
for color_str in data["colors"]:
|
||||
try:
|
||||
colors.append(CardColor(color_str))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Extract rarity
|
||||
rarity = CardRarity.COMMON
|
||||
if "rarity" in data:
|
||||
try:
|
||||
rarity = CardRarity(data["rarity"].lower())
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Extract set and collector number
|
||||
set_code = ""
|
||||
collector_number = ""
|
||||
if "set" in data:
|
||||
set_code = data["set"]
|
||||
if "collectorNumber" in data:
|
||||
collector_number = data["collectorNumber"]
|
||||
|
||||
# Extract image URL
|
||||
image_url = None
|
||||
if "imageUris" in data and "normal" in data["imageUris"]:
|
||||
image_url = data["imageUris"]["normal"]
|
||||
|
||||
return CardData(
|
||||
id=card_id,
|
||||
name=data.get("name", ""),
|
||||
types=types,
|
||||
colors=colors,
|
||||
rarity=rarity,
|
||||
set_code=set_code,
|
||||
collector_number=collector_number,
|
||||
flavor_text=data.get("flavorText"),
|
||||
rules_text=data.get("rulesText"),
|
||||
power=data.get("power"),
|
||||
toughness=data.get("toughness"),
|
||||
artist=data.get("artist"),
|
||||
image_url=image_url,
|
||||
provider_id=data.get("multiverseId"),
|
||||
)
|
||||
|
||||
def get_card_by_id(self, card_id: int) -> Optional[CardData]:
|
||||
"""Get card by ID."""
|
||||
return self.cards.get(card_id)
|
||||
|
||||
def get_card_by_name(self, name: str) -> List[CardData]:
|
||||
"""Get cards by name (case-insensitive)."""
|
||||
name_lower = name.lower()
|
||||
return [card for card in self.cards.values() if card.name.lower() == name_lower]
|
||||
|
||||
def search_cards(
|
||||
self,
|
||||
query: str = "",
|
||||
card_type: Optional[CardType] = None,
|
||||
color: Optional[CardColor] = None,
|
||||
rarity: Optional[CardRarity] = None,
|
||||
set_code: Optional[str] = None,
|
||||
limit: int = 50,
|
||||
) -> List[CardData]:
|
||||
"""Search cards with filters."""
|
||||
results = list(self.cards.values())
|
||||
|
||||
# Filter by query
|
||||
if query:
|
||||
query_lower = query.lower()
|
||||
results = [card for card in results if query_lower in card.name.lower()]
|
||||
|
||||
# Filter by type
|
||||
if card_type:
|
||||
results = [card for card in results if card_type in card.types]
|
||||
|
||||
# Filter by color
|
||||
if color:
|
||||
results = [card for card in results if color in card.colors]
|
||||
|
||||
# Filter by rarity
|
||||
if rarity:
|
||||
results = [card for card in results if card.rarity == rarity]
|
||||
|
||||
# Filter by set
|
||||
if set_code:
|
||||
results = [card for card in results if card.set_code == set_code.upper()]
|
||||
|
||||
return results[:limit]
|
||||
|
||||
def get_random_card(self) -> Optional[CardData]:
|
||||
"""Get a random card from the database."""
|
||||
import random
|
||||
if not self.cards:
|
||||
return None
|
||||
return random.choice(list(self.cards.values()))
|
||||
|
||||
def get_card_count(self) -> int:
|
||||
"""Get total number of cards in database."""
|
||||
return len(self.cards)
|
||||
|
||||
|
||||
# Singleton instance
|
||||
card_database = CardDatabase()
|
||||
|
||||
|
||||
async def import_cards() -> List[CardData]:
|
||||
"""Import cards from MTJSON."""
|
||||
return await card_database.import_from_mtjson()
|
||||
|
||||
|
||||
def search_cards(**kwargs) -> List[CardData]:
|
||||
"""Search cards with filters."""
|
||||
return card_database.search_cards(**kwargs)
|
||||
|
||||
|
||||
def get_card_by_name(name: str) -> List[CardData]:
|
||||
"""Get cards by name."""
|
||||
return card_database.get_card_by_name(name)
|
||||
|
||||
|
||||
def get_card_by_id(card_id: int) -> Optional[CardData]:
|
||||
"""Get card by ID."""
|
||||
return card_database.get_card_by_id(card_id)
|
||||
Reference in New Issue
Block a user