Final project commit: MTGJSON data integration and backend API
This commit is contained in:
@@ -0,0 +1,671 @@
|
||||
"""
|
||||
MTG Card Profile Extractor
|
||||
|
||||
Extracts structured profiles from MTGJSON card data.
|
||||
Identifies mechanics, archetypes, mana costs, targets, and other game-relevant attributes.
|
||||
"""
|
||||
import re
|
||||
from typing import List, Dict, Optional, Set
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class CardProfile:
|
||||
"""
|
||||
Structured profile of a card for interaction analysis.
|
||||
|
||||
Contains all relevant game attributes extracted from MTGJSON data.
|
||||
"""
|
||||
# Basic info
|
||||
id: int
|
||||
name: str
|
||||
mana_cost: Optional[str]
|
||||
type_line: Optional[str]
|
||||
oracle_text: Optional[str]
|
||||
subtypes: Optional[str]
|
||||
supertypes: Optional[str]
|
||||
set_code: Optional[str]
|
||||
|
||||
# Extracted attributes
|
||||
colors: List[str] = None # ['W', 'U', 'B', 'R', 'G']
|
||||
color_identity: List[str] = None
|
||||
mechanics: List[str] = None
|
||||
archetypes: List[str] = None
|
||||
targets: List[str] = None # ['creature', 'artifact', 'player', etc.]
|
||||
triggers: List[str] = None
|
||||
effects: List[str] = None
|
||||
themes: List[str] = None # ['storm', 'tokens', 'draw', etc.]
|
||||
|
||||
def __post_init__(self):
|
||||
"""Initialize lists if None."""
|
||||
if self.colors is None:
|
||||
self.colors = []
|
||||
if self.color_identity is None:
|
||||
self.color_identity = []
|
||||
if self.mechanics is None:
|
||||
self.mechanics = []
|
||||
if self.archetypes is None:
|
||||
self.archetypes = []
|
||||
if self.targets is None:
|
||||
self.targets = []
|
||||
if self.triggers is None:
|
||||
self.triggers = []
|
||||
if self.effects is None:
|
||||
self.effects = []
|
||||
if self.themes is None:
|
||||
self.themes = []
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""Convert profile to dictionary."""
|
||||
return {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'mana_cost': self.mana_cost,
|
||||
'type_line': self.type_line,
|
||||
'oracle_text': self.oracle_text,
|
||||
'subtypes': self.subtypes,
|
||||
'supertypes': self.supertypes,
|
||||
'set_code': self.set_code,
|
||||
'colors': self.colors,
|
||||
'color_identity': self.color_identity,
|
||||
'mechanics': self.mechanics,
|
||||
'archetypes': self.archetypes,
|
||||
'targets': self.targets,
|
||||
'triggers': self.triggers,
|
||||
'effects': self.effects,
|
||||
'themes': self.themes,
|
||||
}
|
||||
|
||||
|
||||
class CardProfileExtractor:
|
||||
"""
|
||||
Extracts card profiles from MTGJSON data.
|
||||
|
||||
Uses regex patterns and curated dictionaries to identify:
|
||||
- Mana costs and color identity
|
||||
- Game mechanics (flying, first strike, etc.)
|
||||
- Archetypes (goblin, elf, vampire, etc.)
|
||||
- Targets (creature, artifact, player, etc.)
|
||||
- Triggers (enters battlefield, dies, attacks, etc.)
|
||||
- Effects (gain flying, draw card, etc.)
|
||||
- Themes (storm, tokens, mill, etc.)
|
||||
"""
|
||||
|
||||
# Color symbols in mana costs
|
||||
COLOR_SYMBOLS = {
|
||||
'{W}': 'W',
|
||||
'{U}': 'U',
|
||||
'{B}': 'B',
|
||||
'{R}': 'R',
|
||||
'{G}': 'G',
|
||||
}
|
||||
|
||||
# Known mechanics and their patterns
|
||||
MECHANICS = {
|
||||
'flying': r'Flying',
|
||||
'first_strike': r'First strike',
|
||||
'double_strike': r'Double strike',
|
||||
'deathtouch': r'Death touch',
|
||||
'lifelink': r'Lifelink',
|
||||
'haste': r'Haste',
|
||||
'trample': r'Trample',
|
||||
'menace': r'Menace',
|
||||
'vigilance': r'Vegilance',
|
||||
'reach': r'Reach',
|
||||
'indestructible': r'Indestructible',
|
||||
'hexproof': r'Hexproof',
|
||||
'shroud': r'Shroud',
|
||||
'defender': r'Defender',
|
||||
'landfall': r'Landfall',
|
||||
'delve': r'Delve',
|
||||
'suspend': r'Suspend',
|
||||
'convoke': r'Convoke',
|
||||
'rampage': r'Rampage',
|
||||
'toxic': r'Toxic',
|
||||
'crew': r'Crew',
|
||||
'equip': r'Equip',
|
||||
'annihilator': r'Annihilator',
|
||||
'spectacle': r'Spectacle',
|
||||
'prowess': r'Prowess',
|
||||
'aftermath': r'Aftermath',
|
||||
'adapt': r'Adapt',
|
||||
'amplify': r'Amplify',
|
||||
'awaken': r'Awaken',
|
||||
'kicker': r'Kicker',
|
||||
'morph': r'Morph',
|
||||
'evolve': r'Evolve',
|
||||
'exalted': r'Exalted',
|
||||
'storm': r'Storm',
|
||||
'madness': r'Madness',
|
||||
'manifest': r'Manifest',
|
||||
'modular': r'Modular',
|
||||
'mutate': r'Mutate',
|
||||
'transform': r'Transform',
|
||||
'unearth': r'Unearth',
|
||||
'persist': r'Persist',
|
||||
'rebound': r'Rebound',
|
||||
'replicate': r'Replicate',
|
||||
'soulshift': r'Soulshift',
|
||||
'dredge': r'Dredge',
|
||||
'devour': r'Devour',
|
||||
'banding': r'Band with',
|
||||
'bestow': r'Bestow',
|
||||
'channel': r'Channel',
|
||||
'clash': r'Clash',
|
||||
'curse': r'Curse',
|
||||
'dwell': r'Dwell',
|
||||
'evoke': r'Evoke',
|
||||
'exploit': r'Exploit',
|
||||
'extort': r'Extort',
|
||||
'flash': r'Flash',
|
||||
'foretell': r'Foretell',
|
||||
'frenzy': r'Frenzy',
|
||||
'grudge': r'Grudge',
|
||||
'heroic': r'Heroic',
|
||||
'hideaway': r'Hideaway',
|
||||
'horrify': r'Horrify',
|
||||
'impetus': r'Impetus',
|
||||
'infect': r'Infect',
|
||||
'journey': r'Journey',
|
||||
'kicker': r'Kicker',
|
||||
'landfall': r'Landfall',
|
||||
'meld': r'Meld',
|
||||
'miracle': r'Miracle',
|
||||
'monstrosity': r'Monstrosity',
|
||||
'morph': r'Morph',
|
||||
'mutate': r'Mutate',
|
||||
'ninja': r'Ninja',
|
||||
'pact': r'Pact',
|
||||
'persist': r'Persist',
|
||||
'provoke': r'Provoke',
|
||||
'quest': r'Quest',
|
||||
'raid': r'Raid',
|
||||
'rebound': r'Rebound',
|
||||
'replicate': r'Replicate',
|
||||
'revolt': r'Revolt',
|
||||
'shroud': r'Shroud',
|
||||
'skulk': r'Skulk',
|
||||
'snow': r'Snow',
|
||||
'splice': r'Splice',
|
||||
'staunch': r'Staunch',
|
||||
'storm': r'Storm',
|
||||
'suspend': r'Suspend',
|
||||
'surge': r'Surge',
|
||||
'swarm': r'Swarm',
|
||||
'thorn': r'Thorn',
|
||||
'toxic': r'Toxic',
|
||||
'transfigure': r'Transfigure',
|
||||
'transform': r'Transform',
|
||||
'unearth': r'Unearth',
|
||||
'unleash': r'Unleash',
|
||||
'vampiric': r'Vampiric',
|
||||
'ward': r'Ward',
|
||||
'willow': r'Willow',
|
||||
'winter': r'Winter',
|
||||
'wither': r'Wither',
|
||||
'wurm': r'Wurm',
|
||||
}
|
||||
|
||||
# Known archetypes and their patterns
|
||||
ARCHETYPES = {
|
||||
'goblin': r'Goblin',
|
||||
'elf': r'Elf',
|
||||
'vampire': r'Veampire',
|
||||
'angel': r'Angel',
|
||||
'dragon': r'Dragon',
|
||||
'human': r'Human',
|
||||
'zombie': r'Zombie',
|
||||
'soldier': r'Soldier',
|
||||
'knight': r'Knight',
|
||||
'wizard': r'Wizard',
|
||||
'spirit': r'Spirit',
|
||||
'demon': r'Demon',
|
||||
'snake': r'Snake',
|
||||
'cat': r'Cat',
|
||||
'wolf': r'Wolf',
|
||||
'bear': r'Bear',
|
||||
'bird': r'Bird',
|
||||
'insect': r'Insect',
|
||||
'horror': r'Horror',
|
||||
'goat': r'Goat',
|
||||
'ox': r'Ox',
|
||||
'elephant': r'Elephant',
|
||||
'whale': r'Whale',
|
||||
'shark': r'Shark',
|
||||
'fish': r'Fish',
|
||||
'serpent': r'Serpent',
|
||||
'lizard': r'Lizard',
|
||||
'scorpion': r'Scorpion',
|
||||
'spider': r'Spider',
|
||||
'rat': r'Rat',
|
||||
'drake': r'Drake',
|
||||
'wyvern': r'Wyvern',
|
||||
'phoenix': r'Phoenix',
|
||||
'lynx': r'Lynx',
|
||||
'jaguar': r'Jaguar',
|
||||
'hydra': r'Hydra',
|
||||
'leviathan': r'Leviathan',
|
||||
'kraken': r'Kraken',
|
||||
'cyclops': r'Cyclops',
|
||||
'golem': r'Golem',
|
||||
'homunculus': r'Homunculus',
|
||||
'clay': r'Clay',
|
||||
'construct': r'Construct',
|
||||
'myr': r'Myr',
|
||||
'pirate': r'Pirate',
|
||||
'pegasus': r'Pegasus',
|
||||
'unicorn': r'Unicorn',
|
||||
'centaur': r'Centaur',
|
||||
'merfolk': r'Merfolk',
|
||||
'mermaid': r'Mermaid',
|
||||
'naga': r'Naga',
|
||||
'satyr': r'Satyr',
|
||||
'dryad': r'Dryad',
|
||||
'treant': r'Treant',
|
||||
'elemental': r'Elemental',
|
||||
'fiend': r'Fiend',
|
||||
'imp': r'Imp',
|
||||
'faerie': r'Faerie',
|
||||
'minion': r'Minion',
|
||||
'abomination': r'Abomination',
|
||||
'beast': r'Beast',
|
||||
'demigod': r'Demigod',
|
||||
'god': r'God',
|
||||
'avatar': r'Avatar',
|
||||
'guardian': r'Guardian',
|
||||
'warrior': r'Warrior',
|
||||
'rogue': r'Rogue',
|
||||
'artificer': r'Artificer',
|
||||
'bard': r'Bard',
|
||||
'monk': r'Monk',
|
||||
'ninja': r'Ninja',
|
||||
'samurai': r'Samurai',
|
||||
'assassin': r'Assassin',
|
||||
'thief': r'Thief',
|
||||
'acrobat': r'Acrobat',
|
||||
'explorer': r'Explorer',
|
||||
'myth': r'Myth',
|
||||
'illusion': r'Illusion',
|
||||
'mirror': r'Mirror',
|
||||
'phantom': r'Phantom',
|
||||
'shapeshifter': r'Shapeshifter',
|
||||
'shaman': r'Shaman',
|
||||
'skeleton': r'Skeleton',
|
||||
'slime': r'Slime',
|
||||
'squirrel': r'Squirrel',
|
||||
'troll': r'Troll',
|
||||
'tyrannosaur': r'Tyrannosaur',
|
||||
'wraith': r'Wraith',
|
||||
'wurm': r'Wurm',
|
||||
}
|
||||
|
||||
# Target types and their patterns
|
||||
TARGET_TYPES = {
|
||||
'creature': r'creature',
|
||||
'artifact': r'artifact',
|
||||
'enchantment': r'enchantment',
|
||||
'instant': r'instant',
|
||||
'sorcery': r'sorcery',
|
||||
'planeswalker': r'planeswalker',
|
||||
'land': r'land',
|
||||
'player': r'player',
|
||||
'spell': r'spell',
|
||||
'permanent': r'permanent',
|
||||
'creature card': r'creature [Cc]ard',
|
||||
}
|
||||
|
||||
# Trigger conditions and their patterns
|
||||
TRIGGERS = {
|
||||
'enters_battlefield': r'when [~|this] enters the battlefield',
|
||||
'leaves_battlefield': r'when [~|this] leaves the battlefield',
|
||||
'attacks': r'whenever [~|this] attacks',
|
||||
'blocks': r'whenever [~|this] blocks',
|
||||
'dies': r'when [~|this] dies',
|
||||
'damage': r'deals [0-9]+ damage',
|
||||
'draws_card': r'draw a card|draw two cards',
|
||||
'gains_life': r'gain [0-9]+ life',
|
||||
'creates_token': r'create a token',
|
||||
'taps': r'tap: add',
|
||||
'untaps': r'untap: add',
|
||||
'destroys': r'destroy target',
|
||||
'exiles': r'exile target',
|
||||
'counters_spell': r'counter target spell',
|
||||
}
|
||||
|
||||
# Game effects and their patterns
|
||||
EFFECTS = {
|
||||
'gain_flying': r'gain flying',
|
||||
'gain_first_strike': r'gain first strike',
|
||||
'gain_double_strike': r'gain double strike',
|
||||
'gain_deathtouch': r'gain deathtouch',
|
||||
'gain_lifelink': r'gain lifelink',
|
||||
'gain_haste': r'gain haste',
|
||||
'gain_trample': r'gain trample',
|
||||
'gain_vigilance': r'gain vigilance',
|
||||
'gain_indestructible': r'gain indestructible',
|
||||
'gain_hexproof': r'gain hexproof',
|
||||
'until_end_of_turn': r'until end of turn',
|
||||
'until_next_turn': r'until your next turn',
|
||||
'deal_damage': r'deal [0-9]+ damage',
|
||||
'gain_life': r'gain [0-9]+ life',
|
||||
'draw_card': r'draw [0-9]+ card',
|
||||
'create_token': r'create [0-9]+ token',
|
||||
'destroy': r'destroy target',
|
||||
'exile': r'exile target',
|
||||
'counter_spell': r'counter target spell',
|
||||
}
|
||||
|
||||
# Theme keywords and their patterns
|
||||
THEMES = {
|
||||
'storm': r'Storm',
|
||||
'tokens': r'create a token',
|
||||
'draw': r'draw a card',
|
||||
'life_gain': r'gain life',
|
||||
'board_wipe': r'destroy all',
|
||||
'reanimate': r'put from grave',
|
||||
'countermagic': r'counter target spell',
|
||||
'card_advantage': r'draw',
|
||||
'mana_acceleration': r'tap: add',
|
||||
'combat_tricks': r'gain [A-Za-z]+ until end of turn',
|
||||
'etb_effects': r'enters the battlefield',
|
||||
'ltb_effects': r'leaves the battlefield',
|
||||
'mill': r'put on bottom of library',
|
||||
'draw_go': r'draw a card',
|
||||
'aggro': r'deal [0-9]+ damage',
|
||||
'control': r'counter target spell',
|
||||
'midrange': r'creature',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the profile extractor."""
|
||||
pass
|
||||
|
||||
def extract_colors(self, mana_cost: Optional[str]) -> List[str]:
|
||||
"""
|
||||
Extract colors from mana cost.
|
||||
|
||||
Args:
|
||||
mana_cost: Mana cost string (e.g., '{1}{R}')
|
||||
|
||||
Returns:
|
||||
List of color symbols (e.g., ['R'])
|
||||
"""
|
||||
if not mana_cost:
|
||||
return []
|
||||
|
||||
colors = []
|
||||
for symbol, color in self.COLOR_SYMBOLS.items():
|
||||
if symbol in mana_cost:
|
||||
if color not in colors:
|
||||
colors.append(color)
|
||||
|
||||
return colors
|
||||
|
||||
def extract_mechanics(self, card_type_line: Optional[str], card_oracle: Optional[str]) -> List[str]:
|
||||
"""
|
||||
Extract game mechanics from card text.
|
||||
|
||||
Args:
|
||||
card_type_line: Card type line (e.g., 'Creature - Goblin Warrior')
|
||||
card_oracle: Card oracle text
|
||||
|
||||
Returns:
|
||||
List of mechanic names (e.g., ['haste', 'trample'])
|
||||
"""
|
||||
mechanics = []
|
||||
|
||||
# Combine type line and oracle text for checking
|
||||
text_to_check = f"{card_type_line or ''} {card_oracle or ''}".upper()
|
||||
|
||||
for mechanic, pattern in self.MECHANICS.items():
|
||||
if re.search(pattern, text_to_check, re.IGNORECASE):
|
||||
if mechanic not in mechanics:
|
||||
mechanics.append(mechanic)
|
||||
|
||||
return mechanics
|
||||
|
||||
def extract_archetypes(self, card_subtypes: Optional[str]) -> List[str]:
|
||||
"""
|
||||
Extract archetypes from card subtypes.
|
||||
|
||||
Args:
|
||||
card_subtypes: Card subtypes (e.g., 'Goblin, Warrior')
|
||||
|
||||
Returns:
|
||||
List of archetype names (e.g., ['goblin'])
|
||||
"""
|
||||
if not card_subtypes:
|
||||
return []
|
||||
|
||||
archetypes = []
|
||||
|
||||
for archetype, pattern in self.ARCHETYPES.items():
|
||||
if re.search(pattern, card_subtypes, re.IGNORECASE):
|
||||
if archetype not in archetypes:
|
||||
archetypes.append(archetype)
|
||||
|
||||
return archetypes
|
||||
|
||||
def extract_targets(self, card_oracle: Optional[str]) -> List[str]:
|
||||
"""
|
||||
Extract target types from oracle text.
|
||||
|
||||
Args:
|
||||
card_oracle: Card oracle text
|
||||
|
||||
Returns:
|
||||
List of target types (e.g., ['creature', 'player'])
|
||||
"""
|
||||
if not card_oracle:
|
||||
return []
|
||||
|
||||
targets = []
|
||||
|
||||
for target, pattern in self.TARGET_TYPES.items():
|
||||
if re.search(pattern, card_oracle, re.IGNORECASE):
|
||||
if target not in targets:
|
||||
targets.append(target)
|
||||
|
||||
return targets
|
||||
|
||||
def extract_triggers(self, card_oracle: Optional[str]) -> List[str]:
|
||||
"""
|
||||
Extract trigger conditions from oracle text.
|
||||
|
||||
Args:
|
||||
card_oracle: Card oracle text
|
||||
|
||||
Returns:
|
||||
List of trigger names (e.g., ['enters_battlefield', 'dies'])
|
||||
"""
|
||||
if not card_oracle:
|
||||
return []
|
||||
|
||||
triggers = []
|
||||
|
||||
for trigger, pattern in self.TRIGGERS.items():
|
||||
if re.search(pattern, card_oracle, re.IGNORECASE):
|
||||
if trigger not in triggers:
|
||||
triggers.append(trigger)
|
||||
|
||||
return triggers
|
||||
|
||||
def extract_effects(self, card_oracle: Optional[str]) -> List[str]:
|
||||
"""
|
||||
Extract game effects from oracle text.
|
||||
|
||||
Args:
|
||||
card_oracle: Card oracle text
|
||||
|
||||
Returns:
|
||||
List of effect names (e.g., ['gain_flying', 'draw_card'])
|
||||
"""
|
||||
if not card_oracle:
|
||||
return []
|
||||
|
||||
effects = []
|
||||
|
||||
for effect, pattern in self.EFFECTS.items():
|
||||
if re.search(pattern, card_oracle, re.IGNORECASE):
|
||||
if effect not in effects:
|
||||
effects.append(effect)
|
||||
|
||||
return effects
|
||||
|
||||
def extract_themes(self, card_mechanics: List[str], card_triggers: List[str],
|
||||
card_effects: List[str], card_targets: List[str]) -> List[str]:
|
||||
"""
|
||||
Extract card themes based on characteristics.
|
||||
|
||||
Args:
|
||||
card_mechanics: List of mechanics
|
||||
card_triggers: List of triggers
|
||||
card_effects: List of effects
|
||||
card_targets: List of targets
|
||||
|
||||
Returns:
|
||||
List of theme names (e.g., ['storm', 'tokens'])
|
||||
"""
|
||||
themes = []
|
||||
|
||||
# Storm theme
|
||||
if 'storm' in card_mechanics or 'storm' in card_targets:
|
||||
themes.append('storm')
|
||||
|
||||
# Token theme
|
||||
if any(e in card_effects for e in ['create_token', 'draw_card']):
|
||||
themes.append('tokens')
|
||||
|
||||
# Mill theme
|
||||
if any(t in card_triggers for t in ['draws_card']):
|
||||
themes.append('mill')
|
||||
|
||||
# Life gain theme
|
||||
if any(e in card_effects for e in ['gain_life', 'draw_card']):
|
||||
themes.append('life_gain')
|
||||
|
||||
# Board wipe theme
|
||||
if any(t in card_triggers for t in ['dies']):
|
||||
themes.append('board_wipe')
|
||||
|
||||
# Reanimate theme
|
||||
if any(t in card_triggers for t in ['leaves_battlefield']):
|
||||
themes.append('reanimate')
|
||||
|
||||
# Countermagic theme
|
||||
if any(e in card_effects for e in ['counter_spell']):
|
||||
themes.append('countermagic')
|
||||
|
||||
# Card advantage theme
|
||||
if any(t in card_triggers for t in ['draws_card']):
|
||||
themes.append('card_advantage')
|
||||
|
||||
# Mana acceleration theme
|
||||
if any(t in card_triggers for t in ['taps']):
|
||||
themes.append('mana_acceleration')
|
||||
|
||||
# Combat tricks theme
|
||||
if any(e in card_effects for e in ['gain_flying', 'gain_first_strike',
|
||||
'gain_double_strike', 'gain_deathtouch',
|
||||
'gain_lifelink', 'gain_vigilance']):
|
||||
themes.append('combat_tricks')
|
||||
|
||||
# ETB effects theme
|
||||
if any(t in card_triggers for t in ['enters_battlefield']):
|
||||
themes.append('etb_effects')
|
||||
|
||||
# LTB effects theme
|
||||
if any(t in card_triggers for t in ['leaves_battlefield']):
|
||||
themes.append('ltb_effects')
|
||||
|
||||
# Aggro theme
|
||||
if any(e in card_effects for e in ['deal_damage']):
|
||||
themes.append('aggro')
|
||||
|
||||
# Control theme
|
||||
if any(e in card_effects for e in ['counter_spell']):
|
||||
themes.append('control')
|
||||
|
||||
# Midrange theme
|
||||
if any(t in card_targets for t in ['creature']):
|
||||
themes.append('midrange')
|
||||
|
||||
return themes
|
||||
|
||||
def extract_profile(self, card_data: Dict) -> CardProfile:
|
||||
"""
|
||||
Extract a complete card profile from MTGJSON data.
|
||||
|
||||
Args:
|
||||
card_data: MTGJSON card dictionary
|
||||
|
||||
Returns:
|
||||
CardProfile object with all extracted attributes
|
||||
"""
|
||||
# Parse subtypes
|
||||
subtypes = None
|
||||
if card_data.get('subtypes'):
|
||||
subtypes = ', '.join(card_data['subtypes'])
|
||||
|
||||
# Parse supertypes
|
||||
supertypes = None
|
||||
if card_data.get('supertypes'):
|
||||
supertypes = ', '.join(card_data['supertypes'])
|
||||
|
||||
# Extract colors from mana cost
|
||||
colors = self.extract_colors(card_data.get('manaCost'))
|
||||
|
||||
# Extract mechanics
|
||||
mechanics = self.extract_mechanics(
|
||||
card_data.get('typeLine'),
|
||||
card_data.get('oracleText')
|
||||
)
|
||||
|
||||
# Extract archetypes
|
||||
archetypes = self.extract_archetypes(subtypes)
|
||||
|
||||
# Extract targets
|
||||
targets = self.extract_targets(card_data.get('oracleText'))
|
||||
|
||||
# Extract triggers
|
||||
triggers = self.extract_triggers(card_data.get('oracleText'))
|
||||
|
||||
# Extract effects
|
||||
effects = self.extract_effects(card_data.get('oracleText'))
|
||||
|
||||
# Extract themes
|
||||
themes = self.extract_themes(mechanics, triggers, effects, targets)
|
||||
|
||||
# Create profile
|
||||
profile = CardProfile(
|
||||
id=card_data.get('id', 0),
|
||||
name=card_data.get('name', ''),
|
||||
mana_cost=card_data.get('manaCost'),
|
||||
type_line=card_data.get('typeLine'),
|
||||
oracle_text=card_data.get('oracleText'),
|
||||
subtypes=subtypes,
|
||||
supertypes=supertypes,
|
||||
set_code=card_data.get('set', {}).get('code') if card_data.get('set') else None,
|
||||
colors=colors,
|
||||
color_identity=card_data.get('colorIdentity'),
|
||||
mechanics=mechanics,
|
||||
archetypes=archetypes,
|
||||
targets=targets,
|
||||
triggers=triggers,
|
||||
effects=effects,
|
||||
themes=themes,
|
||||
)
|
||||
|
||||
return profile
|
||||
|
||||
def extract_profiles_batch(self, cards: List[Dict]) -> List[CardProfile]:
|
||||
"""
|
||||
Extract profiles for a batch of cards.
|
||||
|
||||
Args:
|
||||
cards: List of MTGJSON card dictionaries
|
||||
|
||||
Returns:
|
||||
List of CardProfile objects
|
||||
"""
|
||||
return [self.extract_profile(card) for card in cards]
|
||||
Reference in New Issue
Block a user