Initial commit of mtgonline project

This commit is contained in:
2026-07-18 04:57:40 +00:00
commit 86c12376f8
1870 changed files with 547994 additions and 0 deletions
+225
View File
@@ -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)