scryfall-py/scryfall/models/cards.py

286 lines
8.5 KiB
Python

from datetime import date
from typing import Any, Literal, TYPE_CHECKING
from uuid import UUID
from pydantic import BaseModel, HttpUrl, field_validator
from scryfall.models.base import BaseAPIModel
from scryfall.models.enums import Color
if TYPE_CHECKING:
from scryfall.models.rulings import Ruling
from scryfall.models.sets import Set
class RelatedCard(BaseModel):
"""A card that's related to the current card"""
id: UUID
"""The UUID of the related card"""
object: Literal["related_card"]
"""The object type, literally `related_card`"""
component: Literal["token", "meld_part", "meld_result", "combo_piece"]
"""How the card is related"""
name: str
"""The name of the related card"""
type_line: str
"""The type line of the related card"""
uri: HttpUrl
"""The URL of the related card"""
class CardFace(BaseModel):
"""A card face, for multi-faced cards"""
artist: str | None = None
"""The face artist"""
artist_id: str | None = None
"""The ID of the face artist"""
cmc: float | None = None
"""Card Mana Cost"""
color_indicator: list[Color] | None = None
"""The color indicator(s) of the face"""
colors: list[Color] | None = None
"""The color(s) of the face"""
defense: str | None = None
"""The toughness of the face, if it exists"""
flavor_text: str | None = None
"""The face's flavor text"""
illustration_id: UUID | None = None
"""The face's illustration UUID"""
image_uris: dict[Literal["small", "normal", "large", "png", "art_crop", "border_crop"], HttpUrl] | None = None
"""URLs for the image face for different formats."""
layout: str | None = None
"""Layout of the face."""
loyalty: str | None = None
"""Face loyalty"""
mana_cost: str
"""Face mana cost"""
name: str
"""Name of the face"""
object: Literal["card_face"]
"""Object type, literally `card_face`"""
oracle_id: UUID | None = None
"""Oracle ID of the face"""
oracle_text: str | None = None
"""Oracle text of the face"""
power: str | None = None
"""The face's power"""
printed_name: str | None = None
"""The printed name of the face"""
printed_text: str | None = None
"""The printed text of the face"""
printed_type_line: str | None = None
"""The printed type line of the face"""
toughness: str | None = None
"""The toughness of the face"""
type_line: str | None = None
"""The type line of the face"""
watermark: str | None = None
"""The watermark of the face"""
class Preview(BaseModel):
"""Object for defining when the card was previewed."""
previewed_at: date | None = None
"""When it was previewed"""
source_uri: HttpUrl | None = None
"""The URL of where it was previewed"""
source: str | None = None
"""The source of the preview"""
@field_validator("source_uri", mode="before")
@classmethod
def validate_source_uri(cls, value: Any) -> Any:
if isinstance(value, str):
if len(value) > 0:
return HttpUrl(value)
return None
class Card(BaseAPIModel):
"""Main Card model."""
# Core fields
arena_id: int | None = None
"""MTG Arena ID"""
id: UUID
"""Scryfall card UUID"""
lang: str
"""Card language"""
mtgo_id: int | None = None
"""MTG Online ID"""
mtgo_foil_id: int | None = None
"""MTG Online Foil ID"""
multiverse_ids: list[int] | None = None
"""MTG Multiverse IDs"""
tcgplayer_id: int | None = None
"""TCGPlayer ID"""
tcgplayer_etched_id: int | None = None
"""TCGPlayer Etched ID"""
cardmarket_id: int | None = None
"""Cardmarket ID"""
object: Literal["card"]
"""Object type, literally `card`"""
layout: str
"""Card layout"""
oracle_id: UUID | None = None
"""Oracle UUID"""
prints_search_uri: HttpUrl
"""Print search URL for other printings"""
rulings_uri: HttpUrl
"""Rulings URL"""
scryfall_uri: HttpUrl
"""Scryfall URL"""
uri: HttpUrl
"""Card URL"""
# Gameplay fields
all_parts: list[RelatedCard] | None = None
"""If this card is closely related to other cards, this property will be an array with Related Card objects."""
card_faces: list[CardFace] | None = None
"""All faces of the card"""
cmc: float
"""Card mana value"""
color_identity: list[Color]
"""Card color identity"""
color_indicator: list[Color] | None = None
"""Card color indicator, if any"""
colors: list[Color] | None = None
"""Card colors, if any. Colors may be on the card's faces instead"""
defense: str | None = None
"""Toughness"""
edhrec_rank: int | None = None
"""EDHRec card rank/popularity"""
game_changer: bool | None = None
"""If this car is on the Commander Game Changer list"""
hand_modifier: str | None = None
"""Vanguard card hand modifier"""
keywords: list[str]
"""List of keywords, such as `Flying` or `Cumulative upkeep`"""
legalities: dict[str, Literal["legal", "not_legal", "restricted", "banned"]]
"""List of legalitites across different formats"""
life_modifier: str | None = None
"""The card's life modifier, if it is a Vanguard card. This value will contain a delta, such as `+2`."""
loyalty: str | None = None
"""This loyalty if any. Note that some cards have loyalties that are not numeric, such as X."""
mana_cost: str | None = None
"""The mana cost for this card. This value will be any empty string `""` if the cost is absent. Remember that per the game rules, a missing mana cost and a mana cost of `{0}` are different values. Multi-faced cards will report this value in card faces."""
name: str
"""The name of this card. If this card has multiple faces, this field will contain both names separated by `␣//␣`."""
oracle_text: str | None = None
"""The Oracle text for this card, if any."""
penny_rank: int | None = None
"""This card's rank/popularity on Penny Dreadful. Not all cards are ranked."""
power: str | None = None
"""This card's power, if any. Note that some cards have powers that are not numeric, such as `*`."""
produced_mana: list[Color] | None = None
"""Colors of mana that this card could produce."""
reserved: bool
"""True if this card is on the Reserved List."""
toughness: str | None = None
"""This card's toughness, if any. Note that some cards have toughnesses that are not numeric, such as `*`."""
type_line: str
"""The type line of this card."""
# Print fields
artist: str | None = None
artist_ids: list[UUID] | None = None
attraction_lights: list[int] | None = None
booster: bool
border_color: Literal["black", "white", "borderless", "yellow", "silver", "gold"]
card_back_id: UUID | None = None
collector_number: str
content_warning: bool | None = None
digital: bool
finishes: list[Literal["foil", "nonfoil", "etched"]]
flavor_name: str | None = None
flavor_text: str | None = None
frame_effects: list[str] | None = None
frame: str
full_art: bool
games: list[Literal["paper", "arena", "mtgo"]]
highres_image: bool
illustration_id: UUID | None = None
image_status: Literal["missing", "placeholder", "lowres", "highres_scan"]
image_uris: dict[Literal["small", "normal", "large", "png", "art_crop", "border_crop"], HttpUrl] | None = None
oversized: bool
prices: dict[str, str | None]
printed_name: str | None = None
printed_text: str | None = None
printed_type_line: str | None = None
promo: bool
promo_types: list[str] | None = None
purchase_uris: dict[str, HttpUrl] | None = None
rarity: Literal["common", "uncommon", "rare", "special", "mythic", "bonus"]
related_uris: dict[str, HttpUrl]
released_at: date
reprint: bool
scryfall_set_uri: HttpUrl
set_name: str
set_search_uri: HttpUrl
set_type: str
set_uri: HttpUrl
set: str
set_id: UUID
story_spotlight: bool
textless: bool
variation: bool
variation_of: UUID | None = None
security_stamp: Literal["oval", "triangle", "acorn", "circle", "arena", "heart"] | None = None
watermark: str | None = None
preview: Preview | None = None
async def get_set(self) -> "Set":
"""Get set card is a part of."""
return await self._client.get_set_by_id(self.set_id)
async def get_rulings(self) -> list["Ruling"]:
"""Get rulings for card."""
return await self._client.get_rulings_by_card_id(self.id)