codex 1.4.1__py3-none-any.whl → 1.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of codex might be problematic. Click here for more details.
- codex/applications/websocket.py +1 -1
- codex/librarian/importer/aggregate_metadata.py +38 -25
- codex/librarian/importer/clean_metadata.py +26 -13
- codex/librarian/importer/create_fks.py +1 -1
- codex/librarian/librariand.py +7 -4
- codex/librarian/watchdog/db_snapshot.py +1 -1
- codex/librarian/watchdog/event_batcherd.py +14 -11
- codex/librarian/watchdog/events.py +1 -1
- codex/logger/loggerd.py +14 -11
- codex/migrations/0001_init.py +3 -2
- codex/migrations/0002_auto_20200826_0622.py +4 -2
- codex/migrations/0003_auto_20200831_2033.py +4 -2
- codex/migrations/0004_failedimport.py +4 -2
- codex/migrations/0005_auto_20200918_0146.py +4 -2
- codex/migrations/0006_update_default_names_and_remove_duplicate_comics.py +12 -7
- codex/migrations/0007_auto_20211210_1710.py +3 -2
- codex/migrations/0008_alter_comic_created_at_alter_comic_format_and_more.py +4 -2
- codex/migrations/0009_alter_comic_parent_folder.py +4 -2
- codex/migrations/0010_haystack.py +4 -2
- codex/migrations/0011_library_groups_and_metadata_changes.py +3 -2
- codex/migrations/0012_rename_description_comic_comments.py +4 -2
- codex/migrations/0013_int_issue_count_longer_charfields.py +3 -3
- codex/migrations/0014_pdf_issue_suffix_remove_cover_image_sort_name.py +3 -2
- codex/migrations/0015_link_comics_to_top_level_folders.py +5 -2
- codex/migrations/0016_remove_comic_cover_path_librarianstatus.py +3 -3
- codex/migrations/0017_alter_timestamp_options_alter_adminflag_name_and_more.py +3 -3
- codex/migrations/0018_rename_userbookmark_bookmark.py +3 -2
- codex/migrations/0019_delete_queuejob.py +3 -2
- codex/migrations/0020_remove_search_tables.py +3 -2
- codex/migrations/0021_bookmark_fit_to_choices_read_in_reverse.py +3 -2
- codex/migrations/0022_bookmark_vertical_useractive_null_statuses.py +3 -2
- codex/migrations/0023_rename_credit_creator_and_more.py +3 -2
- codex/migrations/0024_comic_gtin_comic_story_arc_number.py +4 -2
- codex/migrations/0025_add_story_arc_number.py +4 -2
- codex/models.py +3 -4
- codex/search/backend.py +34 -31
- codex/serializers/auth.py +2 -1
- codex/serializers/choices.py +1 -0
- codex/static_root/assets/admin-b2b56cd6.f68d07d2bf93.js +41 -0
- codex/static_root/assets/admin-b2b56cd6.f68d07d2bf93.js.br +0 -0
- codex/static_root/assets/admin-b2b56cd6.f68d07d2bf93.js.gz +0 -0
- codex/static_root/assets/admin-b2b56cd6.js +41 -0
- codex/static_root/assets/admin-b2b56cd6.js.br +0 -0
- codex/static_root/assets/admin-b2b56cd6.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-522f1e6c.089d70878270.js → admin-drawer-panel-efc525ec.ddab36a24e08.js} +1 -1
- codex/static_root/assets/admin-drawer-panel-efc525ec.ddab36a24e08.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-efc525ec.ddab36a24e08.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-522f1e6c.js → admin-drawer-panel-efc525ec.js} +1 -1
- codex/static_root/assets/admin-drawer-panel-efc525ec.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-efc525ec.js.gz +0 -0
- codex/static_root/assets/admin-f2bb1dc8.css +1 -0
- codex/static_root/assets/admin-f2bb1dc8.css.br +0 -0
- codex/static_root/assets/admin-f2bb1dc8.css.gz +0 -0
- codex/static_root/assets/admin-f2bb1dc8.ecec18791c01.css +1 -0
- codex/static_root/assets/admin-f2bb1dc8.ecec18791c01.css.br +0 -0
- codex/static_root/assets/admin-f2bb1dc8.ecec18791c01.css.gz +0 -0
- codex/static_root/assets/{browser-7f7d7134.0fe3749b0f2f.css → browser-198df919.css} +1 -1
- codex/static_root/assets/browser-198df919.css.br +0 -0
- codex/static_root/assets/browser-198df919.css.gz +0 -0
- codex/static_root/assets/{browser-7f7d7134.css → browser-198df919.f06301531790.css} +1 -1
- codex/static_root/assets/browser-198df919.f06301531790.css.br +0 -0
- codex/static_root/assets/browser-198df919.f06301531790.css.gz +0 -0
- codex/static_root/assets/browser-ca158ba5.980d652eb174.js +1 -0
- codex/static_root/assets/browser-ca158ba5.980d652eb174.js.br +0 -0
- codex/static_root/assets/browser-ca158ba5.980d652eb174.js.gz +0 -0
- codex/static_root/assets/browser-ca158ba5.js +1 -0
- codex/static_root/assets/browser-ca158ba5.js.br +0 -0
- codex/static_root/assets/browser-ca158ba5.js.gz +0 -0
- codex/static_root/assets/{http-error-5e17b794.77ceeb2d4641.js → http-error-d31fd3bd.6ab9acf65973.js} +1 -1
- codex/static_root/assets/http-error-d31fd3bd.6ab9acf65973.js.br +0 -0
- codex/static_root/assets/http-error-d31fd3bd.6ab9acf65973.js.gz +0 -0
- codex/static_root/assets/{http-error-5e17b794.js → http-error-d31fd3bd.js} +1 -1
- codex/static_root/assets/http-error-d31fd3bd.js.br +0 -0
- codex/static_root/assets/http-error-d31fd3bd.js.gz +0 -0
- codex/static_root/assets/{main-0898f4bb.181e0145c642.css → main-c11eb0f1.776522baac3b.css} +1 -1
- codex/static_root/assets/main-c11eb0f1.776522baac3b.css.br +0 -0
- codex/static_root/assets/{main-0898f4bb.181e0145c642.css.gz → main-c11eb0f1.776522baac3b.css.gz} +0 -0
- codex/static_root/assets/{main-0898f4bb.css → main-c11eb0f1.css} +1 -1
- codex/static_root/assets/main-c11eb0f1.css.br +0 -0
- codex/static_root/assets/{main-0898f4bb.css.gz → main-c11eb0f1.css.gz} +0 -0
- codex/static_root/assets/main-c5736dea.a4790dbdb569.js +1 -0
- codex/static_root/assets/main-c5736dea.a4790dbdb569.js.br +0 -0
- codex/static_root/assets/main-c5736dea.a4790dbdb569.js.gz +0 -0
- codex/static_root/assets/main-c5736dea.js +1 -0
- codex/static_root/assets/main-c5736dea.js.br +0 -0
- codex/static_root/assets/main-c5736dea.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-83c74d48.b5cccc13c737.css +1 -0
- codex/static_root/assets/metadata-dialog-83c74d48.b5cccc13c737.css.br +0 -0
- codex/static_root/assets/metadata-dialog-83c74d48.b5cccc13c737.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-83c74d48.css +1 -0
- codex/static_root/assets/metadata-dialog-83c74d48.css.br +0 -0
- codex/static_root/assets/metadata-dialog-83c74d48.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-8c0a11ff.b281b7635db5.js +1 -0
- codex/static_root/assets/metadata-dialog-8c0a11ff.b281b7635db5.js.br +0 -0
- codex/static_root/assets/metadata-dialog-8c0a11ff.b281b7635db5.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-8c0a11ff.js +1 -0
- codex/static_root/assets/metadata-dialog-8c0a11ff.js.br +0 -0
- codex/static_root/assets/metadata-dialog-8c0a11ff.js.gz +0 -0
- codex/static_root/assets/{page-pdf-157ba97e.613d7c2beb77.js → page-pdf-ed976750.730244f14d16.js} +1 -1
- codex/static_root/assets/page-pdf-ed976750.730244f14d16.js.br +0 -0
- codex/static_root/assets/page-pdf-ed976750.730244f14d16.js.gz +0 -0
- codex/static_root/assets/{page-pdf-157ba97e.js → page-pdf-ed976750.js} +1 -1
- codex/static_root/assets/page-pdf-ed976750.js.br +0 -0
- codex/static_root/assets/page-pdf-ed976750.js.gz +0 -0
- codex/static_root/assets/reader-5540ffcb.8ea3c63a3154.css +1 -0
- codex/static_root/assets/reader-5540ffcb.8ea3c63a3154.css.br +0 -0
- codex/static_root/assets/reader-5540ffcb.8ea3c63a3154.css.gz +0 -0
- codex/static_root/assets/reader-5540ffcb.css +1 -0
- codex/static_root/assets/reader-5540ffcb.css.br +0 -0
- codex/static_root/assets/reader-5540ffcb.css.gz +0 -0
- codex/static_root/assets/reader-c562377d.7f78718f4c63.js +1 -0
- codex/static_root/assets/reader-c562377d.7f78718f4c63.js.br +0 -0
- codex/static_root/assets/reader-c562377d.7f78718f4c63.js.gz +0 -0
- codex/static_root/assets/reader-c562377d.js +1 -0
- codex/static_root/assets/reader-c562377d.js.br +0 -0
- codex/static_root/assets/reader-c562377d.js.gz +0 -0
- codex/static_root/{manifest.d2f93a519ada.json → manifest.55457ccaa01c.json} +32 -32
- codex/static_root/manifest.55457ccaa01c.json.br +0 -0
- codex/static_root/manifest.55457ccaa01c.json.gz +0 -0
- codex/static_root/manifest.json +32 -32
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/pwa/{offline.37a4206d79f0.html → offline.7bfaf9f94bf9.html} +1 -1
- codex/static_root/pwa/offline.7bfaf9f94bf9.html.br +0 -0
- codex/static_root/pwa/offline.7bfaf9f94bf9.html.gz +0 -0
- codex/static_root/pwa/offline.html +1 -1
- codex/static_root/pwa/offline.html.br +0 -0
- codex/static_root/pwa/offline.html.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/threads.py +1 -1
- codex/views/admin/api_key.py +3 -1
- codex/views/admin/flag.py +3 -1
- codex/views/admin/group.py +3 -1
- codex/views/admin/library.py +5 -4
- codex/views/admin/stats.py +10 -6
- codex/views/admin/tasks.py +35 -30
- codex/views/admin/user.py +4 -2
- codex/views/bookmark.py +6 -4
- codex/views/browser/base.py +30 -28
- codex/views/browser/browser.py +78 -80
- codex/views/browser/browser_annotations.py +15 -10
- codex/views/browser/browser_order_by.py +21 -16
- codex/views/browser/choices.py +37 -22
- codex/views/browser/filters/search.py +19 -16
- codex/views/browser/metadata.py +50 -41
- codex/views/cover.py +3 -1
- codex/views/download.py +4 -2
- codex/views/frontend.py +3 -2
- codex/views/mixins.py +13 -9
- codex/views/opds/authentication_v1.py +45 -41
- codex/views/opds/const.py +20 -13
- codex/views/opds/v1/entry/data.py +2 -1
- codex/views/opds/v1/facets.py +2 -1
- codex/views/opds/v1/feed.py +11 -4
- codex/views/opds/v1/links.py +8 -6
- codex/views/opds/v1/opensearch_v1.py +1 -1
- codex/views/opds/v2/feed.py +2 -1
- codex/views/opds/v2/publications.py +15 -12
- codex/views/reader/page.py +1 -1
- codex/views/session.py +50 -43
- codex/views/template.py +2 -2
- codex/websockets/listener.py +10 -7
- {codex-1.4.1.dist-info → codex-1.4.3.dist-info}/METADATA +24 -28
- {codex-1.4.1.dist-info → codex-1.4.3.dist-info}/RECORD +167 -167
- {codex-1.4.1.dist-info → codex-1.4.3.dist-info}/WHEEL +1 -1
- codex/static_root/assets/admin-12749881.ef0f50bac290.js +0 -41
- codex/static_root/assets/admin-12749881.ef0f50bac290.js.br +0 -0
- codex/static_root/assets/admin-12749881.ef0f50bac290.js.gz +0 -0
- codex/static_root/assets/admin-12749881.js +0 -41
- codex/static_root/assets/admin-12749881.js.br +0 -0
- codex/static_root/assets/admin-12749881.js.gz +0 -0
- codex/static_root/assets/admin-beda768d.a614eee46307.css +0 -1
- codex/static_root/assets/admin-beda768d.a614eee46307.css.br +0 -0
- codex/static_root/assets/admin-beda768d.a614eee46307.css.gz +0 -0
- codex/static_root/assets/admin-beda768d.css +0 -1
- codex/static_root/assets/admin-beda768d.css.br +0 -0
- codex/static_root/assets/admin-beda768d.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.js.gz +0 -0
- codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.br +0 -0
- codex/static_root/assets/browser-7f7d7134.0fe3749b0f2f.css.gz +0 -0
- codex/static_root/assets/browser-7f7d7134.css.br +0 -0
- codex/static_root/assets/browser-7f7d7134.css.gz +0 -0
- codex/static_root/assets/browser-af622672.d51aca96d64d.js +0 -1
- codex/static_root/assets/browser-af622672.d51aca96d64d.js.br +0 -0
- codex/static_root/assets/browser-af622672.d51aca96d64d.js.gz +0 -0
- codex/static_root/assets/browser-af622672.js +0 -1
- codex/static_root/assets/browser-af622672.js.br +0 -0
- codex/static_root/assets/browser-af622672.js.gz +0 -0
- codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.br +0 -0
- codex/static_root/assets/http-error-5e17b794.77ceeb2d4641.js.gz +0 -0
- codex/static_root/assets/http-error-5e17b794.js.br +0 -0
- codex/static_root/assets/http-error-5e17b794.js.gz +0 -0
- codex/static_root/assets/main-0898f4bb.181e0145c642.css.br +0 -0
- codex/static_root/assets/main-0898f4bb.css.br +0 -0
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js +0 -1
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.br +0 -0
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js.gz +0 -0
- codex/static_root/assets/main-9e76a4c3.js +0 -1
- codex/static_root/assets/main-9e76a4c3.js.br +0 -0
- codex/static_root/assets/main-9e76a4c3.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js +0 -1
- codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.br +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.8418785c0453.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.js +0 -1
- codex/static_root/assets/metadata-dialog-62c29ce0.js.br +0 -0
- codex/static_root/assets/metadata-dialog-62c29ce0.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css +0 -1
- codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.br +0 -0
- codex/static_root/assets/metadata-dialog-cb306ffd.cc304996d7bb.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-cb306ffd.css +0 -1
- codex/static_root/assets/metadata-dialog-cb306ffd.css.br +0 -0
- codex/static_root/assets/metadata-dialog-cb306ffd.css.gz +0 -0
- codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.br +0 -0
- codex/static_root/assets/page-pdf-157ba97e.613d7c2beb77.js.gz +0 -0
- codex/static_root/assets/page-pdf-157ba97e.js.br +0 -0
- codex/static_root/assets/page-pdf-157ba97e.js.gz +0 -0
- codex/static_root/assets/reader-36266549.0b2cf1291f27.js +0 -1
- codex/static_root/assets/reader-36266549.0b2cf1291f27.js.br +0 -0
- codex/static_root/assets/reader-36266549.0b2cf1291f27.js.gz +0 -0
- codex/static_root/assets/reader-36266549.js +0 -1
- codex/static_root/assets/reader-36266549.js.br +0 -0
- codex/static_root/assets/reader-36266549.js.gz +0 -0
- codex/static_root/assets/reader-7f004141.506eecc6954b.css +0 -1
- codex/static_root/assets/reader-7f004141.506eecc6954b.css.br +0 -0
- codex/static_root/assets/reader-7f004141.506eecc6954b.css.gz +0 -0
- codex/static_root/assets/reader-7f004141.css +0 -1
- codex/static_root/assets/reader-7f004141.css.br +0 -0
- codex/static_root/assets/reader-7f004141.css.gz +0 -0
- codex/static_root/manifest.d2f93a519ada.json.br +0 -0
- codex/static_root/manifest.d2f93a519ada.json.gz +0 -0
- codex/static_root/pwa/offline.37a4206d79f0.html.br +0 -0
- codex/static_root/pwa/offline.37a4206d79f0.html.gz +0 -0
- {codex-1.4.1.dist-info → codex-1.4.3.dist-info}/LICENSE +0 -0
- {codex-1.4.1.dist-info → codex-1.4.3.dist-info}/entry_points.txt +0 -0
codex/applications/websocket.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Aggregate metadata from comics to prepare for importing."""
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from types import MappingProxyType
|
|
3
4
|
from zipfile import BadZipFile
|
|
4
5
|
|
|
5
6
|
from comicbox.comic_archive import ComicArchive
|
|
@@ -20,9 +21,11 @@ class AggregateMetadataMixin(CleanMetadataMixin):
|
|
|
20
21
|
|
|
21
22
|
_BROWSER_GROUPS = (Publisher, Imprint, Series, Volume)
|
|
22
23
|
_BROWSER_GROUP_TREE_COUNT_FIELDS = frozenset(["volume_count", "issue_count"])
|
|
23
|
-
_GROUP_TREES_INIT =
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
_GROUP_TREES_INIT = MappingProxyType(
|
|
25
|
+
{
|
|
26
|
+
"group_trees": {Publisher: {}, Imprint: {}, Series: {}, Volume: {}},
|
|
27
|
+
}
|
|
28
|
+
)
|
|
26
29
|
_AGGREGATE_COMICBOX_CONFIG = AttrDict({**COMICBOX_CONFIG, "close_fd": False})
|
|
27
30
|
|
|
28
31
|
@staticmethod
|
|
@@ -36,6 +39,36 @@ class AggregateMetadataMixin(CleanMetadataMixin):
|
|
|
36
39
|
file_type = suffix
|
|
37
40
|
return file_type
|
|
38
41
|
|
|
42
|
+
@classmethod
|
|
43
|
+
def _get_group_tree(cls, md):
|
|
44
|
+
"""Create the group tree to counts map for a single comic."""
|
|
45
|
+
# Create group tree
|
|
46
|
+
group_tree = []
|
|
47
|
+
for group_cls in cls._BROWSER_GROUPS:
|
|
48
|
+
group_field = group_cls.__name__.lower()
|
|
49
|
+
# some volumes are read by ComicArchive as ints, cast
|
|
50
|
+
group_name = str(md.get(group_field, Publisher.DEFAULT_NAME))
|
|
51
|
+
# This fixes no imprint or whatever being in md
|
|
52
|
+
md[group_field] = group_name
|
|
53
|
+
group_tree.append(group_name)
|
|
54
|
+
|
|
55
|
+
# Add counts to group tree.
|
|
56
|
+
groups_md = {}
|
|
57
|
+
md_group_count_fields = cls._BROWSER_GROUP_TREE_COUNT_FIELDS & md.keys()
|
|
58
|
+
for key in md_group_count_fields:
|
|
59
|
+
groups_md[key] = md.pop(key)
|
|
60
|
+
return {tuple(group_tree): groups_md}
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _get_m2m_metadata(md, path):
|
|
64
|
+
"""Many_to_many fields get moved into a separate dict."""
|
|
65
|
+
m2m_md = {}
|
|
66
|
+
md_m2m_fields = COMIC_M2M_FIELD_NAMES & md.keys()
|
|
67
|
+
for field in md_m2m_fields:
|
|
68
|
+
m2m_md[field] = md.pop(field)
|
|
69
|
+
m2m_md["folders"] = Path(path).parents
|
|
70
|
+
return m2m_md
|
|
71
|
+
|
|
39
72
|
def _get_path_metadata(self, path):
|
|
40
73
|
"""Get the metatada from comicbox and munge it a little."""
|
|
41
74
|
md = {}
|
|
@@ -50,28 +83,8 @@ class AggregateMetadataMixin(CleanMetadataMixin):
|
|
|
50
83
|
md["path"] = path
|
|
51
84
|
md = self.clean_md(md)
|
|
52
85
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for group_cls in self._BROWSER_GROUPS:
|
|
56
|
-
group_field = group_cls.__name__.lower()
|
|
57
|
-
# some volumes are read by ComicArchive as ints, cast
|
|
58
|
-
group_name = str(md.get(group_field, Publisher.DEFAULT_NAME))
|
|
59
|
-
# This fixes no imprint or whatever being in md
|
|
60
|
-
md[group_field] = group_name
|
|
61
|
-
group_tree.append(group_name)
|
|
62
|
-
|
|
63
|
-
# Add counts to group tree.
|
|
64
|
-
groups_md = {}
|
|
65
|
-
md_group_count_fields = self._BROWSER_GROUP_TREE_COUNT_FIELDS & md.keys()
|
|
66
|
-
for key in md_group_count_fields:
|
|
67
|
-
groups_md[key] = md.pop(key)
|
|
68
|
-
group_tree_md[tuple(group_tree)] = groups_md
|
|
69
|
-
|
|
70
|
-
# Many_to_many fields get moved into a separate dict
|
|
71
|
-
md_m2m_fields = COMIC_M2M_FIELD_NAMES & md.keys()
|
|
72
|
-
for field in md_m2m_fields:
|
|
73
|
-
m2m_md[field] = md.pop(field)
|
|
74
|
-
m2m_md["folders"] = Path(path).parents
|
|
86
|
+
group_tree_md = self._get_group_tree(md)
|
|
87
|
+
m2m_md = self._get_m2m_metadata(md, path)
|
|
75
88
|
|
|
76
89
|
except (UnsupportedArchiveTypeError, BadRarFile, BadZipFile, OSError) as exc:
|
|
77
90
|
self.log.warning(f"Failed to import {path}: {exc}")
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import re
|
|
3
3
|
from contextlib import suppress
|
|
4
4
|
from decimal import Decimal
|
|
5
|
-
from typing import Any, Optional
|
|
5
|
+
from typing import Any, Optional, Union
|
|
6
6
|
|
|
7
7
|
from comicbox.metadata.comic_base import ComicBaseMetadata
|
|
8
8
|
from django.db.models.fields import CharField, DecimalField, PositiveSmallIntegerField
|
|
@@ -54,7 +54,6 @@ _MD_CHAR_KEYS = frozenset(
|
|
|
54
54
|
_TWO_PLACES = Decimal("0.01")
|
|
55
55
|
_PSI_MAX = 2147483647
|
|
56
56
|
_GROUPS = frozenset(("publisher", "imprint", "series", "volume"))
|
|
57
|
-
_URL_MAX_LENGTH = 200
|
|
58
57
|
_M2M_NAMED_KEYS = frozenset(
|
|
59
58
|
(
|
|
60
59
|
"characters",
|
|
@@ -74,10 +73,21 @@ class CleanMetadataMixin(QueuedThread):
|
|
|
74
73
|
"""Clean metadata before importing."""
|
|
75
74
|
|
|
76
75
|
@staticmethod
|
|
77
|
-
def
|
|
76
|
+
def _clean_string(value: Union[bytes, str]):
|
|
77
|
+
"""Replace unstorable, unprintable characters from metadata."""
|
|
78
|
+
# https://stackoverflow.com/questions/27366479/python-3-os-walk-file-paths-unicodeencodeerror-utf-8-codec-cant-encode-s
|
|
79
|
+
if isinstance(value, str):
|
|
80
|
+
value = value.encode("utf8", "replace")
|
|
81
|
+
|
|
82
|
+
return value.decode("utf8", "replace")
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def _clean_decimal(cls, value, field_name: str):
|
|
78
86
|
field: DecimalField = Comic._meta.get_field(field_name) # type: ignore
|
|
79
87
|
try:
|
|
80
88
|
# Comicbox now gives issues as strings, convert them to decimal here.
|
|
89
|
+
if isinstance(value, (str, bytes)):
|
|
90
|
+
value = cls._clean_string(value)
|
|
81
91
|
value = ComicBaseMetadata.parse_decimal(value)
|
|
82
92
|
value = value.quantize(_TWO_PLACES)
|
|
83
93
|
value = value.max(_DECIMAL_ZERO)
|
|
@@ -89,7 +99,9 @@ class CleanMetadataMixin(QueuedThread):
|
|
|
89
99
|
|
|
90
100
|
def _parse_comic_issue(self, md: dict[str, Any]):
|
|
91
101
|
"""Parse the issue field."""
|
|
92
|
-
issue_str = md.get("issue", "")
|
|
102
|
+
issue_str = md.get("issue", "")
|
|
103
|
+
issue_str = self._clean_string(issue_str)
|
|
104
|
+
issue_str = issue_str.strip()
|
|
93
105
|
try:
|
|
94
106
|
match = _PARSE_ISSUE_MATCHER.match(issue_str)
|
|
95
107
|
issue, issue_suffix = match.groups() # type: ignore
|
|
@@ -151,13 +163,14 @@ class CleanMetadataMixin(QueuedThread):
|
|
|
151
163
|
with suppress(KeyError):
|
|
152
164
|
md["name"] = md.pop("title")
|
|
153
165
|
|
|
154
|
-
@
|
|
155
|
-
def _clean_charfield(value: Optional[str], field: CharField) -> Optional[str]:
|
|
166
|
+
@classmethod
|
|
167
|
+
def _clean_charfield(cls, value: Optional[str], field: CharField) -> Optional[str]:
|
|
156
168
|
try:
|
|
157
169
|
if value is None:
|
|
158
170
|
raise ValueError # noqa TRY301
|
|
159
171
|
value = str(value)
|
|
160
172
|
value = value[: field.max_length].strip()
|
|
173
|
+
value = cls._clean_string(value)
|
|
161
174
|
except Exception:
|
|
162
175
|
value = None if field.null else ""
|
|
163
176
|
return value
|
|
@@ -202,14 +215,14 @@ class CleanMetadataMixin(QueuedThread):
|
|
|
202
215
|
good_story_arc_numbers[good_story_arc_name] = number
|
|
203
216
|
md["story_arc_numbers"] = good_story_arc_numbers
|
|
204
217
|
|
|
205
|
-
@
|
|
206
|
-
def _clean_comic_web(md):
|
|
218
|
+
@classmethod
|
|
219
|
+
def _clean_comic_web(cls, md):
|
|
207
220
|
"""URL field is a special charfield."""
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
221
|
+
web = md.get("web")
|
|
222
|
+
if web:
|
|
223
|
+
field: CharField = Comic._meta.get_field("web") # type:ignore
|
|
224
|
+
web = cls._clean_charfield(web, field)
|
|
225
|
+
if not web and web in md:
|
|
213
226
|
del md["web"]
|
|
214
227
|
|
|
215
228
|
@classmethod
|
|
@@ -161,7 +161,7 @@ class CreateForeignKeysMixin(QueuedThread):
|
|
|
161
161
|
parent = Folder.objects.get(path=parent_path)
|
|
162
162
|
except Folder.DoesNotExist:
|
|
163
163
|
if path.parent != Path(library.path):
|
|
164
|
-
self.log.
|
|
164
|
+
self.log.warning(
|
|
165
165
|
f"Can't find parent folder {parent_path}"
|
|
166
166
|
f" for {path} in library {library.path}"
|
|
167
167
|
)
|
codex/librarian/librariand.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from collections import namedtuple
|
|
3
3
|
from multiprocessing import Manager, Process
|
|
4
4
|
from threading import active_count
|
|
5
|
+
from types import MappingProxyType
|
|
5
6
|
|
|
6
7
|
from caseconverter import snakecase
|
|
7
8
|
from comicbox.comic_archive import ComicArchive
|
|
@@ -53,10 +54,12 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
|
|
|
53
54
|
LibraryPollingObserver,
|
|
54
55
|
JanitorThread,
|
|
55
56
|
)
|
|
56
|
-
_THREAD_CLASS_MAP =
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
_THREAD_CLASS_MAP = MappingProxyType(
|
|
58
|
+
{
|
|
59
|
+
snakecase(thread_class.__name__): thread_class
|
|
60
|
+
for thread_class in _THREAD_CLASSES
|
|
61
|
+
}
|
|
62
|
+
)
|
|
60
63
|
LibrarianThreads = namedtuple("LibrarianThreads", _THREAD_CLASS_MAP.keys())
|
|
61
64
|
|
|
62
65
|
proc = None
|
|
@@ -41,7 +41,7 @@ class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
|
|
|
41
41
|
f"Force delete missing path with missing db stat: {path}"
|
|
42
42
|
)
|
|
43
43
|
# This will trigger a deleted event
|
|
44
|
-
stat = Comic.ZERO_STAT
|
|
44
|
+
stat = list(Comic.ZERO_STAT)
|
|
45
45
|
|
|
46
46
|
if force:
|
|
47
47
|
# Fake mtime will trigger modified event
|
|
@@ -7,6 +7,7 @@ and then re-serialize everything in this batcher and the event Handler
|
|
|
7
7
|
"""
|
|
8
8
|
from contextlib import suppress
|
|
9
9
|
from copy import deepcopy
|
|
10
|
+
from types import MappingProxyType
|
|
10
11
|
|
|
11
12
|
from watchdog.events import EVENT_TYPE_MOVED
|
|
12
13
|
|
|
@@ -19,16 +20,18 @@ class WatchdogEventBatcherThread(AggregateMessageQueuedThread):
|
|
|
19
20
|
"""Batch watchdog events into bulk database tasks."""
|
|
20
21
|
|
|
21
22
|
CLS_SUFFIX = -len("Event")
|
|
22
|
-
DBDIFF_TASK_PARAMS =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
DBDIFF_TASK_PARAMS = MappingProxyType(
|
|
24
|
+
{
|
|
25
|
+
"library_id": None,
|
|
26
|
+
"dirs_moved": {},
|
|
27
|
+
"files_moved": {},
|
|
28
|
+
"files_modified": set(),
|
|
29
|
+
"files_created": set(),
|
|
30
|
+
"dirs_deleted": set(),
|
|
31
|
+
"dirs_modified": set(),
|
|
32
|
+
"files_deleted": set(),
|
|
33
|
+
}
|
|
34
|
+
)
|
|
32
35
|
MAX_DELAY = 60
|
|
33
36
|
MAX_ITEMS_PER_GB = 50000
|
|
34
37
|
|
|
@@ -42,7 +45,7 @@ class WatchdogEventBatcherThread(AggregateMessageQueuedThread):
|
|
|
42
45
|
def _ensure_library_args(self, library_id):
|
|
43
46
|
if library_id in self.cache:
|
|
44
47
|
return
|
|
45
|
-
args = deepcopy(self.DBDIFF_TASK_PARAMS)
|
|
48
|
+
args = deepcopy(dict(self.DBDIFF_TASK_PARAMS))
|
|
46
49
|
args["library_id"] = library_id
|
|
47
50
|
self.cache[library_id] = args
|
|
48
51
|
|
|
@@ -19,7 +19,7 @@ from codex.logger_base import LoggerBaseMixin
|
|
|
19
19
|
class CodexLibraryEventHandler(FileSystemEventHandler, LoggerBaseMixin):
|
|
20
20
|
"""Handle watchdog events for comics in a library."""
|
|
21
21
|
|
|
22
|
-
IGNORED_EVENTS =
|
|
22
|
+
IGNORED_EVENTS = frozenset((EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED))
|
|
23
23
|
|
|
24
24
|
def __init__(self, library, *args, **kwargs):
|
|
25
25
|
"""Let us send along he library id."""
|
codex/logger/loggerd.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Central Logging Thread."""
|
|
2
2
|
import logging
|
|
3
3
|
from logging.handlers import QueueListener, RotatingFileHandler
|
|
4
|
+
from types import MappingProxyType
|
|
4
5
|
|
|
5
6
|
from colors import color
|
|
6
7
|
|
|
@@ -10,28 +11,30 @@ from codex.settings.settings import DEBUG, LOG_DIR, LOG_TO_CONSOLE, LOG_TO_FILE
|
|
|
10
11
|
class ColorFormatter(logging.Formatter):
|
|
11
12
|
"""Logging Formatter to add colors and count warning / errors."""
|
|
12
13
|
|
|
13
|
-
FORMAT_COLORS =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
FORMAT_COLORS = MappingProxyType(
|
|
15
|
+
{
|
|
16
|
+
"CRITICAL": {"fg": "red", "style": "bold"},
|
|
17
|
+
"ERROR": {"fg": "red"},
|
|
18
|
+
"WARNING": {"fg": "yellow"},
|
|
19
|
+
"INFO": {"fg": "green"},
|
|
20
|
+
"DEBUG": {"fg": "black", "style": "bold"},
|
|
21
|
+
"NOTSET": {"fg": "blue"},
|
|
22
|
+
}
|
|
23
|
+
)
|
|
22
24
|
|
|
23
25
|
def __init__(self, fmt, **kwargs):
|
|
24
26
|
"""Set up the FORMATS dict."""
|
|
25
27
|
super().__init__(**kwargs)
|
|
28
|
+
self.formatters = {}
|
|
26
29
|
for level_name, args in self.FORMAT_COLORS.items():
|
|
27
30
|
levelno = getattr(logging, level_name)
|
|
28
31
|
template = color(fmt, **args)
|
|
29
32
|
formatter = logging.Formatter(fmt=template, **kwargs)
|
|
30
|
-
self.
|
|
33
|
+
self.formatters[levelno] = formatter
|
|
31
34
|
|
|
32
35
|
def format(self, record): # noqa A003
|
|
33
36
|
"""Format each log message."""
|
|
34
|
-
formatter = self.
|
|
37
|
+
formatter = self.formatters[record.levelno]
|
|
35
38
|
return formatter.format(record)
|
|
36
39
|
|
|
37
40
|
|
codex/migrations/0001_init.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
4
|
from decimal import Decimal
|
|
5
|
+
from typing import ClassVar
|
|
5
6
|
|
|
6
7
|
import django.db.models.deletion
|
|
7
8
|
from django.conf import settings
|
|
@@ -15,12 +16,12 @@ class Migration(migrations.Migration):
|
|
|
15
16
|
|
|
16
17
|
initial = True
|
|
17
18
|
|
|
18
|
-
dependencies = [
|
|
19
|
+
dependencies: ClassVar[list] = [
|
|
19
20
|
("sessions", "0001_initial"),
|
|
20
21
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
21
22
|
]
|
|
22
23
|
|
|
23
|
-
operations = [
|
|
24
|
+
operations: ClassVar[list] = [
|
|
24
25
|
migrations.CreateModel(
|
|
25
26
|
name="Character",
|
|
26
27
|
fields=[
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
"""Generated by Django 3.1 on 2020-08-26 06:22."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
from django.db import migrations
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class Migration(migrations.Migration):
|
|
7
9
|
"""Change libraries verbose name."""
|
|
8
10
|
|
|
9
|
-
dependencies = [
|
|
11
|
+
dependencies: ClassVar[list] = [
|
|
10
12
|
("codex", "0001_init"),
|
|
11
13
|
]
|
|
12
14
|
|
|
13
|
-
operations = [
|
|
15
|
+
operations: ClassVar[list] = [
|
|
14
16
|
migrations.AlterModelOptions(
|
|
15
17
|
name="library",
|
|
16
18
|
options={"verbose_name_plural": "libraries"},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Generated by Django 3.1 on 2020-08-31 20:33."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
import django.db.models.deletion
|
|
4
6
|
from django.db import migrations, models
|
|
5
7
|
|
|
@@ -7,11 +9,11 @@ from django.db import migrations, models
|
|
|
7
9
|
class Migration(migrations.Migration):
|
|
8
10
|
"""Credit roles can be none."""
|
|
9
11
|
|
|
10
|
-
dependencies = [
|
|
12
|
+
dependencies: ClassVar[list] = [
|
|
11
13
|
("codex", "0002_auto_20200826_0622"),
|
|
12
14
|
]
|
|
13
15
|
|
|
14
|
-
operations = [
|
|
16
|
+
operations: ClassVar[list] = [
|
|
15
17
|
migrations.AlterField(
|
|
16
18
|
model_name="credit",
|
|
17
19
|
name="role",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Generated by Django 3.1.1 on 2020-09-14 22:15."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
import django.db.models.deletion
|
|
4
6
|
from django.db import migrations, models
|
|
5
7
|
|
|
@@ -7,11 +9,11 @@ from django.db import migrations, models
|
|
|
7
9
|
class Migration(migrations.Migration):
|
|
8
10
|
"""Keep track of failed imports."""
|
|
9
11
|
|
|
10
|
-
dependencies = [
|
|
12
|
+
dependencies: ClassVar[list] = [
|
|
11
13
|
("codex", "0003_auto_20200831_2033"),
|
|
12
14
|
]
|
|
13
15
|
|
|
14
|
-
operations = [
|
|
16
|
+
operations: ClassVar[list] = [
|
|
15
17
|
migrations.CreateModel(
|
|
16
18
|
name="FailedImport",
|
|
17
19
|
fields=[
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
"""Generated by Django 3.1.1 on 2020-09-18 01:46."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
from django.db import migrations
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class Migration(migrations.Migration):
|
|
7
9
|
"""Update verbose names."""
|
|
8
10
|
|
|
9
|
-
dependencies = [
|
|
11
|
+
dependencies: ClassVar[list] = [
|
|
10
12
|
("codex", "0004_failedimport"),
|
|
11
13
|
]
|
|
12
14
|
|
|
13
|
-
operations = [
|
|
15
|
+
operations: ClassVar[list] = [
|
|
14
16
|
migrations.AlterModelOptions(
|
|
15
17
|
name="comic",
|
|
16
18
|
options={"verbose_name": "Issue"},
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"""Generated by Django 3.2.9 on 2021-11-04 03:03."""
|
|
2
2
|
|
|
3
|
+
from types import MappingProxyType
|
|
4
|
+
from typing import ClassVar
|
|
5
|
+
|
|
3
6
|
from django.db import migrations
|
|
4
7
|
from django.db.models.functions import Now
|
|
5
8
|
|
|
6
|
-
MODEL_NAMES =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
MODEL_NAMES = MappingProxyType(
|
|
10
|
+
{
|
|
11
|
+
"Series": "Default Series",
|
|
12
|
+
"Imprint": "Main Imprint",
|
|
13
|
+
"Publisher": "No Publisher",
|
|
14
|
+
}
|
|
15
|
+
)
|
|
11
16
|
NEW_DEFAULT_NAME = ""
|
|
12
17
|
UPDATE_FIELDS = ("stat", "updated_at")
|
|
13
18
|
|
|
@@ -58,11 +63,11 @@ def remove_duplicate_comics(apps, _schema_editor):
|
|
|
58
63
|
class Migration(migrations.Migration):
|
|
59
64
|
"""Change default names to ''."""
|
|
60
65
|
|
|
61
|
-
dependencies = [
|
|
66
|
+
dependencies: ClassVar[list] = [
|
|
62
67
|
("codex", "0005_auto_20200918_0146"),
|
|
63
68
|
]
|
|
64
69
|
|
|
65
|
-
operations = [
|
|
70
|
+
operations: ClassVar[list] = [
|
|
66
71
|
migrations.RunPython(update_default_names),
|
|
67
72
|
migrations.RunPython(remove_duplicate_comics),
|
|
68
73
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Generated by Django 3.2.9 on 2021-12-11 01:10."""
|
|
2
2
|
|
|
3
3
|
import datetime
|
|
4
|
+
from typing import ClassVar
|
|
4
5
|
|
|
5
6
|
import django.db.models.deletion
|
|
6
7
|
from django.db import migrations, models
|
|
@@ -9,11 +10,11 @@ from django.db import migrations, models
|
|
|
9
10
|
class Migration(migrations.Migration):
|
|
10
11
|
"""Large migration for v0.7.0."""
|
|
11
12
|
|
|
12
|
-
dependencies = [
|
|
13
|
+
dependencies: ClassVar[list] = [
|
|
13
14
|
("codex", "0006_update_default_names_and_remove_duplicate_comics"),
|
|
14
15
|
]
|
|
15
16
|
|
|
16
|
-
operations = [
|
|
17
|
+
operations: ClassVar[list] = [
|
|
17
18
|
migrations.CreateModel(
|
|
18
19
|
name="LatestVersion",
|
|
19
20
|
fields=[
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Generated by Django 4.0 on 2021-12-17 04:36."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
import django.db.models.deletion
|
|
4
6
|
from django.db import migrations, models
|
|
5
7
|
|
|
@@ -7,11 +9,11 @@ from django.db import migrations, models
|
|
|
7
9
|
class Migration(migrations.Migration):
|
|
8
10
|
"""Indexes for filtered and sorted comic fields."""
|
|
9
11
|
|
|
10
|
-
dependencies = [
|
|
12
|
+
dependencies: ClassVar[list] = [
|
|
11
13
|
("codex", "0007_auto_20211210_1710"),
|
|
12
14
|
]
|
|
13
15
|
|
|
14
|
-
operations = [
|
|
16
|
+
operations: ClassVar[list] = [
|
|
15
17
|
migrations.AlterField(
|
|
16
18
|
model_name="comic",
|
|
17
19
|
name="created_at",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Generated by Django 4.0 on 2021-12-19 17:47."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
import django.db.models.deletion
|
|
4
6
|
from django.db import migrations, models
|
|
5
7
|
|
|
@@ -7,11 +9,11 @@ from django.db import migrations, models
|
|
|
7
9
|
class Migration(migrations.Migration):
|
|
8
10
|
"""Override related_in becauese interferes with comic.folders."""
|
|
9
11
|
|
|
10
|
-
dependencies = [
|
|
12
|
+
dependencies: ClassVar[list] = [
|
|
11
13
|
("codex", "0008_alter_comic_created_at_alter_comic_format_and_more"),
|
|
12
14
|
]
|
|
13
15
|
|
|
14
|
-
operations = [
|
|
16
|
+
operations: ClassVar[list] = [
|
|
15
17
|
migrations.AlterField(
|
|
16
18
|
model_name="comic",
|
|
17
19
|
name="parent_folder",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Generated by Django 4.0.1 on 2022-01-16 05:31."""
|
|
2
2
|
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
3
5
|
import django.db.models.deletion
|
|
4
6
|
from django.db import migrations, models
|
|
5
7
|
|
|
@@ -7,11 +9,11 @@ from django.db import migrations, models
|
|
|
7
9
|
class Migration(migrations.Migration):
|
|
8
10
|
"""Haystack search engine."""
|
|
9
11
|
|
|
10
|
-
dependencies = [
|
|
12
|
+
dependencies: ClassVar[list] = [
|
|
11
13
|
("codex", "0009_alter_comic_parent_folder"),
|
|
12
14
|
]
|
|
13
15
|
|
|
14
|
-
operations = [
|
|
16
|
+
operations: ClassVar[list] = [
|
|
15
17
|
migrations.CreateModel(
|
|
16
18
|
# Fake model. Not managed.
|
|
17
19
|
name="QueueJob",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Generated by Django 4.0.1 on 2022-01-31 22:09 & tweaked by aj."""
|
|
2
2
|
from decimal import Decimal
|
|
3
|
+
from typing import ClassVar
|
|
3
4
|
|
|
4
5
|
from django.db import migrations, models
|
|
5
6
|
|
|
@@ -26,12 +27,12 @@ def critical_rating_to_decimal(apps, _schema_editor):
|
|
|
26
27
|
class Migration(migrations.Migration):
|
|
27
28
|
"""Library group ACLS and metadata changes."""
|
|
28
29
|
|
|
29
|
-
dependencies = [
|
|
30
|
+
dependencies: ClassVar[list] = [
|
|
30
31
|
("auth", "0012_alter_user_first_name_max_length"),
|
|
31
32
|
("codex", "0010_haystack"),
|
|
32
33
|
]
|
|
33
34
|
|
|
34
|
-
operations = [
|
|
35
|
+
operations: ClassVar[list] = [
|
|
35
36
|
migrations.AddField(
|
|
36
37
|
model_name="comic",
|
|
37
38
|
name="community_rating",
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"""Generated by Django 4.0.2 on 2022-02-24 20:58."""
|
|
2
|
+
from typing import ClassVar
|
|
3
|
+
|
|
2
4
|
from django.db import migrations
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
class Migration(migrations.Migration):
|
|
6
8
|
"""Rename comic description to comic comments."""
|
|
7
9
|
|
|
8
|
-
dependencies = [
|
|
10
|
+
dependencies: ClassVar[list] = [
|
|
9
11
|
("codex", "0011_library_groups_and_metadata_changes"),
|
|
10
12
|
]
|
|
11
13
|
|
|
12
|
-
operations = [
|
|
14
|
+
operations: ClassVar[list] = [
|
|
13
15
|
migrations.RenameField(
|
|
14
16
|
model_name="comic",
|
|
15
17
|
old_name="description",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Generated by Django 4.0.2 on 2022-03-25 23:16."""
|
|
2
|
-
|
|
3
2
|
from decimal import Decimal
|
|
3
|
+
from typing import ClassVar
|
|
4
4
|
|
|
5
5
|
from django.db import migrations, models
|
|
6
6
|
|
|
@@ -28,11 +28,11 @@ def cast_issue_count(apps, _schema_editor):
|
|
|
28
28
|
class Migration(migrations.Migration):
|
|
29
29
|
"""Larger valid fields."""
|
|
30
30
|
|
|
31
|
-
dependencies = [
|
|
31
|
+
dependencies: ClassVar[list] = [
|
|
32
32
|
("codex", "0012_rename_description_comic_comments"),
|
|
33
33
|
]
|
|
34
34
|
|
|
35
|
-
operations = [
|
|
35
|
+
operations: ClassVar[list] = [
|
|
36
36
|
migrations.RenameField(
|
|
37
37
|
model_name="volume", old_name="issue_count", new_name="issue_count_decimal"
|
|
38
38
|
),
|