codex 1.6.0a9__py3-none-any.whl → 1.6.0a11__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/librarian/importer/cache.py +46 -133
- codex/librarian/importer/create_fks.py +3 -1
- codex/librarian/importer/importer.py +1 -1
- codex/librarian/importer/importerd.py +5 -5
- codex/librarian/importer/tasks.py +2 -2
- codex/librarian/janitor/janitor.py +0 -3
- codex/librarian/librariand.py +0 -2
- codex/librarian/notifier/notifierd.py +1 -1
- codex/librarian/notifier/tasks.py +4 -4
- codex/middleware.py +1 -2
- codex/migrations/0027_import_order_and_covers.py +1 -66
- codex/models/comic.py +0 -1
- codex/models/groups.py +0 -3
- codex/serializers/admin.py +41 -40
- codex/serializers/browser/choices.py +38 -10
- codex/serializers/browser/filters.py +2 -2
- codex/serializers/browser/metadata.py +45 -1
- codex/serializers/browser/mixins.py +35 -2
- codex/serializers/browser/page.py +2 -4
- codex/serializers/browser/settings.py +41 -14
- codex/serializers/choices.py +4 -0
- codex/serializers/fields.py +61 -1
- codex/serializers/models/pycountry.py +11 -51
- codex/serializers/reader.py +12 -1
- codex/serializers/route.py +1 -1
- codex/serializers/settings.py +12 -0
- codex/settings/settings.py +10 -1
- codex/static_root/assets/VCheckbox-BwDyxhU7.831b00df198a.js +1 -0
- codex/static_root/assets/VCheckbox-BwDyxhU7.831b00df198a.js.br +0 -0
- codex/static_root/assets/VCheckbox-BwDyxhU7.831b00df198a.js.gz +0 -0
- codex/static_root/assets/VCheckbox-BwDyxhU7.js +1 -0
- codex/static_root/assets/VCheckbox-BwDyxhU7.js.br +0 -0
- codex/static_root/assets/VCheckbox-BwDyxhU7.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DLzF_WHB.9a47873b8548.js → VCheckboxBtn-DVq5lDK4.5e6464c8d80a.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-DVq5lDK4.5e6464c8d80a.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DVq5lDK4.5e6464c8d80a.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DLzF_WHB.js → VCheckboxBtn-DVq5lDK4.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-DVq5lDK4.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DVq5lDK4.js.gz +0 -0
- codex/static_root/assets/VCombobox-BASCOy0n.7801caa5e192.js +1 -0
- codex/static_root/assets/VCombobox-BASCOy0n.7801caa5e192.js.br +0 -0
- codex/static_root/assets/VCombobox-BASCOy0n.7801caa5e192.js.gz +0 -0
- codex/static_root/assets/VCombobox-BASCOy0n.js +1 -0
- codex/static_root/assets/VCombobox-BASCOy0n.js.br +0 -0
- codex/static_root/assets/VCombobox-BASCOy0n.js.gz +0 -0
- codex/static_root/assets/VDialog-DnbL_v_u.0bd289bda8e6.js +1 -0
- codex/static_root/assets/VDialog-DnbL_v_u.0bd289bda8e6.js.br +0 -0
- codex/static_root/assets/VDialog-DnbL_v_u.0bd289bda8e6.js.gz +0 -0
- codex/static_root/assets/VDialog-DnbL_v_u.js +1 -0
- codex/static_root/assets/VDialog-DnbL_v_u.js.br +0 -0
- codex/static_root/assets/VDialog-DnbL_v_u.js.gz +0 -0
- codex/static_root/assets/{VDialog-C5Nxd_zP.css → VDialog-lQ13FnRR.1c801ad756b2.css} +1 -1
- 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-C5Nxd_zP.d327f505ef0e.css → VDialog-lQ13FnRR.css} +1 -1
- 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-B8nz8lN5.56e5adec4f62.js +1 -0
- codex/static_root/assets/VExpansionPanels-B8nz8lN5.56e5adec4f62.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-B8nz8lN5.56e5adec4f62.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-B8nz8lN5.js +1 -0
- codex/static_root/assets/VExpansionPanels-B8nz8lN5.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-B8nz8lN5.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-B--7K_v5.4a4e9c2d6c42.js → VRadioGroup-BgwJ_b94.55f84f3429b9.js} +1 -1
- codex/static_root/assets/VRadioGroup-BgwJ_b94.55f84f3429b9.js.br +0 -0
- codex/static_root/assets/VRadioGroup-BgwJ_b94.55f84f3429b9.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-B--7K_v5.js → VRadioGroup-BgwJ_b94.js} +1 -1
- codex/static_root/assets/VRadioGroup-BgwJ_b94.js.br +0 -0
- codex/static_root/assets/VRadioGroup-BgwJ_b94.js.gz +0 -0
- codex/static_root/assets/VSelect-BoKpXxwe.a88f065dfe3b.js +1 -0
- codex/static_root/assets/VSelect-BoKpXxwe.a88f065dfe3b.js.br +0 -0
- codex/static_root/assets/VSelect-BoKpXxwe.a88f065dfe3b.js.gz +0 -0
- codex/static_root/assets/VSelect-BoKpXxwe.js +1 -0
- codex/static_root/assets/VSelect-BoKpXxwe.js.br +0 -0
- codex/static_root/assets/VSelect-BoKpXxwe.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CVQ9wkE2.c02186cceba6.js → VSelectionControl-BXJGg8No.0cb36d941d2a.js} +1 -1
- codex/static_root/assets/VSelectionControl-BXJGg8No.0cb36d941d2a.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BXJGg8No.0cb36d941d2a.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-CVQ9wkE2.js → VSelectionControl-BXJGg8No.js} +1 -1
- codex/static_root/assets/VSelectionControl-BXJGg8No.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BXJGg8No.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-C2TL5E34.775adeb5a973.js +1 -0
- codex/static_root/assets/VSlideGroup-C2TL5E34.775adeb5a973.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C2TL5E34.775adeb5a973.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-C2TL5E34.js +1 -0
- codex/static_root/assets/VSlideGroup-C2TL5E34.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C2TL5E34.js.gz +0 -0
- codex/static_root/assets/{VTable-CrWkhkiP.6d530eea0c09.js → VTable-C0FptljX.e4840b635c6e.js} +1 -1
- codex/static_root/assets/VTable-C0FptljX.e4840b635c6e.js.br +0 -0
- codex/static_root/assets/VTable-C0FptljX.e4840b635c6e.js.gz +0 -0
- codex/static_root/assets/{VTable-CrWkhkiP.js → VTable-C0FptljX.js} +1 -1
- codex/static_root/assets/VTable-C0FptljX.js.br +0 -0
- codex/static_root/assets/VTable-C0FptljX.js.gz +0 -0
- codex/static_root/assets/VTextField-Aw8nKhY6.428732ac22d7.js +1 -0
- codex/static_root/assets/VTextField-Aw8nKhY6.428732ac22d7.js.br +0 -0
- codex/static_root/assets/VTextField-Aw8nKhY6.428732ac22d7.js.gz +0 -0
- codex/static_root/assets/VTextField-Aw8nKhY6.js +1 -0
- codex/static_root/assets/VTextField-Aw8nKhY6.js.br +0 -0
- codex/static_root/assets/VTextField-Aw8nKhY6.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D9j_u9MX.828acf3dee55.js +1 -0
- codex/static_root/assets/VWindowItem-D9j_u9MX.828acf3dee55.js.br +0 -0
- codex/static_root/assets/VWindowItem-D9j_u9MX.828acf3dee55.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D9j_u9MX.js +1 -0
- codex/static_root/assets/VWindowItem-D9j_u9MX.js.br +0 -0
- codex/static_root/assets/VWindowItem-D9j_u9MX.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Dmn4wEY3.af7e8490f0e3.js +30 -0
- codex/static_root/assets/admin-drawer-panel-Dmn4wEY3.af7e8490f0e3.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Dmn4wEY3.af7e8490f0e3.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Dmn4wEY3.js +30 -0
- codex/static_root/assets/admin-drawer-panel-Dmn4wEY3.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Dmn4wEY3.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-o87BwD38.ce39427c8201.css → admin-drawer-panel-mne3FIj6.561b3f75fe65.css} +1 -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-o87BwD38.css → admin-drawer-panel-mne3FIj6.css} +1 -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/admin-jf7zR2p_.4741813376d2.js +1 -0
- codex/static_root/assets/admin-jf7zR2p_.4741813376d2.js.br +0 -0
- codex/static_root/assets/admin-jf7zR2p_.4741813376d2.js.gz +0 -0
- codex/static_root/assets/admin-jf7zR2p_.js +1 -0
- codex/static_root/assets/admin-jf7zR2p_.js.br +0 -0
- codex/static_root/assets/admin-jf7zR2p_.js.gz +0 -0
- codex/static_root/assets/browser-BKZuypzF.86e5333bd7d6.js +1 -0
- codex/static_root/assets/browser-BKZuypzF.86e5333bd7d6.js.br +0 -0
- codex/static_root/assets/browser-BKZuypzF.86e5333bd7d6.js.gz +0 -0
- codex/static_root/assets/browser-BKZuypzF.js +1 -0
- codex/static_root/assets/browser-BKZuypzF.js.br +0 -0
- codex/static_root/assets/browser-BKZuypzF.js.gz +0 -0
- codex/static_root/assets/browser-D7xci5SY.css +1 -0
- codex/static_root/assets/browser-D7xci5SY.css.br +0 -0
- codex/static_root/assets/browser-D7xci5SY.css.gz +0 -0
- codex/static_root/assets/browser-D7xci5SY.fa305e14005c.css +1 -0
- codex/static_root/assets/browser-D7xci5SY.fa305e14005c.css.br +0 -0
- codex/static_root/assets/browser-D7xci5SY.fa305e14005c.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +1 -0
- 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 +1 -0
- 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-Cc1e755p.b29d200537bf.js +1 -0
- codex/static_root/assets/change-password-dialog-Cc1e755p.b29d200537bf.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Cc1e755p.b29d200537bf.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Cc1e755p.js +1 -0
- codex/static_root/assets/change-password-dialog-Cc1e755p.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Cc1e755p.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-jwL7B98r.1cfd579cbcfe.js → confirm-dialog-Cyr7HAAz.8ab1d8b6241a.js} +1 -1
- codex/static_root/assets/confirm-dialog-Cyr7HAAz.8ab1d8b6241a.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Cyr7HAAz.8ab1d8b6241a.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-jwL7B98r.js → confirm-dialog-Cyr7HAAz.js} +1 -1
- codex/static_root/assets/confirm-dialog-Cyr7HAAz.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Cyr7HAAz.js.gz +0 -0
- codex/static_root/assets/datetime-column-Cqi99Ze-.49d0ce340183.js +1 -0
- codex/static_root/assets/datetime-column-Cqi99Ze-.49d0ce340183.js.br +0 -0
- codex/static_root/assets/datetime-column-Cqi99Ze-.49d0ce340183.js.gz +0 -0
- codex/static_root/assets/datetime-column-Cqi99Ze-.js +1 -0
- codex/static_root/assets/datetime-column-Cqi99Ze-.js.br +0 -0
- codex/static_root/assets/datetime-column-Cqi99Ze-.js.gz +0 -0
- codex/static_root/assets/filter-CuocBoS2.885d1b8d44cc.js +1 -0
- codex/static_root/assets/filter-CuocBoS2.885d1b8d44cc.js.br +0 -0
- codex/static_root/assets/filter-CuocBoS2.885d1b8d44cc.js.gz +0 -0
- codex/static_root/assets/filter-CuocBoS2.js +1 -0
- codex/static_root/assets/filter-CuocBoS2.js.br +0 -0
- codex/static_root/assets/filter-CuocBoS2.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Da2DBNyz.ffbe5ca37e1c.js → flag-tab-B5dRCBAt.e858f7ec9f55.js} +1 -1
- codex/static_root/assets/flag-tab-B5dRCBAt.e858f7ec9f55.js.br +0 -0
- codex/static_root/assets/flag-tab-B5dRCBAt.e858f7ec9f55.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Da2DBNyz.js → flag-tab-B5dRCBAt.js} +1 -1
- codex/static_root/assets/flag-tab-B5dRCBAt.js.br +0 -0
- codex/static_root/assets/flag-tab-B5dRCBAt.js.gz +0 -0
- codex/static_root/assets/{group-tab-BONYWOc1.6ddf321a4a56.js → group-tab-_nT2o04v.c06032b254dc.js} +1 -1
- codex/static_root/assets/group-tab-_nT2o04v.c06032b254dc.js.br +0 -0
- codex/static_root/assets/group-tab-_nT2o04v.c06032b254dc.js.gz +0 -0
- codex/static_root/assets/{group-tab-BONYWOc1.js → group-tab-_nT2o04v.js} +1 -1
- codex/static_root/assets/group-tab-_nT2o04v.js.br +0 -0
- codex/static_root/assets/group-tab-_nT2o04v.js.gz +0 -0
- codex/static_root/assets/{http-error-D2Jnc20C.32846bc3a43d.js → http-error-CXo9qLiW.d5a337f399c9.js} +1 -1
- codex/static_root/assets/http-error-CXo9qLiW.d5a337f399c9.js.br +0 -0
- codex/static_root/assets/http-error-CXo9qLiW.d5a337f399c9.js.gz +0 -0
- codex/static_root/assets/{http-error-D2Jnc20C.js → http-error-CXo9qLiW.js} +1 -1
- codex/static_root/assets/http-error-CXo9qLiW.js.br +0 -0
- codex/static_root/assets/http-error-CXo9qLiW.js.gz +0 -0
- codex/static_root/assets/{library-tab-B419agXA.js → library-tab-lz5QMD4i.2f04b226ebe8.js} +1 -1
- codex/static_root/assets/library-tab-lz5QMD4i.2f04b226ebe8.js.br +0 -0
- codex/static_root/assets/library-tab-lz5QMD4i.2f04b226ebe8.js.gz +0 -0
- codex/static_root/assets/{library-tab-B419agXA.d4b778172aaa.js → library-tab-lz5QMD4i.js} +1 -1
- codex/static_root/assets/library-tab-lz5QMD4i.js.br +0 -0
- codex/static_root/assets/library-tab-lz5QMD4i.js.gz +0 -0
- codex/static_root/assets/main-17i0EBCp.7093c3f755f1.js +32 -0
- codex/static_root/assets/main-17i0EBCp.7093c3f755f1.js.br +0 -0
- codex/static_root/assets/main-17i0EBCp.7093c3f755f1.js.gz +0 -0
- codex/static_root/assets/main-17i0EBCp.js +32 -0
- codex/static_root/assets/main-17i0EBCp.js.br +0 -0
- codex/static_root/assets/main-17i0EBCp.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BBvsIXTd.ad891da6617a.js +1 -0
- codex/static_root/assets/pagination-toolbar-BBvsIXTd.ad891da6617a.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-BBvsIXTd.ad891da6617a.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BBvsIXTd.js +1 -0
- codex/static_root/assets/pagination-toolbar-BBvsIXTd.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-BBvsIXTd.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B_zL7BQQ.css +1 -0
- codex/static_root/assets/pagination-toolbar-B_zL7BQQ.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B_zL7BQQ.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B_zL7BQQ.d1311bd41edc.css +1 -0
- codex/static_root/assets/pagination-toolbar-B_zL7BQQ.d1311bd41edc.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B_zL7BQQ.d1311bd41edc.css.gz +0 -0
- codex/static_root/assets/{pdf-doc-8RVZqILx.2e7166dd26fb.js → pdf-doc-BpZdmj2g.93e86d9c7927.js} +1 -1
- codex/static_root/assets/pdf-doc-BpZdmj2g.93e86d9c7927.js.br +0 -0
- codex/static_root/assets/pdf-doc-BpZdmj2g.93e86d9c7927.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-8RVZqILx.js → pdf-doc-BpZdmj2g.js} +1 -1
- codex/static_root/assets/pdf-doc-BpZdmj2g.js.br +0 -0
- codex/static_root/assets/pdf-doc-BpZdmj2g.js.gz +0 -0
- codex/static_root/assets/reader-CRJUR2qj.55d626f0f9fa.js +2 -0
- codex/static_root/assets/reader-CRJUR2qj.55d626f0f9fa.js.br +0 -0
- codex/static_root/assets/reader-CRJUR2qj.55d626f0f9fa.js.gz +0 -0
- codex/static_root/assets/reader-CRJUR2qj.js +2 -0
- codex/static_root/assets/reader-CRJUR2qj.js.br +0 -0
- codex/static_root/assets/reader-CRJUR2qj.js.gz +0 -0
- codex/static_root/assets/{reader-CCCfpM3x.1553e45bd775.css → reader-CqXFa_gE.78267e9de0c2.css} +1 -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-CCCfpM3x.css → reader-CqXFa_gE.css} +1 -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-NgDnInw_.3b5ba16f68f0.js +1 -0
- codex/static_root/assets/relation-chips-NgDnInw_.3b5ba16f68f0.js.br +0 -0
- codex/static_root/assets/relation-chips-NgDnInw_.3b5ba16f68f0.js.gz +0 -0
- codex/static_root/assets/relation-chips-NgDnInw_.js +1 -0
- codex/static_root/assets/relation-chips-NgDnInw_.js.br +0 -0
- codex/static_root/assets/relation-chips-NgDnInw_.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BaQT27kc.e43fc1527b66.js +2 -0
- codex/static_root/assets/settings-drawer-BaQT27kc.e43fc1527b66.js.br +0 -0
- codex/static_root/assets/settings-drawer-BaQT27kc.e43fc1527b66.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BaQT27kc.js +2 -0
- codex/static_root/assets/settings-drawer-BaQT27kc.js.br +0 -0
- codex/static_root/assets/settings-drawer-BaQT27kc.js.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +1 -0
- 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 +1 -0
- 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-DoHb54IU.30d7deea7b9f.js +1 -0
- codex/static_root/assets/stats-tab-DoHb54IU.30d7deea7b9f.js.br +0 -0
- codex/static_root/assets/stats-tab-DoHb54IU.30d7deea7b9f.js.gz +0 -0
- codex/static_root/assets/stats-tab-DoHb54IU.js +1 -0
- codex/static_root/assets/stats-tab-DoHb54IU.js.br +0 -0
- codex/static_root/assets/stats-tab-DoHb54IU.js.gz +0 -0
- codex/static_root/assets/task-tab-B88ucu1U.4f240e3d82c4.js +1 -0
- codex/static_root/assets/task-tab-B88ucu1U.4f240e3d82c4.js.br +0 -0
- codex/static_root/assets/task-tab-B88ucu1U.4f240e3d82c4.js.gz +0 -0
- codex/static_root/assets/task-tab-B88ucu1U.js +1 -0
- codex/static_root/assets/task-tab-B88ucu1U.js.br +0 -0
- codex/static_root/assets/task-tab-B88ucu1U.js.gz +0 -0
- codex/static_root/assets/to-case-B7MjLIw-.08fc8412de69.js +1 -0
- codex/static_root/assets/to-case-B7MjLIw-.08fc8412de69.js.br +0 -0
- codex/static_root/assets/to-case-B7MjLIw-.js +1 -0
- codex/static_root/assets/to-case-B7MjLIw-.js.br +0 -0
- codex/static_root/assets/unauthorized-AnKtlgGy.62ee74d4373c.js +1 -0
- codex/static_root/assets/unauthorized-AnKtlgGy.62ee74d4373c.js.br +0 -0
- codex/static_root/assets/unauthorized-AnKtlgGy.62ee74d4373c.js.gz +0 -0
- codex/static_root/assets/unauthorized-AnKtlgGy.js +1 -0
- codex/static_root/assets/unauthorized-AnKtlgGy.js.br +0 -0
- codex/static_root/assets/unauthorized-AnKtlgGy.js.gz +0 -0
- codex/static_root/assets/{unauthorized-DHV6Q1n-.b7847b2872a1.css → unauthorized-CibJYD8o.4c2de98c2b53.css} +1 -1
- 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-DHV6Q1n-.css → unauthorized-CibJYD8o.css} +1 -1
- codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
- codex/static_root/assets/user-tab-BxtZ2u7W.161cc8f0dcce.js +1 -0
- codex/static_root/assets/user-tab-BxtZ2u7W.161cc8f0dcce.js.br +0 -0
- codex/static_root/assets/user-tab-BxtZ2u7W.161cc8f0dcce.js.gz +0 -0
- codex/static_root/assets/user-tab-BxtZ2u7W.js +1 -0
- codex/static_root/assets/user-tab-BxtZ2u7W.js.br +0 -0
- codex/static_root/assets/user-tab-BxtZ2u7W.js.gz +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json +1 -0
- 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-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.a0aaaa6c7ef7.json → choices.2f6b0359e1ef.json} +1 -1
- 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 -1
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/manifest.3851c028b0cb.json +622 -0
- codex/static_root/manifest.3851c028b0cb.json.br +0 -0
- codex/static_root/manifest.3851c028b0cb.json.gz +0 -0
- codex/static_root/manifest.json +245 -252
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/util.py +8 -6
- codex/views/admin/api_key.py +5 -2
- codex/views/admin/stats.py +13 -20
- codex/views/admin/tasks.py +2 -3
- codex/views/auth.py +1 -1
- codex/views/bookmark.py +49 -29
- codex/views/browser/annotations.py +11 -45
- codex/views/browser/base.py +22 -6
- codex/views/browser/breadcrumbs.py +5 -5
- codex/views/browser/browser.py +39 -27
- codex/views/browser/choices.py +22 -37
- codex/views/browser/cover.py +55 -65
- codex/views/browser/filters/annotations.py +33 -10
- codex/views/browser/filters/group.py +24 -15
- codex/views/browser/filters/search.py +3 -4
- codex/views/browser/metadata.py +98 -49
- codex/views/browser/mtime.py +3 -14
- codex/views/browser/order_by.py +1 -6
- codex/views/browser/settings.py +53 -0
- codex/views/browser/validate.py +3 -0
- codex/views/const.py +25 -2
- codex/views/opds/v1/feed.py +3 -5
- codex/views/opds/v2/feed.py +3 -3
- codex/views/reader/arcs.py +42 -51
- codex/views/reader/books.py +47 -63
- codex/views/reader/init.py +125 -0
- codex/views/reader/reader.py +9 -70
- codex/views/session.py +18 -12
- codex/views/settings.py +23 -10
- codex/views/util.py +2 -1
- codex/websockets/consumers.py +14 -10
- {codex-1.6.0a9.dist-info → codex-1.6.0a11.dist-info}/METADATA +1 -1
- {codex-1.6.0a9.dist-info → codex-1.6.0a11.dist-info}/RECORD +332 -328
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js +0 -1
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.br +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.da8282a08876.js.gz +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.js +0 -1
- codex/static_root/assets/VCheckbox-eNspVUje.js.br +0 -0
- codex/static_root/assets/VCheckbox-eNspVUje.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.9a47873b8548.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DLzF_WHB.js.gz +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js +0 -1
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.br +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.d607ada71b06.js.gz +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.js +0 -1
- codex/static_root/assets/VCombobox-BIO7lU_B.js.br +0 -0
- codex/static_root/assets/VCombobox-BIO7lU_B.js.gz +0 -0
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js +0 -1
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.br +0 -0
- codex/static_root/assets/VDialog-87AymJC5.4a46716358c0.js.gz +0 -0
- codex/static_root/assets/VDialog-87AymJC5.js +0 -1
- codex/static_root/assets/VDialog-87AymJC5.js.br +0 -0
- codex/static_root/assets/VDialog-87AymJC5.js.gz +0 -0
- 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-C5Nxd_zP.d327f505ef0e.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js +0 -1
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.9e524c73726a.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js +0 -1
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CgA3shsd.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.4a4e9c2d6c42.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.js.br +0 -0
- codex/static_root/assets/VRadioGroup-B--7K_v5.js.gz +0 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js +0 -1
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.br +0 -0
- codex/static_root/assets/VSelect-BoJq222l.fc714657e214.js.gz +0 -0
- codex/static_root/assets/VSelect-BoJq222l.js +0 -1
- codex/static_root/assets/VSelect-BoJq222l.js.br +0 -0
- codex/static_root/assets/VSelect-BoJq222l.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.c02186cceba6.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CVQ9wkE2.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js +0 -1
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.br +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.85e64443a928.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js +0 -1
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.br +0 -0
- codex/static_root/assets/VSlideGroup-Ce0j2BKK.js.gz +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.br +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.6d530eea0c09.js.gz +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.js.br +0 -0
- codex/static_root/assets/VTable-CrWkhkiP.js.gz +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js +0 -1
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.br +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.2cac13161dfe.js.gz +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.js +0 -1
- codex/static_root/assets/VTextField-CkWbil3K.js.br +0 -0
- codex/static_root/assets/VTextField-CkWbil3K.js.gz +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js +0 -1
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.br +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.98eb7487e039.js.gz +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.js +0 -1
- codex/static_root/assets/VWindowItem-7z55Y4lK.js.br +0 -0
- codex/static_root/assets/VWindowItem-7z55Y4lK.js.gz +0 -0
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js +0 -1
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.br +0 -0
- codex/static_root/assets/admin-D3OK784R.f69891726ec5.js.gz +0 -0
- codex/static_root/assets/admin-D3OK784R.js +0 -1
- codex/static_root/assets/admin-D3OK784R.js.br +0 -0
- codex/static_root/assets/admin-D3OK784R.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js +0 -30
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.5438f252970e.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js +0 -30
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-1pnfvY8O.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.ce39427c8201.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-o87BwD38.css.gz +0 -0
- codex/static_root/assets/browser-BuU2x9y7.css +0 -1
- codex/static_root/assets/browser-BuU2x9y7.css.br +0 -0
- codex/static_root/assets/browser-BuU2x9y7.css.gz +0 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css +0 -1
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.br +0 -0
- codex/static_root/assets/browser-BuU2x9y7.f67595d24664.css.gz +0 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js +0 -1
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.br +0 -0
- codex/static_root/assets/browser-CO9EYHIr.be49367c6a75.js.gz +0 -0
- codex/static_root/assets/browser-CO9EYHIr.js +0 -1
- codex/static_root/assets/browser-CO9EYHIr.js.br +0 -0
- codex/static_root/assets/browser-CO9EYHIr.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js +0 -1
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.188c765431ab.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js +0 -1
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CjZdzaIk.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +0 -1
- 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 +0 -1
- 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-jwL7B98r.1cfd579cbcfe.js.br +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.1cfd579cbcfe.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.js.br +0 -0
- codex/static_root/assets/confirm-dialog-jwL7B98r.js.gz +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js +0 -1
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.br +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.f9172256f091.js.gz +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js +0 -1
- codex/static_root/assets/datetime-column-9w3GcKvo.js.br +0 -0
- codex/static_root/assets/datetime-column-9w3GcKvo.js.gz +0 -0
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js +0 -1
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.br +0 -0
- codex/static_root/assets/filter-C-pghOri.eeb7c2ecf398.js.gz +0 -0
- codex/static_root/assets/filter-C-pghOri.js +0 -1
- codex/static_root/assets/filter-C-pghOri.js.br +0 -0
- codex/static_root/assets/filter-C-pghOri.js.gz +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.br +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.ffbe5ca37e1c.js.gz +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js.br +0 -0
- codex/static_root/assets/flag-tab-Da2DBNyz.js.gz +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.br +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.6ddf321a4a56.js.gz +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.js.br +0 -0
- codex/static_root/assets/group-tab-BONYWOc1.js.gz +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.br +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.32846bc3a43d.js.gz +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.js.br +0 -0
- codex/static_root/assets/http-error-D2Jnc20C.js.gz +0 -0
- codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.br +0 -0
- codex/static_root/assets/library-tab-B419agXA.d4b778172aaa.js.gz +0 -0
- codex/static_root/assets/library-tab-B419agXA.js.br +0 -0
- codex/static_root/assets/library-tab-B419agXA.js.gz +0 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js +0 -32
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.br +0 -0
- codex/static_root/assets/main-CMQwJ9aG.9836fe81c80a.js.gz +0 -0
- codex/static_root/assets/main-CMQwJ9aG.js +0 -32
- codex/static_root/assets/main-CMQwJ9aG.js.br +0 -0
- codex/static_root/assets/main-CMQwJ9aG.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css +0 -1
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css +0 -1
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DWdp_kmz.f98f47e87612.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js +0 -1
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.a295defbcd2f.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js +0 -1
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-LTuj5U-G.js.gz +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.br +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.2e7166dd26fb.js.gz +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.js.br +0 -0
- codex/static_root/assets/pdf-doc-8RVZqILx.js.gz +0 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.br +0 -0
- codex/static_root/assets/reader-CCCfpM3x.1553e45bd775.css.gz +0 -0
- codex/static_root/assets/reader-CCCfpM3x.css.br +0 -0
- codex/static_root/assets/reader-CCCfpM3x.css.gz +0 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js +0 -2
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.br +0 -0
- codex/static_root/assets/reader-DNnK_Chr.114d0e321547.js.gz +0 -0
- codex/static_root/assets/reader-DNnK_Chr.js +0 -2
- codex/static_root/assets/reader-DNnK_Chr.js.br +0 -0
- codex/static_root/assets/reader-DNnK_Chr.js.gz +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js +0 -1
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.br +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.428ba0b5861e.js.gz +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.js +0 -1
- codex/static_root/assets/relation-chips-PYua7aXv.js.br +0 -0
- codex/static_root/assets/relation-chips-PYua7aXv.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js +0 -2
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.br +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.d14f525d8eee.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.js +0 -2
- codex/static_root/assets/settings-drawer-DbN6ISRH.js.br +0 -0
- codex/static_root/assets/settings-drawer-DbN6ISRH.js.gz +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js +0 -1
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.br +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.d5026283841a.js.gz +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.js +0 -1
- codex/static_root/assets/stats-tab-DImNhH8O.js.br +0 -0
- codex/static_root/assets/stats-tab-DImNhH8O.js.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +0 -1
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +0 -2
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css +0 -1
- codex/static_root/assets/stats-tab-TsyG3wH_.css.br +0 -2
- codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js +0 -1
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.br +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.e56a35ff1691.js.gz +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.js +0 -1
- codex/static_root/assets/task-tab-DPD53kjv.js.br +0 -0
- codex/static_root/assets/task-tab-DPD53kjv.js.gz +0 -0
- codex/static_root/assets/to-case-ehC9Ccyj.a7b8e25b391f.js +0 -1
- codex/static_root/assets/to-case-ehC9Ccyj.js +0 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js +0 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.br +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.ee3961abfccb.js.gz +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.js +0 -1
- codex/static_root/assets/unauthorized-BOIx7Y18.js.br +0 -0
- codex/static_root/assets/unauthorized-BOIx7Y18.js.gz +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.b7847b2872a1.css.br +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.b7847b2872a1.css.gz +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.css.br +0 -0
- codex/static_root/assets/unauthorized-DHV6Q1n-.css.gz +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js +0 -1
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.br +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.37dd0440fbdd.js.gz +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.js +0 -1
- codex/static_root/assets/user-tab-BABwfxLO.js.br +0 -0
- codex/static_root/assets/user-tab-BABwfxLO.js.gz +0 -0
- codex/static_root/js/choices-admin.d790df01c20a.json +0 -1
- codex/static_root/js/choices-admin.d790df01c20a.json.br +0 -0
- codex/static_root/js/choices-admin.d790df01c20a.json.gz +0 -0
- codex/static_root/js/choices.a0aaaa6c7ef7.json.br +0 -0
- codex/static_root/js/choices.a0aaaa6c7ef7.json.gz +0 -0
- codex/static_root/manifest.cbea5663003a.json +0 -629
- codex/static_root/manifest.cbea5663003a.json.br +0 -0
- codex/static_root/manifest.cbea5663003a.json.gz +0 -0
- {codex-1.6.0a9.dist-info → codex-1.6.0a11.dist-info}/LICENSE +0 -0
- {codex-1.6.0a9.dist-info → codex-1.6.0a11.dist-info}/WHEEL +0 -0
- {codex-1.6.0a9.dist-info → codex-1.6.0a11.dist-info}/entry_points.txt +0 -0
codex/views/browser/metadata.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from types import MappingProxyType
|
|
4
4
|
|
|
5
|
-
from django.core.exceptions import FieldDoesNotExist
|
|
6
5
|
from django.db.models import Count, IntegerField, Sum, Value
|
|
7
6
|
from drf_spectacular.utils import extend_schema
|
|
8
7
|
from rest_framework.exceptions import NotFound
|
|
@@ -13,8 +12,10 @@ from codex.librarian.importer.const import (
|
|
|
13
12
|
)
|
|
14
13
|
from codex.logger.logging import get_logger
|
|
15
14
|
from codex.models import AdminFlag, Comic
|
|
16
|
-
from codex.
|
|
15
|
+
from codex.models.functions import JsonGroupArray
|
|
16
|
+
from codex.serializers.browser.metadata import PREFETCH_PREFIX, MetadataSerializer
|
|
17
17
|
from codex.views.browser.annotations import BrowserAnnotationsView
|
|
18
|
+
from codex.views.const import METADATA_GROUP_RELATION
|
|
18
19
|
|
|
19
20
|
LOG = get_logger(__name__)
|
|
20
21
|
_ADMIN_OR_FILE_VIEW_ENABLED_COMIC_VALUE_FIELDS = frozenset({"path"})
|
|
@@ -40,7 +41,21 @@ _COMIC_RELATED_VALUE_FIELDS = frozenset({"series__volume_count", "volume__issue_
|
|
|
40
41
|
_PATH_GROUPS = frozenset({"c", "f"})
|
|
41
42
|
_CONTRIBUTOR_RELATIONS = ("role", "person")
|
|
42
43
|
_SUM_FIELDS = frozenset({"page_count", "size"})
|
|
43
|
-
_GROUP_RELS =
|
|
44
|
+
_GROUP_RELS = MappingProxyType(
|
|
45
|
+
{
|
|
46
|
+
"i": ("publisher",),
|
|
47
|
+
"s": (
|
|
48
|
+
"publisher",
|
|
49
|
+
"imprint",
|
|
50
|
+
),
|
|
51
|
+
"v": (
|
|
52
|
+
"publisher",
|
|
53
|
+
"imprint",
|
|
54
|
+
"series",
|
|
55
|
+
),
|
|
56
|
+
"c": ("publisher", "imprint", "series", "volume"),
|
|
57
|
+
}
|
|
58
|
+
)
|
|
44
59
|
|
|
45
60
|
|
|
46
61
|
class MetadataView(BrowserAnnotationsView):
|
|
@@ -54,6 +69,13 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
54
69
|
}
|
|
55
70
|
)
|
|
56
71
|
|
|
72
|
+
def init_request(self):
|
|
73
|
+
"""Initialize request."""
|
|
74
|
+
self.set_admin_flags()
|
|
75
|
+
self.parse_params()
|
|
76
|
+
self.set_model()
|
|
77
|
+
self.set_rel_prefix()
|
|
78
|
+
|
|
57
79
|
def set_valid_browse_nav_groups(self, valid_top_groups): # noqa: ARG002
|
|
58
80
|
"""Limited allowed nav groups for metadata."""
|
|
59
81
|
group = self.kwargs["group"]
|
|
@@ -79,6 +101,7 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
79
101
|
):
|
|
80
102
|
"""Annotate the intersection of value and fk fields."""
|
|
81
103
|
(filtered_qs, qs) = querysets
|
|
104
|
+
|
|
82
105
|
for field in fields:
|
|
83
106
|
# Annotate variant counts
|
|
84
107
|
# If 1 variant, annotate value, otherwise None
|
|
@@ -133,6 +156,49 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
133
156
|
)
|
|
134
157
|
return qs
|
|
135
158
|
|
|
159
|
+
def _query_groups(self):
|
|
160
|
+
"""Query the through models to show group lists."""
|
|
161
|
+
groups = {}
|
|
162
|
+
if not self.model:
|
|
163
|
+
return groups
|
|
164
|
+
group = self.kwargs["group"]
|
|
165
|
+
rel = METADATA_GROUP_RELATION.get(group)
|
|
166
|
+
if not rel:
|
|
167
|
+
return groups
|
|
168
|
+
rel = rel + "__in"
|
|
169
|
+
pks = self.kwargs["pks"]
|
|
170
|
+
group_filter = {rel: pks}
|
|
171
|
+
|
|
172
|
+
for field_name in _GROUP_RELS.get(group, ()):
|
|
173
|
+
field = self.model._meta.get_field(field_name)
|
|
174
|
+
model = field.related_model
|
|
175
|
+
if not model:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
qs = model.objects.filter(**group_filter)
|
|
179
|
+
qs = qs.only("name").distinct()
|
|
180
|
+
group_by = self.get_group_by(model)
|
|
181
|
+
qs = qs.group_by(group_by)
|
|
182
|
+
qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
|
|
183
|
+
qs = qs.values("ids", "name")
|
|
184
|
+
groups[field_name] = qs
|
|
185
|
+
return groups
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def _get_optimized_m2m_query(key, qs):
|
|
189
|
+
if key == "contributors":
|
|
190
|
+
qs = qs.prefetch_related(*_CONTRIBUTOR_RELATIONS)
|
|
191
|
+
qs = qs.only(*_CONTRIBUTOR_RELATIONS)
|
|
192
|
+
elif key == "story_arc_numbers":
|
|
193
|
+
qs = qs.select_related("story_arc")
|
|
194
|
+
qs = qs.only("story_arc", "number")
|
|
195
|
+
elif key == "identifiers":
|
|
196
|
+
qs = qs.select_related("identifier_type")
|
|
197
|
+
qs = qs.only("identifier_type", "nss", "url")
|
|
198
|
+
else:
|
|
199
|
+
qs = qs.only("name")
|
|
200
|
+
return qs
|
|
201
|
+
|
|
136
202
|
def _query_m2m_intersections(self, filtered_qs):
|
|
137
203
|
"""Query the through models to figure out m2m intersections."""
|
|
138
204
|
# Speed ok, but still does a query per m2m model
|
|
@@ -140,31 +206,17 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
140
206
|
pk_field = self.rel_prefix + "pk"
|
|
141
207
|
comic_pks = filtered_qs.values_list(pk_field, flat=True)
|
|
142
208
|
comic_pks_count = comic_pks.count()
|
|
143
|
-
for field_name in COMIC_M2M_FIELD_NAMES
|
|
144
|
-
try:
|
|
145
|
-
if (
|
|
146
|
-
field_name in _GROUP_RELS
|
|
147
|
-
and self.model
|
|
148
|
-
and not self.model._meta.get_field(field_name)
|
|
149
|
-
):
|
|
150
|
-
continue
|
|
151
|
-
except FieldDoesNotExist:
|
|
152
|
-
continue
|
|
153
|
-
|
|
209
|
+
for field_name in COMIC_M2M_FIELD_NAMES:
|
|
154
210
|
model = Comic._meta.get_field(field_name).related_model
|
|
155
211
|
if not model:
|
|
156
212
|
reason = f"No model found for comic field: {field_name}"
|
|
157
213
|
raise ValueError(reason)
|
|
158
214
|
|
|
159
215
|
intersection_qs = model.objects.filter(comic__pk__in=comic_pks)
|
|
160
|
-
if field_name in _GROUP_RELS:
|
|
161
|
-
group_by = "name" if field_name == "volume" else "sort_name"
|
|
162
|
-
intersection_qs = intersection_qs.group_by(group_by)
|
|
163
|
-
if field_name == "identifiers":
|
|
164
|
-
intersection_qs = intersection_qs.select_related("identifier_type")
|
|
165
216
|
intersection_qs = intersection_qs.alias(count=Count("comic")).filter(
|
|
166
217
|
count=comic_pks_count
|
|
167
218
|
)
|
|
219
|
+
intersection_qs = self._get_optimized_m2m_query(field_name, intersection_qs)
|
|
168
220
|
|
|
169
221
|
m2m_intersections[field_name] = intersection_qs
|
|
170
222
|
return m2m_intersections
|
|
@@ -185,42 +237,37 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
185
237
|
"""Values for highlighting the current group."""
|
|
186
238
|
if self.model and not self.is_model_comic:
|
|
187
239
|
# move the name of the group to the correct field
|
|
188
|
-
|
|
240
|
+
field = self.model.__name__.lower() + "_list"
|
|
189
241
|
group_obj = {"pk": obj.pk, "name": obj.name}
|
|
190
|
-
|
|
242
|
+
group_list = [group_obj]
|
|
243
|
+
setattr(obj, field, group_list)
|
|
191
244
|
obj.name = None
|
|
192
245
|
|
|
193
|
-
@staticmethod
|
|
194
|
-
def _get_optimized_m2m_query(key, qs):
|
|
195
|
-
# XXX The prefetch gets removed by field.set() :(
|
|
196
|
-
if key == "contributors":
|
|
197
|
-
qs = qs.prefetch_related(*_CONTRIBUTOR_RELATIONS).only(
|
|
198
|
-
*_CONTRIBUTOR_RELATIONS
|
|
199
|
-
)
|
|
200
|
-
elif key == "story_arc_numbers":
|
|
201
|
-
qs = qs.prefetch_related("story_arc")
|
|
202
|
-
qs = qs.only("story_arc", "number")
|
|
203
|
-
elif key == "identifiers":
|
|
204
|
-
qs = qs.prefetch_related("identifier_type")
|
|
205
|
-
qs = qs.only("identifier_type", "nss", "url")
|
|
206
|
-
else:
|
|
207
|
-
qs = qs.only("name")
|
|
208
|
-
return qs
|
|
209
|
-
|
|
210
246
|
@classmethod
|
|
211
247
|
def _copy_m2m_intersections(cls, obj, m2m_intersections):
|
|
212
248
|
"""Copy the m2m intersections into the object."""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
249
|
+
# XXX It might even be faster to copy everything to a dict and not use the obj.
|
|
250
|
+
for key, qs in m2m_intersections.items():
|
|
251
|
+
serializer_key = (
|
|
252
|
+
f"{PREFETCH_PREFIX}{key}"
|
|
253
|
+
if key in ("identifiers", "contributors", "story_arc_numbers")
|
|
254
|
+
else key
|
|
255
|
+
)
|
|
256
|
+
if hasattr(obj, serializer_key):
|
|
216
257
|
# real db fields need to use their special set method.
|
|
217
|
-
field = getattr(obj,
|
|
218
|
-
field.set(
|
|
258
|
+
field = getattr(obj, serializer_key)
|
|
259
|
+
field.set(
|
|
260
|
+
qs,
|
|
261
|
+
clear=True,
|
|
262
|
+
)
|
|
219
263
|
else:
|
|
220
264
|
# fake db field is just a queryset attached.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
265
|
+
setattr(obj, serializer_key, qs)
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def _copy_groups(obj, groups):
|
|
269
|
+
for field, group_qs in groups.items():
|
|
270
|
+
setattr(obj, field + "_list", group_qs)
|
|
224
271
|
|
|
225
272
|
@staticmethod
|
|
226
273
|
def _copy_conflicting_simple_fields(obj):
|
|
@@ -230,10 +277,11 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
230
277
|
val = getattr(obj, conflict_field)
|
|
231
278
|
setattr(obj, field, val)
|
|
232
279
|
|
|
233
|
-
def _copy_annotations_into_comic_fields(self, obj, m2m_intersections):
|
|
280
|
+
def _copy_annotations_into_comic_fields(self, obj, groups, m2m_intersections):
|
|
234
281
|
"""Copy a bunch of values that i couldn't fit cleanly in the main queryset."""
|
|
235
282
|
self._path_security(obj)
|
|
236
283
|
self._highlight_current_group(obj)
|
|
284
|
+
self._copy_groups(obj, groups)
|
|
237
285
|
self._copy_m2m_intersections(obj, m2m_intersections)
|
|
238
286
|
if not self.is_model_comic:
|
|
239
287
|
self._copy_conflicting_simple_fields(obj)
|
|
@@ -270,10 +318,11 @@ class MetadataView(BrowserAnnotationsView):
|
|
|
270
318
|
except (IndexError, ValueError) as exc:
|
|
271
319
|
self._raise_not_found(exc)
|
|
272
320
|
|
|
321
|
+
groups = self._query_groups()
|
|
273
322
|
m2m_intersections = self._query_m2m_intersections(filtered_qs)
|
|
274
|
-
return self._copy_annotations_into_comic_fields(obj, m2m_intersections) # type: ignore
|
|
323
|
+
return self._copy_annotations_into_comic_fields(obj, groups, m2m_intersections) # type: ignore
|
|
275
324
|
|
|
276
|
-
@extend_schema(
|
|
325
|
+
@extend_schema(parameters=[BrowserAnnotationsView.input_serializer_class])
|
|
277
326
|
def get(self, *_args, **_kwargs):
|
|
278
327
|
"""Get metadata for a filtered browse group."""
|
|
279
328
|
# Init
|
codex/views/browser/mtime.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Get the mtimes for the submitted groups."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
3
|
+
from drf_spectacular.utils import extend_schema
|
|
5
4
|
from rest_framework.response import Response
|
|
6
5
|
|
|
7
6
|
from codex.logger.logging import get_logger
|
|
@@ -21,24 +20,13 @@ class MtimeView(BrowserAnnotationsFilterView):
|
|
|
21
20
|
serializer_class = MtimeSerializer
|
|
22
21
|
|
|
23
22
|
REPARSE_JSON_FIELDS = frozenset({"groups", "filters"})
|
|
23
|
+
TARGET = "mtime"
|
|
24
24
|
|
|
25
25
|
def __init__(self, *args, **kwargs) -> None:
|
|
26
26
|
"""Initialize."""
|
|
27
27
|
super().__init__(*args, **kwargs)
|
|
28
28
|
self.init_bookmark_data()
|
|
29
29
|
|
|
30
|
-
def parse_params(self):
|
|
31
|
-
"""Parse GET params."""
|
|
32
|
-
try:
|
|
33
|
-
validated_data = super().parse_params()
|
|
34
|
-
params = dict(self.params)
|
|
35
|
-
params["groups"] = validated_data["groups"] # type: ignore
|
|
36
|
-
self.params = MappingProxyType(params)
|
|
37
|
-
|
|
38
|
-
except Exception:
|
|
39
|
-
LOG.exception("parse")
|
|
40
|
-
raise
|
|
41
|
-
|
|
42
30
|
def _get_group_mtime(self, item):
|
|
43
31
|
"""Get one group's mtimes."""
|
|
44
32
|
group = item["group"]
|
|
@@ -59,6 +47,7 @@ class MtimeView(BrowserAnnotationsFilterView):
|
|
|
59
47
|
max_mtime = max_none(max_mtime, mtime)
|
|
60
48
|
return max_mtime
|
|
61
49
|
|
|
50
|
+
@extend_schema(parameters=[GroupsMtimeSerializer])
|
|
62
51
|
def get(self, *args, **kwargs):
|
|
63
52
|
"""Get the mtimes for the submitted groups."""
|
|
64
53
|
# Parse Request
|
codex/views/browser/order_by.py
CHANGED
|
@@ -6,7 +6,6 @@ from codex.models import Comic
|
|
|
6
6
|
from codex.views.browser.filters.annotations import (
|
|
7
7
|
BrowserAnnotationsFilterView,
|
|
8
8
|
)
|
|
9
|
-
from codex.views.const import FOLDER_GROUP
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class BrowserOrderByView(BrowserAnnotationsFilterView):
|
|
@@ -14,11 +13,7 @@ class BrowserOrderByView(BrowserAnnotationsFilterView):
|
|
|
14
13
|
|
|
15
14
|
def set_order_key(self):
|
|
16
15
|
"""Get the default order key for the view."""
|
|
17
|
-
order_key: str = self.params
|
|
18
|
-
if not order_key:
|
|
19
|
-
group = self.kwargs.get("group")
|
|
20
|
-
order_key = "filename" if group == FOLDER_GROUP else "sort_name"
|
|
21
|
-
self.order_key: str = order_key
|
|
16
|
+
self.order_key: str = self.params["order_by"]
|
|
22
17
|
order_reverse = self.params.get("order_reverse")
|
|
23
18
|
self.order_agg_func = Max if order_reverse else Min
|
|
24
19
|
|
codex/views/browser/settings.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Browser session view."""
|
|
2
2
|
|
|
3
3
|
from codex.serializers.browser.settings import BrowserSettingsSerializer
|
|
4
|
+
from codex.views.const import GROUP_ORDER
|
|
4
5
|
from codex.views.settings import SettingsView
|
|
5
6
|
|
|
6
7
|
|
|
@@ -11,3 +12,55 @@ class BrowserSettingsView(SettingsView):
|
|
|
11
12
|
serializer_class = BrowserSettingsSerializer # type: ignore
|
|
12
13
|
|
|
13
14
|
SESSION_KEY = SettingsView.BROWSER_SESSION_KEY
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def _strip_breadcrumb_names(params):
|
|
18
|
+
"""Remove names from breadcrumbs."""
|
|
19
|
+
breadcrumbs = params.get("breadcrumbs")
|
|
20
|
+
if not breadcrumbs:
|
|
21
|
+
return params
|
|
22
|
+
nameless_breadcrumbs = []
|
|
23
|
+
for crumb in breadcrumbs:
|
|
24
|
+
crumb.pop("name", None)
|
|
25
|
+
nameless_breadcrumbs.append(crumb)
|
|
26
|
+
params["breadcrumbs"] = nameless_breadcrumbs
|
|
27
|
+
return params
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _validate_browse_top_group(params, group, top_group):
|
|
31
|
+
"""Validate top group for browse groups."""
|
|
32
|
+
show = params["show"]
|
|
33
|
+
if group == "r" or (
|
|
34
|
+
show.get(top_group)
|
|
35
|
+
and GROUP_ORDER.index(top_group) < GROUP_ORDER.index(group)
|
|
36
|
+
):
|
|
37
|
+
return
|
|
38
|
+
|
|
39
|
+
for show_group, on in show.items():
|
|
40
|
+
if on:
|
|
41
|
+
params["top_group"] = show_group
|
|
42
|
+
break
|
|
43
|
+
else:
|
|
44
|
+
params["top_group"] = "c"
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def _validate_top_group(cls, params, group, top_group):
|
|
48
|
+
"""Validate top group."""
|
|
49
|
+
if group == top_group:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
if group in ("f", "a"):
|
|
53
|
+
params["top_group"] = group
|
|
54
|
+
else:
|
|
55
|
+
cls._validate_browse_top_group(params, group, top_group)
|
|
56
|
+
|
|
57
|
+
def validate_settings_get(self, validated_data, params):
|
|
58
|
+
"""Change bad settings."""
|
|
59
|
+
top_group = params["top_group"]
|
|
60
|
+
group = validated_data.get("group", "r") if validated_data else "r"
|
|
61
|
+
self._validate_top_group(params, group, top_group)
|
|
62
|
+
|
|
63
|
+
if not validated_data.get("breadcrumb_names", True):
|
|
64
|
+
params = self._strip_breadcrumb_names(params)
|
|
65
|
+
|
|
66
|
+
return params
|
codex/views/browser/validate.py
CHANGED
|
@@ -15,6 +15,7 @@ from codex.views.const import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
LOG = get_logger(__name__)
|
|
18
|
+
_NO_TOP_GROUP_VALIDATE = frozenset({"metadata"})
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class BrowserValidateView(BrowserBaseView):
|
|
@@ -55,6 +56,8 @@ class BrowserValidateView(BrowserBaseView):
|
|
|
55
56
|
return valid_top_groups
|
|
56
57
|
|
|
57
58
|
def _validate_top_group(self, valid_top_groups):
|
|
59
|
+
# if self.TARGET in _NO_TOP_GROUP_VALIDATE:
|
|
60
|
+
# return
|
|
58
61
|
nav_group = self.kwargs.get("group")
|
|
59
62
|
top_group = self.params.get("top_group")
|
|
60
63
|
if top_group not in valid_top_groups:
|
codex/views/const.py
CHANGED
|
@@ -67,6 +67,25 @@ GROUP_RELATION = MappingProxyType(
|
|
|
67
67
|
STORY_ARC_GROUP: "story_arc_numbers__story_arc",
|
|
68
68
|
}
|
|
69
69
|
)
|
|
70
|
+
FILTER_ONLY_GROUP_RELATION = MappingProxyType(
|
|
71
|
+
{
|
|
72
|
+
**GROUP_NAME_MAP,
|
|
73
|
+
FOLDER_GROUP: "folders",
|
|
74
|
+
STORY_ARC_GROUP: "story_arc_numbers__story_arc",
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
METADATA_GROUP_RELATION = MappingProxyType(
|
|
78
|
+
{
|
|
79
|
+
**GROUP_NAME_MAP,
|
|
80
|
+
COMIC_GROUP: "pk",
|
|
81
|
+
# Possible to list all groups in a non group view, but could get crazy.
|
|
82
|
+
# FOLDER_GROUP: "comic__folder",
|
|
83
|
+
# STORY_ARC_GROUP: "comic__story_arc_number__story_arc"
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
CUSTOM_COVER_GROUP_RELATION = MappingProxyType(
|
|
87
|
+
{**GROUP_NAME_MAP, FOLDER_GROUP: "folder", STORY_ARC_GROUP: "storyarc"}
|
|
88
|
+
)
|
|
70
89
|
GROUP_ORDER = "rpisv"
|
|
71
90
|
MODEL_REL_MAP = MappingProxyType(
|
|
72
91
|
{
|
|
@@ -98,8 +117,12 @@ GROUP_MODELS = (
|
|
|
98
117
|
Imprint,
|
|
99
118
|
Series,
|
|
100
119
|
Volume,
|
|
101
|
-
Comic,
|
|
102
120
|
Folder,
|
|
121
|
+
StoryArc,
|
|
122
|
+
)
|
|
123
|
+
STATS_GROUP_MODELS = (
|
|
124
|
+
*GROUP_MODELS,
|
|
125
|
+
Comic,
|
|
103
126
|
)
|
|
104
127
|
METADATA_MODELS = (
|
|
105
128
|
AgeRating,
|
|
@@ -134,4 +157,4 @@ EPOCH_START = datetime.fromtimestamp(0, tz=timezone.utc)
|
|
|
134
157
|
ONE_INTEGERFIELD = Value(1, PositiveSmallIntegerField())
|
|
135
158
|
NONE_INTEGERFIELD = Value(None, PositiveSmallIntegerField())
|
|
136
159
|
NONE_DATETIMEFIELD = Value(None, DateTimeField())
|
|
137
|
-
|
|
160
|
+
EPOCH_START_DATETIMEFIELD = Value(EPOCH_START)
|
codex/views/opds/v1/feed.py
CHANGED
|
@@ -11,10 +11,10 @@ from rest_framework.throttling import ScopedRateThrottle
|
|
|
11
11
|
from codex.librarian.importer.tasks import LazyImportComicsTask
|
|
12
12
|
from codex.librarian.mp_queue import LIBRARIAN_QUEUE
|
|
13
13
|
from codex.logger.logging import get_logger
|
|
14
|
+
from codex.serializers.browser.settings import OPDSSettingsSerializer
|
|
14
15
|
from codex.serializers.opds.v1 import (
|
|
15
16
|
OPDS1TemplateSerializer,
|
|
16
17
|
)
|
|
17
|
-
from codex.views.browser.browser import BrowserView
|
|
18
18
|
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
19
19
|
from codex.views.opds.auth import OPDSTemplateView
|
|
20
20
|
from codex.views.opds.const import BLANK_TITLE, MimeType
|
|
@@ -56,6 +56,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
56
56
|
|
|
57
57
|
template_name = "opds_v1/index.xml"
|
|
58
58
|
serializer_class = OPDS1TemplateSerializer
|
|
59
|
+
input_serializer_class = OPDSSettingsSerializer
|
|
59
60
|
throttle_classes = (ScopedRateThrottle,)
|
|
60
61
|
throttle_scope = "opds"
|
|
61
62
|
TARGET = "opds1"
|
|
@@ -254,10 +255,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
|
|
|
254
255
|
self._set_user_agent_variables()
|
|
255
256
|
self.skip_order_facets |= self.kwargs.get("group") == "c"
|
|
256
257
|
|
|
257
|
-
@extend_schema(
|
|
258
|
-
request=BrowserView.input_serializer_class,
|
|
259
|
-
parameters=[BrowserView.input_serializer_class],
|
|
260
|
-
)
|
|
258
|
+
@extend_schema(parameters=[input_serializer_class])
|
|
261
259
|
def get(self, *_args, **_kwargs):
|
|
262
260
|
"""Get the feed."""
|
|
263
261
|
self.init_request()
|
codex/views/opds/v2/feed.py
CHANGED
|
@@ -7,8 +7,8 @@ from rest_framework.response import Response
|
|
|
7
7
|
|
|
8
8
|
from codex.logger.logging import get_logger
|
|
9
9
|
from codex.models import AdminFlag
|
|
10
|
+
from codex.serializers.browser.settings import OPDSSettingsSerializer
|
|
10
11
|
from codex.serializers.opds.v2 import OPDS2FeedSerializer
|
|
11
|
-
from codex.views.browser.browser import BrowserView
|
|
12
12
|
from codex.views.const import FALSY, MAX_OBJ_PER_PAGE
|
|
13
13
|
from codex.views.opds.auth import OPDSAuthMixin
|
|
14
14
|
from codex.views.opds.const import BLANK_TITLE
|
|
@@ -36,6 +36,7 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
|
|
|
36
36
|
throttle_scope = "opds"
|
|
37
37
|
|
|
38
38
|
serializer_class = OPDS2FeedSerializer
|
|
39
|
+
input_serializer_class = OPDSSettingsSerializer
|
|
39
40
|
|
|
40
41
|
def _title(self, browser_title):
|
|
41
42
|
"""Create the feed title."""
|
|
@@ -233,8 +234,7 @@ class OPDS2FeedView(OPDSAuthMixin, OPDS2PublicationView):
|
|
|
233
234
|
# self._detect_user_agent()
|
|
234
235
|
|
|
235
236
|
@extend_schema(
|
|
236
|
-
|
|
237
|
-
parameters=[BrowserView.input_serializer_class],
|
|
237
|
+
parameters=[input_serializer_class],
|
|
238
238
|
)
|
|
239
239
|
def get(self, *_args, **_kwargs):
|
|
240
240
|
"""Get the feed."""
|
codex/views/reader/arcs.py
CHANGED
|
@@ -4,7 +4,7 @@ from types import MappingProxyType
|
|
|
4
4
|
|
|
5
5
|
from codex.models import AdminFlag
|
|
6
6
|
from codex.util import max_none
|
|
7
|
-
from codex.views.const import FOLDER_GROUP
|
|
7
|
+
from codex.views.const import FOLDER_GROUP, STORY_ARC_GROUP
|
|
8
8
|
from codex.views.reader.books import ReaderBooksView
|
|
9
9
|
|
|
10
10
|
|
|
@@ -16,6 +16,22 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
16
16
|
super().__init__(*args, **kwargs)
|
|
17
17
|
self.params: MappingProxyType = MappingProxyType({})
|
|
18
18
|
|
|
19
|
+
def _get_group_arc(self, book, attr):
|
|
20
|
+
"""Create the volume arc."""
|
|
21
|
+
group = getattr(book, attr, None)
|
|
22
|
+
if not group:
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
_, arc_pks = self.get_group_pks_from_breadcrumbs([group])
|
|
26
|
+
if group.pk not in arc_pks:
|
|
27
|
+
arc_pks = (group.pk,)
|
|
28
|
+
return {
|
|
29
|
+
"group": attr[0],
|
|
30
|
+
"pks": arc_pks,
|
|
31
|
+
"name": group.name,
|
|
32
|
+
"mtime": group.updated_at,
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
def _get_folder_arc(self, book):
|
|
20
36
|
"""Create the folder arc."""
|
|
21
37
|
efv_flag = (
|
|
@@ -23,39 +39,16 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
23
39
|
.get(key=AdminFlag.FlagChoices.FOLDER_VIEW.value)
|
|
24
40
|
.on
|
|
25
41
|
)
|
|
42
|
+
if not efv_flag:
|
|
43
|
+
return None
|
|
26
44
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
else:
|
|
36
|
-
folder_arc = None
|
|
37
|
-
return folder_arc
|
|
38
|
-
|
|
39
|
-
def _get_series_arc(self, book):
|
|
40
|
-
"""Create the series arc."""
|
|
41
|
-
series = book.series
|
|
42
|
-
if series:
|
|
43
|
-
arc_pks = self.get_series_pks_from_breadcrumbs()
|
|
44
|
-
if book.series.pk not in arc_pks:
|
|
45
|
-
arc_pks = (book.series.pk,)
|
|
46
|
-
arc = (
|
|
47
|
-
{
|
|
48
|
-
"group": "s",
|
|
49
|
-
"pks": arc_pks,
|
|
50
|
-
"name": series.name,
|
|
51
|
-
"mtime": series.updated_at,
|
|
52
|
-
}
|
|
53
|
-
if series
|
|
54
|
-
else None
|
|
55
|
-
)
|
|
56
|
-
else:
|
|
57
|
-
arc = None
|
|
58
|
-
return arc
|
|
45
|
+
folder = book.parent_folder
|
|
46
|
+
return {
|
|
47
|
+
"group": FOLDER_GROUP,
|
|
48
|
+
"pks": (folder.pk,),
|
|
49
|
+
"name": folder.name,
|
|
50
|
+
"mtime": folder.updated_at,
|
|
51
|
+
}
|
|
59
52
|
|
|
60
53
|
def _get_story_arcs(self, book):
|
|
61
54
|
"""Create story arcs."""
|
|
@@ -74,26 +67,24 @@ class ReaderArcsView(ReaderBooksView):
|
|
|
74
67
|
def get_arcs(self, book):
|
|
75
68
|
"""Get all series/folder/story arcs."""
|
|
76
69
|
# create top arcs
|
|
77
|
-
|
|
78
|
-
series_arc = self._get_series_arc(book)
|
|
79
|
-
|
|
80
|
-
# order top arcs
|
|
70
|
+
arcs = []
|
|
81
71
|
top_group = self.params.get("top_group")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
else:
|
|
72
|
+
arc = None
|
|
73
|
+
if series_arc := self._get_group_arc(book, "series"):
|
|
74
|
+
arcs.append(series_arc)
|
|
86
75
|
arc = series_arc
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if
|
|
92
|
-
arcs.append(
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
if volume_arc := self._get_group_arc(book, "volume"):
|
|
77
|
+
arcs.append(volume_arc)
|
|
78
|
+
if not arc:
|
|
79
|
+
arc = volume_arc
|
|
80
|
+
if folder_arc := self._get_folder_arc(book):
|
|
81
|
+
arcs.append(folder_arc)
|
|
82
|
+
if top_group == FOLDER_GROUP:
|
|
83
|
+
arc = folder_arc
|
|
84
|
+
if story_arcs := self._get_story_arcs(book):
|
|
85
|
+
arcs = [*arcs, *story_arcs]
|
|
86
|
+
if top_group == STORY_ARC_GROUP:
|
|
87
|
+
arc = story_arcs[0]
|
|
97
88
|
|
|
98
89
|
max_mtime = None
|
|
99
90
|
for arc in arcs:
|