codex 1.6.0__py3-none-any.whl → 1.6.0a1__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/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-u_aWQJA7.a0db8a7bd681.js +1 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js.br +0 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.a0db8a7bd681.js.gz +0 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.js +1 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.js.br +0 -0
- codex/static_root/assets/VCheckbox-u_aWQJA7.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js → VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.29d48d14ab3b.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.js → VCheckboxBtn-yCN3TB6A.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-yCN3TB6A.js.gz +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js +1 -0
- codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js.br +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.d3c8da1d75b3.js.gz +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.js +1 -0
- codex/static_root/assets/VCombobox-dsIKbjps.js.br +0 -0
- codex/static_root/assets/VCombobox-dsIKbjps.js.gz +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js +1 -0
- codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js.br +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.21bed7754d3a.js.gz +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.js +1 -0
- codex/static_root/assets/VDialog-BNHg8txZ.js.br +0 -0
- codex/static_root/assets/VDialog-BNHg8txZ.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/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js +1 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.8e90fe0b34f4.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.js +1 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CX4W4vZK.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js +1 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js.br +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.685daccd04d8.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.js +1 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.js.br +0 -0
- codex/static_root/assets/VRadioGroup-jCr3CAsk.js.gz +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js +1 -0
- codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js.br +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.ff050ef0d6de.js.gz +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.js +1 -0
- codex/static_root/assets/VSelect--XHNpxvB.js.br +0 -0
- codex/static_root/assets/VSelect--XHNpxvB.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js +1 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.0b2cc426e8b8.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.js +1 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVIKc_ra.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js +1 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js.br +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.7ae5182e5988.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.js +1 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.js.br +0 -0
- codex/static_root/assets/VSlideGroup-CZw5_eNv.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.f0f2106b158b.js → VTable-hGJDFK5t.d2f84d68747b.js} +1 -1
- codex/static_root/assets/VTable-hGJDFK5t.d2f84d68747b.js.br +0 -0
- codex/static_root/assets/VTable-hGJDFK5t.d2f84d68747b.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.js → VTable-hGJDFK5t.js} +1 -1
- codex/static_root/assets/VTable-hGJDFK5t.js.br +0 -0
- codex/static_root/assets/VTable-hGJDFK5t.js.gz +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js +1 -0
- codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js.br +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.eb58405614ad.js.gz +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.js +1 -0
- codex/static_root/assets/VTextField-C8y3lyz9.js.br +0 -0
- codex/static_root/assets/VTextField-C8y3lyz9.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-BHpZIa_r.2b3c5f9594e6.js +1 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js.br +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.2b3c5f9594e6.js.gz +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.js +1 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.js.br +0 -0
- codex/static_root/assets/VWindowItem-BHpZIa_r.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-Bvd-SvGS.412e5b1b7ed1.js +1 -0
- codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js.br +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.412e5b1b7ed1.js.gz +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.js +1 -0
- codex/static_root/assets/admin-Bvd-SvGS.js.br +0 -0
- codex/static_root/assets/admin-Bvd-SvGS.js.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-D2OV5rDa.059e3bde9568.js +30 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.059e3bde9568.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js +30 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-D2OV5rDa.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/browser-rc5ObkeN.0c1f38d229ac.js +1 -0
- codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js.br +0 -0
- codex/static_root/assets/browser-rc5ObkeN.0c1f38d229ac.js.gz +0 -0
- codex/static_root/assets/browser-rc5ObkeN.js +1 -0
- codex/static_root/assets/browser-rc5ObkeN.js.br +0 -0
- codex/static_root/assets/browser-rc5ObkeN.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js +1 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.d4d4b5945cd4.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.js +1 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CRHk2tjq.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-DnHKM44R.d57a0196ac5a.js +1 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.d57a0196ac5a.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.js +1 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DnHKM44R.js.gz +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js +1 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js.br +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.44b65455a2c3.js.gz +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.js +1 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.js.br +0 -0
- codex/static_root/assets/datetime-column-BEqDvdeI.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-_wYL6jQC.dbc7d7e7b855.js +1 -0
- codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js.br +0 -0
- codex/static_root/assets/filter-_wYL6jQC.dbc7d7e7b855.js.gz +0 -0
- codex/static_root/assets/filter-_wYL6jQC.js +1 -0
- codex/static_root/assets/filter-_wYL6jQC.js.br +0 -0
- codex/static_root/assets/filter-_wYL6jQC.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-Dpyvhwb_.2812bd46db2d.js +1 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js.br +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.2812bd46db2d.js.gz +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.js +1 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.js.br +0 -0
- codex/static_root/assets/flag-tab-Dpyvhwb_.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.1afb2ce453ff.js → group-tab-DAjvuSUO.4494d4846fa6.js} +1 -1
- codex/static_root/assets/group-tab-DAjvuSUO.4494d4846fa6.js.br +0 -0
- codex/static_root/assets/group-tab-DAjvuSUO.4494d4846fa6.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.js → group-tab-DAjvuSUO.js} +1 -1
- codex/static_root/assets/group-tab-DAjvuSUO.js.br +0 -0
- codex/static_root/assets/group-tab-DAjvuSUO.js.gz +0 -0
- codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js +1 -0
- codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js.br +0 -0
- codex/static_root/assets/http-error-CDQ-296a.6ea61686beb8.js.gz +0 -0
- codex/static_root/assets/http-error-CDQ-296a.js +1 -0
- codex/static_root/assets/http-error-CDQ-296a.js.br +0 -0
- codex/static_root/assets/http-error-CDQ-296a.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-LsexSRi2.34b88bbbfcdc.js +1 -0
- codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js.br +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.34b88bbbfcdc.js.gz +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.js +1 -0
- codex/static_root/assets/library-tab-LsexSRi2.js.br +0 -0
- codex/static_root/assets/library-tab-LsexSRi2.js.gz +0 -0
- codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js +32 -0
- codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js.br +0 -0
- codex/static_root/assets/main-CjPnwTfF.0f333ec81a42.js.gz +0 -0
- codex/static_root/assets/main-CjPnwTfF.js +32 -0
- codex/static_root/assets/main-CjPnwTfF.js.br +0 -0
- codex/static_root/assets/main-CjPnwTfF.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-B955aa-d.3fd9249f7342.js +1 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.3fd9249f7342.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.js +1 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-B955aa-d.js.gz +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js +35 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js.br +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.74cab27a76e1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.js +35 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.js.br +0 -0
- codex/static_root/assets/pdf-doc-DPDmmEEp.js.gz +0 -0
- codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js +2 -0
- codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js.br +0 -0
- codex/static_root/assets/reader-BuMh4x3f.f4d7f1c97387.js.gz +0 -0
- codex/static_root/assets/reader-BuMh4x3f.js +2 -0
- codex/static_root/assets/reader-BuMh4x3f.js.br +0 -0
- codex/static_root/assets/reader-BuMh4x3f.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/{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/relation-chips-DN_pPmnS.684c4eae4bdb.js +1 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js.br +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.684c4eae4bdb.js.gz +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.js +1 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.js.br +0 -0
- codex/static_root/assets/relation-chips-DN_pPmnS.js.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-D4FgDG4z.de28fd4ce82a.js +2 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js.br +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.de28fd4ce82a.js.gz +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.js +2 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.js.br +0 -0
- codex/static_root/assets/settings-drawer-D4FgDG4z.js.gz +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js +1 -0
- codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js.br +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.a95f7d9b2059.js.gz +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.js +1 -0
- codex/static_root/assets/stats-tab-AwbCeW63.js.br +0 -0
- codex/static_root/assets/stats-tab-AwbCeW63.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-DeHQTHuz.5f967defca9f.js +1 -0
- codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js.br +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.5f967defca9f.js.gz +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.js +1 -0
- codex/static_root/assets/task-tab-DeHQTHuz.js.br +0 -0
- codex/static_root/assets/task-tab-DeHQTHuz.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-BXCbvRsL.bc44d7ba68b3.js +1 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js.br +0 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.bc44d7ba68b3.js.gz +0 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.js +1 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.js.br +0 -0
- codex/static_root/assets/unauthorized-BXCbvRsL.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-Dq4qYzgp.5d1b6c07edd0.js +1 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js.br +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.5d1b6c07edd0.js.gz +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.js +1 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.js.br +0 -0
- codex/static_root/assets/user-tab-Dq4qYzgp.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.b5fe5d7ef44e.json +641 -0
- codex/static_root/manifest.b5fe5d7ef44e.json.br +0 -0
- codex/static_root/manifest.b5fe5d7ef44e.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.0a1.dist-info}/METADATA +15 -22
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/RECORD +448 -466
- 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.0a1.dist-info}/LICENSE +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/WHEEL +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a1.dist-info}/entry_points.txt +0 -0
|
@@ -7,8 +7,10 @@ from django.db.models import (
|
|
|
7
7
|
Avg,
|
|
8
8
|
BooleanField,
|
|
9
9
|
Case,
|
|
10
|
+
DateTimeField,
|
|
10
11
|
F,
|
|
11
12
|
FilteredRelation,
|
|
13
|
+
Max,
|
|
12
14
|
Min,
|
|
13
15
|
Q,
|
|
14
16
|
Sum,
|
|
@@ -18,58 +20,46 @@ from django.db.models import (
|
|
|
18
20
|
from django.db.models.fields import CharField, PositiveSmallIntegerField
|
|
19
21
|
from django.db.models.functions import Least, Reverse, Right, StrIndex
|
|
20
22
|
|
|
21
|
-
from codex.
|
|
22
|
-
from codex.models import (
|
|
23
|
-
BrowserGroupModel,
|
|
24
|
-
Comic,
|
|
25
|
-
Folder,
|
|
26
|
-
StoryArc,
|
|
27
|
-
Volume,
|
|
28
|
-
)
|
|
23
|
+
from codex.models import BrowserGroupModel, Comic, Folder, StoryArc, Volume
|
|
29
24
|
from codex.models.functions import JsonGroupArray
|
|
30
|
-
from codex.views.browser.
|
|
25
|
+
from codex.views.browser.browser_order_by import (
|
|
31
26
|
BrowserOrderByView,
|
|
32
27
|
)
|
|
33
|
-
from codex.views.const import (
|
|
34
|
-
NONE_DATETIMEFIELD,
|
|
35
|
-
NONE_INTEGERFIELD,
|
|
36
|
-
STORY_ARC_GROUP,
|
|
37
|
-
)
|
|
38
28
|
from codex.views.mixins import SharedAnnotationsMixin
|
|
39
29
|
|
|
40
|
-
_ORDER_AGGREGATE_FUNCS = MappingProxyType(
|
|
41
|
-
# These are annotated to order_value because they're simple relations
|
|
42
|
-
{
|
|
43
|
-
"age_rating": Avg,
|
|
44
|
-
"community_rating": Avg,
|
|
45
|
-
"created_at": Min,
|
|
46
|
-
"critical_rating": Avg,
|
|
47
|
-
"date": Min,
|
|
48
|
-
"page_count": Sum,
|
|
49
|
-
"size": Sum,
|
|
50
|
-
"updated_at": Min,
|
|
51
|
-
}
|
|
52
|
-
)
|
|
53
|
-
_ANNOTATED_ORDER_FIELDS = frozenset(
|
|
54
|
-
# These are annotated with their own functions
|
|
55
|
-
{
|
|
56
|
-
"filename",
|
|
57
|
-
"sort_name",
|
|
58
|
-
"search_score",
|
|
59
|
-
"bookmark_updated_at",
|
|
60
|
-
"story_arc_number",
|
|
61
|
-
}
|
|
62
|
-
)
|
|
63
|
-
_ALTERNATE_GROUP_BY: MappingProxyType[type[BrowserGroupModel], str] = MappingProxyType(
|
|
64
|
-
{Comic: "id", Volume: "name", Folder: "name"}
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
LOG = get_logger(__name__)
|
|
68
|
-
|
|
69
30
|
|
|
70
31
|
class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
71
32
|
"""Base class for views that need special metadata annotations."""
|
|
72
33
|
|
|
34
|
+
_NONE_INTEGERFIELD = Value(None, PositiveSmallIntegerField())
|
|
35
|
+
_NONE_DATETIMEFIELD = Value(None, DateTimeField())
|
|
36
|
+
_ORDER_AGGREGATE_FUNCS = MappingProxyType(
|
|
37
|
+
# These are annotated to order_value becauase they're simple relations
|
|
38
|
+
{
|
|
39
|
+
"age_rating": Avg,
|
|
40
|
+
"community_rating": Avg,
|
|
41
|
+
"created_at": Min,
|
|
42
|
+
"critical_rating": Avg,
|
|
43
|
+
"date": Min,
|
|
44
|
+
"page_count": Sum,
|
|
45
|
+
"size": Sum,
|
|
46
|
+
"updated_at": Min,
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
_ANNOTATED_ORDER_FIELDS = frozenset(
|
|
50
|
+
# These are annotated with their own functions
|
|
51
|
+
{
|
|
52
|
+
"filename",
|
|
53
|
+
"sort_name",
|
|
54
|
+
"search_score",
|
|
55
|
+
"bookmark_updated_at",
|
|
56
|
+
"story_arc_number",
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
_ALTERNATE_GROUP_BY: MappingProxyType[type[BrowserGroupModel], str] = (
|
|
60
|
+
MappingProxyType({Comic: "id", Volume: "name"})
|
|
61
|
+
)
|
|
62
|
+
|
|
73
63
|
def __init__(self, *args, **kwargs):
|
|
74
64
|
"""Set params for the type checker."""
|
|
75
65
|
super().__init__(*args, **kwargs)
|
|
@@ -81,7 +71,7 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
81
71
|
"""Get the group by for the model."""
|
|
82
72
|
if model is None:
|
|
83
73
|
model = self.model
|
|
84
|
-
return _ALTERNATE_GROUP_BY.get(model, "sort_name") # type: ignore
|
|
74
|
+
return self._ALTERNATE_GROUP_BY.get(model, "sort_name") # type: ignore
|
|
85
75
|
|
|
86
76
|
def _alias_sort_names(self, qs, model):
|
|
87
77
|
"""Annotate sort_name."""
|
|
@@ -122,7 +112,7 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
122
112
|
# Get story_arc__pk
|
|
123
113
|
group = self.kwargs["group"]
|
|
124
114
|
pks = self.kwargs["pks"]
|
|
125
|
-
if group == STORY_ARC_GROUP and pks:
|
|
115
|
+
if group == self.STORY_ARC_GROUP and pks:
|
|
126
116
|
story_arc_pks = pks
|
|
127
117
|
else:
|
|
128
118
|
story_arc_pks = self.params.get("filters", {}).get( # type: ignore
|
|
@@ -139,7 +129,7 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
139
129
|
)
|
|
140
130
|
story_arc_number = self.order_agg_func("selected_story_arc_number__number")
|
|
141
131
|
else:
|
|
142
|
-
story_arc_number =
|
|
132
|
+
story_arc_number = self._NONE_INTEGERFIELD
|
|
143
133
|
|
|
144
134
|
return qs.alias(story_arc_number=story_arc_number)
|
|
145
135
|
|
|
@@ -158,27 +148,33 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
158
148
|
return qs
|
|
159
149
|
|
|
160
150
|
def _annotate_bookmark_updated_at(self, qs, model):
|
|
161
|
-
if
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
151
|
+
if self.is_opds_1_acquisition or self.order_key == "bookmark_updated_at":
|
|
152
|
+
bm_rel = self.get_bm_rel(model)
|
|
153
|
+
bm_filter = self._get_my_bookmark_filter(bm_rel)
|
|
154
|
+
self.bm_annotation_data[model] = bm_rel, bm_filter # type: ignore
|
|
155
|
+
|
|
156
|
+
updated_at_rel = f"{bm_rel}__updated_at"
|
|
157
|
+
bookmark_updated_at_aggregate = self.order_agg_func(
|
|
158
|
+
updated_at_rel,
|
|
159
|
+
default=self._NONE_DATETIMEFIELD,
|
|
160
|
+
filter=bm_filter,
|
|
161
|
+
)
|
|
162
|
+
if self.TARGET == "opds1":
|
|
163
|
+
qs = qs.annotate(bookmark_updated_at=bookmark_updated_at_aggregate)
|
|
164
|
+
else:
|
|
165
|
+
qs = qs.alias(bookmark_updated_at=bookmark_updated_at_aggregate)
|
|
166
|
+
return qs
|
|
169
167
|
|
|
170
168
|
def _annotate_order_value(self, qs, model):
|
|
171
169
|
"""Annotate a main key for sorting and browser card display."""
|
|
172
170
|
# Determine order func
|
|
173
|
-
if self.TARGET == "metadata":
|
|
174
|
-
return qs
|
|
175
171
|
if model == Folder and self.order_key == "filename":
|
|
176
172
|
order_value = F("name")
|
|
177
|
-
elif model == Comic or self.order_key in _ANNOTATED_ORDER_FIELDS:
|
|
173
|
+
elif model == Comic or self.order_key in self._ANNOTATED_ORDER_FIELDS:
|
|
178
174
|
# These are annotated in browser_annotaions
|
|
179
175
|
order_value = F(self.order_key)
|
|
180
176
|
else:
|
|
181
|
-
agg_func = _ORDER_AGGREGATE_FUNCS[self.order_key]
|
|
177
|
+
agg_func = self._ORDER_AGGREGATE_FUNCS[self.order_key]
|
|
182
178
|
agg_func = self.order_agg_func if agg_func == Min else agg_func
|
|
183
179
|
field = self.rel_prefix + self.order_key
|
|
184
180
|
order_value = agg_func(field)
|
|
@@ -191,6 +187,7 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
191
187
|
|
|
192
188
|
def annotate_order_aggregates(self, qs, model):
|
|
193
189
|
"""Annotate common aggregates between browser and metadata."""
|
|
190
|
+
# ids coming first or before cover_qs works.
|
|
194
191
|
qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
|
|
195
192
|
qs = self._alias_sort_names(qs, model)
|
|
196
193
|
qs = self._alias_filename(qs, model)
|
|
@@ -202,6 +199,19 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
202
199
|
qs = self._annotate_order_value(qs, model)
|
|
203
200
|
return qs
|
|
204
201
|
|
|
202
|
+
def _annotate_cover_pk(self, qs, model):
|
|
203
|
+
"""Annotate the query set for the coverpath for the sort."""
|
|
204
|
+
# Select comics for the children by an outer ref for annotation
|
|
205
|
+
# Order the descendant comics by the sort argumentst
|
|
206
|
+
if model == Comic:
|
|
207
|
+
qs = qs.annotate(cover_pk=F("pk"))
|
|
208
|
+
qs = qs.annotate(cover_mtime=F("updated_at"))
|
|
209
|
+
elif self.admin_flags["dynamic_group_covers"]:
|
|
210
|
+
# At this point it's only been filtered.
|
|
211
|
+
qs = qs.annotate(cover_pk=F(self.rel_prefix + "pk"))
|
|
212
|
+
qs = qs.annotate(cover_pks=JsonGroupArray("cover_pk", distinct=True))
|
|
213
|
+
return qs
|
|
214
|
+
|
|
205
215
|
def _annotate_group(self, qs, model):
|
|
206
216
|
"""Annotate Group."""
|
|
207
217
|
value = "c" if model == Comic else self.model_group # type: ignore
|
|
@@ -209,7 +219,11 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
209
219
|
|
|
210
220
|
def _annotate_bookmarks(self, qs, model):
|
|
211
221
|
"""Hoist up bookmark annotations."""
|
|
212
|
-
|
|
222
|
+
if model in self.bm_annotation_data: # type: ignore
|
|
223
|
+
bm_rel, bm_filter = self.bm_annotation_data[model] # type: ignore
|
|
224
|
+
else:
|
|
225
|
+
bm_rel = self.get_bm_rel(model)
|
|
226
|
+
bm_filter = self._get_my_bookmark_filter(bm_rel)
|
|
213
227
|
|
|
214
228
|
page_rel = f"{bm_rel}__page"
|
|
215
229
|
finished_rel = f"{bm_rel}__finished"
|
|
@@ -250,7 +264,7 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
250
264
|
output_field=PositiveSmallIntegerField(),
|
|
251
265
|
distinct=True,
|
|
252
266
|
)
|
|
253
|
-
qs = qs.
|
|
267
|
+
qs = qs.alias(finished_count=finished_count)
|
|
254
268
|
finished_aggregate = Case(
|
|
255
269
|
When(finished_count=F("child_count"), then=True),
|
|
256
270
|
When(finished_count=0, then=False),
|
|
@@ -258,15 +272,16 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
258
272
|
output_field=BooleanField(),
|
|
259
273
|
)
|
|
260
274
|
|
|
261
|
-
if self.is_opds_1_acquisition
|
|
262
|
-
self.is_model_comic and self.TARGET == "browser"
|
|
263
|
-
):
|
|
275
|
+
if self.is_opds_1_acquisition:
|
|
264
276
|
qs = qs.annotate(page=bookmark_page)
|
|
265
|
-
|
|
277
|
+
else:
|
|
266
278
|
qs = qs.alias(page=bookmark_page)
|
|
267
279
|
|
|
268
280
|
if self.TARGET in frozenset({"metadata", "browser"}):
|
|
269
|
-
qs
|
|
281
|
+
qs.annotate(finished=finished_aggregate)
|
|
282
|
+
else:
|
|
283
|
+
# Only used for progress
|
|
284
|
+
qs.alias(finished=finished_aggregate)
|
|
270
285
|
return qs
|
|
271
286
|
|
|
272
287
|
def _annotate_progress(self, qs):
|
|
@@ -283,11 +298,50 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
|
283
298
|
|
|
284
299
|
def annotate_card_aggregates(self, qs, model):
|
|
285
300
|
"""Annotate aggregates that appear the browser card."""
|
|
301
|
+
qs = self._annotate_cover_pk(qs, model)
|
|
286
302
|
if model == Comic:
|
|
287
303
|
# comic adds order_value for cards late
|
|
288
304
|
qs = self._annotate_order_value(qs, model)
|
|
289
305
|
qs = self._annotate_group(qs, model)
|
|
290
306
|
qs = self.annotate_group_names(qs, model)
|
|
291
307
|
qs = self._annotate_bookmarks(qs, model)
|
|
292
|
-
|
|
293
|
-
|
|
308
|
+
return self._annotate_progress(qs)
|
|
309
|
+
|
|
310
|
+
def annotate_for_metadata(self, qs, _model):
|
|
311
|
+
"""Annotations only for metadata."""
|
|
312
|
+
return qs.annotate(mtime=Max("updated_at"))
|
|
313
|
+
|
|
314
|
+
def _get_cover_pk_query(self, cover_pks):
|
|
315
|
+
"""Get Cover Pk queryset for comic queryset."""
|
|
316
|
+
comic_qs = Comic.objects.filter(pk__in=cover_pks)
|
|
317
|
+
comic_qs = self.annotate_search_score(
|
|
318
|
+
comic_qs, Comic, self.cover_search_score_pairs
|
|
319
|
+
)
|
|
320
|
+
comic_qs = self.annotate_order_aggregates(comic_qs, Comic)
|
|
321
|
+
comic_qs = self.add_order_by(comic_qs, Comic) # type: ignore
|
|
322
|
+
comic_qs = comic_qs.group_by("id")
|
|
323
|
+
return comic_qs.only("pk", "updated_at")
|
|
324
|
+
|
|
325
|
+
def re_cover_multi_groups(self, group_qs):
|
|
326
|
+
"""Python hack to re-cover groups collapsed with group_by."""
|
|
327
|
+
# This would be better in the main query but OuterRef can't access annotations.
|
|
328
|
+
# So cover_pks aggregates all covers from multi groups like ids does.
|
|
329
|
+
if self.is_model_comic or not self.admin_flags["dynamic_group_covers"]: # type: ignore
|
|
330
|
+
return group_qs
|
|
331
|
+
recovered_group_list = []
|
|
332
|
+
for group in group_qs:
|
|
333
|
+
cover_pks = group.cover_pks
|
|
334
|
+
if len(cover_pks) == 1:
|
|
335
|
+
cover_pk = cover_pks[0]
|
|
336
|
+
cover_updated_at = group.updated_at
|
|
337
|
+
else:
|
|
338
|
+
comic_qs = self._get_cover_pk_query(cover_pks)
|
|
339
|
+
# print(comic_qs.explain())
|
|
340
|
+
# print(comic_qs.query)
|
|
341
|
+
cover = comic_qs[0]
|
|
342
|
+
cover_pk = cover.pk
|
|
343
|
+
cover_updated_at = cover.updated_at
|
|
344
|
+
group.cover_pk = cover_pk
|
|
345
|
+
group.cover_mtime = cover_updated_at
|
|
346
|
+
recovered_group_list.append(group)
|
|
347
|
+
return recovered_group_list
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Browser breadcrumbs calculations."""
|
|
2
|
+
|
|
3
|
+
from types import MappingProxyType
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from codex.views.browser.browser_annotations import BrowserAnnotationsView
|
|
7
|
+
from codex.views.const import GROUP_NAME_MAP
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from codex.models.paths import Folder
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BrowserBreadcrumbsView(BrowserAnnotationsView):
|
|
14
|
+
"""Browser breadcrumbs calculations."""
|
|
15
|
+
|
|
16
|
+
def _is_breadcrumbs_child_folder(self, is_child, group, up_group, up_pks) -> bool:
|
|
17
|
+
"""Is this page a child of the folder breadcrumbs."""
|
|
18
|
+
if group != up_group or not self.group_instance: # type: ignore
|
|
19
|
+
return is_child
|
|
20
|
+
parent_folder: Folder | None = self.group_instance.parent_folder # type: ignore
|
|
21
|
+
parent_pk = parent_folder.pk if parent_folder else 0
|
|
22
|
+
return parent_pk in up_pks or (not up_pks and not parent_pk)
|
|
23
|
+
|
|
24
|
+
def _is_breadcrumbs_child_groups(self, is_child, group, up_group, up_pks):
|
|
25
|
+
"""Is this page a child of the group breadcrumbs."""
|
|
26
|
+
show = self.params["show"]
|
|
27
|
+
test_parent = False
|
|
28
|
+
parent_group = None
|
|
29
|
+
for show_group, enabled in reversed(show.items()):
|
|
30
|
+
if not test_parent:
|
|
31
|
+
if show_group == group:
|
|
32
|
+
test_parent = True
|
|
33
|
+
continue
|
|
34
|
+
if not enabled:
|
|
35
|
+
continue
|
|
36
|
+
group_name = GROUP_NAME_MAP[show_group]
|
|
37
|
+
parent_group = getattr(self.group_instance, group_name, None) # type: ignore
|
|
38
|
+
is_child = bool(parent_group) and (parent_group.pk in up_pks)
|
|
39
|
+
break
|
|
40
|
+
if up_group == "r" and not parent_group:
|
|
41
|
+
is_child = True
|
|
42
|
+
return is_child
|
|
43
|
+
|
|
44
|
+
def _is_breadcrumbs_child(self, breadcrumbs) -> bool:
|
|
45
|
+
"""Is the current route a child of the stored breadcrumbs."""
|
|
46
|
+
is_child = False
|
|
47
|
+
if not breadcrumbs:
|
|
48
|
+
return is_child
|
|
49
|
+
group = self.kwargs["group"]
|
|
50
|
+
pks = self.kwargs["pks"]
|
|
51
|
+
up_route = breadcrumbs[-1]
|
|
52
|
+
up_group = up_route["group"]
|
|
53
|
+
up_pks = up_route["pks"]
|
|
54
|
+
if group == self.STORY_ARC_GROUP:
|
|
55
|
+
is_child = group == up_group and pks
|
|
56
|
+
elif group == self.FOLDER_GROUP:
|
|
57
|
+
is_child = self._is_breadcrumbs_child_folder(
|
|
58
|
+
is_child, group, up_group, up_pks
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
is_child = self._is_breadcrumbs_child_groups(
|
|
62
|
+
is_child, group, up_group, up_pks
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return is_child
|
|
66
|
+
|
|
67
|
+
def _create_breadcrumbs_story_arc(self):
|
|
68
|
+
"""Create the only story_arc parent."""
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
"group": self.STORY_ARC_GROUP,
|
|
72
|
+
"pks": (),
|
|
73
|
+
"page": 1,
|
|
74
|
+
"name": "",
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
def _create_breadcrumbs_folder(self):
|
|
79
|
+
"""Create folder parents."""
|
|
80
|
+
breadcrumbs = []
|
|
81
|
+
folder: Folder | None = self.group_instance # type: ignore
|
|
82
|
+
while folder:
|
|
83
|
+
folder = folder.parent_folder # type: ignore
|
|
84
|
+
pks = (folder.pk,) if folder else ()
|
|
85
|
+
name = folder.name if folder else ""
|
|
86
|
+
parent_route = {
|
|
87
|
+
"group": self.FOLDER_GROUP,
|
|
88
|
+
"pks": pks,
|
|
89
|
+
"page": 1,
|
|
90
|
+
"name": name,
|
|
91
|
+
}
|
|
92
|
+
breadcrumbs = [parent_route, *breadcrumbs]
|
|
93
|
+
return breadcrumbs
|
|
94
|
+
|
|
95
|
+
def _create_breadcrumbs_group(self):
|
|
96
|
+
"""Create browse group parents."""
|
|
97
|
+
breadcrumbs = [{"group": self.ROOT_GROUP, "pks": (), "page": 1, "name": ""}]
|
|
98
|
+
show = self.params["show"]
|
|
99
|
+
for show_group, enabled in show.items():
|
|
100
|
+
if not enabled or show_group not in self.valid_nav_groups: # type: ignore
|
|
101
|
+
continue
|
|
102
|
+
attr = GROUP_NAME_MAP[show_group] # + "_id"
|
|
103
|
+
parent_group = getattr(self.group_instance, attr, None) # type: ignore
|
|
104
|
+
if parent_group:
|
|
105
|
+
pks = (parent_group.pk,)
|
|
106
|
+
name = parent_group.name
|
|
107
|
+
parent_group_route = {
|
|
108
|
+
"group": show_group,
|
|
109
|
+
"pks": pks,
|
|
110
|
+
"page": 1,
|
|
111
|
+
"name": name,
|
|
112
|
+
}
|
|
113
|
+
breadcrumbs += [parent_group_route]
|
|
114
|
+
return breadcrumbs
|
|
115
|
+
|
|
116
|
+
def _create_breadcrumbs(self):
|
|
117
|
+
"""Create breadcrumbs from scratch."""
|
|
118
|
+
pks = self.kwargs["pks"]
|
|
119
|
+
if not pks:
|
|
120
|
+
return []
|
|
121
|
+
group = self.kwargs["group"]
|
|
122
|
+
if group == self.STORY_ARC_GROUP:
|
|
123
|
+
breadcrumbs = self._create_breadcrumbs_story_arc()
|
|
124
|
+
elif group == self.FOLDER_GROUP:
|
|
125
|
+
breadcrumbs = self._create_breadcrumbs_folder()
|
|
126
|
+
else:
|
|
127
|
+
breadcrumbs = self._create_breadcrumbs_group()
|
|
128
|
+
return breadcrumbs
|
|
129
|
+
|
|
130
|
+
def _is_breadcrumbs_identical(self, breadcrumbs):
|
|
131
|
+
if not breadcrumbs:
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
up_route = breadcrumbs[-1]
|
|
135
|
+
if not up_route:
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
group = self.kwargs["group"]
|
|
139
|
+
if up_route["group"] != group:
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
pks = self.kwargs["pks"]
|
|
143
|
+
if set(up_route["pks"]) != set(pks):
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
return True
|
|
147
|
+
|
|
148
|
+
def get_parent_breadcrumbs(
|
|
149
|
+
self,
|
|
150
|
+
) -> tuple[dict[str, str | tuple[int, ...] | int], ...]:
|
|
151
|
+
"""Get the breadcrumbs."""
|
|
152
|
+
breadcrumbs: list[dict[str, str | tuple[int, ...] | int]] = list(
|
|
153
|
+
self.get_from_session("breadcrumbs")
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if not self._is_breadcrumbs_identical(breadcrumbs):
|
|
157
|
+
if not self._is_breadcrumbs_child(breadcrumbs):
|
|
158
|
+
breadcrumbs = self._create_breadcrumbs()
|
|
159
|
+
|
|
160
|
+
# Add current route to crumbs
|
|
161
|
+
group = self.kwargs["group"]
|
|
162
|
+
pks = self.kwargs["pks"]
|
|
163
|
+
name = self.group_instance.name if self.group_instance else "" # type: ignore
|
|
164
|
+
page = self.kwargs["page"]
|
|
165
|
+
route = {"group": group, "pks": pks, "page": page, "name": name}
|
|
166
|
+
breadcrumbs += [route]
|
|
167
|
+
|
|
168
|
+
# Save params
|
|
169
|
+
params = dict(self.params)
|
|
170
|
+
params["breadcrumbs"] = breadcrumbs
|
|
171
|
+
self.params = MappingProxyType(params)
|
|
172
|
+
|
|
173
|
+
parent_breadcrumbs = breadcrumbs[:-1] if breadcrumbs else ()
|
|
174
|
+
|
|
175
|
+
return tuple(parent_breadcrumbs)
|
|
@@ -0,0 +1,63 @@
|
|
|
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 __init__(self, *args, **kwargs):
|
|
15
|
+
"""Set params for the type checker."""
|
|
16
|
+
super().__init__(*args, **kwargs)
|
|
17
|
+
self.order_key: str = ""
|
|
18
|
+
|
|
19
|
+
def set_order_key(self):
|
|
20
|
+
"""Get the default order key for the view."""
|
|
21
|
+
order_key: str = self.params.get("order_by", "") # type: ignore
|
|
22
|
+
if not order_key:
|
|
23
|
+
group = self.kwargs.get("group")
|
|
24
|
+
order_key = "filename" if group == self.FOLDER_GROUP else "sort_name"
|
|
25
|
+
self.order_key: str = order_key
|
|
26
|
+
order_reverse = self.params.get("order_reverse")
|
|
27
|
+
self.order_agg_func = Max if order_reverse else Min
|
|
28
|
+
|
|
29
|
+
def add_order_by(self, qs, model):
|
|
30
|
+
"""Create the order_by list."""
|
|
31
|
+
order_fields_head = None
|
|
32
|
+
if model == Comic:
|
|
33
|
+
if self.order_key == "sort_name":
|
|
34
|
+
order_fields_head = [
|
|
35
|
+
*self.comic_sort_names, # type: ignore
|
|
36
|
+
"issue_number",
|
|
37
|
+
"issue_suffix",
|
|
38
|
+
"sort_name",
|
|
39
|
+
]
|
|
40
|
+
else:
|
|
41
|
+
# Comic orders on indexed fields directly
|
|
42
|
+
# Which is allegedly faster than using tmp b-trees (annotations)
|
|
43
|
+
# And since that's every cover sort, it's worth it.
|
|
44
|
+
order_fields_head = [self.order_key]
|
|
45
|
+
# Comic order micro optimizations
|
|
46
|
+
if self.order_key == "story_arc_number":
|
|
47
|
+
order_fields_head += ["date"]
|
|
48
|
+
elif self.order_key == "bookmark_updated_at":
|
|
49
|
+
order_fields_head += ["updated_at"]
|
|
50
|
+
else:
|
|
51
|
+
order_fields_head = ["order_value"]
|
|
52
|
+
|
|
53
|
+
order_fields = (*order_fields_head, "pk")
|
|
54
|
+
|
|
55
|
+
prefix = "-" if self.params.get("order_reverse") else ""
|
|
56
|
+
order_by = []
|
|
57
|
+
if prefix:
|
|
58
|
+
for field in order_fields:
|
|
59
|
+
order_by.append(prefix + field)
|
|
60
|
+
else:
|
|
61
|
+
order_by = order_fields
|
|
62
|
+
|
|
63
|
+
return qs.order_by(*order_by)
|