예제 #1
0
def build_mtgjson_card(
    sf_card: Dict[str, Any], sf_card_face: int = 0
) -> List[Dict[str, Any]]:
    """
    Build a mtgjson card (and all sub pieces of that card)
    :param sf_card: Card to build
    :param sf_card_face: Which part of the card (defaults to 0)
    :return: List of card(s) build (usually 1)
    """
    mtgjson_cards: List[Dict[str, Any]] = []
    mtgjson_card: Dict[str, Any] = {}

    # Let us know what card we're trying to parse -- good for debugging :)
    LOGGER.info("Parsing {0} from {1}".format(sf_card.get("name"), sf_card.get("set")))

    # If flip-type, go to card_faces for alt attributes
    face_data: Dict[str, Any] = sf_card

    if "card_faces" in sf_card:
        mtgjson_card["names"] = sf_card["name"].split(" // ")  # List[str]
        face_data = sf_card["card_faces"][sf_card_face]

        # Prevent duplicate UUIDs for split card halves
        # Remove the last character and replace with the id of the card face
        mtgjson_card["scryfallId"] = sf_card["id"]

        # Split cards and rotational cards have this field, flip cards do not.
        # Remove rotational cards via the additional check
        if "mana_cost" in sf_card and "//" in sf_card["mana_cost"]:
            mtgjson_card["colors"] = get_card_colors(
                sf_card["mana_cost"].split(" // ")[sf_card_face]
            )
            mtgjson_card["faceConvertedManaCost"] = get_cmc(
                sf_card["mana_cost"].split("//")[sf_card_face].strip()
            )
        elif sf_card["layout"] in ["split", "flip", "transform"]:
            # Handle non-normal cards, as they'll a face split
            mtgjson_card["faceConvertedManaCost"] = get_cmc(
                face_data.get("mana_cost", "0").strip()
            )

        # Recursively parse the other cards within this card too
        # Only call recursive if it is the first time we see this card object
        if sf_card_face == 0:
            for i in range(1, len(sf_card["card_faces"])):
                LOGGER.info(
                    "Parsing additional card {0} face {1}".format(
                        sf_card.get("name"), i
                    )
                )
                mtgjson_cards += build_mtgjson_card(sf_card, i)

    # Characteristics that can are not shared to both sides of flip-type cards
    if face_data.get("mana_cost"):
        mtgjson_card["manaCost"] = face_data.get("mana_cost")

    if "colors" not in mtgjson_card:
        if "colors" in face_data:
            mtgjson_card["colors"] = face_data.get("colors")
        else:
            mtgjson_card["colors"] = sf_card.get("colors")

    mtgjson_card["name"] = face_data.get("name")
    mtgjson_card["type"] = face_data.get("type_line")
    mtgjson_card["text"] = face_data.get("oracle_text")

    mtgjson_card["power"] = face_data.get("power")
    mtgjson_card["toughness"] = face_data.get("toughness")
    mtgjson_card["loyalty"] = face_data.get("loyalty")
    mtgjson_card["watermark"] = face_data.get("watermark")

    if "flavor_text" in face_data:
        mtgjson_card["flavorText"] = face_data.get("flavor_text")
    else:
        mtgjson_card["flavorText"] = sf_card.get("flavor_text")

    if "color_indicator" in face_data:
        mtgjson_card["colorIndicator"] = face_data.get("color_indicator")  # List[str]
    elif "color_indicator" in sf_card:
        mtgjson_card["colorIndicator"] = sf_card.get("color_indicator")  # List[str]

    try:
        mtgjson_card["multiverseId"] = sf_card["multiverse_ids"][sf_card_face]  # int
    except IndexError:
        try:
            mtgjson_card["multiverseId"] = sf_card["multiverse_ids"][0]  # int
        except IndexError:
            mtgjson_card["multiverseId"] = None  # int

    # Characteristics that are shared to all sides of flip-type cards, that we don't have to modify
    mtgjson_card["artist"] = sf_card.get("artist")  # str
    mtgjson_card["borderColor"] = sf_card.get("border_color")
    mtgjson_card["colorIdentity"] = sf_card.get("color_identity")  # List[str]

    if "convertedManaCost" not in mtgjson_card:
        mtgjson_card["convertedManaCost"] = sf_card.get("cmc")  # float

    mtgjson_card["frameVersion"] = sf_card.get("frame")
    mtgjson_card["hasFoil"] = sf_card.get("foil")
    mtgjson_card["hasNonFoil"] = sf_card.get("nonfoil")
    mtgjson_card["isOnlineOnly"] = sf_card.get("digital")
    mtgjson_card["isOversized"] = sf_card.get("oversized")
    mtgjson_card["layout"] = sf_card.get("layout")
    mtgjson_card["number"] = sf_card.get("collector_number")
    mtgjson_card["isReserved"] = sf_card.get("reserved")
    mtgjson_card["frameEffect"] = sf_card.get("frame_effect")
    mtgjson_card["tcgplayerProductId"] = sf_card.get("tcgplayer_id")

    # Vanguard fields
    mtgjson_card["life"] = sf_card.get("life_modifier")
    mtgjson_card["hand"] = sf_card.get("hand_modifier")

    # Add a "side" entry for split cards
    # Will only work for two faced cards (not meld, as they don't need this)
    if "names" in mtgjson_card and len(mtgjson_card["names"]) == 2:
        # chr(97) = 'a', chr(98) = 'b', ...
        mtgjson_card["side"] = chr(
            mtgjson_card["names"].index(mtgjson_card["name"]) + 97
        )

    if "scryfallId" not in mtgjson_card:
        mtgjson_card["scryfallId"] = sf_card.get("id")

    # Characteristics that we have to format ourselves from provided data
    mtgjson_card["isTimeshifted"] = (sf_card.get("frame") == "future") or (
        sf_card.get("set") == "tsb"
    )

    mtgjson_card["rarity"] = sf_card.get("rarity")

    # Characteristics that we need custom functions to parse
    print_search_url: str = sf_card["prints_search_uri"].replace("%22", "")
    mtgjson_card["legalities"] = scryfall.parse_legalities(
        sf_card["legalities"]
    )  # Dict[str, str]
    mtgjson_card["rulings"] = sorted(
        scryfall.parse_rulings(sf_card["rulings_uri"]),
        key=lambda ruling: ruling["date"],
    )
    mtgjson_card["printings"] = sorted(
        scryfall.parse_printings(print_search_url)
    )  # List[str]

    card_types: Tuple[List[str], List[str], List[str]] = scryfall.parse_card_types(
        mtgjson_card["type"]
    )
    mtgjson_card["supertypes"] = card_types[0]  # List[str]
    mtgjson_card["types"] = card_types[1]  # List[str]
    mtgjson_card["subtypes"] = card_types[2]  # List[str]

    # Handle meld and all parts tokens issues
    # Will re-address naming if a split card already
    if "all_parts" in sf_card:
        meld_holder = []
        mtgjson_card["names"] = []
        for a_part in sf_card["all_parts"]:
            if a_part["component"] != "token":
                if "//" in a_part.get("name"):
                    mtgjson_card["names"] = a_part.get("name").split(" // ")
                    break

                # This is a meld only-fix, so we ignore tokens/combo pieces
                if "meld" in a_part["component"]:
                    meld_holder.append(a_part["component"])

                    mtgjson_card["names"].append(a_part.get("name"))

        # If the only entry is the original card, empty the names array
        if (
            len(mtgjson_card["names"]) == 1
            and mtgjson_card["name"] in mtgjson_card["names"]
        ):
            del mtgjson_card["names"]

        # Meld cards should be CardA, Meld, CardB. This fixes that via swap
        # meld_holder

        if meld_holder and meld_holder[1] != "meld_result":
            mtgjson_card["names"][1], mtgjson_card["names"][2] = (
                mtgjson_card["names"][2],
                mtgjson_card["names"][1],
            )

    # Since we built meld cards later, we will add the "side" attribute now
    if len(mtgjson_card.get("names", [])) == 3:  # MELD
        if mtgjson_card["name"] == mtgjson_card["names"][0]:
            mtgjson_card["side"] = "a"
        elif mtgjson_card["name"] == mtgjson_card["names"][2]:
            mtgjson_card["side"] = "b"
        else:
            mtgjson_card["side"] = "c"

    # Characteristics that we cannot get from Scryfall
    # Characteristics we have to do further API calls for
    mtgjson_card["foreignData"] = scryfall.parse_foreign(
        print_search_url, mtgjson_card["name"], mtgjson_card["number"], sf_card["set"]
    )

    if mtgjson_card["multiverseId"] is not None:
        gatherer_cards = gatherer.get_cards(mtgjson_card["multiverseId"])
        try:
            gatherer_card = gatherer_cards[sf_card_face]
            mtgjson_card["originalType"] = gatherer_card.original_types
            mtgjson_card["originalText"] = gatherer_card.original_text
        except IndexError:
            LOGGER.warning(
                "Unable to parse originals for {}".format(mtgjson_card["name"])
            )

    mtgjson_cards.append(mtgjson_card)
    return mtgjson_cards
예제 #2
0
def build_mtgjson_card(  # pylint: disable=too-many-branches
        sf_card: Dict[str, Any],
        sf_card_face: int = 0) -> List[Dict[str, Any]]:
    """
    Build a mtgjson card (and all sub pieces of that card)
    :param sf_card: Card to build
    :param sf_card_face: Which part of the card (defaults to 0)
    :return: List of card(s) build (usually 1)
    """
    mtgjson_cards: List[Dict[str, Any]] = []
    mtgjson_card: Dict[str, Any] = {}

    # Let us know what card we're trying to parse -- good for debugging :)
    LOGGER.info("Parsing {0} from {1}".format(sf_card.get("name"),
                                              sf_card.get("set")))

    # If flip-type, go to card_faces for alt attributes
    face_data: Dict[str, Any] = sf_card

    if "card_faces" in sf_card:
        mtgjson_card["names"] = sf_card["name"].split(" // ")  # List[str]
        face_data = sf_card["card_faces"][sf_card_face]

        # Prevent duplicate UUIDs for split card halves
        # Remove the last character and replace with the id of the card face
        mtgjson_card["uuid"] = sf_card["id"][:-1] + str(sf_card_face)

        # Split cards and rotational cards have this field, flip cards do not.
        # Remove rotational cards via the additional check
        if "mana_cost" in sf_card and "//" in sf_card["mana_cost"]:
            mtgjson_card["colors"] = get_card_colors(
                sf_card["mana_cost"].split(" // ")[sf_card_face])
            mtgjson_card["convertedManaCost"] = get_cmc(
                sf_card["mana_cost"].split(" // ")[sf_card_face])

        # Recursively parse the other cards within this card too
        # Only call recursive if it is the first time we see this card object
        if sf_card_face == 0:
            for i in range(1, len(sf_card["card_faces"])):
                LOGGER.info("Parsing additional card {0} face {1}".format(
                    sf_card.get("name"), i))
                mtgjson_cards += build_mtgjson_card(sf_card, i)

    # Characteristics that can are not shared to both sides of flip-type cards
    if face_data.get("mana_cost"):
        mtgjson_card["manaCost"] = face_data.get("mana_cost")  # str

    if "colors" not in mtgjson_card:
        mtgjson_card["colors"] = face_data.get("colors")  # List[str]

    mtgjson_card["name"] = face_data.get("name")  # str
    mtgjson_card["type"] = face_data.get("type_line")  # str
    mtgjson_card["text"] = face_data.get("oracle_text")  # str

    mtgjson_card["power"] = face_data.get("power")  # str
    mtgjson_card["toughness"] = face_data.get("toughness")  # str
    mtgjson_card["loyalty"] = face_data.get("loyalty")  # str
    mtgjson_card["watermark"] = face_data.get("watermark")  # str

    if "color_indicator" in face_data:
        mtgjson_card["colorIndicator"] = face_data.get(
            "color_indicator")  # List[str]
    elif "color_indicator" in sf_card:
        mtgjson_card["colorIndicator"] = sf_card.get(
            "color_indicator")  # List[str]

    try:
        mtgjson_card["multiverseId"] = sf_card["multiverse_ids"][
            sf_card_face]  # int
    except IndexError:
        try:
            mtgjson_card["multiverseId"] = sf_card["multiverse_ids"][0]  # int
        except IndexError:
            mtgjson_card["multiverseId"] = None  # int

    # Characteristics that are shared to all sides of flip-type cards, that we don't have to modify
    mtgjson_card["artist"] = sf_card.get("artist")  # str
    mtgjson_card["borderColor"] = sf_card.get("border_color")
    mtgjson_card["colorIdentity"] = sf_card.get("color_identity")  # List[str]

    if "convertedManaCost" not in mtgjson_card:
        mtgjson_card["convertedManaCost"] = sf_card.get("cmc")  # float

    mtgjson_card["flavorText"] = sf_card.get("flavor_text")  # str
    mtgjson_card["frameVersion"] = sf_card.get("frame")  # str
    mtgjson_card["hasFoil"] = sf_card.get("foil")  # bool
    mtgjson_card["hasNonFoil"] = sf_card.get("nonfoil")  # bool
    mtgjson_card["isOnlineOnly"] = sf_card.get("digital")  # bool
    mtgjson_card["isOversized"] = sf_card.get("oversized")  # bool
    mtgjson_card["layout"] = sf_card.get("layout")  # str
    mtgjson_card["number"] = sf_card.get("collector_number")  # str
    mtgjson_card["isReserved"] = sf_card.get("reserved")  # bool

    if "uuid" not in mtgjson_card:
        mtgjson_card["uuid"] = sf_card.get("id")  # str

    # Characteristics that we have to format ourselves from provided data
    mtgjson_card["timeshifted"] = sf_card.get("timeshifted") or sf_card.get(
        "futureshifted")  # bool
    mtgjson_card["rarity"] = (
        sf_card.get("rarity")
        if not mtgjson_card.get("timeshifted") else "Special")  # str

    # Characteristics that we need custom functions to parse
    print_search_url: str = sf_card["prints_search_uri"].replace("%22", "")
    mtgjson_card["legalities"] = scryfall.parse_legalities(
        sf_card["legalities"])  # Dict[str, str]
    mtgjson_card["rulings"] = sorted(
        scryfall.parse_rulings(sf_card["rulings_uri"]),
        key=lambda ruling: ruling["date"],
    )
    mtgjson_card["printings"] = sorted(
        scryfall.parse_printings(print_search_url))  # List[str]

    card_types: Tuple[List[str], List[str],
                      List[str]] = scryfall.parse_card_types(
                          mtgjson_card["type"])
    mtgjson_card["supertypes"] = card_types[0]  # List[str]
    mtgjson_card["types"] = card_types[1]  # List[str]
    mtgjson_card["subtypes"] = card_types[2]  # List[str]

    # Handle meld issues
    if "all_parts" in sf_card:
        mtgjson_card["names"] = []
        for a_part in sf_card["all_parts"]:
            if "//" in a_part.get("name"):
                mtgjson_card["names"] = a_part.get("name").split(" // ")
                break
            mtgjson_card["names"].append(a_part.get("name"))

    # Characteristics that we cannot get from Scryfall
    # Characteristics we have to do further API calls for
    mtgjson_card["foreignData"] = scryfall.parse_foreign(
        print_search_url, mtgjson_card["name"], sf_card["set"])

    if mtgjson_card["multiverseId"] is not None:
        gatherer_cards = gatherer.get_cards(mtgjson_card["multiverseId"])
        try:
            gatherer_card = gatherer_cards[sf_card_face]
            mtgjson_card["originalType"] = gatherer_card.original_types
            mtgjson_card["originalText"] = gatherer_card.original_text
        except IndexError:
            LOGGER.warning("Unable to parse originals for {}".format(
                mtgjson_card["name"]))

    mtgjson_cards.append(mtgjson_card)
    return mtgjson_cards
예제 #3
0
def build_mtgjson_card(sf_card: Dict[str, Any],
                       sf_card_face: int = 0) -> List[MTGJSONCard]:
    """
    Build a mtgjson card (and all sub pieces of that card)
    :param sf_card: Card to build
    :param sf_card_face: Which part of the card (defaults to 0)
    :return: List of card(s) build (usually 1)
    """
    mtgjson_cards: List[MTGJSONCard] = []
    single_card = MTGJSONCard(sf_card["set"])

    # Let us know what card we're trying to parse -- good for debugging :)
    LOGGER.info("Parsing {0} from {1}".format(sf_card.get("name"),
                                              sf_card.get("set")))

    # If flip-type, go to card_faces for alt attributes
    face_data: Dict[str, Any] = sf_card

    if "card_faces" in sf_card:
        single_card.set_all({
            "names":
            sf_card["name"].split(" // "),
            "scryfallId":
            sf_card["id"],
            "scryfallOracleId":
            sf_card["oracle_id"],
            "scryfallIllustrationId":
            sf_card.get("illustration_id"),
        })
        face_data = sf_card["card_faces"][sf_card_face]

        # Split cards and rotational cards have this field, flip cards do not.
        # Remove rotational cards via the additional check
        if "mana_cost" in sf_card and "//" in sf_card["mana_cost"]:
            single_card.set(
                "colors",
                get_card_colors(
                    sf_card["mana_cost"].split(" // ")[sf_card_face]),
            )
            single_card.set(
                "faceConvertedManaCost",
                get_cmc(
                    sf_card["mana_cost"].split("//")[sf_card_face].strip()),
            )
        elif sf_card["layout"] in ["split", "transform", "aftermath"]:
            # Handle non-normal cards, as they'll a face split
            single_card.set(
                "faceConvertedManaCost",
                get_cmc(face_data.get("mana_cost", "0").strip()),
            )

        # Watermark is only attributed on the front side, so we'll account for it
        single_card.set(
            "watermark",
            sf_card["card_faces"][0].get("watermark", None),
            single_card.clean_up_watermark,
        )

        if sf_card["card_faces"][-1]["oracle_text"].startswith("Aftermath"):
            single_card.set("layout", "aftermath")

        single_card.set("artist",
                        sf_card["card_faces"][sf_card_face].get("artist", ""))

        # Recursively parse the other cards within this card too
        # Only call recursive if it is the first time we see this card object
        if sf_card_face == 0:
            for i in range(1, len(sf_card["card_faces"])):
                LOGGER.info("Parsing additional card {0} face {1}".format(
                    sf_card.get("name"), i))
                mtgjson_cards += build_mtgjson_card(sf_card, i)
    else:
        single_card.set_all({
            "scryfallId":
            sf_card.get("id"),
            "scryfallOracleId":
            sf_card["oracle_id"],
            "scryfallIllustrationId":
            sf_card.get("illustration_id"),
        })

    # Characteristics that can are not shared to both sides of flip-type cards
    if face_data.get("mana_cost"):
        single_card.set("manaCost", face_data.get("mana_cost"))

    if "colors" not in single_card.keys():
        if "colors" in face_data:
            single_card.set("colors", face_data.get("colors"))
        else:
            single_card.set("colors", sf_card.get("colors"))

    single_card.set_all({
        "borderColor": sf_card.get("border_color"),
        "colorIdentity": sf_card.get("color_identity"),
        "convertedManaCost": sf_card.get("cmc"),
        "frameEffect": sf_card.get("frame_effect"),
        "frameVersion": sf_card.get("frame"),
        "hand": sf_card.get("hand_modifier"),
        "hasFoil": sf_card.get("foil"),
        "hasNonFoil": sf_card.get("nonfoil"),
        "isFullArt": sf_card.get("full_art"),
        "isOnlineOnly": sf_card.get("digital"),
        "isOversized": sf_card.get("oversized"),
        "isPromo": sf_card.get("promo"),
        "isReprint": sf_card.get("reprint"),
        "isReserved": sf_card.get("reserved"),
        "isStorySpotlight": sf_card.get("story_spotlight"),
        "isTextless": sf_card.get("textless"),
        "life": sf_card.get("life_modifier"),
        "loyalty": face_data.get("loyalty"),
        "name": face_data.get("name"),
        "number": sf_card.get("collector_number"),
        "power": face_data.get("power"),
        "tcgplayerProductId": sf_card.get("tcgplayer_id"),
        "text": face_data.get("oracle_text"),
        "toughness": face_data.get("toughness"),
        "type": face_data.get("type_line"),
    })

    # Set MKM IDs if it exists
    if MKM_API.get(None):
        mkm_card_found = False
        for key, mkm_obj in MKM_SET_CARDS.get().items():
            if single_card.get("name").lower() not in key:
                continue

            if "number" not in mkm_obj.keys() or (
                    mkm_obj.get("number") in single_card.get("number")):
                single_card.set_all({
                    "mcmId": mkm_obj["idProduct"],
                    "mcmMetaId": mkm_obj["idMetaproduct"],
                })
                single_card.set_mkm_url(mkm_obj["website"])
                mkm_card_found = True
                break

        if not mkm_card_found:
            LOGGER.warning("Unable to find MKM information for #{} {}".format(
                single_card.get("number"), single_card.get("name")))

    if "artist" not in single_card.keys():
        single_card.set("artist", sf_card.get("artist"))

    if "layout" not in single_card.keys():
        single_card.set("layout", sf_card.get("layout"))

    if "watermark" not in single_card.keys():
        single_card.set(
            "watermark",
            face_data.get("watermark", None),
            single_card.clean_up_watermark,
        )

    # "isPaper", "isMtgo", "isArena"
    for game_mode in sf_card.get("games", []):
        single_card.set("is{}".format(game_mode.capitalize()), True)

    if "flavor_text" in face_data:
        single_card.set("flavorText", face_data.get("flavor_text"))
    else:
        single_card.set("flavorText", sf_card.get("flavor_text"))

    if "color_indicator" in face_data:
        single_card.set("colorIndicator", face_data.get("color_indicator"))
    elif "color_indicator" in sf_card:
        single_card.set("colorIndicator", sf_card.get("color_indicator"))

    try:
        single_card.set("multiverseId",
                        sf_card["multiverse_ids"][sf_card_face])
    except IndexError:
        try:
            single_card.set("multiverseId", sf_card["multiverse_ids"][0])
        except IndexError:
            single_card.set("multiverseId", None)

    # Add a "side" entry for split cards
    # Will only work for two faced cards (not meld, as they don't need this)
    if "names" in single_card.keys() and single_card.names_count(2):
        # chr(97) = 'a', chr(98) = 'b', ...
        single_card.set(
            "side",
            chr(single_card.get("names").index(single_card.get("name")) + 97))

    # Characteristics that we have to format ourselves from provided data
    single_card.set(
        "isTimeshifted",
        (sf_card.get("frame") == "future") or (sf_card.get("set") == "tsb"),
    )

    single_card.set("rarity", sf_card.get("rarity"))

    # Characteristics that we need custom functions to parse
    print_search_url: str = sf_card["prints_search_uri"].replace("%22", "")
    single_card.set("legalities",
                    scryfall.parse_legalities(sf_card["legalities"]))
    single_card.set(
        "rulings",
        sorted(
            scryfall.parse_rulings(sf_card["rulings_uri"]),
            key=lambda ruling: ruling["date"],
        ),
    )
    single_card.set("printings",
                    sorted(scryfall.parse_printings(print_search_url)))

    card_types: Tuple[List[str], List[str],
                      List[str]] = scryfall.parse_card_types(
                          single_card.get("type"))
    single_card.set("supertypes", card_types[0])
    single_card.set("types", card_types[1])
    single_card.set("subtypes", card_types[2])

    # Handle meld and all parts tokens issues
    # Will re-address naming if a split card already
    if "all_parts" in sf_card:
        meld_holder = []
        single_card.set("names", [])
        for a_part in sf_card["all_parts"]:
            if a_part["component"] != "token":
                if "//" in a_part.get("name"):
                    single_card.set("names", a_part.get("name").split(" // "))
                    break

                # This is a meld only-fix, so we ignore tokens/combo pieces
                if "meld" in a_part["component"]:
                    meld_holder.append(a_part["component"])

                    single_card.append("names", a_part.get("name"))

        # If the only entry is the original card, empty the names array
        if single_card.names_count(1) and single_card.get(
                "name") in single_card.get("names"):
            single_card.remove("names")

        # Meld cards should be CardA, Meld, CardB. This fixes that via swap
        # meld_holder

        if meld_holder and meld_holder[1] != "meld_result":
            single_card.get("names")[1], single_card.get("names")[2] = (
                single_card.get("names")[2],
                single_card.get("names")[1],
            )

    # Since we built meld cards later, we will add the "side" attribute now
    if single_card.names_count(3):  # MELD
        if single_card.get("name") == single_card.get("names")[0]:
            single_card.set("side", "a")
        elif single_card.get("name") == single_card.get("names")[2]:
            single_card.set("side", "b")
        else:
            single_card.set("side", "c")

    # Characteristics that we cannot get from Scryfall
    # Characteristics we have to do further API calls for
    single_card.set(
        "foreignData",
        scryfall.parse_foreign(
            print_search_url,
            single_card.get("name"),
            single_card.get("number"),
            sf_card["set"],
        ),
    )

    if single_card.get("multiverseId") is not None:
        gatherer_cards = gatherer.get_cards(single_card.get("multiverseId"),
                                            single_card.set_code)
        try:
            gatherer_card = gatherer_cards[sf_card_face]
            single_card.set("originalType", gatherer_card.original_types)
            single_card.set("originalText", gatherer_card.original_text)
        except IndexError:
            LOGGER.warning("Unable to parse originals for {}".format(
                single_card.get("name")))

    mtgjson_cards.append(single_card)
    return mtgjson_cards
예제 #4
0
def build_mtgjson_card(
    sf_card: Dict[str, Any], sf_card_face: int = 0
) -> List[MTGJSONCard]:
    """
    Build a mtgjson card (and all sub pieces of that card)
    :param sf_card: Card to build
    :param sf_card_face: Which part of the card (defaults to 0)
    :return: List of card(s) build (usually 1)
    """
    mtgjson_cards: List[MTGJSONCard] = []
    single_card = MTGJSONCard(sf_card["set"])

    # Let us know what card we're trying to parse -- good for debugging :)
    LOGGER.info("Parsing {0} from {1}".format(sf_card.get("name"), sf_card.get("set")))

    # If flip-type, go to card_faces for alt attributes
    face_data: Dict[str, Any] = sf_card

    if "card_faces" in sf_card:
        single_card.set_all(
            {
                "names": sf_card["name"].split(" // "),
                "scryfallId": sf_card["id"],
                "scryfallOracleId": sf_card["oracle_id"],
                "scryfallIllustrationId": sf_card.get("illustration_id"),
            }
        )
        face_data = sf_card["card_faces"][sf_card_face]

        # Split cards and rotational cards have this field, flip cards do not.
        # Remove rotational cards via the additional check
        if "mana_cost" in sf_card and "//" in sf_card["mana_cost"]:
            single_card.set(
                "colors",
                get_card_colors(sf_card["mana_cost"].split(" // ")[sf_card_face]),
            )
            single_card.set(
                "faceConvertedManaCost",
                get_cmc(sf_card["mana_cost"].split("//")[sf_card_face].strip()),
            )
        elif sf_card["layout"] in ["split", "transform", "aftermath"]:
            # Handle non-normal cards, as they'll a face split
            single_card.set(
                "faceConvertedManaCost",
                get_cmc(face_data.get("mana_cost", "0").strip()),
            )

        # Watermark is only attributed on the front side, so we'll account for it
        single_card.set(
            "watermark",
            sf_card["card_faces"][0].get("watermark", None),
            single_card.clean_up_watermark,
        )

        if sf_card["card_faces"][-1]["oracle_text"].startswith("Aftermath"):
            single_card.set("layout", "aftermath")

        single_card.set("artist", sf_card["card_faces"][sf_card_face].get("artist", ""))

        # Recursively parse the other cards within this card too
        # Only call recursive if it is the first time we see this card object
        if sf_card_face == 0:
            for i in range(1, len(sf_card["card_faces"])):
                LOGGER.info(
                    "Parsing additional card {0} face {1}".format(
                        sf_card.get("name"), i
                    )
                )
                mtgjson_cards += build_mtgjson_card(sf_card, i)
    else:
        single_card.set_all(
            {
                "scryfallId": sf_card.get("id"),
                "scryfallOracleId": sf_card["oracle_id"],
                "scryfallIllustrationId": sf_card.get("illustration_id"),
            }
        )

    # Characteristics that can are not shared to both sides of flip-type cards
    if face_data.get("mana_cost"):
        single_card.set("manaCost", face_data.get("mana_cost"))

    if "colors" not in single_card.keys():
        if "colors" in face_data:
            single_card.set("colors", face_data.get("colors"))
        else:
            single_card.set("colors", sf_card.get("colors"))

    single_card.set_all(
        {
            "name": face_data.get("name"),
            "type": face_data.get("type_line"),
            "text": face_data.get("oracle_text"),
            "power": face_data.get("power"),
            "toughness": face_data.get("toughness"),
            "loyalty": face_data.get("loyalty"),
            "borderColor": sf_card.get("border_color"),
            "colorIdentity": sf_card.get("color_identity"),
            "frameVersion": sf_card.get("frame"),
            "hasFoil": sf_card.get("foil"),
            "hasNonFoil": sf_card.get("nonfoil"),
            "isOnlineOnly": sf_card.get("digital"),
            "isOversized": sf_card.get("oversized"),
            "number": sf_card.get("collector_number"),
            "isReserved": sf_card.get("reserved"),
            "frameEffect": sf_card.get("frame_effect"),
            "tcgplayerProductId": sf_card.get("tcgplayer_id"),
            "life": sf_card.get("life_modifier"),
            "hand": sf_card.get("hand_modifier"),
            "convertedManaCost": sf_card.get("cmc"),
        }
    )

    # Set MKM IDs if it exists
    if MKM_API.get(None):
        mkm_card_found = False
        for key, mkm_obj in MKM_SET_CARDS.get().items():
            if single_card.get("name").lower() not in key:
                continue

            if "number" not in mkm_obj.keys() or (
                mkm_obj.get("number") in single_card.get("number")
            ):
                single_card.set_all(
                    {
                        "mcmId": mkm_obj["idProduct"],
                        "mcmMetaId": mkm_obj["idMetaproduct"],
                    }
                )
                single_card.set_mkm_url(mkm_obj["website"])
                mkm_card_found = True
                break

        if not mkm_card_found:
            LOGGER.warning(
                "Unable to find MKM information for #{} {}".format(
                    single_card.get("number"), single_card.get("name")
                )
            )

    if "artist" not in single_card.keys():
        single_card.set("artist", sf_card.get("artist"))

    if "layout" not in single_card.keys():
        single_card.set("layout", sf_card.get("layout"))

    if "watermark" not in single_card.keys():
        single_card.set(
            "watermark",
            face_data.get("watermark", None),
            single_card.clean_up_watermark,
        )

    if "flavor_text" in face_data:
        single_card.set("flavorText", face_data.get("flavor_text"))
    else:
        single_card.set("flavorText", sf_card.get("flavor_text"))

    if "color_indicator" in face_data:
        single_card.set("colorIndicator", face_data.get("color_indicator"))
    elif "color_indicator" in sf_card:
        single_card.set("colorIndicator", sf_card.get("color_indicator"))

    try:
        single_card.set("multiverseId", sf_card["multiverse_ids"][sf_card_face])
    except IndexError:
        try:
            single_card.set("multiverseId", sf_card["multiverse_ids"][0])
        except IndexError:
            single_card.set("multiverseId", None)

    # Add a "side" entry for split cards
    # Will only work for two faced cards (not meld, as they don't need this)
    if "names" in single_card.keys() and single_card.names_count(2):
        # chr(97) = 'a', chr(98) = 'b', ...
        single_card.set(
            "side", chr(single_card.get("names").index(single_card.get("name")) + 97)
        )

    # Characteristics that we have to format ourselves from provided data
    single_card.set(
        "isTimeshifted",
        (sf_card.get("frame") == "future") or (sf_card.get("set") == "tsb"),
    )

    single_card.set("rarity", sf_card.get("rarity"))

    # Characteristics that we need custom functions to parse
    print_search_url: str = sf_card["prints_search_uri"].replace("%22", "")
    single_card.set("legalities", scryfall.parse_legalities(sf_card["legalities"]))
    single_card.set(
        "rulings",
        sorted(
            scryfall.parse_rulings(sf_card["rulings_uri"]),
            key=lambda ruling: ruling["date"],
        ),
    )
    single_card.set("printings", sorted(scryfall.parse_printings(print_search_url)))

    card_types: Tuple[List[str], List[str], List[str]] = scryfall.parse_card_types(
        single_card.get("type")
    )
    single_card.set("supertypes", card_types[0])
    single_card.set("types", card_types[1])
    single_card.set("subtypes", card_types[2])

    # Handle meld and all parts tokens issues
    # Will re-address naming if a split card already
    if "all_parts" in sf_card:
        meld_holder = []
        single_card.set("names", [])
        for a_part in sf_card["all_parts"]:
            if a_part["component"] != "token":
                if "//" in a_part.get("name"):
                    single_card.set("names", a_part.get("name").split(" // "))
                    break

                # This is a meld only-fix, so we ignore tokens/combo pieces
                if "meld" in a_part["component"]:
                    meld_holder.append(a_part["component"])

                    single_card.append("names", a_part.get("name"))

        # If the only entry is the original card, empty the names array
        if single_card.names_count(1) and single_card.get("name") in single_card.get(
            "names"
        ):
            single_card.remove("names")

        # Meld cards should be CardA, Meld, CardB. This fixes that via swap
        # meld_holder

        if meld_holder and meld_holder[1] != "meld_result":
            single_card.get("names")[1], single_card.get("names")[2] = (
                single_card.get("names")[2],
                single_card.get("names")[1],
            )

    # Since we built meld cards later, we will add the "side" attribute now
    if single_card.names_count(3):  # MELD
        if single_card.get("name") == single_card.get("names")[0]:
            single_card.set("side", "a")
        elif single_card.get("name") == single_card.get("names")[2]:
            single_card.set("side", "b")
        else:
            single_card.set("side", "c")

    # Characteristics that we cannot get from Scryfall
    # Characteristics we have to do further API calls for
    single_card.set(
        "foreignData",
        scryfall.parse_foreign(
            print_search_url,
            single_card.get("name"),
            single_card.get("number"),
            sf_card["set"],
        ),
    )

    if single_card.get("multiverseId") is not None:
        gatherer_cards = gatherer.get_cards(single_card.get("multiverseId"))
        try:
            gatherer_card = gatherer_cards[sf_card_face]
            single_card.set("originalType", gatherer_card.original_types)
            single_card.set("originalText", gatherer_card.original_text)
        except IndexError:
            LOGGER.warning(
                "Unable to parse originals for {}".format(single_card.get("name"))
            )

    mtgjson_cards.append(single_card)
    return mtgjson_cards