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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Base view for ordering the query."""
|
|
2
2
|
from os import sep
|
|
3
3
|
|
|
4
|
-
from django.db.models import Avg,
|
|
5
|
-
from django.db.models.functions import
|
|
4
|
+
from django.db.models import Avg, F, Max, Min, Sum, Value
|
|
5
|
+
from django.db.models.functions import Reverse, Right, StrIndex
|
|
6
6
|
|
|
7
|
-
from codex.models import Comic, Folder
|
|
7
|
+
from codex.models import Comic, Folder, StoryArc
|
|
8
8
|
from codex.views.browser.base import BrowserBaseView
|
|
9
9
|
|
|
10
10
|
|
|
@@ -22,33 +22,18 @@ class BrowserOrderByView(BrowserBaseView):
|
|
|
22
22
|
"size": Sum,
|
|
23
23
|
"updated_at": Min,
|
|
24
24
|
"search_score": Min,
|
|
25
|
+
"story_arc_number": Min,
|
|
25
26
|
}
|
|
26
27
|
_SEP_VALUE = Value(sep)
|
|
27
|
-
|
|
28
|
-
("a", "an", "the") # en
|
|
29
|
-
+ ("un", "unos", "unas", "el", "los", "la", "las") # es
|
|
30
|
-
+ ("un", "une", "le", "les", "la", "les", "l'") # fr
|
|
31
|
-
+ ("o", "a", "os") # pt
|
|
32
|
-
# pt "as" conflicts with English
|
|
33
|
-
+ ("der", "dem", "des", "das") # de
|
|
34
|
-
# de: "den & die conflict with English
|
|
35
|
-
+ ("il", "lo", "gli", "la", "le", "l'") # it
|
|
36
|
-
# it: "i" conflicts with English
|
|
37
|
-
+ ("de", "het", "een") # nl
|
|
38
|
-
+ ("en", "ett") # sw
|
|
39
|
-
+ ("en", "ei", "et") # no
|
|
40
|
-
+ ("en", "et") # da
|
|
41
|
-
+ ("el", "la", "els", "les", "un", "una", "uns", "unes", "na") # ct
|
|
42
|
-
)
|
|
43
|
-
NONE_CHARFIELD = Value(None, CharField())
|
|
28
|
+
_ANNOTATED_ORDER_FIELDS = frozenset(("sort_name", "bookmark_updated_at"))
|
|
44
29
|
|
|
45
|
-
def
|
|
30
|
+
def set_order_key(self):
|
|
46
31
|
"""Get the default order key for the view."""
|
|
47
32
|
order_key = self.params.get("order_by")
|
|
48
33
|
if not order_key:
|
|
49
34
|
group = self.kwargs.get("group")
|
|
50
35
|
order_key = "path" if group == self.FOLDER_GROUP else "sort_name"
|
|
51
|
-
|
|
36
|
+
self.order_key = order_key
|
|
52
37
|
|
|
53
38
|
@classmethod
|
|
54
39
|
def _get_path_query_func(cls, field):
|
|
@@ -57,95 +42,56 @@ class BrowserOrderByView(BrowserBaseView):
|
|
|
57
42
|
field, StrIndex(Reverse(field), cls._SEP_VALUE) - 1 # type: ignore
|
|
58
43
|
)
|
|
59
44
|
|
|
60
|
-
def get_aggregate_func(self,
|
|
61
|
-
"""
|
|
62
|
-
|
|
45
|
+
def get_aggregate_func(self, model, field):
|
|
46
|
+
"""Order by aggregate."""
|
|
47
|
+
# get agg_func
|
|
48
|
+
agg_func = self._ORDER_AGGREGATE_FUNCS[field]
|
|
49
|
+
if agg_func == Min and self.params.get("order_reverse"):
|
|
50
|
+
agg_func = Max
|
|
51
|
+
|
|
52
|
+
# get full_field
|
|
53
|
+
self.kwargs.get("group")
|
|
54
|
+
if model == StoryArc and field == "story_arc_number":
|
|
55
|
+
full_field = "storyarcnumber__number"
|
|
56
|
+
else:
|
|
57
|
+
if self.order_key == "story_arc_number":
|
|
58
|
+
field = "story_arc_numbers__number"
|
|
59
|
+
full_field = self.rel_prefix + field
|
|
60
|
+
if field == "path":
|
|
61
|
+
full_field = self._get_path_query_func(full_field)
|
|
62
|
+
|
|
63
|
+
return agg_func(full_field)
|
|
63
64
|
|
|
65
|
+
def get_order_value(self, model):
|
|
66
|
+
"""Get a complete function for aggregating an attribute."""
|
|
64
67
|
# Determine order func
|
|
65
|
-
if
|
|
66
|
-
# use default sorting.
|
|
67
|
-
func = self.NONE_CHARFIELD
|
|
68
|
-
elif field == "path" and model in (Comic, Folder):
|
|
68
|
+
if self.order_key == "path" and model in (Comic, Folder):
|
|
69
69
|
# special path sorting.
|
|
70
|
-
func = self._get_path_query_func(
|
|
71
|
-
elif model == Comic:
|
|
70
|
+
func = self._get_path_query_func(self.order_key)
|
|
71
|
+
elif model == Comic or self.order_key in self._ANNOTATED_ORDER_FIELDS:
|
|
72
72
|
# agg_none uses group fields not comic fields.
|
|
73
|
-
func = F(
|
|
73
|
+
func = F(self.order_key)
|
|
74
74
|
else:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
# get agg_func
|
|
78
|
-
agg_func = self._ORDER_AGGREGATE_FUNCS[field]
|
|
79
|
-
if agg_func == Min and self.params.get("order_reverse"):
|
|
80
|
-
agg_func = Max
|
|
81
|
-
|
|
82
|
-
# get full_field
|
|
83
|
-
full_field = "comic__" + field
|
|
84
|
-
if field == "path":
|
|
85
|
-
full_field = self._get_path_query_func(full_field)
|
|
86
|
-
|
|
87
|
-
func = agg_func(full_field)
|
|
75
|
+
func = self.get_aggregate_func(model, self.order_key)
|
|
88
76
|
return func
|
|
89
77
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
lowercase_first_word=Case(
|
|
103
|
-
When(Q(first_space_index__gt=0), then=lowercase_first_word)
|
|
104
|
-
),
|
|
105
|
-
default=Value(""),
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
queryset = queryset.annotate(
|
|
109
|
-
sort_name=Case(
|
|
110
|
-
When(
|
|
111
|
-
lowercase_first_word__in=cls._ARTICLES,
|
|
112
|
-
then=Substr(
|
|
113
|
-
first_field, F("first_space_index") + 1 # type: ignore
|
|
114
|
-
),
|
|
115
|
-
),
|
|
116
|
-
default=first_field,
|
|
117
|
-
)
|
|
118
|
-
)
|
|
119
|
-
ordering = ("sort_name", *ordering[1:])
|
|
120
|
-
return queryset, ordering
|
|
121
|
-
|
|
122
|
-
def get_order_by(self, model, queryset, for_cover_pk=False):
|
|
123
|
-
"""Create the order_by list.
|
|
124
|
-
|
|
125
|
-
Order on pk to give duplicates a consistent position.
|
|
126
|
-
"""
|
|
127
|
-
# order_prefix
|
|
128
|
-
prefix = "-" if self.params.get("order_reverse") else ""
|
|
129
|
-
|
|
130
|
-
# order_fields
|
|
131
|
-
order_key = self.get_order_key()
|
|
132
|
-
if for_cover_pk:
|
|
133
|
-
prefix += "comic__"
|
|
134
|
-
ordering = []
|
|
135
|
-
if order_key and order_key != "sort_name":
|
|
136
|
-
ordering += [order_key]
|
|
137
|
-
ordering += [*Comic.ORDERING]
|
|
138
|
-
elif order_key == "sort_name" or not order_key:
|
|
139
|
-
ordering = model.ORDERING
|
|
140
|
-
group = self.kwargs.get("group")
|
|
141
|
-
if group != self.FOLDER_GROUP:
|
|
142
|
-
# Can't annotate after union
|
|
143
|
-
queryset, ordering = self._order_without_articles(queryset, ordering)
|
|
78
|
+
def add_order_by(self, queryset, model):
|
|
79
|
+
"""Create the order_by list."""
|
|
80
|
+
prefix = ""
|
|
81
|
+
if self.params.get("order_reverse"):
|
|
82
|
+
prefix += "-"
|
|
83
|
+
|
|
84
|
+
if self.order_key == "sort_name":
|
|
85
|
+
order_fields = ("order_value", *model.ORDERING[1:])
|
|
86
|
+
elif self.order_key == "bookmark_updated_at":
|
|
87
|
+
order_fields = ("order_value", "updated_at", "created_at", "pk")
|
|
88
|
+
elif self.order_key == "story_arc_number" and model == Comic:
|
|
89
|
+
order_fields = ("order_value", "date", *model.ORDERING)
|
|
144
90
|
else:
|
|
145
|
-
|
|
146
|
-
|
|
91
|
+
order_fields = ("order_value", *model.ORDERING)
|
|
92
|
+
|
|
93
|
+
order_by = []
|
|
94
|
+
for field in order_fields:
|
|
95
|
+
order_by.append(prefix + field)
|
|
147
96
|
|
|
148
|
-
|
|
149
|
-
# add prefixes to all order_by fields
|
|
150
|
-
ordering = (prefix + field for field in ordering)
|
|
151
|
-
return queryset.order_by(*ordering)
|
|
97
|
+
return queryset.order_by(*order_by)
|
codex/views/browser/choices.py
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
"""View for marking comics read and unread."""
|
|
2
2
|
import pycountry
|
|
3
3
|
from caseconverter import snakecase
|
|
4
|
+
from django.db.models import QuerySet
|
|
4
5
|
from drf_spectacular.utils import extend_schema
|
|
5
6
|
from rest_framework.response import Response
|
|
6
7
|
|
|
7
8
|
from codex.logger.logging import get_logger
|
|
8
|
-
from codex.models import
|
|
9
|
+
from codex.models import (
|
|
10
|
+
Comic,
|
|
11
|
+
CreatorPerson,
|
|
12
|
+
Folder,
|
|
13
|
+
Imprint,
|
|
14
|
+
Publisher,
|
|
15
|
+
Series,
|
|
16
|
+
StoryArc,
|
|
17
|
+
Volume,
|
|
18
|
+
)
|
|
9
19
|
from codex.serializers.browser import (
|
|
10
20
|
BrowserChoicesSerializer,
|
|
11
21
|
BrowserFilterChoicesSerializer,
|
|
@@ -22,23 +32,37 @@ class BrowserChoicesViewBase(BrowserBaseView):
|
|
|
22
32
|
|
|
23
33
|
permission_classes = [IsAuthenticatedOrEnabledNonUsers]
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
_CREATORS_PERSON_REL = "creators__person"
|
|
36
|
+
_STORY_ARC_REL = "story_arc_numbers__story_arc"
|
|
37
|
+
_NULL_NAMED_ROW = {"pk": -1, "name": "_none_"}
|
|
38
|
+
_BACK_REL_MAP = {CreatorPerson: "creator__", StoryArc: "storyarcnumber__"}
|
|
39
|
+
_REL_MAP = {
|
|
40
|
+
Publisher: "publisher",
|
|
41
|
+
Imprint: "imprint",
|
|
42
|
+
Series: "series",
|
|
43
|
+
Volume: "volume",
|
|
44
|
+
Comic: "pk",
|
|
45
|
+
Folder: "parent_folder",
|
|
46
|
+
StoryArc: "story_arc_numbers__story_arc",
|
|
47
|
+
}
|
|
27
48
|
|
|
28
49
|
@staticmethod
|
|
29
|
-
def get_field_choices_query(
|
|
50
|
+
def get_field_choices_query(comic_qs, field_name):
|
|
30
51
|
"""Get distinct values for the field."""
|
|
31
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
comic_qs.exclude(**{field_name: None})
|
|
54
|
+
.values_list(field_name, flat=True)
|
|
55
|
+
.distinct()
|
|
56
|
+
)
|
|
32
57
|
|
|
33
|
-
|
|
34
|
-
def get_m2m_field_query(cls, rel, comic_qs, model):
|
|
58
|
+
def get_m2m_field_query(self, model, comic_qs: QuerySet):
|
|
35
59
|
"""Get distinct m2m value objects for the relation."""
|
|
36
|
-
|
|
60
|
+
back_rel = self._BACK_REL_MAP.get(model, "")
|
|
61
|
+
back_rel += "comic__"
|
|
62
|
+
back_rel += self._REL_MAP[self.model]
|
|
63
|
+
back_rel += "__in"
|
|
37
64
|
return (
|
|
38
|
-
model.objects.filter(**{
|
|
39
|
-
.prefetch_related(comic_rel)
|
|
40
|
-
.values("pk", "name")
|
|
41
|
-
.distinct()
|
|
65
|
+
model.objects.filter(**{back_rel: comic_qs}).values("pk", "name").distinct()
|
|
42
66
|
)
|
|
43
67
|
|
|
44
68
|
@staticmethod
|
|
@@ -46,12 +70,14 @@ class BrowserChoicesViewBase(BrowserBaseView):
|
|
|
46
70
|
"""Get if null values exists for an m2m field."""
|
|
47
71
|
return comic_qs.filter(**{f"{rel}__isnull": True}).exists()
|
|
48
72
|
|
|
49
|
-
|
|
50
|
-
def _get_rel_and_model(cls, field_name):
|
|
73
|
+
def _get_rel_and_model(self, field_name):
|
|
51
74
|
"""Return the relation and model for the field name."""
|
|
52
|
-
if field_name ==
|
|
53
|
-
rel =
|
|
75
|
+
if field_name == self.CREATOR_PERSON_UI_FIELD:
|
|
76
|
+
rel = self._CREATORS_PERSON_REL
|
|
54
77
|
model = CreatorPerson
|
|
78
|
+
elif field_name == self.STORY_ARC_UI_FIELD:
|
|
79
|
+
rel = self._STORY_ARC_REL
|
|
80
|
+
model = StoryArc
|
|
55
81
|
else:
|
|
56
82
|
remote_field = getattr(
|
|
57
83
|
Comic._meta.get_field(field_name), "remote_field", None
|
|
@@ -59,12 +85,21 @@ class BrowserChoicesViewBase(BrowserBaseView):
|
|
|
59
85
|
rel = field_name
|
|
60
86
|
model = remote_field.model if remote_field else None
|
|
61
87
|
|
|
88
|
+
rel = self.rel_prefix + rel
|
|
89
|
+
|
|
62
90
|
return rel, model
|
|
63
91
|
|
|
64
92
|
def get_object(self):
|
|
65
93
|
"""Get the comic subquery use for the choices."""
|
|
66
|
-
object_filter, _ = self.get_query_filters(
|
|
67
|
-
return
|
|
94
|
+
object_filter, _ = self.get_query_filters(self.model, True)
|
|
95
|
+
return self.model.objects.filter(object_filter)
|
|
96
|
+
|
|
97
|
+
def _set_model(self):
|
|
98
|
+
"""Set the model to query."""
|
|
99
|
+
group = self.kwargs["group"]
|
|
100
|
+
if group == self.ROOT_GROUP:
|
|
101
|
+
group = self.params.get("top_group", "p")
|
|
102
|
+
self.model = self.GROUP_MODEL_MAP[group]
|
|
68
103
|
|
|
69
104
|
|
|
70
105
|
class BrowserChoicesAvailableView(BrowserChoicesViewBase):
|
|
@@ -72,23 +107,20 @@ class BrowserChoicesAvailableView(BrowserChoicesViewBase):
|
|
|
72
107
|
|
|
73
108
|
serializer_class = BrowserFilterChoicesSerializer
|
|
74
109
|
|
|
75
|
-
CREATORS_PERSON_REL = "creators__person"
|
|
76
|
-
|
|
77
110
|
@classmethod
|
|
78
|
-
def _get_field_choices_count(cls,
|
|
111
|
+
def _get_field_choices_count(cls, comic_qs, field_name):
|
|
79
112
|
"""Create a pk:name object for fields without tables."""
|
|
80
|
-
return cls.get_field_choices_query(
|
|
113
|
+
return cls.get_field_choices_query(comic_qs, field_name).count()
|
|
81
114
|
|
|
82
|
-
|
|
83
|
-
def _get_m2m_field_choices_count(cls, rel, comic_qs, model):
|
|
115
|
+
def _get_m2m_field_choices_count(self, model, comic_qs, rel):
|
|
84
116
|
"""Get choices with nulls where there are nulls."""
|
|
85
|
-
count =
|
|
117
|
+
count = self.get_m2m_field_query(model, comic_qs).count()
|
|
86
118
|
|
|
87
119
|
# Detect if there are null choices.
|
|
88
120
|
# Regretabbly with another query, but doing a forward query
|
|
89
121
|
# on the comic above restricts all results to only the filtered
|
|
90
122
|
# rows. :(
|
|
91
|
-
if
|
|
123
|
+
if self.does_m2m_null_exist(comic_qs, rel):
|
|
92
124
|
count += 1
|
|
93
125
|
|
|
94
126
|
return count
|
|
@@ -97,16 +129,21 @@ class BrowserChoicesAvailableView(BrowserChoicesViewBase):
|
|
|
97
129
|
def get(self, *args, **kwargs):
|
|
98
130
|
"""Return all choices with more than one choice."""
|
|
99
131
|
self.parse_params()
|
|
132
|
+
self._set_model()
|
|
133
|
+
self.set_rel_prefix(self.model)
|
|
100
134
|
comic_qs = self.get_object()
|
|
101
135
|
|
|
102
136
|
data = {}
|
|
103
137
|
for field_name in self.serializer_class().get_fields(): # type: ignore
|
|
138
|
+
if field_name == "story_arcs" and self.model == StoryArc:
|
|
139
|
+
# don't allow filtering on story arc in story arc view.
|
|
140
|
+
continue
|
|
104
141
|
rel, m2m_model = self._get_rel_and_model(field_name)
|
|
105
142
|
|
|
106
143
|
if m2m_model:
|
|
107
|
-
count = self._get_m2m_field_choices_count(
|
|
144
|
+
count = self._get_m2m_field_choices_count(m2m_model, comic_qs, rel)
|
|
108
145
|
else:
|
|
109
|
-
count = self._get_field_choices_count(
|
|
146
|
+
count = self._get_field_choices_count(comic_qs, rel)
|
|
110
147
|
|
|
111
148
|
filters = self.params.get("filters", {})
|
|
112
149
|
data[field_name] = count > 1 or field_name in filters
|
|
@@ -120,10 +157,9 @@ class BrowserChoicesView(BrowserChoicesViewBase):
|
|
|
120
157
|
|
|
121
158
|
serializer_class = BrowserChoicesSerializer
|
|
122
159
|
|
|
123
|
-
|
|
124
|
-
def _get_field_choices(cls, field_name, comic_qs):
|
|
160
|
+
def _get_field_choices(self, comic_qs, field_name):
|
|
125
161
|
"""Create a pk:name object for fields without tables."""
|
|
126
|
-
qs =
|
|
162
|
+
qs = self.get_field_choices_query(comic_qs, field_name)
|
|
127
163
|
|
|
128
164
|
if field_name == "country":
|
|
129
165
|
lookup = pycountry.countries
|
|
@@ -139,18 +175,17 @@ class BrowserChoicesView(BrowserChoicesViewBase):
|
|
|
139
175
|
|
|
140
176
|
return choices
|
|
141
177
|
|
|
142
|
-
|
|
143
|
-
def _get_m2m_field_choices(cls, rel, comic_qs, model):
|
|
178
|
+
def _get_m2m_field_choices(self, model, comic_qs, rel):
|
|
144
179
|
"""Get choices with nulls where there are nulls."""
|
|
145
|
-
qs =
|
|
180
|
+
qs = self.get_m2m_field_query(model, comic_qs)
|
|
146
181
|
|
|
147
182
|
# Detect if there are null choices.
|
|
148
183
|
# Regretabbly with another query, but doing a forward query
|
|
149
184
|
# on the comic above restrcts all results to only the filtered
|
|
150
185
|
# rows. :(
|
|
151
|
-
if
|
|
186
|
+
if self.does_m2m_null_exist(comic_qs, rel):
|
|
152
187
|
choices = list(qs)
|
|
153
|
-
choices.append(
|
|
188
|
+
choices.append(self._NULL_NAMED_ROW)
|
|
154
189
|
else:
|
|
155
190
|
choices = qs
|
|
156
191
|
return choices
|
|
@@ -159,6 +194,8 @@ class BrowserChoicesView(BrowserChoicesViewBase):
|
|
|
159
194
|
def get(self, *args, **kwargs):
|
|
160
195
|
"""Return all choices with more than one choice."""
|
|
161
196
|
self.parse_params()
|
|
197
|
+
self._set_model()
|
|
198
|
+
self.set_rel_prefix(self.model)
|
|
162
199
|
|
|
163
200
|
field_name = snakecase(self.kwargs["field_name"])
|
|
164
201
|
|
|
@@ -166,9 +203,9 @@ class BrowserChoicesView(BrowserChoicesViewBase):
|
|
|
166
203
|
|
|
167
204
|
comic_qs = self.get_object()
|
|
168
205
|
if m2m_model:
|
|
169
|
-
choices = self._get_m2m_field_choices(
|
|
206
|
+
choices = self._get_m2m_field_choices(m2m_model, comic_qs, rel)
|
|
170
207
|
else:
|
|
171
|
-
choices = self._get_field_choices(
|
|
208
|
+
choices = self._get_field_choices(comic_qs, rel)
|
|
172
209
|
|
|
173
210
|
serializer = self.get_serializer(choices, many=True)
|
|
174
211
|
return Response(serializer.data)
|
|
@@ -9,12 +9,10 @@ class BookmarkFilterMixin:
|
|
|
9
9
|
|
|
10
10
|
_BOOKMARK_FILTERS = frozenset(set(CHOICES["bookmarkFilter"].keys()) - {"ALL"})
|
|
11
11
|
|
|
12
|
-
def get_bm_rel(self,
|
|
12
|
+
def get_bm_rel(self, model):
|
|
13
13
|
"""Create bookmark relation."""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
bm_rel = "comic__" + bm_rel
|
|
17
|
-
return bm_rel
|
|
14
|
+
rel_prefix = self.get_rel_prefix(model) # type: ignore
|
|
15
|
+
return rel_prefix + "bookmark"
|
|
18
16
|
|
|
19
17
|
def _get_my_bookmark_filter(self, bm_rel):
|
|
20
18
|
"""Get a filter for my session or user defined bookmarks."""
|
|
@@ -27,12 +25,11 @@ class BookmarkFilterMixin:
|
|
|
27
25
|
}
|
|
28
26
|
return Q(**my_bookmarks_kwargs)
|
|
29
27
|
|
|
30
|
-
def get_bookmark_filter(self,
|
|
28
|
+
def get_bookmark_filter(self, model):
|
|
31
29
|
"""Build bookmark query."""
|
|
32
|
-
|
|
33
|
-
choice = self.params["filters"].get("bookmark", "ALL") # type: ignore
|
|
30
|
+
choice = self.params["filters"].get("bookmark", "ALL") # type: ignore
|
|
34
31
|
if choice in self._BOOKMARK_FILTERS:
|
|
35
|
-
bm_rel = self.get_bm_rel(
|
|
32
|
+
bm_rel = self.get_bm_rel(model)
|
|
36
33
|
my_bookmark_filter = self._get_my_bookmark_filter(bm_rel)
|
|
37
34
|
if choice in ("UNREAD", "IN_PROGRESS"):
|
|
38
35
|
my_not_finished_filter = my_bookmark_filter & Q(
|
|
@@ -7,18 +7,21 @@ from codex.views.session import BrowserSessionViewBase
|
|
|
7
7
|
class ComicFieldFilter(BrowserSessionViewBase):
|
|
8
8
|
"""Comic field filters."""
|
|
9
9
|
|
|
10
|
-
def _filter_by_comic_field(self, field
|
|
10
|
+
def _filter_by_comic_field(self, field):
|
|
11
11
|
"""Filter by a comic any2many attribute."""
|
|
12
12
|
filter_list = self.params["filters"].get(field) # type: ignore
|
|
13
13
|
filter_query = Q()
|
|
14
14
|
if not filter_list:
|
|
15
15
|
return filter_query
|
|
16
|
-
query_prefix = "" if is_model_comic else "comic__"
|
|
17
16
|
|
|
18
17
|
if field == self.CREATOR_PERSON_UI_FIELD:
|
|
19
|
-
rel =
|
|
18
|
+
rel = "creators__person"
|
|
19
|
+
elif field == self.STORY_ARC_UI_FIELD:
|
|
20
|
+
rel = "storyarcnumber__story_arc"
|
|
20
21
|
else:
|
|
21
|
-
rel =
|
|
22
|
+
rel = field
|
|
23
|
+
|
|
24
|
+
rel = self.rel_prefix + rel # type: ignore
|
|
22
25
|
|
|
23
26
|
for index, val in enumerate(filter_list):
|
|
24
27
|
# None values in a list don't work right so test for them separately
|
|
@@ -29,9 +32,9 @@ class ComicFieldFilter(BrowserSessionViewBase):
|
|
|
29
32
|
filter_query |= Q(**{f"{rel}__in": filter_list})
|
|
30
33
|
return filter_query
|
|
31
34
|
|
|
32
|
-
def get_comic_field_filter(self
|
|
35
|
+
def get_comic_field_filter(self):
|
|
33
36
|
"""Filter the comics based on the form filters."""
|
|
34
37
|
comic_field_filter = Q()
|
|
35
38
|
for attribute in self.FILTER_ATTRIBUTES:
|
|
36
|
-
comic_field_filter &= self._filter_by_comic_field(attribute
|
|
39
|
+
comic_field_filter &= self._filter_by_comic_field(attribute)
|
|
37
40
|
return comic_field_filter
|
|
@@ -7,35 +7,20 @@ from codex.views.mixins import GroupACLMixin
|
|
|
7
7
|
class GroupFilterMixin(GroupACLMixin):
|
|
8
8
|
"""Group Filters."""
|
|
9
9
|
|
|
10
|
-
def _get_folders_filter(self):
|
|
11
|
-
"""Get a filter for ALL parent folders not just immediate one."""
|
|
12
|
-
pk = self.kwargs.get("pk") # type: ignore
|
|
13
|
-
return Q(folders__in=[pk]) if pk else Q()
|
|
14
|
-
|
|
15
|
-
def _get_browser_group_filter(self):
|
|
16
|
-
"""Get the objects we'll be displaying."""
|
|
17
|
-
# Get the instances that are children of the group_instance
|
|
18
|
-
# And the filtered comics that are children of the group_instance
|
|
19
|
-
group_filter = Q()
|
|
20
|
-
pk = self.kwargs.get("pk") # type: ignore
|
|
21
|
-
group = self.kwargs.get("group") # type: ignore
|
|
22
|
-
if pk or group == self.FOLDER_GROUP:
|
|
23
|
-
if not pk:
|
|
24
|
-
pk = None
|
|
25
|
-
group_relation = self.GROUP_RELATION[group]
|
|
26
|
-
group_filter |= Q(**{group_relation: pk})
|
|
27
|
-
|
|
28
|
-
return group_filter
|
|
29
|
-
|
|
30
10
|
def get_group_filter(self, choices):
|
|
31
11
|
"""Get filter for the displayed group."""
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
12
|
+
pk = self.kwargs.get("pk") # type: ignore
|
|
13
|
+
group = self.kwargs.get("group") # type: ignore
|
|
14
|
+
if pk:
|
|
15
|
+
group_relation = "comic__" if choices else ""
|
|
16
|
+
if choices and group == self.FOLDER_GROUP:
|
|
17
|
+
group_relation += "folders"
|
|
18
|
+
else:
|
|
19
|
+
group_relation += self.GROUP_RELATION[group]
|
|
20
|
+
group_filter = Q(**{group_relation: pk})
|
|
21
|
+
elif group == self.FOLDER_GROUP:
|
|
22
|
+
group_filter = Q(parent_folder=None)
|
|
37
23
|
else:
|
|
38
|
-
|
|
39
|
-
group_filter = self._get_browser_group_filter()
|
|
24
|
+
group_filter = Q()
|
|
40
25
|
|
|
41
26
|
return group_filter
|
|
@@ -23,19 +23,14 @@ class SearchFilterMixin:
|
|
|
23
23
|
LOG.warning("While searching:")
|
|
24
24
|
LOG.exception(exc)
|
|
25
25
|
|
|
26
|
-
def _get_search_query_filter(self, text,
|
|
26
|
+
def _get_search_query_filter(self, text, search_scores):
|
|
27
27
|
"""Get the search filter and scores."""
|
|
28
|
-
#
|
|
28
|
+
rel = self.rel_prefix + "pk__in" # type: ignore
|
|
29
29
|
self._get_search_scores(text, search_scores)
|
|
30
|
-
|
|
31
|
-
# Create query
|
|
32
|
-
prefix = ""
|
|
33
|
-
if not is_model_comic:
|
|
34
|
-
prefix = "comic__"
|
|
35
|
-
query_dict = {f"{prefix}pk__in": search_scores.keys()}
|
|
30
|
+
query_dict = {rel: search_scores.keys()}
|
|
36
31
|
return Q(**query_dict)
|
|
37
32
|
|
|
38
|
-
def get_search_filter(self
|
|
33
|
+
def get_search_filter(self):
|
|
39
34
|
"""Preparse search, search and return the filter and scores."""
|
|
40
35
|
search_filter = Q()
|
|
41
36
|
search_scores = {}
|
|
@@ -48,7 +43,7 @@ class SearchFilterMixin:
|
|
|
48
43
|
if query_string:
|
|
49
44
|
# Query haystack
|
|
50
45
|
search_filter = self._get_search_query_filter(
|
|
51
|
-
query_string,
|
|
46
|
+
query_string, search_scores
|
|
52
47
|
)
|
|
53
48
|
except Exception as exc:
|
|
54
49
|
LOG.warning(exc)
|