codex 1.7.0__py3-none-any.whl → 1.7.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/db.py +17 -32
- codex/exceptions.py +3 -2
- codex/librarian/covers/coverd.py +4 -3
- codex/librarian/covers/create.py +7 -8
- codex/librarian/covers/path.py +2 -1
- codex/librarian/covers/purge.py +2 -2
- codex/librarian/janitor/integrity.py +10 -12
- codex/librarian/janitor/tasks.py +1 -1
- codex/librarian/librariand.py +0 -5
- codex/librarian/watchdog/emitter.py +19 -21
- codex/librarian/watchdog/event_batcherd.py +1 -0
- codex/migrations/0030_nocase_collation_day_month_indexes_status_types.py +0 -4
- codex/models/admin.py +5 -0
- codex/models/functions.py +5 -6
- codex/models/query.py +0 -6
- codex/run.py +9 -11
- codex/serializers/admin/tasks.py +2 -2
- codex/serializers/browser/mixins.py +14 -18
- codex/serializers/browser/settings.py +4 -4
- codex/serializers/choices.py +196 -0
- codex/serializers/fields.py +5 -9
- codex/startup.py +4 -6
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js +1 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.gz +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js +1 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js +1 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js +1 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.gz +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js +1 -0
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.gz +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.js +1 -0
- codex/static_root/assets/VCombobox-D1_KU27A.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.js.gz +0 -0
- codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css +1 -0
- codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.br +0 -0
- codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.gz +0 -0
- codex/static_root/assets/VDialog-38np3TSA.css +1 -0
- codex/static_root/assets/VDialog-38np3TSA.css.br +0 -0
- codex/static_root/assets/VDialog-38np3TSA.css.gz +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js +1 -0
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.br +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.gz +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.js +1 -0
- codex/static_root/assets/VDialog-GbxvFMMN.js.br +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js +1 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js +1 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js +1 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js +1 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js.gz +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css +1 -0
- codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.br +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.gz +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.css +1 -0
- codex/static_root/assets/VSelect-Ca2bIOso.css.br +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.css.gz +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js +1 -0
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.br +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.gz +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.js +1 -0
- codex/static_root/assets/VSelect-txrnRiNJ.js.br +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js +1 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js +1 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css +1 -0
- codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.br +0 -0
- codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.gz +0 -0
- codex/static_root/assets/VSlideGroup-Dk_1upHt.css +1 -0
- codex/static_root/assets/VSlideGroup-Dk_1upHt.css.br +0 -0
- codex/static_root/assets/VSlideGroup-Dk_1upHt.css.gz +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js +1 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js +1 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js.gz +0 -0
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js +1 -0
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.br +0 -0
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.gz +0 -0
- codex/static_root/assets/VTable-2af995xo.js +1 -0
- codex/static_root/assets/VTable-2af995xo.js.br +0 -0
- codex/static_root/assets/VTable-2af995xo.js.gz +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +1 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css +1 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js +1 -0
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.br +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.gz +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.js +1 -0
- codex/static_root/assets/VTextField-G6CMj7yO.js.br +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js +1 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.br +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.gz +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js +1 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js.br +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js.gz +0 -0
- codex/static_root/assets/{admin-DJ7pIXDQ.css → admin-CWEjaFr5.7d4523387384.css} +1 -1
- codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.br +0 -0
- codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.gz +0 -0
- codex/static_root/assets/{admin-DJ7pIXDQ.e2a77af73ef2.css → admin-CWEjaFr5.css} +1 -1
- codex/static_root/assets/admin-CWEjaFr5.css.br +0 -0
- codex/static_root/assets/admin-CWEjaFr5.css.gz +0 -0
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js +1 -0
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.br +0 -0
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.gz +0 -0
- codex/static_root/assets/admin-CsPAIQeG.js +1 -0
- codex/static_root/assets/admin-CsPAIQeG.js.br +0 -0
- codex/static_root/assets/admin-CsPAIQeG.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js +30 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js +30 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css +1 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css +1 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.gz +0 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js +1 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js.br +0 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js.gz +0 -0
- codex/static_root/assets/browser-C18l93yc.js +1 -0
- codex/static_root/assets/browser-C18l93yc.js.br +0 -0
- codex/static_root/assets/browser-C18l93yc.js.gz +0 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css +1 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.br +0 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.gz +0 -0
- codex/static_root/assets/browser-DaAqlBYO.css +1 -0
- codex/static_root/assets/browser-DaAqlBYO.css.br +0 -0
- codex/static_root/assets/browser-DaAqlBYO.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js +1 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js +1 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css +1 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css +1 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.gz +0 -0
- codex/static_root/assets/confirm-dialog-BmhedLCx.7c2103d319d4.css +1 -0
- codex/static_root/assets/confirm-dialog-BmhedLCx.css +1 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js +1 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js +1 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js.gz +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js +1 -0
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.br +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.gz +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.js +1 -0
- codex/static_root/assets/datetime-column-uSqKZU37.js.br +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.js.gz +0 -0
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js +1 -0
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.br +0 -0
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.gz +0 -0
- codex/static_root/assets/filter-CBl5KEsB.js +1 -0
- codex/static_root/assets/filter-CBl5KEsB.js.br +0 -0
- codex/static_root/assets/filter-CBl5KEsB.js.gz +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js +1 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.br +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.gz +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js +1 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js.br +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js.gz +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.css +1 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.css.br +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.css.gz +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css +1 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.br +0 -0
- codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.gz +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js +1 -0
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.br +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.gz +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.js +1 -0
- codex/static_root/assets/group-tab-BOW9kigw.js.br +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.js.gz +0 -0
- codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css +1 -0
- codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.br +0 -0
- codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.gz +0 -0
- codex/static_root/assets/group-tab-CBKuS3Iv.css +1 -0
- codex/static_root/assets/group-tab-CBKuS3Iv.css.br +0 -0
- codex/static_root/assets/group-tab-CBKuS3Iv.css.gz +0 -0
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js +1 -0
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.br +4 -0
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.gz +0 -0
- codex/static_root/assets/http-error-C6yuLApo.js +1 -0
- codex/static_root/assets/http-error-C6yuLApo.js.br +4 -0
- codex/static_root/assets/http-error-C6yuLApo.js.gz +0 -0
- codex/static_root/assets/{library-tab-Wew8YOLv.0c1780a6b68e.css → library-tab-Bh7ErAk7.7c44200ee727.css} +1 -1
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.br +2 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.gz +0 -0
- codex/static_root/assets/{library-tab-Wew8YOLv.css → library-tab-Bh7ErAk7.css} +1 -1
- codex/static_root/assets/library-tab-Bh7ErAk7.css.br +2 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.css.gz +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js +1 -0
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.br +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.gz +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.js +1 -0
- codex/static_root/assets/library-tab-D3WOtpE0.js.br +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.js.gz +0 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js +6 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.br +0 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.gz +0 -0
- codex/static_root/assets/main-CmjU2oTp.js +6 -0
- codex/static_root/assets/main-CmjU2oTp.js.br +0 -0
- codex/static_root/assets/main-CmjU2oTp.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js +1 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js +1 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js +1 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js +1 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-ctqZIwOP.7e7b2c815ea4.js → pdf-doc-BcRNpoaW.882f6e967869.js} +3 -3
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-ctqZIwOP.js → pdf-doc-BcRNpoaW.js} +3 -3
- codex/static_root/assets/pdf-doc-BcRNpoaW.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.js.gz +0 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js +2 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.br +0 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.gz +0 -0
- codex/static_root/assets/reader-DIPRg2VB.js +2 -0
- codex/static_root/assets/reader-DIPRg2VB.js.br +0 -0
- codex/static_root/assets/reader-DIPRg2VB.js.gz +0 -0
- codex/static_root/assets/{reader-nD70BeEz.06e85e73c391.css → reader-DveZZr9x.7b21b9f8f628.css} +1 -1
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.gz +0 -0
- codex/static_root/assets/{reader-nD70BeEz.css → reader-DveZZr9x.css} +1 -1
- codex/static_root/assets/reader-DveZZr9x.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.css.gz +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js +1 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.br +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.gz +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.js +1 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.js.br +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js +2 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.br +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.js +2 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.js.br +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.js.gz +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js +1 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.br +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.gz +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js +1 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js.br +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js.gz +0 -0
- codex/static_root/assets/{stats-tab-BAYTqQNg.5f715428c394.css → stats-tab-DpfGsVX3.1cb66f2649a9.css} +1 -1
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.gz +0 -0
- codex/static_root/assets/{stats-tab-BAYTqQNg.css → stats-tab-DpfGsVX3.css} +1 -1
- codex/static_root/assets/stats-tab-DpfGsVX3.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.css.gz +0 -0
- codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css +1 -0
- codex/static_root/assets/{task-tab--cXi48kg.5dfd6890a1c2.css.br → task-tab-1XiWn9xP.06cb8e1b794d.css.br} +1 -1
- codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css.gz +0 -0
- codex/static_root/assets/task-tab-1XiWn9xP.css +1 -0
- codex/static_root/assets/{task-tab--cXi48kg.css.br → task-tab-1XiWn9xP.css.br} +1 -1
- codex/static_root/assets/task-tab-1XiWn9xP.css.gz +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js +1 -0
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.br +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.gz +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.js +1 -0
- codex/static_root/assets/task-tab-C8D0qRP_.js.br +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.js.gz +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js +1 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.br +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.gz +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js +1 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js.br +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js.gz +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js +1 -0
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.br +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.gz +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.js +1 -0
- codex/static_root/assets/user-tab-BU31Z92y.js.br +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.js.gz +0 -0
- codex/static_root/js/choices-admin.8aa64c911203.json +1 -0
- codex/static_root/js/choices-admin.8aa64c911203.json.br +0 -0
- codex/static_root/js/choices-admin.8aa64c911203.json.gz +0 -0
- codex/static_root/js/choices-admin.json +1 -0
- 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.2f6b0359e1ef.json +1 -0
- codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
- codex/static_root/js/choices.json +1 -0
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/manifest.45cb46e19686.json +635 -0
- codex/static_root/manifest.45cb46e19686.json.br +0 -0
- codex/static_root/manifest.45cb46e19686.json.gz +0 -0
- codex/static_root/manifest.json +300 -377
- 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 +6 -5
- codex/threads.py +1 -1
- codex/urls/api/auth.py +1 -1
- codex/urls/api/browser.py +1 -1
- codex/views/admin/library.py +13 -18
- codex/views/admin/stats.py +16 -23
- codex/views/admin/tasks.py +2 -1
- codex/views/auth.py +77 -42
- codex/views/bookmark.py +153 -16
- codex/views/browser/annotations.py +339 -0
- codex/views/browser/base.py +139 -0
- codex/views/browser/breadcrumbs.py +11 -90
- codex/views/browser/browser.py +110 -44
- codex/views/browser/choices.py +18 -15
- codex/views/browser/cover.py +20 -10
- codex/views/browser/filters/annotations.py +125 -0
- codex/views/browser/filters/bookmark.py +19 -18
- codex/views/browser/filters/field.py +3 -8
- codex/views/browser/filters/group.py +6 -9
- codex/views/browser/filters/search/fts.py +19 -5
- codex/views/browser/filters/search/parse.py +24 -54
- codex/views/browser/metadata.py +332 -0
- codex/views/browser/mtime.py +10 -2
- codex/views/browser/order_by.py +22 -22
- codex/views/browser/page_in_bounds.py +2 -2
- codex/views/browser/paginate.py +15 -9
- codex/views/browser/title.py +6 -1
- codex/views/browser/validate.py +33 -68
- codex/views/download.py +1 -1
- codex/views/mixins.py +15 -27
- codex/views/opds/const.py +3 -2
- codex/views/opds/urls.py +2 -2
- codex/views/opds/util.py +3 -2
- codex/views/opds/v1/entry/links.py +1 -3
- codex/views/opds/v1/facets.py +12 -65
- codex/views/opds/v1/feed.py +64 -5
- codex/views/opds/v1/links.py +4 -5
- codex/views/opds/v2/feed.py +28 -1
- codex/views/opds/v2/links.py +1 -26
- codex/views/opds/v2/publications.py +6 -5
- codex/views/opds/v2/top_links.py +6 -3
- codex/views/public.py +2 -2
- codex/views/reader/arcs.py +3 -0
- codex/views/reader/books.py +41 -57
- codex/views/reader/{params.py → init.py} +31 -37
- codex/views/reader/page.py +4 -10
- codex/views/reader/reader.py +2 -1
- codex/views/session.py +54 -43
- codex/views/version.py +1 -1
- {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/METADATA +5 -2
- {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/RECORD +385 -419
- codex/choices.py +0 -419
- codex/choices_to_json.py +0 -122
- codex/librarian/bookmark/__init__.py +0 -1
- codex/librarian/bookmark/bookmarkd.py +0 -38
- codex/librarian/bookmark/tasks.py +0 -19
- codex/librarian/bookmark/update.py +0 -154
- codex/static_root/assets/VCheckbox-D_CWEmqR.84f2b49311e8.js +0 -1
- codex/static_root/assets/VCheckbox-D_CWEmqR.84f2b49311e8.js.br +0 -0
- codex/static_root/assets/VCheckbox-D_CWEmqR.84f2b49311e8.js.gz +0 -0
- codex/static_root/assets/VCheckbox-D_CWEmqR.js +0 -1
- codex/static_root/assets/VCheckbox-D_CWEmqR.js.br +0 -0
- codex/static_root/assets/VCheckbox-D_CWEmqR.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-B1BhcFvi.0eaf3c516474.js +0 -1
- codex/static_root/assets/VCheckboxBtn-B1BhcFvi.0eaf3c516474.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B1BhcFvi.0eaf3c516474.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-B1BhcFvi.js +0 -1
- codex/static_root/assets/VCheckboxBtn-B1BhcFvi.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-B1BhcFvi.js.gz +0 -0
- codex/static_root/assets/VCombobox-CZAW6_3Q.0e2401e819a5.js +0 -1
- codex/static_root/assets/VCombobox-CZAW6_3Q.0e2401e819a5.js.br +0 -0
- codex/static_root/assets/VCombobox-CZAW6_3Q.0e2401e819a5.js.gz +0 -0
- codex/static_root/assets/VCombobox-CZAW6_3Q.js +0 -1
- codex/static_root/assets/VCombobox-CZAW6_3Q.js.br +0 -0
- codex/static_root/assets/VCombobox-CZAW6_3Q.js.gz +0 -0
- codex/static_root/assets/VDialog-CSHgHIlG.a0bcd336cb7b.js +0 -1
- codex/static_root/assets/VDialog-CSHgHIlG.a0bcd336cb7b.js.br +0 -0
- codex/static_root/assets/VDialog-CSHgHIlG.a0bcd336cb7b.js.gz +0 -0
- codex/static_root/assets/VDialog-CSHgHIlG.js +0 -1
- codex/static_root/assets/VDialog-CSHgHIlG.js.br +0 -0
- codex/static_root/assets/VDialog-CSHgHIlG.js.gz +0 -0
- codex/static_root/assets/VDialog-CSvrKKb2.css +0 -1
- codex/static_root/assets/VDialog-CSvrKKb2.css.br +0 -0
- codex/static_root/assets/VDialog-CSvrKKb2.css.gz +0 -0
- codex/static_root/assets/VDialog-CSvrKKb2.fd4d50904b17.css +0 -1
- codex/static_root/assets/VDialog-CSvrKKb2.fd4d50904b17.css.br +0 -0
- codex/static_root/assets/VDialog-CSvrKKb2.fd4d50904b17.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CG6MpdMC.e96ae8afad97.js +0 -1
- codex/static_root/assets/VExpansionPanels-CG6MpdMC.e96ae8afad97.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CG6MpdMC.e96ae8afad97.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CG6MpdMC.js +0 -1
- codex/static_root/assets/VExpansionPanels-CG6MpdMC.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CG6MpdMC.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-CAgQxkiU.f26ecb0c3e34.js +0 -1
- codex/static_root/assets/VRadioGroup-CAgQxkiU.f26ecb0c3e34.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CAgQxkiU.f26ecb0c3e34.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-CAgQxkiU.js +0 -1
- codex/static_root/assets/VRadioGroup-CAgQxkiU.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CAgQxkiU.js.gz +0 -0
- codex/static_root/assets/VSelect-C1gez8rl.9b11d2f00faf.js +0 -1
- codex/static_root/assets/VSelect-C1gez8rl.9b11d2f00faf.js.br +0 -0
- codex/static_root/assets/VSelect-C1gez8rl.9b11d2f00faf.js.gz +0 -0
- codex/static_root/assets/VSelect-C1gez8rl.js +0 -1
- codex/static_root/assets/VSelect-C1gez8rl.js.br +0 -0
- codex/static_root/assets/VSelect-C1gez8rl.js.gz +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css +0 -1
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.br +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.gz +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.css +0 -1
- codex/static_root/assets/VSelect-DLu8bpcs.css.br +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-DBqDlKjK.160beaa1b56c.js +0 -1
- codex/static_root/assets/VSelectionControl-DBqDlKjK.160beaa1b56c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DBqDlKjK.160beaa1b56c.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DBqDlKjK.js +0 -1
- codex/static_root/assets/VSelectionControl-DBqDlKjK.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DBqDlKjK.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css +0 -1
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.br +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.gz +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.css +0 -1
- codex/static_root/assets/VSlideGroup-BtXPhMOT.css.br +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.css.gz +0 -0
- codex/static_root/assets/VSlideGroup-SqR_NAL1.40ba1d838a6e.js +0 -1
- codex/static_root/assets/VSlideGroup-SqR_NAL1.40ba1d838a6e.js.br +0 -0
- codex/static_root/assets/VSlideGroup-SqR_NAL1.40ba1d838a6e.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-SqR_NAL1.js +0 -1
- codex/static_root/assets/VSlideGroup-SqR_NAL1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-SqR_NAL1.js.gz +0 -0
- codex/static_root/assets/VTable-CYC-wMpk.8f15d66044da.js +0 -1
- codex/static_root/assets/VTable-CYC-wMpk.8f15d66044da.js.br +0 -0
- codex/static_root/assets/VTable-CYC-wMpk.8f15d66044da.js.gz +0 -0
- codex/static_root/assets/VTable-CYC-wMpk.js +0 -1
- codex/static_root/assets/VTable-CYC-wMpk.js.br +0 -0
- codex/static_root/assets/VTable-CYC-wMpk.js.gz +0 -0
- codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css +0 -1
- codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css.br +0 -0
- codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css.gz +0 -0
- codex/static_root/assets/VTextField-BnCEPZIq.css +0 -1
- codex/static_root/assets/VTextField-BnCEPZIq.css.br +0 -0
- codex/static_root/assets/VTextField-BnCEPZIq.css.gz +0 -0
- codex/static_root/assets/VTextField-DMRKRbbT.911ac84d8c9d.js +0 -1
- codex/static_root/assets/VTextField-DMRKRbbT.911ac84d8c9d.js.br +0 -0
- codex/static_root/assets/VTextField-DMRKRbbT.911ac84d8c9d.js.gz +0 -0
- codex/static_root/assets/VTextField-DMRKRbbT.js +0 -1
- codex/static_root/assets/VTextField-DMRKRbbT.js.br +0 -0
- codex/static_root/assets/VTextField-DMRKRbbT.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D0P2xNJl.6a10e3f3a153.js +0 -1
- codex/static_root/assets/VWindowItem-D0P2xNJl.6a10e3f3a153.js.br +0 -0
- codex/static_root/assets/VWindowItem-D0P2xNJl.6a10e3f3a153.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D0P2xNJl.js +0 -1
- codex/static_root/assets/VWindowItem-D0P2xNJl.js.br +0 -0
- codex/static_root/assets/VWindowItem-D0P2xNJl.js.gz +0 -0
- codex/static_root/assets/admin-CD90VMen.4c2b6f2a5111.js +0 -1
- codex/static_root/assets/admin-CD90VMen.4c2b6f2a5111.js.br +0 -0
- codex/static_root/assets/admin-CD90VMen.4c2b6f2a5111.js.gz +0 -0
- codex/static_root/assets/admin-CD90VMen.js +0 -1
- codex/static_root/assets/admin-CD90VMen.js.br +0 -0
- codex/static_root/assets/admin-CD90VMen.js.gz +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.css.br +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.css.gz +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.br +0 -0
- codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.gz +0 -0
- codex/static_root/assets/admin-menu-B9bzD6hV.c13b7df2ff57.css +0 -1
- codex/static_root/assets/admin-menu-B9bzD6hV.c13b7df2ff57.css.br +0 -0
- codex/static_root/assets/admin-menu-B9bzD6hV.c13b7df2ff57.css.gz +0 -0
- codex/static_root/assets/admin-menu-B9bzD6hV.css +0 -1
- codex/static_root/assets/admin-menu-B9bzD6hV.css.br +0 -0
- codex/static_root/assets/admin-menu-B9bzD6hV.css.gz +0 -0
- codex/static_root/assets/admin-menu-BpEPGKr_.c9f9178c5a82.js +0 -1
- codex/static_root/assets/admin-menu-BpEPGKr_.c9f9178c5a82.js.br +0 -0
- codex/static_root/assets/admin-menu-BpEPGKr_.c9f9178c5a82.js.gz +0 -0
- codex/static_root/assets/admin-menu-BpEPGKr_.js +0 -1
- codex/static_root/assets/admin-menu-BpEPGKr_.js.br +0 -0
- codex/static_root/assets/admin-menu-BpEPGKr_.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-DDF4rY07.4f1158b650ab.js +0 -1
- codex/static_root/assets/admin-settings-button-progress-DDF4rY07.4f1158b650ab.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-DDF4rY07.4f1158b650ab.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-DDF4rY07.js +0 -1
- codex/static_root/assets/admin-settings-button-progress-DDF4rY07.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-DDF4rY07.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-DUUQfVsZ.2548c49a853e.css +0 -1
- codex/static_root/assets/admin-settings-button-progress-DUUQfVsZ.css +0 -1
- codex/static_root/assets/browser-C0KS3g2E.0bf1c9c6319c.js +0 -1
- codex/static_root/assets/browser-C0KS3g2E.0bf1c9c6319c.js.br +0 -0
- codex/static_root/assets/browser-C0KS3g2E.0bf1c9c6319c.js.gz +0 -0
- codex/static_root/assets/browser-C0KS3g2E.js +0 -1
- codex/static_root/assets/browser-C0KS3g2E.js.br +0 -0
- codex/static_root/assets/browser-C0KS3g2E.js.gz +0 -0
- codex/static_root/assets/browser-JFL2hLjt.21cb2f7335f7.css +0 -1
- codex/static_root/assets/browser-JFL2hLjt.21cb2f7335f7.css.br +0 -0
- codex/static_root/assets/browser-JFL2hLjt.21cb2f7335f7.css.gz +0 -0
- codex/static_root/assets/browser-JFL2hLjt.css +0 -1
- codex/static_root/assets/browser-JFL2hLjt.css.br +0 -0
- codex/static_root/assets/browser-JFL2hLjt.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-DXJalgW4.a9f34729599d.js +0 -1
- codex/static_root/assets/change-password-dialog-DXJalgW4.a9f34729599d.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DXJalgW4.a9f34729599d.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DXJalgW4.js +0 -1
- codex/static_root/assets/change-password-dialog-DXJalgW4.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DXJalgW4.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css +0 -1
- codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css.br +0 -0
- codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-DrIXdKUC.css +0 -1
- codex/static_root/assets/change-password-dialog-DrIXdKUC.css.br +0 -0
- codex/static_root/assets/change-password-dialog-DrIXdKUC.css.gz +0 -0
- codex/static_root/assets/confirm-dialog-B7w6z2rf.472011c06253.css +0 -1
- codex/static_root/assets/confirm-dialog-B7w6z2rf.css +0 -1
- codex/static_root/assets/confirm-dialog-CqDEpU9k.e1811488121d.js +0 -1
- codex/static_root/assets/confirm-dialog-CqDEpU9k.e1811488121d.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CqDEpU9k.e1811488121d.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CqDEpU9k.js +0 -1
- codex/static_root/assets/confirm-dialog-CqDEpU9k.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CqDEpU9k.js.gz +0 -0
- codex/static_root/assets/datetime-column-UxNaJr8b.c06a11531af3.js +0 -1
- codex/static_root/assets/datetime-column-UxNaJr8b.c06a11531af3.js.br +0 -0
- codex/static_root/assets/datetime-column-UxNaJr8b.c06a11531af3.js.gz +0 -0
- codex/static_root/assets/datetime-column-UxNaJr8b.js +0 -1
- codex/static_root/assets/datetime-column-UxNaJr8b.js.br +0 -0
- codex/static_root/assets/datetime-column-UxNaJr8b.js.gz +0 -0
- codex/static_root/assets/filter-DskcqZHA.e707b5a05b4b.js +0 -1
- codex/static_root/assets/filter-DskcqZHA.e707b5a05b4b.js.br +0 -0
- codex/static_root/assets/filter-DskcqZHA.e707b5a05b4b.js.gz +0 -0
- codex/static_root/assets/filter-DskcqZHA.js +0 -1
- codex/static_root/assets/filter-DskcqZHA.js.br +0 -0
- codex/static_root/assets/filter-DskcqZHA.js.gz +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css +0 -1
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.br +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.gz +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.css +0 -1
- codex/static_root/assets/flag-tab-CqWovuBu.css.br +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.css.gz +0 -0
- codex/static_root/assets/flag-tab-xBeWEQ0w.582e77d9e885.js +0 -1
- codex/static_root/assets/flag-tab-xBeWEQ0w.582e77d9e885.js.br +0 -0
- codex/static_root/assets/flag-tab-xBeWEQ0w.582e77d9e885.js.gz +0 -0
- codex/static_root/assets/flag-tab-xBeWEQ0w.js +0 -1
- codex/static_root/assets/flag-tab-xBeWEQ0w.js.br +0 -0
- codex/static_root/assets/flag-tab-xBeWEQ0w.js.gz +0 -0
- codex/static_root/assets/group-tab-Czt2x61x.56e2886b1bac.js +0 -1
- codex/static_root/assets/group-tab-Czt2x61x.56e2886b1bac.js.br +0 -0
- codex/static_root/assets/group-tab-Czt2x61x.56e2886b1bac.js.gz +0 -0
- codex/static_root/assets/group-tab-Czt2x61x.js +0 -1
- codex/static_root/assets/group-tab-Czt2x61x.js.br +0 -0
- codex/static_root/assets/group-tab-Czt2x61x.js.gz +0 -0
- codex/static_root/assets/group-tab-DkIN1Heu.948e907b1e6b.css +0 -1
- codex/static_root/assets/group-tab-DkIN1Heu.948e907b1e6b.css.br +0 -0
- codex/static_root/assets/group-tab-DkIN1Heu.948e907b1e6b.css.gz +0 -0
- codex/static_root/assets/group-tab-DkIN1Heu.css +0 -1
- codex/static_root/assets/group-tab-DkIN1Heu.css.br +0 -0
- codex/static_root/assets/group-tab-DkIN1Heu.css.gz +0 -0
- codex/static_root/assets/http-error-NHcwE5e_.d49089e40589.js +0 -1
- codex/static_root/assets/http-error-NHcwE5e_.d49089e40589.js.br +0 -0
- codex/static_root/assets/http-error-NHcwE5e_.d49089e40589.js.gz +0 -0
- codex/static_root/assets/http-error-NHcwE5e_.js +0 -1
- codex/static_root/assets/http-error-NHcwE5e_.js.br +0 -0
- codex/static_root/assets/http-error-NHcwE5e_.js.gz +0 -0
- codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css +0 -1
- codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css.br +0 -0
- codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css.gz +0 -0
- codex/static_root/assets/index-CL_6g4tJ.css +0 -1
- codex/static_root/assets/index-CL_6g4tJ.css.br +0 -0
- codex/static_root/assets/index-CL_6g4tJ.css.gz +0 -0
- codex/static_root/assets/index-DYNNO5kn.24a2d270253a.js +0 -1
- codex/static_root/assets/index-DYNNO5kn.24a2d270253a.js.br +0 -0
- codex/static_root/assets/index-DYNNO5kn.24a2d270253a.js.gz +0 -0
- codex/static_root/assets/index-DYNNO5kn.js +0 -1
- codex/static_root/assets/index-DYNNO5kn.js.br +0 -0
- codex/static_root/assets/index-DYNNO5kn.js.gz +0 -0
- codex/static_root/assets/library-tab-CgIqcjvj.60327dfa8ecb.js +0 -1
- codex/static_root/assets/library-tab-CgIqcjvj.60327dfa8ecb.js.br +0 -0
- codex/static_root/assets/library-tab-CgIqcjvj.60327dfa8ecb.js.gz +0 -0
- codex/static_root/assets/library-tab-CgIqcjvj.js +0 -1
- codex/static_root/assets/library-tab-CgIqcjvj.js.br +0 -0
- codex/static_root/assets/library-tab-CgIqcjvj.js.gz +0 -0
- codex/static_root/assets/library-tab-Wew8YOLv.0c1780a6b68e.css.br +0 -0
- codex/static_root/assets/library-tab-Wew8YOLv.0c1780a6b68e.css.gz +0 -0
- codex/static_root/assets/library-tab-Wew8YOLv.css.br +0 -0
- codex/static_root/assets/library-tab-Wew8YOLv.css.gz +0 -0
- codex/static_root/assets/main-B3EwXQ3Q.14dfc21e272c.js +0 -35
- codex/static_root/assets/main-B3EwXQ3Q.14dfc21e272c.js.br +0 -0
- codex/static_root/assets/main-B3EwXQ3Q.14dfc21e272c.js.gz +0 -0
- codex/static_root/assets/main-B3EwXQ3Q.js +0 -35
- codex/static_root/assets/main-B3EwXQ3Q.js.br +0 -0
- codex/static_root/assets/main-B3EwXQ3Q.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-YkzUXx0B.f39d84e2380a.js +0 -1
- codex/static_root/assets/pager-full-pdf-YkzUXx0B.f39d84e2380a.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-YkzUXx0B.f39d84e2380a.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-YkzUXx0B.js +0 -1
- codex/static_root/assets/pager-full-pdf-YkzUXx0B.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-YkzUXx0B.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CrruiVov.3bf9b795350d.js +0 -1
- codex/static_root/assets/pagination-toolbar-CrruiVov.3bf9b795350d.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CrruiVov.3bf9b795350d.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CrruiVov.js +0 -1
- codex/static_root/assets/pagination-toolbar-CrruiVov.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CrruiVov.js.gz +0 -0
- codex/static_root/assets/pdf-doc-ctqZIwOP.7e7b2c815ea4.js.br +0 -0
- codex/static_root/assets/pdf-doc-ctqZIwOP.7e7b2c815ea4.js.gz +0 -0
- codex/static_root/assets/pdf-doc-ctqZIwOP.js.br +0 -0
- codex/static_root/assets/pdf-doc-ctqZIwOP.js.gz +0 -0
- codex/static_root/assets/reader-DPLhdrJD.268417c618c5.js +0 -2
- codex/static_root/assets/reader-DPLhdrJD.268417c618c5.js.br +0 -0
- codex/static_root/assets/reader-DPLhdrJD.268417c618c5.js.gz +0 -0
- codex/static_root/assets/reader-DPLhdrJD.js +0 -2
- codex/static_root/assets/reader-DPLhdrJD.js.br +0 -0
- codex/static_root/assets/reader-DPLhdrJD.js.gz +0 -0
- codex/static_root/assets/reader-nD70BeEz.06e85e73c391.css.br +0 -0
- codex/static_root/assets/reader-nD70BeEz.06e85e73c391.css.gz +0 -0
- codex/static_root/assets/reader-nD70BeEz.css.br +0 -0
- codex/static_root/assets/reader-nD70BeEz.css.gz +0 -0
- codex/static_root/assets/relation-chips-DOALMcWL.5d3b8100ba03.js +0 -1
- codex/static_root/assets/relation-chips-DOALMcWL.5d3b8100ba03.js.br +0 -0
- codex/static_root/assets/relation-chips-DOALMcWL.5d3b8100ba03.js.gz +0 -0
- codex/static_root/assets/relation-chips-DOALMcWL.js +0 -1
- codex/static_root/assets/relation-chips-DOALMcWL.js.br +0 -0
- codex/static_root/assets/relation-chips-DOALMcWL.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BVj1U_LC.c68369e0c2de.js +0 -2
- codex/static_root/assets/settings-drawer-BVj1U_LC.c68369e0c2de.js.br +0 -0
- codex/static_root/assets/settings-drawer-BVj1U_LC.c68369e0c2de.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BVj1U_LC.js +0 -2
- codex/static_root/assets/settings-drawer-BVj1U_LC.js.br +0 -0
- codex/static_root/assets/settings-drawer-BVj1U_LC.js.gz +0 -0
- codex/static_root/assets/stats-tab-BAYTqQNg.5f715428c394.css.br +0 -0
- codex/static_root/assets/stats-tab-BAYTqQNg.5f715428c394.css.gz +0 -0
- codex/static_root/assets/stats-tab-BAYTqQNg.css.br +0 -0
- codex/static_root/assets/stats-tab-BAYTqQNg.css.gz +0 -0
- codex/static_root/assets/stats-tab-DDLwwwnP.35305258097b.js +0 -1
- codex/static_root/assets/stats-tab-DDLwwwnP.35305258097b.js.br +0 -0
- codex/static_root/assets/stats-tab-DDLwwwnP.35305258097b.js.gz +0 -0
- codex/static_root/assets/stats-tab-DDLwwwnP.js +0 -1
- codex/static_root/assets/stats-tab-DDLwwwnP.js.br +0 -0
- codex/static_root/assets/stats-tab-DDLwwwnP.js.gz +0 -0
- codex/static_root/assets/task-tab--cXi48kg.5dfd6890a1c2.css +0 -1
- codex/static_root/assets/task-tab--cXi48kg.5dfd6890a1c2.css.gz +0 -0
- codex/static_root/assets/task-tab--cXi48kg.css +0 -1
- codex/static_root/assets/task-tab--cXi48kg.css.gz +0 -0
- codex/static_root/assets/task-tab-BpP3n2dO.c853eb565200.js +0 -1
- codex/static_root/assets/task-tab-BpP3n2dO.c853eb565200.js.br +0 -0
- codex/static_root/assets/task-tab-BpP3n2dO.c853eb565200.js.gz +0 -0
- codex/static_root/assets/task-tab-BpP3n2dO.js +0 -1
- codex/static_root/assets/task-tab-BpP3n2dO.js.br +0 -0
- codex/static_root/assets/task-tab-BpP3n2dO.js.gz +0 -0
- codex/static_root/assets/transition-3g2arGgl.c6e6f91b7978.js +0 -1
- codex/static_root/assets/transition-3g2arGgl.c6e6f91b7978.js.br +0 -0
- codex/static_root/assets/transition-3g2arGgl.c6e6f91b7978.js.gz +0 -0
- codex/static_root/assets/transition-3g2arGgl.js +0 -1
- codex/static_root/assets/transition-3g2arGgl.js.br +0 -0
- codex/static_root/assets/transition-3g2arGgl.js.gz +0 -0
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css +0 -1
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.br +0 -4
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.gz +0 -0
- codex/static_root/assets/transition-5LRFTPo5.css +0 -1
- codex/static_root/assets/transition-5LRFTPo5.css.br +0 -4
- codex/static_root/assets/transition-5LRFTPo5.css.gz +0 -0
- codex/static_root/assets/unauthorized-CdumQ1Vf.79d9a61eb572.js +0 -1
- codex/static_root/assets/unauthorized-CdumQ1Vf.79d9a61eb572.js.br +0 -0
- codex/static_root/assets/unauthorized-CdumQ1Vf.79d9a61eb572.js.gz +0 -0
- codex/static_root/assets/unauthorized-CdumQ1Vf.js +0 -1
- codex/static_root/assets/unauthorized-CdumQ1Vf.js.br +0 -0
- codex/static_root/assets/unauthorized-CdumQ1Vf.js.gz +0 -0
- codex/static_root/assets/user-tab-D6oOKZTP.05944b00c700.js +0 -1
- codex/static_root/assets/user-tab-D6oOKZTP.05944b00c700.js.br +0 -0
- codex/static_root/assets/user-tab-D6oOKZTP.05944b00c700.js.gz +0 -0
- codex/static_root/assets/user-tab-D6oOKZTP.js +0 -1
- codex/static_root/assets/user-tab-D6oOKZTP.js.br +0 -0
- codex/static_root/assets/user-tab-D6oOKZTP.js.gz +0 -0
- codex/static_root/manifest.c5f1de88b1ac.json +0 -712
- codex/static_root/manifest.c5f1de88b1ac.json.br +0 -0
- codex/static_root/manifest.c5f1de88b1ac.json.gz +0 -0
- codex/views/browser/annotate/__init__.py +0 -1
- codex/views/browser/annotate/bookmark.py +0 -112
- codex/views/browser/annotate/card.py +0 -61
- codex/views/browser/annotate/order.py +0 -247
- codex/views/browser/filters/filter.py +0 -69
- codex/views/browser/group_mtime.py +0 -83
- codex/views/browser/metadata/__init__.py +0 -1
- codex/views/browser/metadata/annotate.py +0 -109
- codex/views/browser/metadata/copy_intersections.py +0 -90
- codex/views/browser/metadata/metadata.py +0 -83
- codex/views/browser/metadata/query_intersections.py +0 -112
- codex/views/browser/params.py +0 -64
- codex/views/timezone.py +0 -54
- {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/LICENSE +0 -0
- {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/WHEEL +0 -0
- {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/entry_points.txt +0 -0
|
Binary file
|
|
Binary file
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Browser annotation methods."""
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"""Base view for metadata annotations."""
|
|
2
|
-
|
|
3
|
-
from django.db.models import (
|
|
4
|
-
Case,
|
|
5
|
-
F,
|
|
6
|
-
Sum,
|
|
7
|
-
Value,
|
|
8
|
-
When,
|
|
9
|
-
)
|
|
10
|
-
from django.db.models.fields import BooleanField, PositiveSmallIntegerField
|
|
11
|
-
from django.db.models.functions import Least
|
|
12
|
-
from django.db.models.functions.comparison import Coalesce
|
|
13
|
-
|
|
14
|
-
from codex.logger.logging import get_logger
|
|
15
|
-
from codex.models import (
|
|
16
|
-
Comic,
|
|
17
|
-
)
|
|
18
|
-
from codex.models.functions import JsonGroupArray
|
|
19
|
-
from codex.views.browser.annotate.order import BrowserAnnotateOrderView
|
|
20
|
-
|
|
21
|
-
LOG = get_logger(__name__)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class BrowserAnnotateBookmarkView(BrowserAnnotateOrderView):
|
|
25
|
-
"""Base class for views that need special metadata annotations."""
|
|
26
|
-
|
|
27
|
-
def _get_group_bookmark_page_annotation(
|
|
28
|
-
self, qs, bm_rel, bm_filter, page_rel, finished_rel
|
|
29
|
-
):
|
|
30
|
-
"""Get bookmark page subquery."""
|
|
31
|
-
finished_filter = {finished_rel: True}
|
|
32
|
-
prefix = "" if qs.model is Comic else self.rel_prefix
|
|
33
|
-
page_count = prefix + "page_count"
|
|
34
|
-
# Can't use a filtered relation for page & finished because of this
|
|
35
|
-
# page_count case.
|
|
36
|
-
bookmark_page_case = Case(
|
|
37
|
-
When(**{bm_rel: None}, then=0),
|
|
38
|
-
When(**finished_filter, then=page_count),
|
|
39
|
-
default=page_rel,
|
|
40
|
-
output_field=PositiveSmallIntegerField(),
|
|
41
|
-
)
|
|
42
|
-
return Sum(
|
|
43
|
-
bookmark_page_case,
|
|
44
|
-
default=0,
|
|
45
|
-
filter=bm_filter,
|
|
46
|
-
output_field=PositiveSmallIntegerField(),
|
|
47
|
-
distinct=True,
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
@classmethod
|
|
51
|
-
def _get_group_bookmark_finished_annotation(cls, qs, bm_filter, finished_rel):
|
|
52
|
-
"""Get finished_count subquery."""
|
|
53
|
-
finished_count = Sum(
|
|
54
|
-
finished_rel,
|
|
55
|
-
default=0,
|
|
56
|
-
filter=bm_filter,
|
|
57
|
-
output_field=PositiveSmallIntegerField(),
|
|
58
|
-
# distinct breaks this sum and only returns one. idk why.
|
|
59
|
-
)
|
|
60
|
-
qs = qs.alias(finished_count=finished_count)
|
|
61
|
-
|
|
62
|
-
finished_aggregate = Case(
|
|
63
|
-
When(finished_count=F("child_count"), then=True),
|
|
64
|
-
When(finished_count=0, then=False),
|
|
65
|
-
default=None,
|
|
66
|
-
output_field=BooleanField(),
|
|
67
|
-
)
|
|
68
|
-
return qs, finished_aggregate
|
|
69
|
-
|
|
70
|
-
def annotate_bookmarks(self, qs):
|
|
71
|
-
"""Hoist up bookmark annotations."""
|
|
72
|
-
bm_rel = self._get_bm_rel(qs.model)
|
|
73
|
-
bm_filter = self._get_my_bookmark_filter(bm_rel)
|
|
74
|
-
page_rel = f"{bm_rel}__page"
|
|
75
|
-
finished_rel = f"{bm_rel}__finished"
|
|
76
|
-
|
|
77
|
-
if qs.model is Comic:
|
|
78
|
-
bookmark_page = Sum(page_rel, filter=bm_filter, default=0)
|
|
79
|
-
finished_aggregate = Sum(finished_rel, filter=bm_filter, default=False)
|
|
80
|
-
else:
|
|
81
|
-
bookmark_page = self._get_group_bookmark_page_annotation(
|
|
82
|
-
qs, bm_rel, bm_filter, page_rel, finished_rel
|
|
83
|
-
)
|
|
84
|
-
qs, finished_aggregate = self._get_group_bookmark_finished_annotation(
|
|
85
|
-
qs, bm_filter, finished_rel
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
(self.TARGET == "browser" and qs.model is Comic)
|
|
90
|
-
or self.is_opds_acquisition
|
|
91
|
-
or self.TARGET == "metadata"
|
|
92
|
-
):
|
|
93
|
-
qs = qs.annotate(page=bookmark_page)
|
|
94
|
-
else:
|
|
95
|
-
qs = qs.alias(page=bookmark_page)
|
|
96
|
-
|
|
97
|
-
qs = qs.annotate(finished=finished_aggregate)
|
|
98
|
-
|
|
99
|
-
if not self.bmua_is_max:
|
|
100
|
-
mbmua = self.get_max_bookmark_updated_at_aggregate(qs.model, JsonGroupArray)
|
|
101
|
-
qs = qs.annotate(bookmark_updated_ats=mbmua)
|
|
102
|
-
return qs
|
|
103
|
-
|
|
104
|
-
def annotate_progress(self, qs):
|
|
105
|
-
"""Compute progress for each member of a qs."""
|
|
106
|
-
# Requires bookmark and annotation hoisted from bookmarks.
|
|
107
|
-
# Requires page_count native to comic or aggregated
|
|
108
|
-
# Page counts can be null with metadata turned off.
|
|
109
|
-
# Least guard is for rare instances when bookmarks are set to
|
|
110
|
-
# invalid high values
|
|
111
|
-
progress = Least(Coalesce(F("page"), 0) * 100.0 / F("page_count"), Value(100.0))
|
|
112
|
-
return qs.annotate(progress=progress)
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"""Base view for metadata annotations."""
|
|
2
|
-
|
|
3
|
-
from types import MappingProxyType
|
|
4
|
-
|
|
5
|
-
from django.db.models import (
|
|
6
|
-
Value,
|
|
7
|
-
)
|
|
8
|
-
from django.db.models.aggregates import Count
|
|
9
|
-
from django.db.models.fields import CharField
|
|
10
|
-
|
|
11
|
-
from codex.logger.logging import get_logger
|
|
12
|
-
from codex.models.comic import Comic
|
|
13
|
-
from codex.models.functions import JsonGroupArray
|
|
14
|
-
from codex.models.groups import BrowserGroupModel, Imprint, Publisher, Series, Volume
|
|
15
|
-
from codex.views.browser.annotate.bookmark import BrowserAnnotateBookmarkView
|
|
16
|
-
|
|
17
|
-
_GROUP_BY: MappingProxyType[type[BrowserGroupModel], str] = MappingProxyType(
|
|
18
|
-
{Publisher: "sort_name", Imprint: "sort_name", Series: "sort_name", Volume: "name"}
|
|
19
|
-
)
|
|
20
|
-
LOG = get_logger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class BrowserAnnotateCardView(BrowserAnnotateBookmarkView):
|
|
24
|
-
"""Base class for views that need special metadata annotations."""
|
|
25
|
-
|
|
26
|
-
def add_group_by(self, qs):
|
|
27
|
-
"""Get the group by for the model."""
|
|
28
|
-
if group_by := _GROUP_BY.get(qs.model): # type: ignore
|
|
29
|
-
qs = qs.group_by(group_by)
|
|
30
|
-
return qs
|
|
31
|
-
|
|
32
|
-
def _annotate_child_count(self, qs):
|
|
33
|
-
"""Annotate child chount for card."""
|
|
34
|
-
if qs.model is Comic:
|
|
35
|
-
return qs
|
|
36
|
-
rel = self.rel_prefix + "pk"
|
|
37
|
-
count_func = Count(rel, distinct=True)
|
|
38
|
-
ann = {"child_count": count_func}
|
|
39
|
-
if self.TARGET == "opds2":
|
|
40
|
-
if qs.model is not Comic:
|
|
41
|
-
qs = qs.alias(**ann)
|
|
42
|
-
else:
|
|
43
|
-
qs = qs.annotate(**ann)
|
|
44
|
-
return qs
|
|
45
|
-
|
|
46
|
-
def _annotate_group(self, qs):
|
|
47
|
-
"""Annotate Group."""
|
|
48
|
-
value = "c" if qs.model is Comic else self.model_group # type: ignore
|
|
49
|
-
return qs.annotate(group=Value(value, CharField(max_length=1)))
|
|
50
|
-
|
|
51
|
-
def annotate_card_aggregates(self, qs):
|
|
52
|
-
"""Annotate aggregates that appear the browser card."""
|
|
53
|
-
if qs.model is Comic:
|
|
54
|
-
# comic adds order_value for cards late
|
|
55
|
-
qs = self.annotate_order_value(qs)
|
|
56
|
-
qs = self._annotate_group(qs)
|
|
57
|
-
qs = self.annotate_group_names(qs)
|
|
58
|
-
qs = self._annotate_child_count(qs)
|
|
59
|
-
qs = self.annotate_bookmarks(qs)
|
|
60
|
-
qs = self.annotate_progress(qs)
|
|
61
|
-
return qs.annotate(updated_ats=JsonGroupArray("updated_at", distinct=True))
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
"""Base view for metadata annotations."""
|
|
2
|
-
|
|
3
|
-
from os import sep
|
|
4
|
-
from types import MappingProxyType
|
|
5
|
-
|
|
6
|
-
from django.db.models import (
|
|
7
|
-
F,
|
|
8
|
-
FilteredRelation,
|
|
9
|
-
Q,
|
|
10
|
-
Value,
|
|
11
|
-
)
|
|
12
|
-
from django.db.models.aggregates import Avg, Max, Min, Sum
|
|
13
|
-
from django.db.models.fields import CharField
|
|
14
|
-
from django.db.models.functions import Reverse, Right, StrIndex
|
|
15
|
-
|
|
16
|
-
from codex.logger.logging import get_logger
|
|
17
|
-
from codex.models import (
|
|
18
|
-
Comic,
|
|
19
|
-
Folder,
|
|
20
|
-
StoryArc,
|
|
21
|
-
)
|
|
22
|
-
from codex.models.functions import ComicFTSRank, JsonGroupArray
|
|
23
|
-
from codex.views.browser.order_by import (
|
|
24
|
-
BrowserOrderByView,
|
|
25
|
-
)
|
|
26
|
-
from codex.views.const import (
|
|
27
|
-
NONE_INTEGERFIELD,
|
|
28
|
-
STORY_ARC_GROUP,
|
|
29
|
-
)
|
|
30
|
-
from codex.views.mixins import SharedAnnotationsMixin
|
|
31
|
-
|
|
32
|
-
_ORDER_AGGREGATE_FUNCS = MappingProxyType(
|
|
33
|
-
# These are annotated to order_value because they're simple relations
|
|
34
|
-
{
|
|
35
|
-
"age_rating": Avg,
|
|
36
|
-
"community_rating": Avg,
|
|
37
|
-
"created_at": Min,
|
|
38
|
-
"critical_rating": Avg,
|
|
39
|
-
"date": Min,
|
|
40
|
-
"page_count": Sum,
|
|
41
|
-
"size": Sum,
|
|
42
|
-
"updated_at": Min,
|
|
43
|
-
}
|
|
44
|
-
)
|
|
45
|
-
_ANNOTATED_ORDER_FIELDS = frozenset(
|
|
46
|
-
# These are annotated with their own functions
|
|
47
|
-
{
|
|
48
|
-
"filename",
|
|
49
|
-
"sort_name",
|
|
50
|
-
"search_score",
|
|
51
|
-
"bookmark_updated_at",
|
|
52
|
-
"story_arc_number",
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
LOG = get_logger(__name__)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class BrowserAnnotateOrderView(BrowserOrderByView, SharedAnnotationsMixin):
|
|
60
|
-
"""Base class for views that need special metadata annotations."""
|
|
61
|
-
|
|
62
|
-
CARD_TARGETS = frozenset({"browser", "metadata"})
|
|
63
|
-
_OPDS_TARGETS = frozenset({"opds1", "opds2"})
|
|
64
|
-
_PAGE_COUNT_TARGETS = frozenset(CARD_TARGETS | _OPDS_TARGETS)
|
|
65
|
-
_COVER_AND_CARD_TARGETS = frozenset(CARD_TARGETS | {"cover"})
|
|
66
|
-
|
|
67
|
-
def __init__(self, *args, **kwargs):
|
|
68
|
-
"""Set params for the type checker."""
|
|
69
|
-
super().__init__(*args, **kwargs)
|
|
70
|
-
self._order_agg_func: type[Min | Max] | None = None
|
|
71
|
-
self._is_opds_acquisition: bool | None = None
|
|
72
|
-
self._opds_acquisition_groups: frozenset[str] | None = None
|
|
73
|
-
self.bmua_is_max = False
|
|
74
|
-
|
|
75
|
-
@property
|
|
76
|
-
def opds_acquisition_groups(self):
|
|
77
|
-
"""Memoize the opds acquisition groups."""
|
|
78
|
-
if self._opds_acquisition_groups is None:
|
|
79
|
-
groups = {"a", "f", "c"}
|
|
80
|
-
groups |= {*self.valid_nav_groups[-2:]}
|
|
81
|
-
self._opds_acquisition_groups = frozenset(groups)
|
|
82
|
-
return self._opds_acquisition_groups
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def is_opds_acquisition(self):
|
|
86
|
-
"""Memoize if we're in an opds acquisition view."""
|
|
87
|
-
if self._is_opds_acquisition is None:
|
|
88
|
-
is_opds_acquisition = self.TARGET in self._OPDS_TARGETS
|
|
89
|
-
if is_opds_acquisition:
|
|
90
|
-
group = self.kwargs.get("group")
|
|
91
|
-
is_opds_acquisition &= group in self.opds_acquisition_groups
|
|
92
|
-
if is_opds_acquisition and group == "a":
|
|
93
|
-
pks = self.kwargs["pks"]
|
|
94
|
-
is_opds_acquisition &= bool(pks and 0 not in pks)
|
|
95
|
-
self._is_opds_acquisition = is_opds_acquisition
|
|
96
|
-
return self._is_opds_acquisition
|
|
97
|
-
|
|
98
|
-
@property
|
|
99
|
-
def order_agg_func(self):
|
|
100
|
-
"""Get the order aggregate function."""
|
|
101
|
-
if self._order_agg_func is None:
|
|
102
|
-
order_reverse = self.params.get("order_reverse")
|
|
103
|
-
self._order_agg_func = Max if order_reverse else Min
|
|
104
|
-
return self._order_agg_func
|
|
105
|
-
|
|
106
|
-
def _alias_sort_names(self, qs):
|
|
107
|
-
"""Annotate sort_name."""
|
|
108
|
-
if self.order_key != "sort_name" and not (
|
|
109
|
-
qs.model is StoryArc and self.order_key == "story_arc_number"
|
|
110
|
-
):
|
|
111
|
-
return qs
|
|
112
|
-
pks = self.kwargs.get("pks")
|
|
113
|
-
show = MappingProxyType(self.params["show"]) # type: ignore
|
|
114
|
-
# TODO too many annotations for order?
|
|
115
|
-
# Move other annotations to card.
|
|
116
|
-
# eager publisher for imprint of course
|
|
117
|
-
sort_name_annotations = self.get_sort_name_annotations(
|
|
118
|
-
qs.model, self.model_group, pks, show
|
|
119
|
-
)
|
|
120
|
-
if sort_name_annotations:
|
|
121
|
-
qs = qs.alias(**sort_name_annotations)
|
|
122
|
-
if qs.model is Comic:
|
|
123
|
-
self._comic_sort_names = tuple(sort_name_annotations.keys())
|
|
124
|
-
return qs
|
|
125
|
-
|
|
126
|
-
def _alias_filename(self, qs):
|
|
127
|
-
"""Calculate filename from path in the db."""
|
|
128
|
-
if self.order_key != "filename":
|
|
129
|
-
return qs
|
|
130
|
-
if qs.model is Folder:
|
|
131
|
-
filename = F("name")
|
|
132
|
-
else:
|
|
133
|
-
prefix = "" if qs.model == Comic else self.rel_prefix
|
|
134
|
-
path_rel = prefix + "path"
|
|
135
|
-
filename = self.order_agg_func(
|
|
136
|
-
Right(
|
|
137
|
-
path_rel,
|
|
138
|
-
StrIndex(Reverse(F(path_rel)), Value(sep)) - 1, # type: ignore
|
|
139
|
-
output_field=CharField(),
|
|
140
|
-
)
|
|
141
|
-
)
|
|
142
|
-
return qs.alias(filename=filename)
|
|
143
|
-
|
|
144
|
-
def _alias_story_arc_number(self, qs):
|
|
145
|
-
if self.order_key != "story_arc_number":
|
|
146
|
-
return qs
|
|
147
|
-
|
|
148
|
-
# Get story_arc__pk
|
|
149
|
-
group = self.kwargs["group"]
|
|
150
|
-
pks = self.kwargs["pks"]
|
|
151
|
-
if group == STORY_ARC_GROUP and pks:
|
|
152
|
-
story_arc_pks = pks
|
|
153
|
-
else:
|
|
154
|
-
story_arc_pks = self.params.get("filters", {}).get( # type: ignore
|
|
155
|
-
"story_arcs", ()
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# If we have one annotate it.
|
|
159
|
-
if story_arc_pks:
|
|
160
|
-
rel = self.get_rel_prefix(qs.model) + "story_arc_numbers"
|
|
161
|
-
condition_rel = "pk" if qs.model is StoryArc else rel + "__story_arc"
|
|
162
|
-
condition = Q(**{f"{condition_rel}__in": story_arc_pks})
|
|
163
|
-
qs = qs.alias(
|
|
164
|
-
selected_story_arc_number=FilteredRelation(rel, condition=condition),
|
|
165
|
-
)
|
|
166
|
-
story_arc_number = self.order_agg_func("selected_story_arc_number__number")
|
|
167
|
-
else:
|
|
168
|
-
story_arc_number = NONE_INTEGERFIELD
|
|
169
|
-
|
|
170
|
-
return qs.alias(story_arc_number=story_arc_number)
|
|
171
|
-
|
|
172
|
-
def _annotate_page_count(self, qs):
|
|
173
|
-
"""Hoist up total page_count of children."""
|
|
174
|
-
# Used for sorting and progress
|
|
175
|
-
if qs.model is Comic or (
|
|
176
|
-
self.order_key != "page_count"
|
|
177
|
-
and self.TARGET not in self._PAGE_COUNT_TARGETS
|
|
178
|
-
):
|
|
179
|
-
return qs
|
|
180
|
-
|
|
181
|
-
rel = self.rel_prefix + "page_count"
|
|
182
|
-
page_count_sum = Sum(rel, distinct=True)
|
|
183
|
-
if self.TARGET == "browser":
|
|
184
|
-
qs = qs.alias(page_count=page_count_sum)
|
|
185
|
-
else:
|
|
186
|
-
qs = qs.annotate(page_count=page_count_sum)
|
|
187
|
-
return qs
|
|
188
|
-
|
|
189
|
-
def _annotate_bookmark_updated_at(self, qs):
|
|
190
|
-
if self.is_opds_acquisition or self.order_key == "bookmark_updated_at":
|
|
191
|
-
bmua_agg = self.get_max_bookmark_updated_at_aggregate(
|
|
192
|
-
qs.model, agg_func=self.order_agg_func
|
|
193
|
-
)
|
|
194
|
-
# This is used by annotate.bookmark to avoid a
|
|
195
|
-
# similar query.
|
|
196
|
-
self.bmua_is_max = self.order_agg_func is Max
|
|
197
|
-
qs = qs.annotate(bookmark_updated_at=bmua_agg)
|
|
198
|
-
# This is used by the serializer to compute mtime
|
|
199
|
-
return qs.annotate(bmua_is_max=Value(self.bmua_is_max))
|
|
200
|
-
|
|
201
|
-
def annotate_order_value(self, qs):
|
|
202
|
-
"""Annotate a main key for sorting and browser card display."""
|
|
203
|
-
# Determine order func
|
|
204
|
-
if self.TARGET == "metadata":
|
|
205
|
-
return qs
|
|
206
|
-
if qs.model is Folder and self.order_key == "filename":
|
|
207
|
-
order_value = F("name")
|
|
208
|
-
elif qs.model is Comic or self.order_key in _ANNOTATED_ORDER_FIELDS:
|
|
209
|
-
# These are annotated in browser_annotaions
|
|
210
|
-
order_value = F(self.order_key)
|
|
211
|
-
else:
|
|
212
|
-
agg_func = _ORDER_AGGREGATE_FUNCS[self.order_key]
|
|
213
|
-
agg_func = self.order_agg_func if agg_func == Min else agg_func
|
|
214
|
-
field = self.rel_prefix + self.order_key
|
|
215
|
-
order_value = agg_func(field)
|
|
216
|
-
|
|
217
|
-
if self.TARGET == "browser":
|
|
218
|
-
qs = qs.annotate(order_value=order_value)
|
|
219
|
-
else:
|
|
220
|
-
qs = qs.alias(order_value=order_value)
|
|
221
|
-
return qs
|
|
222
|
-
|
|
223
|
-
def _annotate_search_scores(self, qs):
|
|
224
|
-
"""Annotate Search Scores."""
|
|
225
|
-
if (
|
|
226
|
-
self.TARGET not in self._COVER_AND_CARD_TARGETS
|
|
227
|
-
or self.order_key != "search_score"
|
|
228
|
-
):
|
|
229
|
-
return qs
|
|
230
|
-
|
|
231
|
-
# Rank is always the max of the relations, cannot aggregate?
|
|
232
|
-
# group by here fixes duplicates with story_arc, probably because it's a long relation
|
|
233
|
-
return qs.annotate(search_score=ComicFTSRank()).group_by("id")
|
|
234
|
-
|
|
235
|
-
def annotate_order_aggregates(self, qs):
|
|
236
|
-
"""Annotate common aggregates between browser and metadata."""
|
|
237
|
-
qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
|
|
238
|
-
qs = self._annotate_search_scores(qs)
|
|
239
|
-
qs = self._alias_sort_names(qs)
|
|
240
|
-
qs = self._alias_filename(qs)
|
|
241
|
-
qs = self._alias_story_arc_number(qs)
|
|
242
|
-
qs = self._annotate_page_count(qs)
|
|
243
|
-
qs = self._annotate_bookmark_updated_at(qs)
|
|
244
|
-
if qs.model is not Comic:
|
|
245
|
-
# comic orders on indexed fields when it can
|
|
246
|
-
qs = self.annotate_order_value(qs)
|
|
247
|
-
return qs
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"""Browser Filters."""
|
|
2
|
-
|
|
3
|
-
from django.db.models.query import QuerySet
|
|
4
|
-
from django.db.models.query_utils import Q
|
|
5
|
-
|
|
6
|
-
from codex.logger.logging import get_logger
|
|
7
|
-
from codex.models.comic import Comic
|
|
8
|
-
from codex.views.browser.filters.bookmark import BrowserFilterBookmarkView
|
|
9
|
-
|
|
10
|
-
LOG = get_logger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class BrowserFilterView(BrowserFilterBookmarkView):
|
|
14
|
-
"""Browser Filters."""
|
|
15
|
-
|
|
16
|
-
TARGET = ""
|
|
17
|
-
|
|
18
|
-
def force_inner_joins(self, qs):
|
|
19
|
-
"""Force INNER JOINS to filter empty groups."""
|
|
20
|
-
demote_tables = {"codex_library"}
|
|
21
|
-
if qs.model is not Comic:
|
|
22
|
-
demote_tables.add("codex_comic")
|
|
23
|
-
if self.fts_mode:
|
|
24
|
-
# Forcing INNER JOINS required to make fts5 work
|
|
25
|
-
demote_tables.add("codex_comicfts")
|
|
26
|
-
return qs.demote_joins(demote_tables)
|
|
27
|
-
|
|
28
|
-
def _get_query_filters(
|
|
29
|
-
self,
|
|
30
|
-
model,
|
|
31
|
-
group=None,
|
|
32
|
-
pks=None,
|
|
33
|
-
page_mtime=False,
|
|
34
|
-
bookmark_filter=True,
|
|
35
|
-
) -> Q:
|
|
36
|
-
"""Return all the filters except the group filter."""
|
|
37
|
-
big_include_filter = Q()
|
|
38
|
-
big_exclude_filter = Q()
|
|
39
|
-
big_include_filter &= self.get_group_acl_filter(model, self.request.user)
|
|
40
|
-
big_include_filter &= self.get_group_filter(group, pks, page_mtime=page_mtime)
|
|
41
|
-
big_include_filter &= self.get_comic_field_filter(model)
|
|
42
|
-
if bookmark_filter:
|
|
43
|
-
big_include_filter &= self.get_bookmark_filter(model)
|
|
44
|
-
include_search_filter, exclude_search_filter, fts_q = self.get_search_filters(
|
|
45
|
-
model
|
|
46
|
-
)
|
|
47
|
-
big_include_filter &= include_search_filter
|
|
48
|
-
big_exclude_filter &= exclude_search_filter
|
|
49
|
-
|
|
50
|
-
return big_include_filter & ~big_exclude_filter & fts_q
|
|
51
|
-
|
|
52
|
-
def get_filtered_queryset(
|
|
53
|
-
self,
|
|
54
|
-
model,
|
|
55
|
-
group=None,
|
|
56
|
-
pks=None,
|
|
57
|
-
page_mtime=False,
|
|
58
|
-
bookmark_filter=True,
|
|
59
|
-
) -> QuerySet:
|
|
60
|
-
"""Get a filtered queryset for the model."""
|
|
61
|
-
query_filters = self._get_query_filters(
|
|
62
|
-
model,
|
|
63
|
-
group=group,
|
|
64
|
-
pks=pks,
|
|
65
|
-
page_mtime=page_mtime,
|
|
66
|
-
bookmark_filter=bookmark_filter,
|
|
67
|
-
)
|
|
68
|
-
# Distinct necissary for folder view with search
|
|
69
|
-
return model.objects.filter(query_filters).distinct()
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"""Group Mtime Function."""
|
|
2
|
-
|
|
3
|
-
from logging import DEBUG, WARNING
|
|
4
|
-
|
|
5
|
-
from django.db.models.aggregates import Aggregate, Max
|
|
6
|
-
from django.db.models.functions import Greatest
|
|
7
|
-
from django.db.utils import OperationalError
|
|
8
|
-
|
|
9
|
-
from codex.logger.logging import get_logger
|
|
10
|
-
from codex.models.functions import JsonGroupArray
|
|
11
|
-
from codex.views.browser.filters.filter import BrowserFilterView
|
|
12
|
-
from codex.views.const import EPOCH_START, EPOCH_START_DATETIMEFIELD, NONE_DATETIMEFIELD
|
|
13
|
-
|
|
14
|
-
_FTS5_PREFIX = "fts5: "
|
|
15
|
-
LOG = get_logger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class BrowserGroupMtimeView(BrowserFilterView):
|
|
19
|
-
"""Annotations that also filter."""
|
|
20
|
-
|
|
21
|
-
def __init__(self, *args, **kwargs):
|
|
22
|
-
"""Initialize memoized values."""
|
|
23
|
-
super().__init__(*args, **kwargs)
|
|
24
|
-
self._is_bookmark_filtered: bool | None = None
|
|
25
|
-
|
|
26
|
-
@property
|
|
27
|
-
def is_bookmark_filtered(self):
|
|
28
|
-
"""Is bookmark filter in effect."""
|
|
29
|
-
if self._is_bookmark_filtered is None:
|
|
30
|
-
self._is_bookmark_filtered = bool(
|
|
31
|
-
self.params.get("filters", {}).get("bookmark")
|
|
32
|
-
)
|
|
33
|
-
return self._is_bookmark_filtered
|
|
34
|
-
|
|
35
|
-
def _handle_operational_error(self, err):
|
|
36
|
-
msg = err.args[0] if err.args else ""
|
|
37
|
-
if msg.startswith(_FTS5_PREFIX):
|
|
38
|
-
level = DEBUG
|
|
39
|
-
self.search_error = msg.removeprefix(_FTS5_PREFIX)
|
|
40
|
-
else:
|
|
41
|
-
level = WARNING
|
|
42
|
-
msg = str(err)
|
|
43
|
-
LOG.log(level, f"Query Error: {msg}")
|
|
44
|
-
|
|
45
|
-
def get_max_bookmark_updated_at_aggregate(
|
|
46
|
-
self, model, agg_func: type[Aggregate] = Max, default=NONE_DATETIMEFIELD
|
|
47
|
-
):
|
|
48
|
-
"""Get filtered maximum bookmark updated_at relation."""
|
|
49
|
-
bm_rel = self._get_bm_rel(model)
|
|
50
|
-
bm_filter = self._get_my_bookmark_filter(bm_rel)
|
|
51
|
-
bmua_rel = f"{bm_rel}__updated_at"
|
|
52
|
-
args = {"default": default, "filter": bm_filter}
|
|
53
|
-
if agg_func is JsonGroupArray:
|
|
54
|
-
args["distinct"] = True
|
|
55
|
-
return agg_func(bmua_rel, **args)
|
|
56
|
-
|
|
57
|
-
def get_group_mtime(self, model, group=None, pks=None, page_mtime=False):
|
|
58
|
-
"""Get a filtered mtime for browser pages and mtime checker."""
|
|
59
|
-
qs = self.get_filtered_queryset(
|
|
60
|
-
model,
|
|
61
|
-
group=group,
|
|
62
|
-
pks=pks,
|
|
63
|
-
page_mtime=page_mtime,
|
|
64
|
-
bookmark_filter=self.is_bookmark_filtered,
|
|
65
|
-
)
|
|
66
|
-
mua = Max("updated_at", default=EPOCH_START_DATETIMEFIELD)
|
|
67
|
-
mbua = self.get_max_bookmark_updated_at_aggregate(
|
|
68
|
-
model, default=EPOCH_START_DATETIMEFIELD
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
try:
|
|
72
|
-
qs = qs.annotate(max=Greatest(mua, mbua))
|
|
73
|
-
# Forcing inner joins makes search work
|
|
74
|
-
# Can't run demote_joins on aggregate.
|
|
75
|
-
qs = self.force_inner_joins(qs)
|
|
76
|
-
first = qs.first()
|
|
77
|
-
mtime = first.max if first else EPOCH_START
|
|
78
|
-
except OperationalError as exc:
|
|
79
|
-
self._handle_operational_error(exc)
|
|
80
|
-
mtime = None
|
|
81
|
-
if mtime == NotImplemented:
|
|
82
|
-
mtime = None
|
|
83
|
-
return mtime
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Metadata View."""
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"""Metadata Annotations."""
|
|
2
|
-
|
|
3
|
-
from django.db.models import Count, IntegerField, Sum, Value
|
|
4
|
-
|
|
5
|
-
from codex.logger.logging import get_logger
|
|
6
|
-
from codex.models import Comic
|
|
7
|
-
from codex.views.browser.annotate.card import BrowserAnnotateCardView
|
|
8
|
-
|
|
9
|
-
LOG = get_logger(__name__)
|
|
10
|
-
_ADMIN_OR_FILE_VIEW_ENABLED_COMIC_VALUE_FIELDS = frozenset({"path"})
|
|
11
|
-
_COMIC_VALUE_FIELDS_CONFLICTING = frozenset(
|
|
12
|
-
{
|
|
13
|
-
"created_at",
|
|
14
|
-
"name",
|
|
15
|
-
"path",
|
|
16
|
-
"updated_at",
|
|
17
|
-
}
|
|
18
|
-
)
|
|
19
|
-
_DISABLED_VALUE_FIELD_NAMES = frozenset(
|
|
20
|
-
{"id", "pk", "sort_name", "stat"} | _COMIC_VALUE_FIELDS_CONFLICTING
|
|
21
|
-
)
|
|
22
|
-
_COMIC_VALUE_FIELD_NAMES = frozenset(
|
|
23
|
-
# contains path
|
|
24
|
-
field.name
|
|
25
|
-
for field in Comic._meta.get_fields()
|
|
26
|
-
if not field.is_relation and field.name not in _DISABLED_VALUE_FIELD_NAMES
|
|
27
|
-
)
|
|
28
|
-
_COMIC_VALUE_FIELDS_CONFLICTING_PREFIX = "conflict_"
|
|
29
|
-
_COMIC_RELATED_VALUE_FIELDS = frozenset({"series__volume_count", "volume__issue_count"})
|
|
30
|
-
_PATH_GROUPS = frozenset({"c", "f"})
|
|
31
|
-
_SUM_FIELDS = frozenset({"page_count", "size"})
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class MetadataAnnotateView(BrowserAnnotateCardView):
|
|
35
|
-
"""Metadata Annotations."""
|
|
36
|
-
|
|
37
|
-
def _get_comic_value_fields(self):
|
|
38
|
-
"""Include the path field for staff."""
|
|
39
|
-
fields = set(_COMIC_VALUE_FIELD_NAMES)
|
|
40
|
-
group = self.kwargs["group"]
|
|
41
|
-
if (
|
|
42
|
-
not (self.is_admin and self.admin_flags["folder_view"])
|
|
43
|
-
or group not in _PATH_GROUPS
|
|
44
|
-
):
|
|
45
|
-
fields -= _ADMIN_OR_FILE_VIEW_ENABLED_COMIC_VALUE_FIELDS
|
|
46
|
-
return tuple(fields)
|
|
47
|
-
|
|
48
|
-
def _intersection_annotate(
|
|
49
|
-
self,
|
|
50
|
-
querysets,
|
|
51
|
-
fields,
|
|
52
|
-
related_suffix="",
|
|
53
|
-
annotation_prefix="",
|
|
54
|
-
):
|
|
55
|
-
"""Annotate the intersection of value and fk fields."""
|
|
56
|
-
(filtered_qs, qs) = querysets
|
|
57
|
-
|
|
58
|
-
for field in fields:
|
|
59
|
-
# Annotate variant counts
|
|
60
|
-
# If 1 variant, annotate value, otherwise None
|
|
61
|
-
ann_field = annotation_prefix + field.replace("__", "_")
|
|
62
|
-
|
|
63
|
-
full_field = self.rel_prefix + field
|
|
64
|
-
|
|
65
|
-
if field in _SUM_FIELDS:
|
|
66
|
-
val = Sum(full_field)
|
|
67
|
-
else:
|
|
68
|
-
# group_by makes the filter work, but prevents its
|
|
69
|
-
# use as a subquery in the big query.
|
|
70
|
-
sq = filtered_qs.alias(
|
|
71
|
-
filter_count=Count(full_field, distinct=True)
|
|
72
|
-
).filter(filter_count=1)
|
|
73
|
-
sq = self.add_group_by(sq)
|
|
74
|
-
sq = self.force_inner_joins(sq)
|
|
75
|
-
sq = sq.values_list(full_field + related_suffix, flat=True)
|
|
76
|
-
try:
|
|
77
|
-
val = sq[0]
|
|
78
|
-
except IndexError:
|
|
79
|
-
val = None
|
|
80
|
-
if field.endswith("count"):
|
|
81
|
-
output_field = IntegerField()
|
|
82
|
-
else:
|
|
83
|
-
output_field = Comic._meta.get_field(field).__class__()
|
|
84
|
-
val = Value(val, output_field)
|
|
85
|
-
qs = qs.annotate(**{ann_field: val})
|
|
86
|
-
|
|
87
|
-
return filtered_qs, qs
|
|
88
|
-
|
|
89
|
-
def _annotate_values_and_fks(self, qs, filtered_qs):
|
|
90
|
-
"""Annotate comic values and comic foreign key values."""
|
|
91
|
-
# Simple Values
|
|
92
|
-
querysets = (filtered_qs, qs)
|
|
93
|
-
if qs.model is not Comic:
|
|
94
|
-
comic_value_fields = self._get_comic_value_fields()
|
|
95
|
-
querysets = self._intersection_annotate(querysets, comic_value_fields)
|
|
96
|
-
|
|
97
|
-
# Conflicting Simple Values
|
|
98
|
-
querysets = self._intersection_annotate(
|
|
99
|
-
querysets,
|
|
100
|
-
_COMIC_VALUE_FIELDS_CONFLICTING,
|
|
101
|
-
annotation_prefix=_COMIC_VALUE_FIELDS_CONFLICTING_PREFIX,
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
# Foreign Keys with special count values
|
|
105
|
-
_, qs = self._intersection_annotate(
|
|
106
|
-
querysets,
|
|
107
|
-
_COMIC_RELATED_VALUE_FIELDS,
|
|
108
|
-
)
|
|
109
|
-
return qs
|