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/views/browser/browser.py
CHANGED
|
@@ -20,6 +20,7 @@ from codex.models import (
|
|
|
20
20
|
Library,
|
|
21
21
|
Publisher,
|
|
22
22
|
Series,
|
|
23
|
+
StoryArc,
|
|
23
24
|
Timestamp,
|
|
24
25
|
Volume,
|
|
25
26
|
)
|
|
@@ -49,9 +50,11 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
49
50
|
Imprint: ("publisher",),
|
|
50
51
|
Publisher: (None,),
|
|
51
52
|
Folder: ("parent_folder",),
|
|
53
|
+
StoryArc: (None,),
|
|
52
54
|
}
|
|
55
|
+
DEFAULT_ROUTE_NAME = "browser"
|
|
53
56
|
_DEFAULT_ROUTE = {
|
|
54
|
-
"name":
|
|
57
|
+
"name": DEFAULT_ROUTE_NAME,
|
|
55
58
|
"params": deepcopy(DEFAULTS["route"]),
|
|
56
59
|
}
|
|
57
60
|
_OPDS_M2M_RELS = (
|
|
@@ -59,7 +62,8 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
59
62
|
"genres",
|
|
60
63
|
"locations",
|
|
61
64
|
"series_groups",
|
|
62
|
-
"
|
|
65
|
+
"story_arc_numbers",
|
|
66
|
+
"story_arc_numbers__story_arc",
|
|
63
67
|
"tags",
|
|
64
68
|
"teams",
|
|
65
69
|
"creators",
|
|
@@ -88,37 +92,16 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
88
92
|
return queryset.annotate(**group_names)
|
|
89
93
|
|
|
90
94
|
def _add_annotations(self, queryset, model, search_scores):
|
|
91
|
-
"""Annotations for display and sorting.
|
|
92
|
-
|
|
93
|
-
model is neccissary because this gets called twice by folder
|
|
94
|
-
view. once for folders, once for the comics.
|
|
95
|
-
"""
|
|
96
|
-
is_model_comic = model == Comic
|
|
97
|
-
##############################
|
|
98
|
-
# Annotate Common Aggregates #
|
|
99
|
-
##############################
|
|
95
|
+
"""Annotations for display and sorting."""
|
|
100
96
|
queryset = self.annotate_common_aggregates(queryset, model, search_scores)
|
|
101
|
-
if not is_model_comic:
|
|
102
|
-
# EXTRA FILTER for empty group
|
|
103
|
-
queryset = queryset.filter(child_count__gt=0)
|
|
104
97
|
|
|
105
|
-
|
|
106
|
-
# Annotate Group #
|
|
107
|
-
##################
|
|
98
|
+
# Annotate Group
|
|
108
99
|
self.model_group = self._MODEL_GROUP_MAP[model]
|
|
109
100
|
queryset = queryset.annotate(
|
|
110
101
|
group=Value(self.model_group, CharField(max_length=1))
|
|
111
102
|
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
# Sortable aggregates #
|
|
115
|
-
#######################
|
|
116
|
-
order_key = self.get_order_key()
|
|
117
|
-
order_func = self.get_aggregate_func(order_key, model)
|
|
118
|
-
queryset = queryset.annotate(order_value=order_func)
|
|
119
|
-
|
|
120
|
-
queryset = self._annotate_group_names(queryset, model)
|
|
121
|
-
return queryset
|
|
103
|
+
# Hoist Group Names
|
|
104
|
+
return self._annotate_group_names(queryset, model)
|
|
122
105
|
|
|
123
106
|
def _get_model_group(self):
|
|
124
107
|
"""Get the group of the models to browse."""
|
|
@@ -127,7 +110,10 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
127
110
|
# the child of the current nav group or 'c'
|
|
128
111
|
group = self.kwargs["group"]
|
|
129
112
|
if group == self.FOLDER_GROUP:
|
|
130
|
-
return
|
|
113
|
+
return group
|
|
114
|
+
if group == self.STORY_ARC_GROUP:
|
|
115
|
+
pk = self.kwargs["pk"]
|
|
116
|
+
return self.COMIC_GROUP if pk else group
|
|
131
117
|
if group == self.valid_nav_groups[-1]:
|
|
132
118
|
# special case for lowest valid group
|
|
133
119
|
return self.COMIC_GROUP
|
|
@@ -141,35 +127,36 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
141
127
|
model_group = self._get_model_group()
|
|
142
128
|
self.model = self.GROUP_MODEL_MAP[model_group]
|
|
143
129
|
|
|
144
|
-
def
|
|
145
|
-
"""Create queryset."""
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
group_filter = object_filter
|
|
149
|
-
group_qs = self.model.objects.filter(group_filter)
|
|
150
|
-
group_qs = self._add_annotations(group_qs, self.model, search_scores)
|
|
151
|
-
group_qs = self.get_order_by(self.model, group_qs)
|
|
130
|
+
def _get_group_queryset(self, object_filter, search_scores):
|
|
131
|
+
"""Create group queryset."""
|
|
132
|
+
if self.model == Comic:
|
|
133
|
+
qs = self.model.objects.none()
|
|
152
134
|
else:
|
|
153
|
-
|
|
135
|
+
qs = self.model.objects.filter(object_filter)
|
|
136
|
+
qs = self._add_annotations(qs, self.model, search_scores)
|
|
137
|
+
qs = self.add_order_by(qs, self.model)
|
|
138
|
+
return qs
|
|
154
139
|
|
|
155
|
-
|
|
140
|
+
def _get_book_queryset(self, object_filter, search_scores):
|
|
141
|
+
"""Create book queryset."""
|
|
156
142
|
group = self.kwargs.get("group")
|
|
157
143
|
if self.model == Comic or group == self.FOLDER_GROUP:
|
|
158
144
|
if group == self.FOLDER_GROUP:
|
|
159
145
|
comic_object_filter, comic_search_scores = self.get_query_filters(
|
|
160
|
-
|
|
146
|
+
self.model, False
|
|
161
147
|
)
|
|
162
148
|
else:
|
|
163
149
|
comic_object_filter = object_filter
|
|
164
150
|
comic_search_scores = search_scores
|
|
165
151
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
152
|
+
qs = Comic.objects.filter(comic_object_filter)
|
|
153
|
+
qs = self._add_annotations(qs, Comic, comic_search_scores)
|
|
154
|
+
qs = self.add_order_by(qs, Comic)
|
|
155
|
+
if limit := self.params.get("limit"):
|
|
156
|
+
qs = qs[:limit]
|
|
169
157
|
else:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return group_qs, book_qs
|
|
158
|
+
qs = Comic.objects.none()
|
|
159
|
+
return qs
|
|
173
160
|
|
|
174
161
|
def _get_folder_up_route(self):
|
|
175
162
|
"""Get out parent's pk."""
|
|
@@ -186,11 +173,17 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
186
173
|
|
|
187
174
|
return up_group, up_pk
|
|
188
175
|
|
|
176
|
+
def _get_story_arc_up_route(self):
|
|
177
|
+
"""Get one level hierarchy."""
|
|
178
|
+
up_group = self.STORY_ARC_GROUP
|
|
179
|
+
up_group = 0 if self.group_instance else None
|
|
180
|
+
return self.STORY_ARC_GROUP, up_group
|
|
181
|
+
|
|
189
182
|
def _set_group_instance(self):
|
|
190
183
|
"""Create group_class instance."""
|
|
191
184
|
pk = self.kwargs.get("pk")
|
|
192
185
|
self.group_instance: Optional[
|
|
193
|
-
Union[Folder, Publisher, Imprint, Series, Volume]
|
|
186
|
+
Union[Folder, Publisher, Imprint, Series, Volume, StoryArc]
|
|
194
187
|
] = None
|
|
195
188
|
if not pk:
|
|
196
189
|
return
|
|
@@ -322,7 +315,7 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
322
315
|
)
|
|
323
316
|
|
|
324
317
|
if book_qs.count():
|
|
325
|
-
book_max_obj_per_page = self.MAX_OBJ_PER_PAGE - group_qs.count()
|
|
318
|
+
book_max_obj_per_page = max(0, self.MAX_OBJ_PER_PAGE - group_qs.count())
|
|
326
319
|
page_counts = (num_pages, total_count, Comic)
|
|
327
320
|
group_qs, num_pages, total_count = self._paginate_section(
|
|
328
321
|
group_qs, book_max_obj_per_page, page_counts
|
|
@@ -333,11 +326,11 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
333
326
|
def get_object(self):
|
|
334
327
|
"""Validate settings and get the querysets."""
|
|
335
328
|
self._set_browse_model()
|
|
329
|
+
self.set_rel_prefix(self.model)
|
|
336
330
|
self._set_group_instance() # Placed up here to invalidate earlier
|
|
337
331
|
# Create the main query with the filters
|
|
338
|
-
is_model_comic = self.model == Comic
|
|
339
332
|
try:
|
|
340
|
-
object_filter, search_scores = self.get_query_filters(
|
|
333
|
+
object_filter, search_scores = self.get_query_filters(self.model, False)
|
|
341
334
|
except Folder.DoesNotExist:
|
|
342
335
|
pk = self.kwargs.get("pk")
|
|
343
336
|
self._raise_redirect(
|
|
@@ -348,7 +341,8 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
348
341
|
search_scores = {}
|
|
349
342
|
group = self.kwargs.get("group")
|
|
350
343
|
|
|
351
|
-
group_qs
|
|
344
|
+
group_qs = self._get_group_queryset(object_filter, search_scores)
|
|
345
|
+
book_qs = self._get_book_queryset(object_filter, search_scores)
|
|
352
346
|
|
|
353
347
|
# Paginate
|
|
354
348
|
group_qs, book_qs, num_pages, total_count = self._paginate(group_qs, book_qs)
|
|
@@ -356,6 +350,8 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
356
350
|
# get additional context
|
|
357
351
|
if group == self.FOLDER_GROUP:
|
|
358
352
|
up_group, up_pk = self._get_folder_up_route()
|
|
353
|
+
elif group == self.STORY_ARC_GROUP:
|
|
354
|
+
up_group, up_pk = self._get_story_arc_up_route()
|
|
359
355
|
else:
|
|
360
356
|
up_group, up_pk = self._get_browse_up_route()
|
|
361
357
|
browser_page_title = self._get_browser_page_title()
|
|
@@ -459,9 +455,7 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
459
455
|
|
|
460
456
|
top_group = self.params["top_group"]
|
|
461
457
|
if top_group != self.FOLDER_GROUP:
|
|
462
|
-
|
|
463
|
-
settings_mask = {"top_group": self.FOLDER_GROUP}
|
|
464
|
-
self._raise_redirect({"group": self.FOLDER_GROUP}, reason, settings_mask)
|
|
458
|
+
self.params["top_group"] = self.FOLDER_GROUP
|
|
465
459
|
|
|
466
460
|
# set valid folder nav groups
|
|
467
461
|
self.valid_nav_groups = (self.FOLDER_GROUP,)
|
|
@@ -500,12 +494,25 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
500
494
|
reason = f"Redirect r with {pk=} to pk 0"
|
|
501
495
|
self._raise_redirect({"pk": 0}, reason)
|
|
502
496
|
|
|
497
|
+
def _validate_story_arc_settings(self):
|
|
498
|
+
"""Validate story arc settings."""
|
|
499
|
+
top_group = self.params["top_group"]
|
|
500
|
+
if top_group != self.STORY_ARC_GROUP:
|
|
501
|
+
self.params["top_group"] = self.STORY_ARC_GROUP
|
|
502
|
+
|
|
503
|
+
def _set_route_param(self):
|
|
504
|
+
"""Set the route param."""
|
|
505
|
+
group = self.kwargs.get("group", "r")
|
|
506
|
+
pk = self.kwargs.get("pk", 0)
|
|
507
|
+
page = self.kwargs.get("page", 1)
|
|
508
|
+
self.params["route"] = {"group": group, "pk": pk, "page": page}
|
|
509
|
+
|
|
503
510
|
def validate_settings(self):
|
|
504
511
|
"""Validate group and top group settings."""
|
|
505
512
|
group = self.kwargs.get("group")
|
|
506
|
-
|
|
513
|
+
self.set_order_key()
|
|
507
514
|
enable_folder_view = False
|
|
508
|
-
if group == self.FOLDER_GROUP or order_key == "path":
|
|
515
|
+
if group == self.FOLDER_GROUP or self.order_key == "path":
|
|
509
516
|
key = AdminFlag.FlagChoices.FOLDER_VIEW.value
|
|
510
517
|
try:
|
|
511
518
|
enable_folder_view = AdminFlag.objects.only("on").get(key=key).on
|
|
@@ -515,11 +522,13 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
515
522
|
|
|
516
523
|
if group == self.FOLDER_GROUP:
|
|
517
524
|
self._validate_folder_settings(enable_folder_view)
|
|
525
|
+
elif group == self.STORY_ARC_GROUP:
|
|
526
|
+
self._validate_story_arc_settings()
|
|
518
527
|
else:
|
|
519
528
|
self._validate_browser_group_settings()
|
|
520
529
|
|
|
521
530
|
# Validate path sort
|
|
522
|
-
if order_key == "path" and not enable_folder_view:
|
|
531
|
+
if self.order_key == "path" and not enable_folder_view:
|
|
523
532
|
pk = self.kwargs("pk")
|
|
524
533
|
page = self.kwargs("page")
|
|
525
534
|
route_changes = {"group": group, "pk": pk, "page": page}
|
|
@@ -532,6 +541,7 @@ class BrowserView(BrowserAnnotationsView):
|
|
|
532
541
|
"""Get browser settings."""
|
|
533
542
|
self.parse_params()
|
|
534
543
|
self.validate_settings()
|
|
544
|
+
self._set_route_param()
|
|
535
545
|
data = self.get_object()
|
|
536
546
|
serializer = self.get_serializer(data)
|
|
537
547
|
self.save_params_to_session(self.params)
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"""Base view for metadata annotations."""
|
|
2
|
+
from os.path import sep
|
|
3
|
+
|
|
2
4
|
from django.db.models import (
|
|
3
5
|
BooleanField,
|
|
4
6
|
Case,
|
|
7
|
+
CharField,
|
|
5
8
|
Count,
|
|
6
9
|
DateTimeField,
|
|
7
10
|
F,
|
|
11
|
+
FilteredRelation,
|
|
12
|
+
Min,
|
|
8
13
|
OuterRef,
|
|
9
14
|
Q,
|
|
10
15
|
Subquery,
|
|
@@ -13,10 +18,9 @@ from django.db.models import (
|
|
|
13
18
|
When,
|
|
14
19
|
)
|
|
15
20
|
from django.db.models.fields import PositiveSmallIntegerField
|
|
16
|
-
from django.db.models.functions import Least
|
|
21
|
+
from django.db.models.functions import Least, Lower, Reverse, Right, StrIndex, Substr
|
|
17
22
|
|
|
18
|
-
from codex.models import Comic
|
|
19
|
-
from codex.views.browser.base import BrowserBaseView
|
|
23
|
+
from codex.models import Comic
|
|
20
24
|
from codex.views.browser.browser_order_by import BrowserOrderByView
|
|
21
25
|
|
|
22
26
|
|
|
@@ -24,31 +28,36 @@ class BrowserAnnotationsView(BrowserOrderByView):
|
|
|
24
28
|
"""Base class for views that need special metadata annotations."""
|
|
25
29
|
|
|
26
30
|
_ONE_INTEGERFIELD = Value(1, PositiveSmallIntegerField())
|
|
27
|
-
|
|
28
|
-
GROUP_MODEL_MAP = {
|
|
29
|
-
BrowserBaseView.ROOT_GROUP: None,
|
|
30
|
-
"p": Publisher,
|
|
31
|
-
"i": Imprint,
|
|
32
|
-
"s": Series,
|
|
33
|
-
"v": Volume,
|
|
34
|
-
BrowserBaseView.COMIC_GROUP: Comic,
|
|
35
|
-
BrowserBaseView.FOLDER_GROUP: Folder,
|
|
36
|
-
}
|
|
31
|
+
_NONE_INTEGERFIELD = Value(None, PositiveSmallIntegerField())
|
|
37
32
|
_NONE_DATETIMEFIELD = Value(None, DateTimeField())
|
|
33
|
+
_ARTICLES = frozenset(
|
|
34
|
+
("a", "an", "the") # en # noqa RUF005
|
|
35
|
+
+ ("un", "unos", "unas", "el", "los", "la", "las") # es
|
|
36
|
+
+ ("un", "une", "le", "les", "la", "les", "l'") # fr
|
|
37
|
+
+ ("o", "a", "os") # pt
|
|
38
|
+
# pt "as" conflicts with English
|
|
39
|
+
+ ("der", "dem", "des", "das") # de
|
|
40
|
+
# de: "den & die conflict with English
|
|
41
|
+
+ ("il", "lo", "gli", "la", "le", "l'") # it
|
|
42
|
+
# it: "i" conflicts with English
|
|
43
|
+
+ ("de", "het", "een") # nl
|
|
44
|
+
+ ("en", "ett") # sw
|
|
45
|
+
+ ("en", "ei", "et") # no
|
|
46
|
+
+ ("en", "et") # da
|
|
47
|
+
+ ("el", "la", "els", "les", "un", "una", "uns", "unes", "na") # ct
|
|
48
|
+
)
|
|
38
49
|
|
|
39
50
|
is_opds_1_acquisition = False
|
|
40
51
|
|
|
41
|
-
def _annotate_search_score(self, queryset,
|
|
52
|
+
def _annotate_search_score(self, queryset, search_scores):
|
|
42
53
|
"""Annotate the search score for ordering by search score."""
|
|
43
|
-
order_key
|
|
44
|
-
if order_key != "search_score":
|
|
54
|
+
if self.order_key != "search_score":
|
|
45
55
|
return queryset
|
|
46
|
-
prefix = "comic__" if not is_model_comic else ""
|
|
47
56
|
whens = []
|
|
48
57
|
for pk, score in search_scores.items():
|
|
49
|
-
when = {
|
|
58
|
+
when = {self.rel_prefix + "pk": pk, "then": score}
|
|
50
59
|
whens.append(When(**when))
|
|
51
|
-
annotate = {
|
|
60
|
+
annotate = {self.rel_prefix + "search_score": Case(*whens, default=0.0)}
|
|
52
61
|
return queryset.annotate(**annotate)
|
|
53
62
|
|
|
54
63
|
def _annotate_cover_pk(self, queryset, model):
|
|
@@ -60,28 +69,134 @@ class BrowserAnnotationsView(BrowserOrderByView):
|
|
|
60
69
|
else:
|
|
61
70
|
# This creates two subqueries. It would be better condensed into one.
|
|
62
71
|
# but there's no way to annotate an object or multiple values.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
cover_pk = Subquery(
|
|
72
|
+
cover_qs = queryset.filter(pk=OuterRef("pk"))
|
|
73
|
+
cover_qs = self.add_order_by(cover_qs, model)
|
|
74
|
+
cover_pk = Subquery(cover_qs.values(self.rel_prefix + "pk")[:1])
|
|
66
75
|
return queryset.annotate(cover_pk=cover_pk)
|
|
67
76
|
|
|
68
|
-
def _annotate_page_count(self, qs):
|
|
77
|
+
def _annotate_page_count(self, qs, model):
|
|
69
78
|
"""Hoist up total page_count of children."""
|
|
70
79
|
# Used for sorting and progress
|
|
71
|
-
|
|
80
|
+
if model == Comic:
|
|
81
|
+
return qs
|
|
82
|
+
|
|
83
|
+
page_count_sum = Sum(self.rel_prefix + "page_count", distinct=True)
|
|
72
84
|
return qs.annotate(page_count=page_count_sum)
|
|
73
85
|
|
|
74
|
-
def
|
|
75
|
-
"""
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
def _annotate_child_count(self, qs, model):
|
|
87
|
+
"""Annotate Child Count."""
|
|
88
|
+
self.kwargs.get("group")
|
|
89
|
+
if model == Comic:
|
|
90
|
+
child_count_sum = self._ONE_INTEGERFIELD
|
|
91
|
+
else:
|
|
92
|
+
child_count_sum = Count(self.rel_prefix + "pk", distinct=True)
|
|
93
|
+
qs = qs.annotate(child_count=child_count_sum)
|
|
94
|
+
if model != Comic:
|
|
95
|
+
# XXX Extra filter for empty groups
|
|
96
|
+
qs = qs.filter(child_count__gt=0)
|
|
97
|
+
return qs
|
|
98
|
+
|
|
99
|
+
def _annotate_bookmark_updated_at(self, qs, bm_rel, bm_filter):
|
|
100
|
+
"""Annotate bookmark_updated_at."""
|
|
101
|
+
if not self.is_opds_1_acquisition and self.order_key != "bookmark_updated_at":
|
|
102
|
+
return qs
|
|
103
|
+
|
|
104
|
+
updated_at_rel = f"{bm_rel}__updated_at"
|
|
105
|
+
bookmark_updated_at_aggregate = Min(
|
|
106
|
+
updated_at_rel,
|
|
107
|
+
default=self._NONE_DATETIMEFIELD,
|
|
108
|
+
filter=bm_filter,
|
|
109
|
+
)
|
|
110
|
+
return qs.annotate(bookmark_updated_at=bookmark_updated_at_aggregate)
|
|
111
|
+
|
|
112
|
+
def _annotate_sort_name(self, queryset, model):
|
|
113
|
+
"""Sort groups by name ignoring articles."""
|
|
114
|
+
if self.order_key != "sort_name":
|
|
115
|
+
return queryset
|
|
116
|
+
|
|
117
|
+
if self.kwargs.get("group") == self.FOLDER_GROUP and model == Comic:
|
|
118
|
+
# File View Filename
|
|
119
|
+
queryset = queryset.annotate(
|
|
120
|
+
sort_name=Right(
|
|
121
|
+
"path",
|
|
122
|
+
StrIndex(Reverse(F("path")), Value(sep)) - 1, # type: ignore
|
|
123
|
+
output_field=CharField(),
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
return queryset
|
|
127
|
+
|
|
128
|
+
##################################################
|
|
129
|
+
# Otherwise Remove articles from the browse name #
|
|
130
|
+
##################################################
|
|
131
|
+
|
|
132
|
+
# first_space_index
|
|
133
|
+
first_field = model.ORDERING[0]
|
|
134
|
+
queryset = queryset.annotate(
|
|
135
|
+
first_space_index=StrIndex(first_field, Value(" "))
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# lowercase_first_word
|
|
139
|
+
lowercase_first_word = Lower(
|
|
140
|
+
Substr(first_field, 1, length=(F("first_space_index") - 1)) # type: ignore
|
|
141
|
+
)
|
|
142
|
+
queryset = queryset.annotate(
|
|
143
|
+
lowercase_first_word=Case(
|
|
144
|
+
When(Q(first_space_index__gt=0), then=lowercase_first_word)
|
|
145
|
+
),
|
|
146
|
+
default=Value(""),
|
|
147
|
+
)
|
|
78
148
|
|
|
149
|
+
# sort_name
|
|
150
|
+
return queryset.annotate(
|
|
151
|
+
sort_name=Case(
|
|
152
|
+
When(
|
|
153
|
+
lowercase_first_word__in=self._ARTICLES,
|
|
154
|
+
then=Substr(
|
|
155
|
+
first_field, F("first_space_index") + 1 # type: ignore
|
|
156
|
+
),
|
|
157
|
+
),
|
|
158
|
+
default=first_field,
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _annotate_story_arc_number(self, qs):
|
|
163
|
+
if self.order_key != "story_arc_number":
|
|
164
|
+
return qs
|
|
165
|
+
|
|
166
|
+
# Get story_arc__pk
|
|
167
|
+
group = self.kwargs["group"]
|
|
168
|
+
pk = self.kwargs["pk"]
|
|
169
|
+
if group == self.STORY_ARC_GROUP and pk:
|
|
170
|
+
story_arc_pk = pk
|
|
171
|
+
elif story_arc_pks := self.params.get("filters", {}).get("story_arcs", []):
|
|
172
|
+
story_arc_pk = story_arc_pks[0]
|
|
173
|
+
else:
|
|
174
|
+
story_arc_pk = None
|
|
175
|
+
|
|
176
|
+
# If we have one annotate it.
|
|
177
|
+
if story_arc_pk:
|
|
178
|
+
san_rel = self.rel_prefix + "story_arc_numbers"
|
|
179
|
+
rel = f"{san_rel}"
|
|
180
|
+
condition = Q(**{f"{san_rel}__story_arc": story_arc_pk})
|
|
181
|
+
qs = qs.annotate(
|
|
182
|
+
selected_story_arc_number=FilteredRelation(rel, condition=condition),
|
|
183
|
+
story_arc_number=F("selected_story_arc_number__number"),
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
qs = qs.annotate(story_arc_number=self._NONE_INTEGERFIELD)
|
|
187
|
+
return qs
|
|
188
|
+
|
|
189
|
+
def _annotate_order_value(self, qs, model):
|
|
190
|
+
"""Annotate a main key for sorting."""
|
|
191
|
+
order_func = self.get_order_value(model)
|
|
192
|
+
return qs.annotate(order_value=order_func)
|
|
193
|
+
|
|
194
|
+
def _annotate_bookmarks(self, qs, model, bm_rel, bm_filter):
|
|
195
|
+
"""Hoist up bookmark annoations."""
|
|
79
196
|
page_rel = f"{bm_rel}__page"
|
|
80
197
|
finished_rel = f"{bm_rel}__finished"
|
|
81
|
-
updated_at_rel = f"{bm_rel}__updated_at"
|
|
82
|
-
bookmark_updated_at = None
|
|
83
198
|
|
|
84
|
-
if
|
|
199
|
+
if model == Comic:
|
|
85
200
|
# Hoist up the bookmark and finished states
|
|
86
201
|
bookmark_page = Sum(
|
|
87
202
|
page_rel,
|
|
@@ -95,14 +210,12 @@ class BrowserAnnotationsView(BrowserOrderByView):
|
|
|
95
210
|
filter=bm_filter,
|
|
96
211
|
output_field=BooleanField(),
|
|
97
212
|
)
|
|
98
|
-
if self.is_opds_1_acquisition:
|
|
99
|
-
bookmark_updated_at = F(updated_at_rel)
|
|
100
213
|
else:
|
|
101
214
|
# Aggregate bookmark and finished states
|
|
102
215
|
bookmark_page = Sum(
|
|
103
216
|
Case(
|
|
104
217
|
When(**{bm_rel: None}, then=0),
|
|
105
|
-
When(**{finished_rel: True}, then="
|
|
218
|
+
When(**{finished_rel: True}, then=f"{self.rel_prefix}page_count"),
|
|
106
219
|
default=page_rel,
|
|
107
220
|
output_field=PositiveSmallIntegerField(),
|
|
108
221
|
),
|
|
@@ -130,16 +243,10 @@ class BrowserAnnotationsView(BrowserOrderByView):
|
|
|
130
243
|
output_field=BooleanField(),
|
|
131
244
|
)
|
|
132
245
|
|
|
133
|
-
|
|
246
|
+
return qs.annotate(
|
|
134
247
|
page=bookmark_page,
|
|
135
248
|
finished=finished_aggregate,
|
|
136
249
|
)
|
|
137
|
-
if bookmark_updated_at:
|
|
138
|
-
qs = qs.annotate(
|
|
139
|
-
bookmark_updated_at=bookmark_updated_at,
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
return qs
|
|
143
250
|
|
|
144
251
|
@staticmethod
|
|
145
252
|
def _annotate_progress(queryset):
|
|
@@ -154,15 +261,17 @@ class BrowserAnnotationsView(BrowserOrderByView):
|
|
|
154
261
|
|
|
155
262
|
def annotate_common_aggregates(self, qs, model, search_scores):
|
|
156
263
|
"""Annotate common aggregates between browser and metadata."""
|
|
157
|
-
|
|
158
|
-
qs = self.
|
|
264
|
+
qs = self._annotate_search_score(qs, search_scores)
|
|
265
|
+
qs = self._annotate_child_count(qs, model)
|
|
266
|
+
qs = self._annotate_page_count(qs, model)
|
|
267
|
+
bm_rel = self.get_bm_rel(model)
|
|
268
|
+
bm_filter = self._get_my_bookmark_filter(bm_rel)
|
|
269
|
+
qs = self._annotate_bookmark_updated_at(qs, bm_rel, bm_filter)
|
|
270
|
+
qs = self._annotate_sort_name(qs, model)
|
|
271
|
+
qs = self._annotate_story_arc_number(qs)
|
|
272
|
+
qs = self._annotate_order_value(qs, model)
|
|
273
|
+
# cover depends on the above annotations for order-by
|
|
159
274
|
qs = self._annotate_cover_pk(qs, model)
|
|
160
|
-
|
|
161
|
-
child_count_sum = self._ONE_INTEGERFIELD
|
|
162
|
-
else:
|
|
163
|
-
qs = self._annotate_page_count(qs)
|
|
164
|
-
child_count_sum = Count("comic__pk", distinct=True)
|
|
165
|
-
qs = qs.annotate(child_count=child_count_sum)
|
|
166
|
-
qs = self._annotate_bookmarks(qs, is_model_comic)
|
|
275
|
+
qs = self._annotate_bookmarks(qs, model, bm_rel, bm_filter)
|
|
167
276
|
qs = self._annotate_progress(qs)
|
|
168
277
|
return qs
|