codex 1.4.0a0__py3-none-any.whl → 1.4.1__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/config_default.yaml +12 -4
- codex/db_functions.py +4 -2
- codex/integrity.py +17 -6
- codex/librarian/covers/coverd.py +3 -11
- codex/librarian/covers/create.py +15 -21
- codex/librarian/covers/tasks.py +2 -16
- codex/librarian/importer/aggregate_metadata.py +75 -41
- codex/librarian/importer/clean_metadata.py +30 -7
- codex/librarian/importer/create_fks.py +154 -55
- codex/librarian/importer/deleted.py +11 -2
- codex/librarian/importer/failed_imports.py +44 -5
- codex/librarian/importer/importerd.py +37 -12
- codex/librarian/importer/link_comics.py +54 -31
- codex/librarian/importer/moved.py +55 -11
- codex/librarian/importer/query_fks.py +210 -48
- codex/librarian/importer/tasks.py +7 -7
- codex/librarian/janitor/cleanup.py +17 -5
- codex/librarian/librariand.py +10 -0
- codex/librarian/watchdog/events.py +11 -14
- codex/librarian/watchdog/observers.py +5 -1
- codex/logger/loggerd.py +7 -3
- codex/logger/logging.py +1 -1
- codex/migrations/0024_comic_gtin_comic_story_arc_number.py +24 -0
- codex/migrations/0025_add_story_arc_number.py +83 -0
- codex/models.py +21 -11
- codex/search/backend.py +1 -1
- codex/search/indexes.py +1 -1
- codex/serializers/browser.py +1 -0
- codex/serializers/metadata.py +5 -1
- codex/serializers/models.py +16 -1
- codex/serializers/opds/v1.py +1 -0
- codex/serializers/opds/v2.py +5 -2
- codex/serializers/reader.py +55 -16
- codex/settings/settings.py +1 -1
- codex/static_root/assets/admin-12749881.ef0f50bac290.js +41 -0
- 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 +41 -0
- 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 +1 -0
- 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 +1 -0
- 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-41c225cc.3f84583b435b.css +1 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.3f84583b435b.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.css +1 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-41c225cc.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-522f1e6c.089d70878270.js +1 -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 +1 -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 +1 -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 +1 -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 +1 -0
- 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 +1 -0
- 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 +1 -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 +1 -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-a6ac9581.2fd9e52cbcc3.css → main-0898f4bb.181e0145c642.css} +1 -1
- codex/static_root/assets/main-0898f4bb.181e0145c642.css.br +0 -0
- codex/static_root/assets/{main-a6ac9581.2fd9e52cbcc3.css.gz → main-0898f4bb.181e0145c642.css.gz} +0 -0
- codex/static_root/assets/{main-a6ac9581.css → main-0898f4bb.css} +1 -1
- codex/static_root/assets/main-0898f4bb.css.br +0 -0
- codex/static_root/assets/{main-a6ac9581.css.gz → main-0898f4bb.css.gz} +0 -0
- codex/static_root/assets/main-9e76a4c3.6844a407d14c.js +1 -0
- 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 +1 -0
- 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 +1 -0
- 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 +1 -0
- 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-785c4cfc.694a251cda37.css → metadata-dialog-cb306ffd.cc304996d7bb.css} +1 -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-785c4cfc.css → metadata-dialog-cb306ffd.css} +1 -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-abfd509d.3870dab8eaf4.js → page-pdf-157ba97e.613d7c2beb77.js} +61 -51
- 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-abfd509d.js → page-pdf-157ba97e.js} +61 -51
- 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 +1 -0
- 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 +1 -0
- 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 +1 -0
- 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 +1 -0
- codex/static_root/assets/reader-7f004141.css.br +0 -0
- codex/static_root/assets/reader-7f004141.css.gz +0 -0
- codex/static_root/js/choices-admin.24cecf0a0568.json +1 -0
- codex/static_root/js/choices-admin.24cecf0a0568.json.br +0 -0
- codex/static_root/js/choices-admin.24cecf0a0568.json.gz +0 -0
- codex/static_root/js/choices-admin.json +1 -1
- codex/static_root/js/choices-admin.json.br +0 -0
- codex/static_root/js/choices-admin.json.gz +0 -0
- codex/static_root/js/choices.8c58714cf5b2.json +1 -0
- codex/static_root/js/choices.8c58714cf5b2.json.br +5 -0
- codex/static_root/js/choices.8c58714cf5b2.json.gz +0 -0
- codex/static_root/js/choices.json +1 -1
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/{manifest.64a989215af8.json → manifest.d2f93a519ada.json} +34 -34
- codex/static_root/manifest.d2f93a519ada.json.br +0 -0
- codex/static_root/manifest.d2f93a519ada.json.gz +0 -0
- codex/static_root/manifest.json +34 -34
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/templates/headers-script-globals.html +1 -1
- codex/templates/{opds → opds_v1}/index.xml +3 -1
- codex/templates/{opds/opensearch.xml → opds_v1/opensearch_v1.xml} +1 -1
- codex/templates/search/indexes/codex/comic_text.txt +2 -2
- codex/urls/converters.py +1 -1
- codex/urls/opds/authentication.py +1 -1
- codex/urls/opds/root.py +8 -12
- codex/urls/opds/v1.py +12 -5
- codex/urls/opds/v2.py +2 -2
- codex/views/admin/tasks.py +6 -1
- codex/views/bookmark.py +2 -2
- codex/views/browser/base.py +23 -7
- codex/views/browser/browser.py +66 -56
- codex/views/browser/browser_annotations.py +159 -50
- codex/views/browser/browser_order_by.py +51 -105
- codex/views/browser/choices.py +75 -38
- codex/views/browser/filters/bookmark.py +6 -9
- codex/views/browser/filters/field.py +9 -6
- codex/views/browser/filters/group.py +12 -27
- codex/views/browser/filters/search.py +5 -10
- codex/views/browser/metadata.py +44 -19
- codex/views/download.py +1 -1
- codex/views/frontend.py +2 -3
- codex/views/mixins.py +15 -2
- codex/views/opds/const.py +8 -1
- codex/views/opds/util.py +37 -1
- codex/views/opds/v1/__init__.py +1 -1
- codex/views/opds/v1/data.py +21 -0
- codex/views/opds/v1/entry/__init__.py +1 -0
- codex/views/opds/v1/entry/data.py +23 -0
- codex/views/opds/v1/entry/entry.py +151 -0
- codex/views/opds/v1/entry/links.py +135 -0
- codex/views/opds/v1/facets.py +190 -0
- codex/views/opds/v1/feed.py +199 -0
- codex/views/opds/v1/links.py +198 -0
- codex/views/opds/{opensearch.py → v1/opensearch_v1.py} +3 -3
- codex/views/opds/v2/__init__.py +1 -1
- codex/views/opds/v2/const.py +10 -2
- codex/views/opds/v2/feed.py +82 -21
- codex/views/opds/v2/links.py +1 -1
- codex/views/opds/v2/publications.py +1 -1
- codex/views/opds/v2/top_links.py +1 -1
- codex/views/reader/page.py +6 -7
- codex/views/reader/reader.py +191 -61
- codex/views/session.py +2 -1
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/METADATA +10 -41
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/RECORD +187 -185
- codex/librarian/importer/db_ops.py +0 -248
- codex/pdf.py +0 -115
- codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js +0 -48
- codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js.br +0 -0
- codex/static_root/assets/admin-73d93dc7.2c3eb62e50a0.js.gz +0 -0
- codex/static_root/assets/admin-73d93dc7.js +0 -48
- codex/static_root/assets/admin-73d93dc7.js.br +0 -0
- codex/static_root/assets/admin-73d93dc7.js.gz +0 -0
- codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css +0 -1
- codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css.br +0 -0
- codex/static_root/assets/admin-79555229.5f2c4cb3a73c.css.gz +0 -0
- codex/static_root/assets/admin-79555229.css +0 -1
- codex/static_root/assets/admin-79555229.css.br +0 -0
- codex/static_root/assets/admin-79555229.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js +0 -1
- codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.a85324c9ccd8.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.js +0 -1
- codex/static_root/assets/admin-drawer-panel-64bcc083.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-64bcc083.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css +0 -1
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.br +0 -2
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.2c0814fa2a9b.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.css +0 -1
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.br +0 -2
- codex/static_root/assets/admin-drawer-panel-cce8c0aa.css.gz +0 -0
- codex/static_root/assets/browser-7325db61.css +0 -1
- codex/static_root/assets/browser-7325db61.css.br +0 -0
- codex/static_root/assets/browser-7325db61.css.gz +0 -0
- codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css +0 -1
- codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.br +0 -0
- codex/static_root/assets/browser-7325db61.ed2cfbf8e8ee.css.gz +0 -0
- codex/static_root/assets/browser-d2caeed7.2262000a6d55.js +0 -1
- codex/static_root/assets/browser-d2caeed7.2262000a6d55.js.br +0 -0
- codex/static_root/assets/browser-d2caeed7.2262000a6d55.js.gz +0 -0
- codex/static_root/assets/browser-d2caeed7.js +0 -1
- codex/static_root/assets/browser-d2caeed7.js.br +0 -0
- codex/static_root/assets/browser-d2caeed7.js.gz +0 -0
- codex/static_root/assets/http-error-0221c37d.480d5066da92.js +0 -1
- codex/static_root/assets/http-error-0221c37d.480d5066da92.js.br +0 -0
- codex/static_root/assets/http-error-0221c37d.480d5066da92.js.gz +0 -0
- codex/static_root/assets/http-error-0221c37d.js +0 -1
- codex/static_root/assets/http-error-0221c37d.js.br +0 -0
- codex/static_root/assets/http-error-0221c37d.js.gz +0 -0
- codex/static_root/assets/main-a6ac9581.2fd9e52cbcc3.css.br +0 -0
- codex/static_root/assets/main-a6ac9581.css.br +0 -0
- codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js +0 -1
- codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js.br +0 -0
- codex/static_root/assets/main-e33dcfb0.a65044fc1a08.js.gz +0 -0
- codex/static_root/assets/main-e33dcfb0.js +0 -1
- codex/static_root/assets/main-e33dcfb0.js.br +0 -0
- codex/static_root/assets/main-e33dcfb0.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.br +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.694a251cda37.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.css.br +0 -0
- codex/static_root/assets/metadata-dialog-785c4cfc.css.gz +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js +0 -1
- codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js.br +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.d12b42b1c9da.js.gz +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.js +0 -1
- codex/static_root/assets/metadata-dialog-8b0e8aaa.js.br +0 -0
- codex/static_root/assets/metadata-dialog-8b0e8aaa.js.gz +0 -0
- codex/static_root/assets/page-pdf-abfd509d.3870dab8eaf4.js.br +0 -0
- codex/static_root/assets/page-pdf-abfd509d.3870dab8eaf4.js.gz +0 -0
- codex/static_root/assets/page-pdf-abfd509d.js.br +0 -0
- codex/static_root/assets/page-pdf-abfd509d.js.gz +0 -0
- codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css +0 -1
- codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css.br +0 -0
- codex/static_root/assets/reader-a8b8f766.875abdd0d22e.css.gz +0 -0
- codex/static_root/assets/reader-a8b8f766.css +0 -1
- codex/static_root/assets/reader-a8b8f766.css.br +0 -0
- codex/static_root/assets/reader-a8b8f766.css.gz +0 -0
- codex/static_root/assets/reader-fe9345d2.759c31f82998.js +0 -1
- codex/static_root/assets/reader-fe9345d2.759c31f82998.js.br +0 -0
- codex/static_root/assets/reader-fe9345d2.759c31f82998.js.gz +0 -0
- codex/static_root/assets/reader-fe9345d2.js +0 -1
- codex/static_root/assets/reader-fe9345d2.js.br +0 -0
- codex/static_root/assets/reader-fe9345d2.js.gz +0 -0
- codex/static_root/js/choices-admin.3d958ea7f83b.json +0 -1
- codex/static_root/js/choices-admin.3d958ea7f83b.json.br +0 -0
- codex/static_root/js/choices-admin.3d958ea7f83b.json.gz +0 -0
- codex/static_root/js/choices.6bfc2a3d293f.json +0 -1
- codex/static_root/js/choices.6bfc2a3d293f.json.br +0 -0
- codex/static_root/js/choices.6bfc2a3d293f.json.gz +0 -0
- codex/static_root/manifest.64a989215af8.json.br +0 -0
- codex/static_root/manifest.64a989215af8.json.gz +0 -0
- codex/urls/opds/opensearch.py +0 -18
- codex/views/opds/v1/browser.py +0 -346
- codex/views/opds/v1/entry.py +0 -278
- codex/views/opds/v1/start.py +0 -28
- codex/views/opds/v1/util.py +0 -162
- codex/views/opds/v2/start.py +0 -28
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/LICENSE +0 -0
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/WHEEL +0 -0
- {codex-1.4.0a0.dist-info → codex-1.4.1.dist-info}/entry_points.txt +0 -0
codex/logger/loggerd.py
CHANGED
|
@@ -41,7 +41,7 @@ class CodexLogQueueListener(QueueListener):
|
|
|
41
41
|
_LOG_FMT = "{asctime} {levelname:7} {message}"
|
|
42
42
|
_DEBUG_LOG_FMT = "{asctime} {levelname:7} {name:25} {message}"
|
|
43
43
|
_DATEFMT = "%Y-%m-%d %H:%M:%S %Z"
|
|
44
|
-
|
|
44
|
+
_FORMATTER_STYLE = "{"
|
|
45
45
|
_LOG_PATH = LOG_DIR / "codex.log"
|
|
46
46
|
_LOG_MAX_BYTES = 10 * 1024 * 1024
|
|
47
47
|
|
|
@@ -54,7 +54,9 @@ class CodexLogQueueListener(QueueListener):
|
|
|
54
54
|
handler = RotatingFileHandler(
|
|
55
55
|
cls._LOG_PATH, maxBytes=cls._LOG_MAX_BYTES, backupCount=30, delay=True
|
|
56
56
|
)
|
|
57
|
-
formatter = logging.Formatter(
|
|
57
|
+
formatter = logging.Formatter(
|
|
58
|
+
fmt, style=cls._FORMATTER_STYLE, datefmt=cls._DATEFMT
|
|
59
|
+
)
|
|
58
60
|
handler.setFormatter(formatter)
|
|
59
61
|
except Exception as exc:
|
|
60
62
|
print("ERROR creating file logging handler", exc)
|
|
@@ -66,7 +68,9 @@ class CodexLogQueueListener(QueueListener):
|
|
|
66
68
|
handler = None
|
|
67
69
|
try:
|
|
68
70
|
handler = logging.StreamHandler()
|
|
69
|
-
formatter = ColorFormatter(
|
|
71
|
+
formatter = ColorFormatter(
|
|
72
|
+
fmt, style=cls._FORMATTER_STYLE, datefmt=cls._DATEFMT
|
|
73
|
+
)
|
|
70
74
|
handler.setFormatter(formatter)
|
|
71
75
|
except Exception as exc:
|
|
72
76
|
print("ERROR creating console logging handler", exc)
|
codex/logger/logging.py
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Generated by Django 4.2.1 on 2023-05-10 22:44."""
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
"""Add fields."""
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
("codex", "0023_rename_credit_creator_and_more"),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="comic",
|
|
16
|
+
name="gtin",
|
|
17
|
+
field=models.CharField(db_index=True, default="", max_length=32),
|
|
18
|
+
),
|
|
19
|
+
migrations.AddField(
|
|
20
|
+
model_name="comic",
|
|
21
|
+
name="story_arc_number",
|
|
22
|
+
field=models.PositiveSmallIntegerField(db_index=True, null=True),
|
|
23
|
+
),
|
|
24
|
+
]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Generated by Django 4.2.1 on 2023-05-17 19:22."""
|
|
2
|
+
import django.db.models.deletion
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _create_story_arc_numbers(apps, _schema_editor):
|
|
7
|
+
comic_model = apps.get_model("codex", "comic")
|
|
8
|
+
san_model = apps.get_model("codex", "StoryArcNumber")
|
|
9
|
+
num_sans = 0
|
|
10
|
+
|
|
11
|
+
comics = comic_model.objects.exclude(story_arcs=None)
|
|
12
|
+
print()
|
|
13
|
+
print(f"Comics with story arcs: {comics.count()}")
|
|
14
|
+
# Create a StoryArcNumber for each comic
|
|
15
|
+
for comic in comics:
|
|
16
|
+
sans = set()
|
|
17
|
+
first_done = False
|
|
18
|
+
for sa in comic.story_arcs.all():
|
|
19
|
+
number = None if first_done else comic.story_arc_number
|
|
20
|
+
kwargs = {"story_arc": sa, "number": number}
|
|
21
|
+
san, created = san_model.objects.get_or_create(defaults=kwargs, **kwargs)
|
|
22
|
+
num_sans += int(created)
|
|
23
|
+
sans.add(san)
|
|
24
|
+
first_done = True
|
|
25
|
+
comic.story_arc_numbers.add(*sans)
|
|
26
|
+
comic.save()
|
|
27
|
+
|
|
28
|
+
num_sas = apps.get_model("codex", "StoryArc").objects.count()
|
|
29
|
+
print(f"Created {num_sans} StoryArcNumbers for {num_sas} StoryArcs")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Migration(migrations.Migration):
|
|
33
|
+
"""Run Migrations."""
|
|
34
|
+
|
|
35
|
+
dependencies = [
|
|
36
|
+
("codex", "0024_comic_gtin_comic_story_arc_number"),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
operations = [
|
|
40
|
+
migrations.CreateModel(
|
|
41
|
+
name="StoryArcNumber",
|
|
42
|
+
fields=[
|
|
43
|
+
(
|
|
44
|
+
"id",
|
|
45
|
+
models.AutoField(
|
|
46
|
+
auto_created=True,
|
|
47
|
+
primary_key=True,
|
|
48
|
+
serialize=False,
|
|
49
|
+
verbose_name="ID",
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
53
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
54
|
+
("number", models.PositiveIntegerField(default=None, null=True)),
|
|
55
|
+
(
|
|
56
|
+
"story_arc",
|
|
57
|
+
models.ForeignKey(
|
|
58
|
+
db_index=True,
|
|
59
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
60
|
+
to="codex.storyarc",
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
],
|
|
64
|
+
),
|
|
65
|
+
migrations.AlterUniqueTogether(
|
|
66
|
+
name="storyarcnumber",
|
|
67
|
+
unique_together={("story_arc", "number")},
|
|
68
|
+
),
|
|
69
|
+
migrations.AddField(
|
|
70
|
+
model_name="comic",
|
|
71
|
+
name="story_arc_numbers",
|
|
72
|
+
field=models.ManyToManyField(to="codex.storyarcnumber"),
|
|
73
|
+
),
|
|
74
|
+
migrations.RunPython(_create_story_arc_numbers),
|
|
75
|
+
migrations.RemoveField(
|
|
76
|
+
model_name="comic",
|
|
77
|
+
name="story_arc_number",
|
|
78
|
+
),
|
|
79
|
+
migrations.RemoveField(
|
|
80
|
+
model_name="comic",
|
|
81
|
+
name="story_arcs",
|
|
82
|
+
),
|
|
83
|
+
]
|
codex/models.py
CHANGED
|
@@ -199,10 +199,22 @@ class SeriesGroup(NamedModel):
|
|
|
199
199
|
"""A series group the series is part of."""
|
|
200
200
|
|
|
201
201
|
|
|
202
|
-
class StoryArc(NamedModel):
|
|
202
|
+
class StoryArc(NamedModel, BrowserGroupModel):
|
|
203
203
|
"""A story arc the comic is part of."""
|
|
204
204
|
|
|
205
205
|
|
|
206
|
+
class StoryArcNumber(BaseModel):
|
|
207
|
+
"""A story arc number the comic represents."""
|
|
208
|
+
|
|
209
|
+
story_arc = ForeignKey(StoryArc, db_index=True, on_delete=CASCADE)
|
|
210
|
+
number = PositiveIntegerField(null=True, default=None)
|
|
211
|
+
|
|
212
|
+
class Meta:
|
|
213
|
+
"""Declare constraints and indexes."""
|
|
214
|
+
|
|
215
|
+
unique_together = ("story_arc", "number")
|
|
216
|
+
|
|
217
|
+
|
|
206
218
|
class Location(NamedModel):
|
|
207
219
|
"""A location that appears in the comic."""
|
|
208
220
|
|
|
@@ -343,13 +355,15 @@ class Comic(WatchedPath):
|
|
|
343
355
|
read_ltr = BooleanField(db_index=True, default=True)
|
|
344
356
|
scan_info = CharField(max_length=MAX_NAME_LEN, default="")
|
|
345
357
|
web = URLField(default="")
|
|
358
|
+
gtin = CharField(db_index=True, max_length=MAX_FIELD_LEN, default="")
|
|
359
|
+
|
|
346
360
|
# ManyToMany
|
|
347
361
|
characters = ManyToManyField(Character)
|
|
348
362
|
creators = ManyToManyField(Creator)
|
|
349
363
|
genres = ManyToManyField(Genre)
|
|
350
364
|
locations = ManyToManyField(Location)
|
|
351
365
|
series_groups = ManyToManyField(SeriesGroup)
|
|
352
|
-
|
|
366
|
+
story_arc_numbers = ManyToManyField(StoryArcNumber)
|
|
353
367
|
tags = ManyToManyField(Tag)
|
|
354
368
|
teams = ManyToManyField(Team)
|
|
355
369
|
# Ignore these, they seem useless:
|
|
@@ -465,18 +479,14 @@ class Comic(WatchedPath):
|
|
|
465
479
|
return title
|
|
466
480
|
|
|
467
481
|
@classmethod
|
|
468
|
-
def get_filename(cls, obj
|
|
482
|
+
def get_filename(cls, obj):
|
|
469
483
|
"""Get the fileaname from dict."""
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
fn += "." + ft.lower()
|
|
473
|
-
return fn
|
|
484
|
+
path = Path(obj.path)
|
|
485
|
+
return path.stem + path.suffix
|
|
474
486
|
|
|
475
|
-
def filename(self
|
|
487
|
+
def filename(self):
|
|
476
488
|
"""Create a filename for download."""
|
|
477
|
-
self.
|
|
478
|
-
self.volume_name = self.volume.name
|
|
479
|
-
return self.get_filename(self, issue_max)
|
|
489
|
+
return self.get_filename(self)
|
|
480
490
|
|
|
481
491
|
def __str__(self):
|
|
482
492
|
"""Most common text representation for logging."""
|
codex/search/backend.py
CHANGED
codex/search/indexes.py
CHANGED
|
@@ -21,7 +21,7 @@ class ComicIndex(ModelSearchIndex, Indexable):
|
|
|
21
21
|
locations = CharField(model_attr="locations__name", null=True)
|
|
22
22
|
series_groups = CharField(model_attr="series_groups__name", null=True)
|
|
23
23
|
story_arcs = CharField(
|
|
24
|
-
model_attr="
|
|
24
|
+
model_attr="story_arc_numbers__story_arc__name",
|
|
25
25
|
null=True,
|
|
26
26
|
)
|
|
27
27
|
tags = CharField(model_attr="tags__name", null=True)
|
codex/serializers/browser.py
CHANGED
|
@@ -162,6 +162,7 @@ class BrowserSettingsSerializer(Serializer):
|
|
|
162
162
|
twenty_four_hour_time = BooleanField(required=False)
|
|
163
163
|
top_group = ChoiceField(choices=tuple(CHOICES["topGroup"].keys()), required=False)
|
|
164
164
|
opds_metadata = BooleanField(required=False)
|
|
165
|
+
limit = IntegerField(required=False)
|
|
165
166
|
|
|
166
167
|
|
|
167
168
|
class BrowserCardSerializer(BrowserAggregateSerializerMixin):
|
codex/serializers/metadata.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from rest_framework.serializers import CharField, IntegerField
|
|
3
3
|
|
|
4
4
|
from codex.serializers.mixins import BrowserAggregateSerializerMixin
|
|
5
|
-
from codex.serializers.models import ComicSerializer
|
|
5
|
+
from codex.serializers.models import ComicSerializer, StoryArcSerializer
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class MetadataSerializer(BrowserAggregateSerializerMixin, ComicSerializer):
|
|
@@ -12,3 +12,7 @@ class MetadataSerializer(BrowserAggregateSerializerMixin, ComicSerializer):
|
|
|
12
12
|
parent_folder_pk = IntegerField(read_only=True, required=False)
|
|
13
13
|
series_volume_count = IntegerField(read_only=True)
|
|
14
14
|
volume_issue_count = IntegerField(read_only=True)
|
|
15
|
+
story_arcs = StoryArcSerializer(
|
|
16
|
+
many=True,
|
|
17
|
+
allow_null=True,
|
|
18
|
+
)
|
codex/serializers/models.py
CHANGED
|
@@ -24,6 +24,7 @@ from codex.models import (
|
|
|
24
24
|
Series,
|
|
25
25
|
SeriesGroup,
|
|
26
26
|
StoryArc,
|
|
27
|
+
StoryArcNumber,
|
|
27
28
|
Tag,
|
|
28
29
|
Team,
|
|
29
30
|
Volume,
|
|
@@ -233,6 +234,20 @@ class StoryArcSerializer(NamedModelSerializer):
|
|
|
233
234
|
model = StoryArc
|
|
234
235
|
|
|
235
236
|
|
|
237
|
+
class StoryArcNumberSerializer(Serializer):
|
|
238
|
+
"""StoryArc model."""
|
|
239
|
+
|
|
240
|
+
story_arc = StoryArcSerializer()
|
|
241
|
+
number = IntegerField(read_only=True, allow_null=True)
|
|
242
|
+
|
|
243
|
+
class Meta(NamedModelMeta):
|
|
244
|
+
"""Configure model."""
|
|
245
|
+
|
|
246
|
+
model = StoryArcNumber
|
|
247
|
+
fields = ("pk", "story_arc", "number")
|
|
248
|
+
depth = 1
|
|
249
|
+
|
|
250
|
+
|
|
236
251
|
class TagSerializer(NamedModelSerializer):
|
|
237
252
|
"""Tag model."""
|
|
238
253
|
|
|
@@ -276,7 +291,7 @@ class ComicSerializer(ModelSerializer):
|
|
|
276
291
|
genres = GenreSerializer(many=True, allow_null=True)
|
|
277
292
|
locations = LocationSerializer(many=True, allow_null=True)
|
|
278
293
|
series_groups = SeriesGroupSerializer(many=True, allow_null=True)
|
|
279
|
-
|
|
294
|
+
story_arc_numbers = StoryArcNumberSerializer(
|
|
280
295
|
many=True,
|
|
281
296
|
allow_null=True,
|
|
282
297
|
)
|
codex/serializers/opds/v1.py
CHANGED
|
@@ -60,6 +60,7 @@ class OPDS1TemplateSerializer(Serializer):
|
|
|
60
60
|
"""OPDS Browser Template Serializer."""
|
|
61
61
|
|
|
62
62
|
opds_ns = CharField(read_only=True)
|
|
63
|
+
is_acquisition = BooleanField(read_only=True)
|
|
63
64
|
id_tag = CharField(read_only=True)
|
|
64
65
|
title = CharField(read_only=True)
|
|
65
66
|
updated = DateTimeField(read_only=True, default_timezone=UTC_TZ)
|
codex/serializers/opds/v2.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"""OPDS 2 Serializers."""
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
2
4
|
from rest_framework.fields import (
|
|
3
5
|
BooleanField,
|
|
4
6
|
CharField,
|
|
@@ -59,7 +61,7 @@ class OPDS2LinkSerializer(Serializer):
|
|
|
59
61
|
children = CharListField(read_only=True, required=False)
|
|
60
62
|
properties = OPDS2LinkPropertiesSerializer(read_only=True, required=False)
|
|
61
63
|
|
|
62
|
-
def get_rel(self, obj):
|
|
64
|
+
def get_rel(self, obj) -> Optional[str]:
|
|
63
65
|
"""Allow for CharField or CharListField types."""
|
|
64
66
|
rel = obj.get("rel")
|
|
65
67
|
if not isinstance(rel, (list, str)):
|
|
@@ -118,7 +120,8 @@ class OPDS2PublicationMetadataSerializer(OPDS2MetadataSerializer):
|
|
|
118
120
|
https://readium.org/webpub-manifest/schema/metadata.schema.json
|
|
119
121
|
"""
|
|
120
122
|
|
|
121
|
-
#
|
|
123
|
+
# possibly change to @ on output if this is really needed
|
|
124
|
+
# @type = CharField(read_only=True, required=False)
|
|
122
125
|
publisher = CharField(read_only=True, required=False)
|
|
123
126
|
imprint = CharField(read_only=True, required=False)
|
|
124
127
|
language = CharField(read_only=True, required=False)
|
codex/serializers/reader.py
CHANGED
|
@@ -25,27 +25,66 @@ class ReaderSettingsSerializer(Serializer):
|
|
|
25
25
|
vertical = BooleanField(allow_null=True, required=False)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class ReaderArcSerializer(Serializer):
|
|
29
|
+
"""A group of comics or a story arc."""
|
|
30
|
+
|
|
31
|
+
group = CharField(read_only=True)
|
|
32
|
+
pk = IntegerField(read_only=True)
|
|
33
|
+
name = CharField(read_only=True)
|
|
34
|
+
|
|
35
|
+
|
|
28
36
|
class ReaderComicSerializer(Serializer):
|
|
29
|
-
"""
|
|
37
|
+
"""Prev, Next and Current Comic info."""
|
|
30
38
|
|
|
31
39
|
pk = IntegerField(read_only=True)
|
|
32
|
-
|
|
33
|
-
issue = DecimalField(
|
|
34
|
-
max_digits=None, decimal_places=3, read_only=True, coerce_to_string=False
|
|
35
|
-
)
|
|
36
|
-
issue_suffix = CharField(read_only=True)
|
|
37
|
-
issue_count = IntegerField(read_only=True)
|
|
40
|
+
settings = ReaderSettingsSerializer(read_only=True)
|
|
38
41
|
max_page = IntegerField(read_only=True)
|
|
39
|
-
series_name = CharField(read_only=True)
|
|
40
|
-
volume_name = CharField(read_only=True)
|
|
41
|
-
series_index = IntegerField(read_only=True)
|
|
42
42
|
read_ltr = BooleanField(allow_null=True, read_only=True)
|
|
43
|
-
settings = ReaderSettingsSerializer(read_only=True)
|
|
44
|
-
filename = CharField(read_only=True)
|
|
45
43
|
|
|
46
44
|
|
|
47
|
-
class
|
|
48
|
-
"""Information about the
|
|
45
|
+
class ReaderCurrentArcSerializer(Serializer):
|
|
46
|
+
"""Information about the current Arc."""
|
|
47
|
+
|
|
48
|
+
group = CharField(read_only=True)
|
|
49
|
+
pk = IntegerField(read_only=True)
|
|
50
|
+
index = IntegerField(read_only=True)
|
|
51
|
+
count = IntegerField(read_only=True, required=False)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ReaderCurrentComicSerializer(ReaderComicSerializer):
|
|
55
|
+
"""Current comic only Serializer."""
|
|
56
|
+
|
|
57
|
+
# For title
|
|
58
|
+
series_name = CharField(read_only=True, required=False)
|
|
59
|
+
volume_name = CharField(read_only=True, required=False)
|
|
60
|
+
issue = DecimalField(
|
|
61
|
+
max_digits=None,
|
|
62
|
+
decimal_places=3,
|
|
63
|
+
read_only=True,
|
|
64
|
+
coerce_to_string=False,
|
|
65
|
+
required=False,
|
|
66
|
+
)
|
|
67
|
+
issue_suffix = CharField(read_only=True, required=False)
|
|
68
|
+
issue_count = IntegerField(
|
|
69
|
+
read_only=True,
|
|
70
|
+
required=False,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
file_type = CharField(read_only=True, required=False)
|
|
74
|
+
filename = CharField(read_only=True, required=False)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ReaderBooksSerializer(Serializer):
|
|
78
|
+
"""All comics relevant to the reader."""
|
|
79
|
+
|
|
80
|
+
current = ReaderCurrentComicSerializer(read_only=True)
|
|
81
|
+
prev_book = ReaderComicSerializer(read_only=True, required=False)
|
|
82
|
+
next_book = ReaderComicSerializer(read_only=True, required=False)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ReaderComicsSerializer(Serializer):
|
|
86
|
+
"""Books and arcs."""
|
|
49
87
|
|
|
50
|
-
books =
|
|
51
|
-
|
|
88
|
+
books = ReaderBooksSerializer(read_only=True)
|
|
89
|
+
arcs = ReaderArcSerializer(many=True, read_only=True)
|
|
90
|
+
arc = ReaderCurrentArcSerializer(read_only=True)
|
codex/settings/settings.py
CHANGED
|
@@ -181,7 +181,7 @@ PORT = int(HYPERCORN_CONFIG.bind[0].split(":")[1])
|
|
|
181
181
|
|
|
182
182
|
# Static files (CSS, JavaScript, Images)
|
|
183
183
|
# https://docs.djangoproject.com/en/dev/howto/static-files/
|
|
184
|
-
# WHITENOISE_KEEP_ONLY_HASHED_FILES is not usable with vite chunking
|
|
184
|
+
# WHITENOISE_KEEP_ONLY_HASHED_FILES is still not usable with vite chunking
|
|
185
185
|
WHITENOISE_STATIC_PREFIX = "static/"
|
|
186
186
|
WHITENOISE_IMMUTABLE_FILE_TEST = immutable_file_test
|
|
187
187
|
STATIC_ROOT = CODEX_PATH / "static_root"
|