comicbox.transforms.xml_credits

[docs] module comicbox.transforms.xml_credits

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
"""XML Credits Mixin."""

from collections.abc import Callable, Mapping
from enum import Enum
from typing import Any, TypeVar

from loguru import logger

from comicbox.fields.enum_fields import EnumField
from comicbox.schemas.comicbox import (
    CREDITS_KEY,
    ROLES_KEY,
)
from comicbox.transforms.comicbox.credits import add_credit_role_to_comicbox_credits
from comicbox.transforms.spec import MetaSpec


def _create_role_enum_to_alias_map(
    role_aliases: Mapping[Any, tuple[Any, ...]],
) -> dict:
    """Create role map for native enum value to a list of aliases."""
    role_map = {}
    for native_enum, aliases in role_aliases.items():
        key_variations = set()
        all_aliases = (*aliases, native_enum)
        for alias in all_aliases:
            key_variations |= EnumField.get_key_variations(alias)
        role_map[native_enum.value] = frozenset(key_variations)
    return role_map


def _xml_credits_to_cb(role_name_persons_map: dict[str, Any]) -> dict:
    comicbox_credits = {}
    for role_name, persons in role_name_persons_map.items():
        try:
            if not role_name or not persons:
                continue
            for person_name in persons:
                add_credit_role_to_comicbox_credits(
                    person_name, role_name, comicbox_credits
                )
        except Exception:
            logger.exception(f"Parsing credit tag {role_name} {persons}")
    return comicbox_credits


def xml_credits_transform_to_cb(role_tags_enum: type[Enum]) -> MetaSpec:
    """Transform xml credit tags to comicbox credits."""
    return MetaSpec(
        key_map={CREDITS_KEY: tuple(r.value for r in role_tags_enum)},
        spec=_xml_credits_to_cb,
    )


def _xml_credits_from_cb(role_aliases: frozenset, comicbox_credits: dict) -> set:
    person_names = set()
    for person_name, comicbox_credit in comicbox_credits.items():
        try:
            if not person_name:
                continue
            comicbox_roles = comicbox_credit.get(ROLES_KEY)
            if not comicbox_roles:
                continue
            lower_roles = frozenset({role.lower() for role in comicbox_roles})
            if lower_roles & role_aliases:
                person_names.add(person_name)
        except Exception as exc:
            logger.warning(
                f"Transforming comicbox credit {comicbox_credit} to xml tag: {exc}"
            )
    return person_names


def get_from_cb_func(
    role_aliases: frozenset[str],
) -> Callable[[dict[str, Any]], set[Any]]:
    """Create a function that gets person names from comicbox_credits for one xml credit tag."""

    def from_cb(comicbox_credits: dict[str, Any]) -> set[Any]:
        return _xml_credits_from_cb(role_aliases, comicbox_credits)

    return from_cb


_RoleTagT = TypeVar("_RoleTagT", bound=Enum)


def xml_credits_transform_from_cb(
    role_tags_enum: type[_RoleTagT], role_aliases: Mapping[_RoleTagT, tuple[Any, ...]]
) -> tuple:
    """Transform comicbox credits into several xml tag credits."""
    role_map = _create_role_enum_to_alias_map(role_aliases)
    metaspecs = []
    for role_tag_enum in role_tags_enum:
        role_tag = role_tag_enum.value
        tag_aliases: frozenset[str] = role_map.get(role_tag, frozenset())
        func = get_from_cb_func(tag_aliases)
        metaspec = MetaSpec(key_map={role_tag: CREDITS_KEY}, spec=func)
        metaspecs.append(metaspec)
    return tuple(metaspecs)