comicbox.schemas.filename

[docs] module comicbox.schemas.filename

 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
"""
Parse comic book archive names using the simple 'parse' parser.

A more sophisticated library like pyparsing or rebulk might be able to
build a faster, more powerful matching engine with fewer parsers with
optional fields. But this brute force method with the parse library is
effective, simple and easy to read and to contribute to.
"""

from typing import Any

from comicfn2dict import comicfn2dict, dict2comicfn
from marshmallow.fields import Nested
from typing_extensions import override

from comicbox.fields.collection_fields import StringListField
from comicbox.fields.fields import StringField
from comicbox.fields.number_fields import IntegerField
from comicbox.schemas.base import BaseRenderModule, BaseSchema, BaseSubSchema
from comicbox.schemas.comicbox import (
    ISSUE_KEY,
    SERIES_KEY,
    VOLUME_ISSUE_COUNT_KEY,
    VOLUME_KEY,
)

SERIES_TAG = SERIES_KEY
VOLUME_TAG = VOLUME_KEY
ISSUE_COUNT_TAG = VOLUME_ISSUE_COUNT_KEY
ISSUE_TAG = ISSUE_KEY
_OTHER_SCHEMA_STARTS = ("<?", "<!")
_OTHER_SCHEMA_ENDS = ("{", ":")


class FilenameRenderModule(BaseRenderModule):
    """Filename Render module."""

    @override
    @classmethod
    def dumps(cls, obj: dict, *args: Any, **kwargs: Any) -> str:
        """Dump dict to filename string."""
        data: dict = obj.get(FilenameSchema.ROOT_TAG, {})
        return dict2comicfn(data, *args, **kwargs)

    @staticmethod
    def _is_non_filename_format(s: str | bytes | bytearray) -> bool:
        """Detect if the input is xml, yaml or json."""
        if not s:
            return True
        t = str(s).split("\n")[0].strip().lower()
        return t.startswith(_OTHER_SCHEMA_STARTS) or t.endswith(_OTHER_SCHEMA_ENDS)

    @override
    @classmethod
    def loads(
        cls,
        s: str | bytes | bytearray,
        *args: Any,
        **kwargs: Any,
    ) -> dict[str, dict] | None:
        """Load filename to dict."""
        if cls._is_non_filename_format(s):
            return None

        if cleaned_s := cls.clean_string(s):
            sub_data = comicfn2dict(cleaned_s, *args, **kwargs)
            return {FilenameSchema.ROOT_TAG: sub_data}
        return None


class FilenameSubSchema(BaseSubSchema):
    """File name sub schema."""

    ext = StringField()
    issue = StringField()
    issue_count = IntegerField(minimum=0)
    original_format = StringField()
    remainders = StringListField()
    series = StringField()
    scan_info = StringField()
    title = StringField()
    volume = IntegerField()
    year = IntegerField()


class FilenameSchema(BaseSchema):
    """File name schema."""

    ROOT_TAG: str = "comicfn2dict"
    ROOT_KEYPATH: str = ROOT_TAG

    comicfn2dict = Nested(FilenameSubSchema)

    class Meta(BaseSchema.Meta):
        """Schema Options."""

        render_module = FilenameRenderModule