codex 1.6.0__py3-none-any.whl → 1.6.0a0__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/exceptions.py +18 -22
- codex/integrity.py +3 -66
- codex/librarian/covers/coverd.py +2 -2
- codex/librarian/covers/create.py +29 -50
- codex/librarian/covers/path.py +6 -8
- codex/librarian/covers/purge.py +18 -28
- codex/librarian/covers/tasks.py +2 -3
- codex/librarian/importer/{aggregate.py → aggregate_metadata.py} +88 -58
- codex/librarian/importer/{extract.py → clean_metadata.py} +4 -33
- codex/librarian/importer/const.py +13 -46
- codex/librarian/importer/create_comics.py +17 -101
- codex/librarian/importer/create_fks.py +134 -99
- codex/librarian/importer/deleted.py +28 -103
- codex/librarian/importer/failed_imports.py +64 -42
- codex/librarian/importer/importerd.py +290 -71
- codex/librarian/importer/link_comics.py +26 -26
- codex/librarian/importer/moved.py +81 -128
- codex/librarian/importer/query_fks.py +122 -142
- codex/librarian/importer/status.py +39 -8
- codex/librarian/importer/tasks.py +9 -26
- codex/librarian/importer/update_comics.py +89 -0
- codex/librarian/janitor/cleanup.py +2 -26
- codex/librarian/janitor/failed_imports.py +1 -1
- codex/librarian/janitor/janitor.py +1 -13
- codex/librarian/janitor/janitord.py +1 -1
- codex/librarian/janitor/status.py +0 -1
- codex/librarian/janitor/tasks.py +0 -5
- codex/librarian/librariand.py +5 -9
- codex/librarian/notifier/notifierd.py +1 -1
- codex/librarian/notifier/tasks.py +4 -4
- codex/librarian/search/update.py +1 -1
- codex/librarian/watchdog/db_snapshot.py +6 -9
- codex/librarian/watchdog/emitter.py +0 -3
- codex/librarian/watchdog/event_batcherd.py +3 -19
- codex/librarian/watchdog/events.py +36 -176
- codex/librarian/watchdog/observers.py +3 -13
- codex/middleware.py +2 -1
- codex/migrations/0027_sort_name.py +149 -0
- codex/models/admin.py +2 -0
- codex/models/base.py +0 -3
- codex/models/comic.py +9 -13
- codex/models/{util.py → const.py} +2 -15
- codex/models/groups.py +14 -28
- codex/models/library.py +3 -11
- codex/models/paths.py +14 -61
- codex/permissions.py +1 -1
- codex/search/backend_search.py +8 -5
- codex/search/query.py +2 -25
- codex/search/writing.py +1 -1
- codex/serializers/admin.py +44 -53
- codex/serializers/browser/choices.py +42 -37
- codex/serializers/browser/filters.py +131 -32
- codex/serializers/browser/metadata.py +1 -45
- codex/serializers/browser/mixins.py +4 -37
- codex/serializers/browser/page.py +6 -3
- codex/serializers/browser/settings.py +11 -42
- codex/serializers/choices.py +1 -14
- codex/serializers/models/pycountry.py +51 -11
- codex/serializers/opds/v2.py +16 -10
- codex/serializers/opds/v2_unused.py +3 -5
- codex/serializers/reader.py +7 -21
- codex/serializers/redirect.py +2 -3
- codex/serializers/route.py +7 -16
- codex/settings/settings.py +8 -23
- codex/startup.py +3 -77
- codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js +1 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.br +0 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.gz +0 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.js +1 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.js.br +0 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js → VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.js → VCheckboxBtn-D3izCBa7.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.gz +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js +1 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.br +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.gz +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.js +1 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.js.br +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.js.gz +0 -0
- codex/static_root/assets/{VDialog-lQ13FnRR.1c801ad756b2.css → VDialog-C5Nxd_zP.css} +1 -1
- codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
- codex/static_root/assets/{VDialog-lQ13FnRR.css → VDialog-C5Nxd_zP.d327f505ef0e.css} +1 -1
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js +1 -0
- codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.br +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.gz +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.js +1 -0
- codex/static_root/assets/VDialog-yDKzaGEj.js.br +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js +1 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js +1 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js +1 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.js +1 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.js.gz +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js +1 -0
- codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.br +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.gz +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.js +1 -0
- codex/static_root/assets/VSelect-BKXcujPy.js.br +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js +1 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.js +1 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js +1 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.js +1 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.f0f2106b158b.js → VTable-9vsIQObs.0a135029e924.js} +1 -1
- codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.br +0 -0
- codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.js → VTable-9vsIQObs.js} +1 -1
- codex/static_root/assets/VTable-9vsIQObs.js.br +0 -0
- codex/static_root/assets/VTable-9vsIQObs.js.gz +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js +1 -0
- codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.br +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.gz +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.js +1 -0
- codex/static_root/assets/VTextField-DI9HAmWh.js.br +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.js.gz +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +1 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css +1 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js +1 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.br +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.gz +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.js +1 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.js.br +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.js.gz +0 -0
- codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js +1 -0
- codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.br +0 -0
- codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.gz +0 -0
- codex/static_root/assets/admin-2BTkfSQA.js +1 -0
- codex/static_root/assets/admin-2BTkfSQA.js.br +0 -0
- codex/static_root/assets/admin-2BTkfSQA.js.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-BbrOU9Y9.29936748af9e.css} +1 -1
- codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
- codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.css → admin-BbrOU9Y9.css} +1 -1
- codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
- codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +1 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +1 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js +30 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.js +30 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.gz +0 -0
- codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js +1 -0
- codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.br +0 -0
- codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.gz +0 -0
- codex/static_root/assets/browser-BA-AwUn-.js +1 -0
- codex/static_root/assets/browser-BA-AwUn-.js.br +0 -0
- codex/static_root/assets/browser-BA-AwUn-.js.gz +0 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css +1 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.br +0 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.gz +0 -0
- codex/static_root/assets/browser-CZeVqRNy.css +1 -0
- codex/static_root/assets/browser-CZeVqRNy.css.br +0 -0
- codex/static_root/assets/browser-CZeVqRNy.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js +1 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.br +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.js +1 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.js.br +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +1 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css +1 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js +1 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.js +1 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.js.gz +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js +1 -0
- codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.br +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.gz +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.js +1 -0
- codex/static_root/assets/datetime-column-DejrPZxl.js.br +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +1 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css +1 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
- codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js +1 -0
- codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.br +0 -0
- codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.gz +0 -0
- codex/static_root/assets/filter-DHo57nTp.js +1 -0
- codex/static_root/assets/filter-DHo57nTp.js.br +0 -0
- codex/static_root/assets/filter-DHo57nTp.js.gz +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +1 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css +1 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js +1 -0
- codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.br +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.gz +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.js +1 -0
- codex/static_root/assets/flag-tab-CagMLGSE.js.br +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.js → group-tab-BTAjhv32.ac196780d0a9.js} +1 -1
- codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.br +0 -0
- codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.1afb2ce453ff.js → group-tab-BTAjhv32.js} +1 -1
- codex/static_root/assets/group-tab-BTAjhv32.js.br +0 -0
- codex/static_root/assets/group-tab-BTAjhv32.js.gz +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js +1 -0
- codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.br +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.gz +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.js +1 -0
- codex/static_root/assets/http-error-DaHEsKbT.js.br +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.js.gz +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css +1 -0
- codex/static_root/assets/{http-error-DKulXuTl.95e42988d5d5.css.br → http-error-_wlDqEcp.5b78d3e332c9.css.br} +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.gz +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.css +1 -0
- codex/static_root/assets/{http-error-DKulXuTl.css.br → http-error-_wlDqEcp.css.br} +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.css.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +1 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.js +1 -0
- codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +1 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +2 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css +1 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css.br +2 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
- codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js +1 -0
- codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.br +0 -0
- codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.gz +0 -0
- codex/static_root/assets/library-tab-Cud4639b.js +1 -0
- codex/static_root/assets/library-tab-Cud4639b.js.br +0 -0
- codex/static_root/assets/library-tab-Cud4639b.js.gz +0 -0
- codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js +32 -0
- codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.br +0 -0
- codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.gz +0 -0
- codex/static_root/assets/main-BnzL_Byv.js +32 -0
- codex/static_root/assets/main-BnzL_Byv.js.br +0 -0
- codex/static_root/assets/main-BnzL_Byv.js.gz +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-sK7HC5h4.6f04ddfc5f1a.css} +1 -1
- codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-sK7HC5h4.6f04ddfc5f1a.css.gz} +0 -0
- codex/static_root/assets/{main-Ztp855PW.css → main-sK7HC5h4.css} +1 -1
- codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.css.gz → main-sK7HC5h4.css.gz} +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css +1 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css +1 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js +1 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.js +1 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.js.gz +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js +35 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.br +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.gz +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.js +35 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.js.br +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.js.gz +0 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css +1 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.br +0 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.gz +0 -0
- codex/static_root/assets/reader-Dspy46e8.css +1 -0
- codex/static_root/assets/reader-Dspy46e8.css.br +0 -0
- codex/static_root/assets/reader-Dspy46e8.css.gz +0 -0
- codex/static_root/assets/reader-L85y7IXD.6de58f450421.js +2 -0
- codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.br +0 -0
- codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.gz +0 -0
- codex/static_root/assets/reader-L85y7IXD.js +2 -0
- codex/static_root/assets/reader-L85y7IXD.js.br +0 -0
- codex/static_root/assets/reader-L85y7IXD.js.gz +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js +1 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.br +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.gz +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.js +1 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.js.br +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.js.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-D6T6FX7G.css} +1 -1
- codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
- codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-D6T6FX7G.db095b2497e4.css} +1 -1
- codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
- codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css +1 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.br +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css +1 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css.br +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css.gz +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js +2 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.br +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.js +2 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.js.br +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.js.gz +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js +1 -0
- codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.br +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.gz +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.js +1 -0
- codex/static_root/assets/stats-tab-CQa3-W05.js.br +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.js.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +1 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +2 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css +1 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css.br +2 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js +1 -0
- codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.br +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.gz +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.js +1 -0
- codex/static_root/assets/task-tab-OPhBm-O2.js.br +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.js.gz +0 -0
- codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +1 -0
- codex/static_root/assets/to-case-kUm8NPpW.js +1 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js +1 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.br +0 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.gz +0 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.js +1 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.js.br +0 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.js.gz +0 -0
- codex/static_root/assets/{unauthorized-CibJYD8o.4c2de98c2b53.css → unauthorized-DWgytUow.ca2f13dbdbf5.css} +1 -1
- codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.br +0 -0
- codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.gz +0 -0
- codex/static_root/assets/{unauthorized-CibJYD8o.css → unauthorized-DWgytUow.css} +1 -1
- codex/static_root/assets/unauthorized-DWgytUow.css.br +0 -0
- codex/static_root/assets/unauthorized-DWgytUow.css.gz +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js +1 -0
- codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.br +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.gz +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.js +1 -0
- codex/static_root/assets/user-tab-BKkxa2WL.js.br +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.js.gz +0 -0
- codex/static_root/js/choices-admin.248384ece3f6.json +1 -0
- codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
- codex/static_root/js/choices-admin.248384ece3f6.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.b60817ba0a93.json +1 -0
- codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
- codex/static_root/js/choices.b60817ba0a93.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.c5fa10a7dfdc.json +641 -0
- codex/static_root/manifest.c5fa10a7dfdc.json.br +0 -0
- codex/static_root/manifest.c5fa10a7dfdc.json.gz +0 -0
- codex/static_root/manifest.json +298 -279
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/status_controller.py +2 -11
- codex/urls/api/auth.py +1 -2
- codex/urls/api/browser.py +4 -13
- codex/urls/api/reader.py +9 -3
- codex/urls/api/v3.py +0 -2
- codex/urls/opds/binary.py +1 -1
- codex/views/admin/api_key.py +8 -7
- codex/views/admin/flag.py +7 -2
- codex/views/admin/group.py +6 -2
- codex/views/admin/library.py +37 -44
- codex/views/admin/stats.py +83 -20
- codex/views/admin/tasks.py +12 -26
- codex/views/admin/user.py +9 -3
- codex/views/auth.py +54 -31
- codex/views/bookmark.py +72 -92
- codex/views/browser/base.py +90 -42
- codex/views/browser/browser.py +371 -92
- codex/views/browser/{annotations.py → browser_annotations.py} +121 -67
- codex/views/browser/browser_breadcrumbs.py +175 -0
- codex/views/browser/browser_order_by.py +63 -0
- codex/views/browser/choices.py +85 -38
- codex/views/browser/const.py +3 -0
- codex/views/browser/filters/annotations.py +19 -87
- codex/views/browser/filters/bookmark.py +12 -24
- codex/views/browser/filters/field.py +6 -8
- codex/views/browser/filters/group.py +14 -35
- codex/views/browser/filters/search.py +20 -31
- codex/views/browser/metadata.py +73 -112
- codex/views/browser/session.py +12 -0
- codex/views/const.py +0 -140
- codex/views/cover.py +77 -0
- codex/views/download.py +8 -5
- codex/views/frontend.py +2 -4
- codex/views/opds/authentication_v1.py +45 -46
- codex/views/opds/binary.py +10 -5
- codex/views/opds/const.py +0 -13
- codex/views/opds/urls.py +4 -6
- codex/views/opds/util.py +12 -4
- codex/views/opds/v1/entry/links.py +33 -18
- codex/views/opds/v1/facets.py +7 -7
- codex/views/opds/v1/feed.py +45 -55
- codex/views/opds/v1/links.py +5 -7
- codex/views/opds/v1/opensearch_v1.py +6 -2
- codex/views/opds/v2/feed.py +26 -25
- codex/views/opds/v2/links.py +2 -4
- codex/views/opds/v2/publications.py +54 -34
- codex/views/opds/v2/top_links.py +2 -2
- codex/views/reader/page.py +8 -15
- codex/views/reader/reader.py +322 -19
- codex/views/reader/{settings.py → session.py} +2 -4
- codex/views/session.py +102 -83
- codex/views/template.py +3 -3
- codex/views/version.py +2 -2
- codex/websockets/consumers.py +10 -14
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/METADATA +15 -22
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/RECORD +449 -467
- codex/librarian/importer/cache.py +0 -93
- codex/librarian/importer/create_covers.py +0 -96
- codex/librarian/importer/importer.py +0 -96
- codex/librarian/importer/init.py +0 -265
- codex/librarian/importer/link_covers.py +0 -73
- codex/librarian/importer/query_covers.py +0 -52
- codex/migrations/0027_import_order_and_covers.py +0 -280
- codex/serializers/browser/mtime.py +0 -21
- codex/serializers/fields.py +0 -149
- codex/serializers/settings.py +0 -12
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js +0 -1
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.br +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.js +0 -1
- codex/static_root/assets/VCheckbox-DanrzdHo.js.br +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.gz +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js +0 -1
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.br +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.gz +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.js +0 -1
- codex/static_root/assets/VCombobox-DDvdoPTG.js.br +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.js.gz +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js +0 -1
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.br +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.gz +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.js +0 -1
- codex/static_root/assets/VDialog-Bd5dN_E1.js.br +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.js.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js +0 -1
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js +0 -1
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js +0 -1
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js +0 -1
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js.gz +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js +0 -1
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.br +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.gz +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.js +0 -1
- codex/static_root/assets/VSelect-C_yPbyCG.js.br +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js +0 -1
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js +0 -1
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js +0 -1
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js +0 -1
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js.gz +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.br +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.gz +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.js.br +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.js.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js +0 -1
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.br +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.gz +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.js +0 -1
- codex/static_root/assets/VTextField-o1N9ylIb.js.br +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js +0 -1
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.br +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.js +0 -1
- codex/static_root/assets/VWindowItem-C_zvuz9C.js.br +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.js.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js +0 -1
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.br +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.gz +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.js +0 -1
- codex/static_root/assets/admin-DHyHEWZ3.js.br +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js +0 -30
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js +0 -30
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css +0 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css +0 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js +0 -1
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.br +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.gz +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.js +0 -1
- codex/static_root/assets/browser-D0Vjmmp_.js.br +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.js.gz +0 -0
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css +0 -1
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.br +0 -0
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.gz +0 -0
- codex/static_root/assets/browser-D7QQDneO.css +0 -1
- codex/static_root/assets/browser-D7QQDneO.css.br +0 -0
- codex/static_root/assets/browser-D7QQDneO.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js +0 -1
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js +0 -1
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js +0 -1
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js +0 -1
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js.gz +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +0 -1
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +0 -1
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
- codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +0 -1
- codex/static_root/assets/datetime-column-DeCthByU.css +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.br +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.gz +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.js +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.js.br +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.js.gz +0 -0
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js +0 -1
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.br +0 -0
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.gz +0 -0
- codex/static_root/assets/filter-DgPg2ts9.js +0 -1
- codex/static_root/assets/filter-DgPg2ts9.js.br +0 -0
- codex/static_root/assets/filter-DgPg2ts9.js.gz +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js +0 -1
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.br +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.gz +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.js +0 -1
- codex/static_root/assets/flag-tab-9qxOALxP.js.br +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.js.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.br +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.gz +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.js.br +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.js.gz +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js +0 -1
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.br +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.gz +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.js +0 -1
- codex/static_root/assets/http-error-B4FHoKfs.js.br +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.js.gz +0 -0
- codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css +0 -1
- codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.gz +0 -0
- codex/static_root/assets/http-error-DKulXuTl.css +0 -1
- codex/static_root/assets/http-error-DKulXuTl.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +0 -1
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css +0 -1
- codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js +0 -1
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.br +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.gz +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.js +0 -1
- codex/static_root/assets/library-tab-BWo0cmzk.js.br +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.js.gz +0 -0
- codex/static_root/assets/main-Ztp855PW.css.br +0 -0
- codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js +0 -32
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.br +0 -0
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.gz +0 -0
- codex/static_root/assets/main-ipmlipkF.js +0 -32
- codex/static_root/assets/main-ipmlipkF.js.br +0 -0
- codex/static_root/assets/main-ipmlipkF.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js +0 -1
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js +0 -1
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js +0 -305
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.br +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.js +0 -305
- codex/static_root/assets/pdf-doc-C2StaYUz.js.br +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.js.gz +0 -0
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js +0 -2
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.br +0 -0
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.gz +0 -0
- codex/static_root/assets/reader-CCnq3j1-.js +0 -2
- codex/static_root/assets/reader-CCnq3j1-.js.br +0 -0
- codex/static_root/assets/reader-CCnq3j1-.js.gz +0 -0
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css +0 -1
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.br +0 -0
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.gz +0 -0
- codex/static_root/assets/reader-CqXFa_gE.css +0 -1
- codex/static_root/assets/reader-CqXFa_gE.css.br +0 -0
- codex/static_root/assets/reader-CqXFa_gE.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js +0 -1
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.br +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.gz +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.js +0 -1
- codex/static_root/assets/relation-chips-C9ysW0tU.js.br +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js +0 -2
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.br +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.js +0 -2
- codex/static_root/assets/settings-drawer-C0ofzAhW.js.br +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.js.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css +0 -1
- codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js +0 -1
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.br +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.gz +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.js +0 -1
- codex/static_root/assets/stats-tab-BqJj7j1w.js.br +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.js.gz +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js +0 -1
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.br +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.gz +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.js +0 -1
- codex/static_root/assets/task-tab-BsPzNk8S.js.br +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.js.gz +0 -0
- codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js +0 -1
- codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js.br +0 -0
- codex/static_root/assets/to-case-KvqtSug2.js +0 -1
- codex/static_root/assets/to-case-KvqtSug2.js.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js +0 -1
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.br +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.gz +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.js +0 -1
- codex/static_root/assets/unauthorized-DwSdNzX2.js.br +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.js.gz +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js +0 -1
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.br +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.gz +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.js +0 -1
- codex/static_root/assets/user-tab-DrJOb9Ys.js.br +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.js.gz +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json +0 -1
- codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json +0 -1
- codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
- codex/static_root/manifest.1c84116d9a0a.json +0 -622
- codex/static_root/manifest.1c84116d9a0a.json.br +0 -0
- codex/static_root/manifest.1c84116d9a0a.json.gz +0 -0
- codex/util.py +0 -12
- codex/views/admin/auth.py +0 -28
- codex/views/browser/breadcrumbs.py +0 -202
- codex/views/browser/cover.py +0 -165
- codex/views/browser/mtime.py +0 -61
- codex/views/browser/order_by.py +0 -65
- codex/views/browser/paginate.py +0 -106
- codex/views/browser/settings.py +0 -66
- codex/views/browser/title.py +0 -48
- codex/views/browser/validate.py +0 -161
- codex/views/opds/auth.py +0 -17
- codex/views/public.py +0 -36
- codex/views/reader/arcs.py +0 -92
- codex/views/reader/books.py +0 -181
- codex/views/reader/init.py +0 -125
- codex/views/settings.py +0 -47
- codex/views/util.py +0 -77
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/LICENSE +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/WHEEL +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
"""Browser breadcrumbs calculations."""
|
|
2
|
-
|
|
3
|
-
from contextlib import suppress
|
|
4
|
-
from types import MappingProxyType
|
|
5
|
-
|
|
6
|
-
from codex.logger.logging import get_logger
|
|
7
|
-
from codex.views.browser.paginate import BrowserPaginateView
|
|
8
|
-
from codex.views.const import FOLDER_GROUP, GROUP_NAME_MAP, GROUP_ORDER, STORY_ARC_GROUP
|
|
9
|
-
from codex.views.util import Route
|
|
10
|
-
|
|
11
|
-
LOG = get_logger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class BrowserBreadcrumbsView(BrowserPaginateView):
|
|
15
|
-
"""Browser breadcrumbs calculations."""
|
|
16
|
-
|
|
17
|
-
def _init_breadcrumbs(self, valid_groups):
|
|
18
|
-
"""Load breadcrumbs and determine if they should be searched for a graft."""
|
|
19
|
-
breadcrumbs: list[Route] = list(self.params.get("breadcrumbs", []))
|
|
20
|
-
old_breadcrumbs = [Route(**crumb) for crumb in breadcrumbs]
|
|
21
|
-
invalid = not old_breadcrumbs or old_breadcrumbs[-1].group not in valid_groups
|
|
22
|
-
if invalid:
|
|
23
|
-
old_breadcrumbs = []
|
|
24
|
-
return old_breadcrumbs, invalid
|
|
25
|
-
|
|
26
|
-
def _breadcrumbs_save(self, breadcrumbs):
|
|
27
|
-
"""Save the breadcrumbs to params."""
|
|
28
|
-
params = dict(self.params)
|
|
29
|
-
params["breadcrumbs"] = tuple(crumb.dict() for crumb in breadcrumbs)
|
|
30
|
-
self.params = MappingProxyType(params)
|
|
31
|
-
|
|
32
|
-
def _breadcrumbs_graft_or_create_story_arc(self) -> tuple[tuple[Route, ...], bool]:
|
|
33
|
-
"""Graft or create story_arc breadcrumbs."""
|
|
34
|
-
old_breadcrumbs, changed = self._init_breadcrumbs(STORY_ARC_GROUP)
|
|
35
|
-
|
|
36
|
-
pks = self.kwargs["pks"]
|
|
37
|
-
page = self.kwargs["page"]
|
|
38
|
-
gi = self.group_instance # type: ignore
|
|
39
|
-
name = gi.name if gi else ""
|
|
40
|
-
group_crumb = Route(STORY_ARC_GROUP, pks, page, name)
|
|
41
|
-
|
|
42
|
-
if old_breadcrumbs and old_breadcrumbs[-1] & group_crumb:
|
|
43
|
-
# Graft. Hurray
|
|
44
|
-
breadcrumbs = old_breadcrumbs
|
|
45
|
-
else:
|
|
46
|
-
# Create
|
|
47
|
-
new_breadcrumbs = [group_crumb]
|
|
48
|
-
if group_crumb.pks and 0 not in group_crumb.pks:
|
|
49
|
-
# Add head.
|
|
50
|
-
new_breadcrumbs = [Route(STORY_ARC_GROUP, ()), *new_breadcrumbs]
|
|
51
|
-
changed = True
|
|
52
|
-
breadcrumbs = new_breadcrumbs
|
|
53
|
-
|
|
54
|
-
return tuple(breadcrumbs), changed
|
|
55
|
-
|
|
56
|
-
@staticmethod
|
|
57
|
-
def _breadcrumb_find_branch(reverse_breadcrumbs, crumb) -> list[Route] | None:
|
|
58
|
-
"""Find a graftable breadcrumb branch."""
|
|
59
|
-
for index, breadcrumb in enumerate(reverse_breadcrumbs):
|
|
60
|
-
if breadcrumb & crumb:
|
|
61
|
-
return list(reversed(reverse_breadcrumbs[index:]))
|
|
62
|
-
return None
|
|
63
|
-
|
|
64
|
-
def _breadcrumbs_graft_or_create_folder(self) -> tuple[tuple[Route, ...], bool]:
|
|
65
|
-
"""Graft or create folder breadcrumbs."""
|
|
66
|
-
old_breadcrumbs, changed = self._init_breadcrumbs(FOLDER_GROUP)
|
|
67
|
-
|
|
68
|
-
reversed_breadcrumbs = list(reversed(old_breadcrumbs))
|
|
69
|
-
|
|
70
|
-
pks = self.kwargs["pks"]
|
|
71
|
-
page = self.kwargs["page"]
|
|
72
|
-
folder = self.group_instance # type: ignore
|
|
73
|
-
name = folder.name if folder and pks else ""
|
|
74
|
-
group_crumb = Route(FOLDER_GROUP, pks, page, name)
|
|
75
|
-
new_breadcrumbs = []
|
|
76
|
-
|
|
77
|
-
while True:
|
|
78
|
-
branch = self._breadcrumb_find_branch(reversed_breadcrumbs, group_crumb)
|
|
79
|
-
if branch and not branch[0].pks:
|
|
80
|
-
# Branch must have the top as a head.
|
|
81
|
-
# Graft. Hurray.
|
|
82
|
-
new_breadcrumbs = branch + new_breadcrumbs
|
|
83
|
-
break
|
|
84
|
-
|
|
85
|
-
# Add head
|
|
86
|
-
new_breadcrumbs = [group_crumb, *new_breadcrumbs]
|
|
87
|
-
changed = True
|
|
88
|
-
if not group_crumb.pks:
|
|
89
|
-
break
|
|
90
|
-
|
|
91
|
-
# parent next
|
|
92
|
-
if not folder:
|
|
93
|
-
break
|
|
94
|
-
folder = folder.parent_folder
|
|
95
|
-
if folder:
|
|
96
|
-
group_crumb = Route(FOLDER_GROUP, (folder.pk,), 1, name=folder.name)
|
|
97
|
-
else:
|
|
98
|
-
group_crumb = Route(FOLDER_GROUP, (), 1, name="")
|
|
99
|
-
|
|
100
|
-
breadcrumbs = new_breadcrumbs
|
|
101
|
-
|
|
102
|
-
return tuple(breadcrumbs), changed
|
|
103
|
-
|
|
104
|
-
def _get_breadcrumbs_group_crumb(self, group):
|
|
105
|
-
"""Create the crumb for this group."""
|
|
106
|
-
gi = self.group_instance # type: ignore
|
|
107
|
-
if not gi:
|
|
108
|
-
pks = ()
|
|
109
|
-
page = 1
|
|
110
|
-
name = ""
|
|
111
|
-
if group == self.kwargs["group"]:
|
|
112
|
-
# create self crumb
|
|
113
|
-
pks = self.kwargs["pks"]
|
|
114
|
-
page = self.kwargs["page"]
|
|
115
|
-
name = gi.name if gi else ""
|
|
116
|
-
else:
|
|
117
|
-
page = 1
|
|
118
|
-
if (attr := GROUP_NAME_MAP.get(group)) and (
|
|
119
|
-
parent_group := getattr(gi, attr, None)
|
|
120
|
-
):
|
|
121
|
-
pks = (parent_group.pk,)
|
|
122
|
-
name = parent_group.name
|
|
123
|
-
else:
|
|
124
|
-
pks = ()
|
|
125
|
-
name = ""
|
|
126
|
-
|
|
127
|
-
return Route(group, pks, page, name)
|
|
128
|
-
|
|
129
|
-
def _breadcrumbs_graft_or_create_group_crumb(
|
|
130
|
-
self, group, old_breadcrumbs, new_breadcrumbs, changed
|
|
131
|
-
) -> tuple[bool, bool]:
|
|
132
|
-
"""Graft or create one browse group breadcrumb."""
|
|
133
|
-
group_crumb = self._get_breadcrumbs_group_crumb(group)
|
|
134
|
-
|
|
135
|
-
if old_breadcrumbs and (
|
|
136
|
-
(group_crumb == old_breadcrumbs[-1])
|
|
137
|
-
or (changed and (group_crumb & old_breadcrumbs[-1]))
|
|
138
|
-
and not old_breadcrumbs[0].pks
|
|
139
|
-
):
|
|
140
|
-
# Graft. Hurray
|
|
141
|
-
new_breadcrumbs[0:0] = old_breadcrumbs
|
|
142
|
-
done = True
|
|
143
|
-
else:
|
|
144
|
-
# Insert the new node
|
|
145
|
-
new_breadcrumbs.insert(0, group_crumb)
|
|
146
|
-
changed = True
|
|
147
|
-
done = False
|
|
148
|
-
return done, changed
|
|
149
|
-
|
|
150
|
-
def _breadcrumbs_graft_or_create_group(self) -> tuple[tuple[Route, ...], bool]:
|
|
151
|
-
"""Graft or create browse group breadcrumbs."""
|
|
152
|
-
old_breadcrumbs, changed = self._init_breadcrumbs(GROUP_ORDER)
|
|
153
|
-
|
|
154
|
-
vng = self.valid_nav_groups # type: ignore
|
|
155
|
-
test_groups = tuple(reversed(vng[:-1]))
|
|
156
|
-
new_breadcrumbs = []
|
|
157
|
-
level = done = False
|
|
158
|
-
try:
|
|
159
|
-
browser_group_index = GROUP_ORDER.index(self.kwargs["group"])
|
|
160
|
-
except ValueError:
|
|
161
|
-
browser_group_index = -1
|
|
162
|
-
|
|
163
|
-
for group in test_groups:
|
|
164
|
-
try:
|
|
165
|
-
with suppress(ValueError):
|
|
166
|
-
level = level or GROUP_ORDER.index(group) <= browser_group_index
|
|
167
|
-
if level:
|
|
168
|
-
done, changed = self._breadcrumbs_graft_or_create_group_crumb(
|
|
169
|
-
group, old_breadcrumbs, new_breadcrumbs, changed
|
|
170
|
-
)
|
|
171
|
-
try:
|
|
172
|
-
if old_breadcrumbs and GROUP_ORDER.index(
|
|
173
|
-
old_breadcrumbs[-1].group
|
|
174
|
-
) >= GROUP_ORDER.index(group):
|
|
175
|
-
# Trim old_breadcrumbs to match to group
|
|
176
|
-
old_breadcrumbs.pop(-1)
|
|
177
|
-
except ValueError:
|
|
178
|
-
old_breadcrumbs = []
|
|
179
|
-
|
|
180
|
-
if done:
|
|
181
|
-
break
|
|
182
|
-
except Exception:
|
|
183
|
-
LOG.exception("group loop")
|
|
184
|
-
|
|
185
|
-
return tuple(new_breadcrumbs), changed
|
|
186
|
-
|
|
187
|
-
def get_breadcrumbs(
|
|
188
|
-
self,
|
|
189
|
-
) -> tuple[Route, ...]:
|
|
190
|
-
"""Graft or create breadcrumbs by browser mode."""
|
|
191
|
-
group = self.kwargs["group"]
|
|
192
|
-
if group == FOLDER_GROUP:
|
|
193
|
-
breadcrumbs, changed = self._breadcrumbs_graft_or_create_folder()
|
|
194
|
-
elif group == STORY_ARC_GROUP:
|
|
195
|
-
breadcrumbs, changed = self._breadcrumbs_graft_or_create_story_arc()
|
|
196
|
-
else:
|
|
197
|
-
breadcrumbs, changed = self._breadcrumbs_graft_or_create_group()
|
|
198
|
-
|
|
199
|
-
if changed:
|
|
200
|
-
self._breadcrumbs_save(breadcrumbs)
|
|
201
|
-
|
|
202
|
-
return breadcrumbs
|
codex/views/browser/cover.py
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"""Comic cover thumbnail view."""
|
|
2
|
-
|
|
3
|
-
from django.db.models.query import Q
|
|
4
|
-
from django.http.response import StreamingHttpResponse
|
|
5
|
-
from drf_spectacular.types import OpenApiTypes
|
|
6
|
-
from drf_spectacular.utils import extend_schema
|
|
7
|
-
from rest_framework.renderers import BaseRenderer
|
|
8
|
-
|
|
9
|
-
from codex.librarian.covers.create import CoverCreateMixin
|
|
10
|
-
from codex.librarian.covers.path import CoverPathMixin
|
|
11
|
-
from codex.librarian.mp_queue import LIBRARIAN_QUEUE
|
|
12
|
-
from codex.logger.logging import get_logger
|
|
13
|
-
from codex.models import Comic, Volume
|
|
14
|
-
from codex.models.groups import Folder
|
|
15
|
-
from codex.models.paths import CustomCover
|
|
16
|
-
from codex.serializers.browser.settings import BrowserCoverInputSerializer
|
|
17
|
-
from codex.views.browser.annotations import BrowserAnnotationsView
|
|
18
|
-
from codex.views.const import (
|
|
19
|
-
CUSTOM_COVER_GROUP_RELATION,
|
|
20
|
-
GROUP_RELATION,
|
|
21
|
-
MISSING_COVER_FN,
|
|
22
|
-
MISSING_COVER_NAME_MAP,
|
|
23
|
-
STATIC_IMG_PATH,
|
|
24
|
-
)
|
|
25
|
-
from codex.views.util import chunker
|
|
26
|
-
|
|
27
|
-
LOG = get_logger(__name__)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class WEBPRenderer(BaseRenderer):
|
|
31
|
-
"""Render WEBP images."""
|
|
32
|
-
|
|
33
|
-
media_type = "image/webp"
|
|
34
|
-
format = "webp"
|
|
35
|
-
charset = None
|
|
36
|
-
render_style = "binary"
|
|
37
|
-
|
|
38
|
-
def render(self, data, *_args, **_kwargs):
|
|
39
|
-
"""Return raw data."""
|
|
40
|
-
return data
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class CoverView(BrowserAnnotationsView):
|
|
44
|
-
"""ComicCover View."""
|
|
45
|
-
|
|
46
|
-
input_serializer_class = BrowserCoverInputSerializer
|
|
47
|
-
renderer_classes = (WEBPRenderer,)
|
|
48
|
-
content_type = "image/webp"
|
|
49
|
-
TARGET = "cover"
|
|
50
|
-
REPARSE_JSON_FIELDS = frozenset(
|
|
51
|
-
BrowserAnnotationsView.REPARSE_JSON_FIELDS | {"parent"}
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
def get_group_filter(self, group=None, pks=None, page_mtime=False):
|
|
55
|
-
"""Get group filter for First Cover View."""
|
|
56
|
-
if self.params.get("dynamic_covers") or self.model in (Volume, Folder):
|
|
57
|
-
return super().get_group_filter(group=group, pks=pks, page_mtime=page_mtime)
|
|
58
|
-
|
|
59
|
-
# First cover group filter relies on sort names to look outside the browser supplied pks
|
|
60
|
-
# For multi_groups not in the browser query.
|
|
61
|
-
pks = self.kwargs["pks"]
|
|
62
|
-
qs = self.model.objects.filter(pk__in=pks) # type: ignore
|
|
63
|
-
sort_names = qs.values_list("sort_name", flat=True).distinct()
|
|
64
|
-
model_rel = GROUP_RELATION[self.model_group]
|
|
65
|
-
group_filter = {f"{model_rel}__sort_name__in": sort_names}
|
|
66
|
-
|
|
67
|
-
parent = self.params.get("parent", {})
|
|
68
|
-
if parent_pks := parent.get("pks"):
|
|
69
|
-
parent_rel = GROUP_RELATION[parent["group"]]
|
|
70
|
-
group_filter[f"{parent_rel}__pk__in"] = parent_pks
|
|
71
|
-
return Q(**group_filter)
|
|
72
|
-
|
|
73
|
-
def get_model_group(self):
|
|
74
|
-
"""Return the url group."""
|
|
75
|
-
return self.kwargs["group"]
|
|
76
|
-
|
|
77
|
-
def init_request(self):
|
|
78
|
-
"""Initialize request."""
|
|
79
|
-
self.parse_params()
|
|
80
|
-
self.set_model()
|
|
81
|
-
|
|
82
|
-
def _get_comic_cover(self):
|
|
83
|
-
pks = self.kwargs["pks"]
|
|
84
|
-
return pks[0], False
|
|
85
|
-
|
|
86
|
-
def _get_custom_cover(self):
|
|
87
|
-
"""Get Custom Cover."""
|
|
88
|
-
if self.model == Volume or not self.params.get("custom_covers"):
|
|
89
|
-
return None
|
|
90
|
-
group = self.kwargs["group"]
|
|
91
|
-
group_rel = CUSTOM_COVER_GROUP_RELATION[group]
|
|
92
|
-
pks = self.kwargs["pks"]
|
|
93
|
-
comic_filter = {f"{group_rel}__in": pks}
|
|
94
|
-
qs = CustomCover.objects.filter(**comic_filter)
|
|
95
|
-
qs = qs.only("pk")
|
|
96
|
-
return qs.first()
|
|
97
|
-
|
|
98
|
-
def _get_dynamic_cover(self):
|
|
99
|
-
"""Get dynamic cover."""
|
|
100
|
-
self.set_order_key()
|
|
101
|
-
comic_qs = self.get_filtered_queryset(Comic)
|
|
102
|
-
comic_qs = self.annotate_order_aggregates(comic_qs, Comic)
|
|
103
|
-
comic_qs = self.add_order_by(comic_qs, Comic)
|
|
104
|
-
comic_qs = comic_qs.only("pk")
|
|
105
|
-
group_by = self.get_group_by(Comic)
|
|
106
|
-
comic_qs.group_by(group_by)
|
|
107
|
-
comic = comic_qs.first()
|
|
108
|
-
cover_pk = comic.pk if comic else 0
|
|
109
|
-
return cover_pk, False
|
|
110
|
-
|
|
111
|
-
def _get_cover_pk(self) -> tuple[int, bool]:
|
|
112
|
-
"""Get Cover Pk queryset for comic queryset."""
|
|
113
|
-
if self.model == Comic:
|
|
114
|
-
cover_pk, custom = self._get_comic_cover()
|
|
115
|
-
elif custom_cover := self._get_custom_cover():
|
|
116
|
-
cover_pk = custom_cover.pk
|
|
117
|
-
custom = True
|
|
118
|
-
else:
|
|
119
|
-
cover_pk, custom = self._get_dynamic_cover()
|
|
120
|
-
return cover_pk, custom
|
|
121
|
-
|
|
122
|
-
def _get_missing_cover_path(self):
|
|
123
|
-
"""Get the missing cover, which is a default svg if fetched for a group."""
|
|
124
|
-
group: str = self.kwargs["group"]
|
|
125
|
-
cover_name = MISSING_COVER_NAME_MAP.get(group)
|
|
126
|
-
if cover_name:
|
|
127
|
-
cover_fn = cover_name + ".svg"
|
|
128
|
-
content_type = "image/svg+xml"
|
|
129
|
-
else:
|
|
130
|
-
cover_fn = MISSING_COVER_FN
|
|
131
|
-
content_type = "image/webp"
|
|
132
|
-
cover_path = STATIC_IMG_PATH / cover_fn
|
|
133
|
-
return cover_path, content_type
|
|
134
|
-
|
|
135
|
-
def _get_cover_data(self, pk, custom):
|
|
136
|
-
thumb_buffer = None
|
|
137
|
-
content_type = "image/webp"
|
|
138
|
-
|
|
139
|
-
cover_path = CoverPathMixin.get_cover_path(pk, custom)
|
|
140
|
-
if not cover_path.exists():
|
|
141
|
-
thumb_buffer = CoverCreateMixin.create_cover_from_path(
|
|
142
|
-
pk, cover_path, LOG, LIBRARIAN_QUEUE, custom
|
|
143
|
-
)
|
|
144
|
-
if not thumb_buffer:
|
|
145
|
-
cover_path, content_type = self._get_missing_cover_path()
|
|
146
|
-
elif cover_path.stat().st_size == 0:
|
|
147
|
-
cover_path, content_type = self._get_missing_cover_path()
|
|
148
|
-
|
|
149
|
-
cover_file = thumb_buffer if thumb_buffer else cover_path.open("rb")
|
|
150
|
-
return cover_file, content_type
|
|
151
|
-
|
|
152
|
-
@extend_schema(
|
|
153
|
-
parameters=[BrowserAnnotationsView.input_serializer_class],
|
|
154
|
-
responses={(200, content_type): OpenApiTypes.BINARY},
|
|
155
|
-
)
|
|
156
|
-
def get(self, *args, **kwargs): # type: ignore
|
|
157
|
-
"""Get comic cover."""
|
|
158
|
-
try:
|
|
159
|
-
self.init_request()
|
|
160
|
-
pk, custom = self._get_cover_pk()
|
|
161
|
-
cover_file, content_type = self._get_cover_data(pk, custom)
|
|
162
|
-
|
|
163
|
-
return StreamingHttpResponse(chunker(cover_file), content_type=content_type)
|
|
164
|
-
except Exception:
|
|
165
|
-
LOG.exception("get")
|
codex/views/browser/mtime.py
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"""Get the mtimes for the submitted groups."""
|
|
2
|
-
|
|
3
|
-
from drf_spectacular.utils import extend_schema
|
|
4
|
-
from rest_framework.response import Response
|
|
5
|
-
|
|
6
|
-
from codex.logger.logging import get_logger
|
|
7
|
-
from codex.models.groups import Publisher
|
|
8
|
-
from codex.serializers.browser.mtime import GroupsMtimeSerializer, MtimeSerializer
|
|
9
|
-
from codex.util import max_none
|
|
10
|
-
from codex.views.browser.filters.annotations import BrowserAnnotationsFilterView
|
|
11
|
-
from codex.views.const import GROUP_MODEL_MAP
|
|
12
|
-
|
|
13
|
-
LOG = get_logger(__name__)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class MtimeView(BrowserAnnotationsFilterView):
|
|
17
|
-
"""Get the mtimes for the submitted groups."""
|
|
18
|
-
|
|
19
|
-
input_serializer_class = GroupsMtimeSerializer
|
|
20
|
-
serializer_class = MtimeSerializer
|
|
21
|
-
|
|
22
|
-
REPARSE_JSON_FIELDS = frozenset({"groups", "filters"})
|
|
23
|
-
TARGET = "mtime"
|
|
24
|
-
|
|
25
|
-
def __init__(self, *args, **kwargs) -> None:
|
|
26
|
-
"""Initialize."""
|
|
27
|
-
super().__init__(*args, **kwargs)
|
|
28
|
-
self.init_bookmark_data()
|
|
29
|
-
|
|
30
|
-
def _get_group_mtime(self, item):
|
|
31
|
-
"""Get one group's mtimes."""
|
|
32
|
-
group = item["group"]
|
|
33
|
-
pks = item["pks"]
|
|
34
|
-
|
|
35
|
-
model = GROUP_MODEL_MAP[group]
|
|
36
|
-
if not model:
|
|
37
|
-
model = Publisher
|
|
38
|
-
|
|
39
|
-
return self.get_group_mtime(model, group, pks)
|
|
40
|
-
|
|
41
|
-
def get_max_groups_mtime(self):
|
|
42
|
-
"""Get max mtime for all groups."""
|
|
43
|
-
max_mtime = None
|
|
44
|
-
|
|
45
|
-
for item in self.params["groups"]: # type: ignore
|
|
46
|
-
mtime = self._get_group_mtime(item)
|
|
47
|
-
max_mtime = max_none(max_mtime, mtime)
|
|
48
|
-
return max_mtime
|
|
49
|
-
|
|
50
|
-
@extend_schema(parameters=[GroupsMtimeSerializer])
|
|
51
|
-
def get(self, *args, **kwargs):
|
|
52
|
-
"""Get the mtimes for the submitted groups."""
|
|
53
|
-
# Parse Request
|
|
54
|
-
self.parse_params()
|
|
55
|
-
|
|
56
|
-
max_mtime = self.get_max_groups_mtime()
|
|
57
|
-
|
|
58
|
-
# Serialize Response
|
|
59
|
-
result = {"max_mtime": max_mtime}
|
|
60
|
-
serializer = self.get_serializer(result)
|
|
61
|
-
return Response(serializer.data)
|
codex/views/browser/order_by.py
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"""Base view for ordering the query."""
|
|
2
|
-
|
|
3
|
-
from django.db.models.aggregates import Max, Min
|
|
4
|
-
|
|
5
|
-
from codex.models import Comic
|
|
6
|
-
from codex.views.browser.filters.annotations import (
|
|
7
|
-
BrowserAnnotationsFilterView,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class BrowserOrderByView(BrowserAnnotationsFilterView):
|
|
12
|
-
"""Base class for views that need ordering."""
|
|
13
|
-
|
|
14
|
-
def set_order_key(self):
|
|
15
|
-
"""Get the default order key for the view."""
|
|
16
|
-
self.order_key: str = self.params["order_by"]
|
|
17
|
-
order_reverse = self.params.get("order_reverse")
|
|
18
|
-
self.order_agg_func = Max if order_reverse else Min
|
|
19
|
-
|
|
20
|
-
def _add_comic_order_by(self, order_key, comic_sort_names):
|
|
21
|
-
"""Order by for comics (and covers)."""
|
|
22
|
-
if not order_key:
|
|
23
|
-
order_key = self.order_key
|
|
24
|
-
if order_key == "sort_name":
|
|
25
|
-
if not comic_sort_names:
|
|
26
|
-
comic_sort_names = self.comic_sort_names # type: ignore
|
|
27
|
-
order_fields_head = [
|
|
28
|
-
*comic_sort_names,
|
|
29
|
-
"issue_number",
|
|
30
|
-
"issue_suffix",
|
|
31
|
-
"sort_name",
|
|
32
|
-
]
|
|
33
|
-
else:
|
|
34
|
-
# Comic orders on indexed fields directly
|
|
35
|
-
# Which is allegedly faster than using tmp b-trees (annotations)
|
|
36
|
-
# And since that's every cover sort, it's worth it.
|
|
37
|
-
order_fields_head = [order_key]
|
|
38
|
-
# Comic order micro optimizations
|
|
39
|
-
if order_key == "story_arc_number":
|
|
40
|
-
order_fields_head += ["date"]
|
|
41
|
-
elif order_key == "bookmark_updated_at":
|
|
42
|
-
order_fields_head += ["updated_at"]
|
|
43
|
-
return order_fields_head
|
|
44
|
-
|
|
45
|
-
def add_order_by( # noqa: PLR0913
|
|
46
|
-
self, qs, model, order_key="", do_reverse=True, comic_sort_names=None
|
|
47
|
-
):
|
|
48
|
-
"""Create the order_by list."""
|
|
49
|
-
order_fields_head = ()
|
|
50
|
-
if model == Comic:
|
|
51
|
-
order_fields_head = self._add_comic_order_by(order_key, comic_sort_names)
|
|
52
|
-
else:
|
|
53
|
-
order_fields_head = ["order_value"]
|
|
54
|
-
|
|
55
|
-
order_fields = (*order_fields_head, "pk")
|
|
56
|
-
|
|
57
|
-
prefix = "-" if do_reverse and self.params.get("order_reverse") else ""
|
|
58
|
-
order_by = []
|
|
59
|
-
if prefix:
|
|
60
|
-
for field in order_fields:
|
|
61
|
-
order_by.append(prefix + field)
|
|
62
|
-
else:
|
|
63
|
-
order_by = order_fields
|
|
64
|
-
|
|
65
|
-
return qs.order_by(*order_by)
|
codex/views/browser/paginate.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
"""Browser pagination."""
|
|
2
|
-
|
|
3
|
-
from math import ceil
|
|
4
|
-
|
|
5
|
-
from django.core.paginator import EmptyPage, Paginator
|
|
6
|
-
|
|
7
|
-
from codex.logger.logging import get_logger
|
|
8
|
-
from codex.models import (
|
|
9
|
-
Comic,
|
|
10
|
-
Folder,
|
|
11
|
-
)
|
|
12
|
-
from codex.views.browser.annotations import BrowserAnnotationsView
|
|
13
|
-
from codex.views.const import (
|
|
14
|
-
MAX_OBJ_PER_PAGE,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
LOG = get_logger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class BrowserPaginateView(BrowserAnnotationsView):
|
|
21
|
-
"""Browser pagination."""
|
|
22
|
-
|
|
23
|
-
def _check_page_in_bounds(self, total_count):
|
|
24
|
-
"""Redirect page out of bounds."""
|
|
25
|
-
pks = self.kwargs.get("pks")
|
|
26
|
-
page = self.kwargs.get("page", 1)
|
|
27
|
-
num_pages = ceil(total_count / MAX_OBJ_PER_PAGE)
|
|
28
|
-
if (not pks and page == 1) or (page >= 1 and page <= num_pages):
|
|
29
|
-
# Don't redirect if on the root page for the group.
|
|
30
|
-
# Or page within valid range.
|
|
31
|
-
return num_pages
|
|
32
|
-
|
|
33
|
-
# Adjust route mask for redirect
|
|
34
|
-
group = self.kwargs.get("group")
|
|
35
|
-
if num_pages and page > 1:
|
|
36
|
-
# Try page 1 first.
|
|
37
|
-
new_page = num_pages if num_pages and page > num_pages else 1
|
|
38
|
-
pks = pks if pks else "0"
|
|
39
|
-
else:
|
|
40
|
-
# Redirect to root for the group.
|
|
41
|
-
pks = "0"
|
|
42
|
-
new_page = 1
|
|
43
|
-
|
|
44
|
-
reason = f"{page=} does not exist!"
|
|
45
|
-
route_changes = {"group": group, "pks": pks, "page": new_page}
|
|
46
|
-
return self.raise_redirect(reason, route_mask=route_changes)
|
|
47
|
-
|
|
48
|
-
def _paginate_section(self, model, qs, page):
|
|
49
|
-
"""Paginate a group or Comic section."""
|
|
50
|
-
orphans = 0 if self.model_group == "f" or self.params.get("q") else 5
|
|
51
|
-
paginator = Paginator(qs, MAX_OBJ_PER_PAGE, orphans=orphans)
|
|
52
|
-
try:
|
|
53
|
-
qs = paginator.page(page).object_list
|
|
54
|
-
except EmptyPage:
|
|
55
|
-
if model != Folder:
|
|
56
|
-
model_name = self.model.__name__ if self.model else "NO_MODEL"
|
|
57
|
-
LOG.warning(f"No {model_name}s on page {page}")
|
|
58
|
-
qs = model.objects.none()
|
|
59
|
-
|
|
60
|
-
return qs
|
|
61
|
-
|
|
62
|
-
def _paginate_groups(self, group_qs, total_group_count):
|
|
63
|
-
"""Paginate the group object list before books."""
|
|
64
|
-
if total_group_count:
|
|
65
|
-
page = self.kwargs.get("page", 1)
|
|
66
|
-
page_group_qs = self._paginate_section(self.model, group_qs, page)
|
|
67
|
-
else:
|
|
68
|
-
page_group_qs = self.model.objects.none() # type: ignore
|
|
69
|
-
return page_group_qs
|
|
70
|
-
|
|
71
|
-
def _paginate_books(self, book_qs, total_group_count, page_obj_count):
|
|
72
|
-
"""Paginate the book object list based on how many group/folders are showing."""
|
|
73
|
-
if page_obj_count >= MAX_OBJ_PER_PAGE:
|
|
74
|
-
# No books for this page
|
|
75
|
-
page_book_qs = Comic.objects.none()
|
|
76
|
-
else:
|
|
77
|
-
group_remainder = total_group_count % MAX_OBJ_PER_PAGE
|
|
78
|
-
if page_obj_count:
|
|
79
|
-
# There are books after the groups on the same page
|
|
80
|
-
# Manually add books without the paginator
|
|
81
|
-
book_limit = MAX_OBJ_PER_PAGE - group_remainder
|
|
82
|
-
page_book_qs = book_qs[:book_limit]
|
|
83
|
-
else:
|
|
84
|
-
# There are books after the groups on a new page
|
|
85
|
-
if group_remainder:
|
|
86
|
-
book_offset = MAX_OBJ_PER_PAGE - group_remainder
|
|
87
|
-
page_book_qs = book_qs[book_offset:]
|
|
88
|
-
else:
|
|
89
|
-
page_book_qs = book_qs
|
|
90
|
-
|
|
91
|
-
num_group_and_mixed_pages = ceil(total_group_count / MAX_OBJ_PER_PAGE)
|
|
92
|
-
book_only_page = self.kwargs.get("page", 1) - num_group_and_mixed_pages
|
|
93
|
-
page_book_qs = self._paginate_section(
|
|
94
|
-
Comic, page_book_qs, book_only_page
|
|
95
|
-
)
|
|
96
|
-
return page_book_qs
|
|
97
|
-
|
|
98
|
-
def paginate(self, group_qs, book_qs, group_count, book_count):
|
|
99
|
-
"""Paginate the queryset into a group and book object lists."""
|
|
100
|
-
num_pages = self._check_page_in_bounds(group_count + book_count)
|
|
101
|
-
page_group_qs = self._paginate_groups(group_qs, group_count)
|
|
102
|
-
page_group_count = page_group_qs.count()
|
|
103
|
-
page_book_qs = self._paginate_books(book_qs, group_count, page_group_count)
|
|
104
|
-
page_book_count = page_book_qs.count()
|
|
105
|
-
|
|
106
|
-
return page_group_qs, page_book_qs, num_pages, page_group_count, page_book_count
|
codex/views/browser/settings.py
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"""Browser session view."""
|
|
2
|
-
|
|
3
|
-
from codex.serializers.browser.settings import BrowserSettingsSerializer
|
|
4
|
-
from codex.views.const import GROUP_ORDER
|
|
5
|
-
from codex.views.settings import SettingsView
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class BrowserSettingsView(SettingsView):
|
|
9
|
-
"""Get Browser Settings."""
|
|
10
|
-
|
|
11
|
-
# Put Browser Settings is normally done through BrowserView.get()
|
|
12
|
-
serializer_class = BrowserSettingsSerializer # type: ignore
|
|
13
|
-
|
|
14
|
-
SESSION_KEY = SettingsView.BROWSER_SESSION_KEY
|
|
15
|
-
|
|
16
|
-
@staticmethod
|
|
17
|
-
def _strip_breadcrumb_names(params):
|
|
18
|
-
"""Remove names from breadcrumbs."""
|
|
19
|
-
breadcrumbs = params.get("breadcrumbs")
|
|
20
|
-
if not breadcrumbs:
|
|
21
|
-
return params
|
|
22
|
-
nameless_breadcrumbs = []
|
|
23
|
-
for crumb in breadcrumbs:
|
|
24
|
-
crumb.pop("name", None)
|
|
25
|
-
nameless_breadcrumbs.append(crumb)
|
|
26
|
-
params["breadcrumbs"] = nameless_breadcrumbs
|
|
27
|
-
return params
|
|
28
|
-
|
|
29
|
-
@staticmethod
|
|
30
|
-
def _validate_browse_top_group(params, group, top_group):
|
|
31
|
-
"""Validate top group for browse groups."""
|
|
32
|
-
show = params["show"]
|
|
33
|
-
if group == "r" or (
|
|
34
|
-
show.get(top_group)
|
|
35
|
-
and GROUP_ORDER.index(top_group) < GROUP_ORDER.index(group)
|
|
36
|
-
):
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
for show_group, on in show.items():
|
|
40
|
-
if on:
|
|
41
|
-
params["top_group"] = show_group
|
|
42
|
-
break
|
|
43
|
-
else:
|
|
44
|
-
params["top_group"] = "c"
|
|
45
|
-
|
|
46
|
-
@classmethod
|
|
47
|
-
def _validate_top_group(cls, params, group, top_group):
|
|
48
|
-
"""Validate top group."""
|
|
49
|
-
if group == top_group:
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
if group in ("f", "a"):
|
|
53
|
-
params["top_group"] = group
|
|
54
|
-
else:
|
|
55
|
-
cls._validate_browse_top_group(params, group, top_group)
|
|
56
|
-
|
|
57
|
-
def validate_settings_get(self, validated_data, params):
|
|
58
|
-
"""Change bad settings."""
|
|
59
|
-
top_group = params["top_group"]
|
|
60
|
-
group = validated_data.get("group", "r") if validated_data else "r"
|
|
61
|
-
self._validate_top_group(params, group, top_group)
|
|
62
|
-
|
|
63
|
-
if not validated_data.get("breadcrumb_names", True):
|
|
64
|
-
params = self._strip_breadcrumb_names(params)
|
|
65
|
-
|
|
66
|
-
return params
|