codex 1.7.0a0__py3-none-any.whl → 1.7.0a2__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/choices.py +424 -0
- codex/choices_to_json.py +122 -0
- codex/db.py +31 -15
- codex/exceptions.py +2 -3
- codex/librarian/bookmark/__init__.py +1 -0
- codex/librarian/bookmark/bookmarkd.py +38 -0
- codex/librarian/bookmark/tasks.py +19 -0
- codex/librarian/bookmark/update.py +154 -0
- codex/librarian/covers/coverd.py +3 -4
- codex/librarian/covers/create.py +8 -7
- codex/librarian/covers/path.py +1 -2
- codex/librarian/covers/purge.py +2 -2
- codex/librarian/janitor/integrity.py +1 -0
- codex/librarian/librariand.py +5 -0
- codex/librarian/watchdog/emitter.py +21 -19
- codex/librarian/watchdog/event_batcherd.py +0 -1
- codex/migrations/0030_nocase_collation_day_month_indexes_status_types.py +4 -0
- codex/models/admin.py +0 -5
- codex/models/functions.py +6 -5
- codex/models/query.py +6 -0
- codex/run.py +11 -9
- codex/serializers/admin/tasks.py +2 -2
- codex/serializers/browser/mixins.py +18 -14
- codex/serializers/browser/settings.py +4 -4
- codex/serializers/fields.py +9 -5
- codex/startup.py +6 -4
- codex/static_root/assets/VCheckbox-CCOzQpCw.a65ad95bd21a.js +1 -0
- codex/static_root/assets/VCheckbox-CCOzQpCw.a65ad95bd21a.js.br +0 -0
- codex/static_root/assets/VCheckbox-CCOzQpCw.a65ad95bd21a.js.gz +0 -0
- codex/static_root/assets/VCheckbox-CCOzQpCw.js +1 -0
- codex/static_root/assets/VCheckbox-CCOzQpCw.js.br +0 -0
- codex/static_root/assets/VCheckbox-CCOzQpCw.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DnBH3a96.876fda35b85a.js +1 -0
- codex/static_root/assets/VCheckboxBtn-DnBH3a96.876fda35b85a.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DnBH3a96.876fda35b85a.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DnBH3a96.js +1 -0
- codex/static_root/assets/VCheckboxBtn-DnBH3a96.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DnBH3a96.js.gz +0 -0
- codex/static_root/assets/VCombobox-DwwoR4yO.e7a4db7ab7f0.js +1 -0
- codex/static_root/assets/VCombobox-DwwoR4yO.e7a4db7ab7f0.js.br +0 -0
- codex/static_root/assets/VCombobox-DwwoR4yO.e7a4db7ab7f0.js.gz +0 -0
- codex/static_root/assets/VCombobox-DwwoR4yO.js +1 -0
- codex/static_root/assets/VCombobox-DwwoR4yO.js.br +0 -0
- codex/static_root/assets/VCombobox-DwwoR4yO.js.gz +0 -0
- codex/static_root/assets/VDialog-CSvrKKb2.css +1 -0
- 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 +1 -0
- 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/VDialog-DbN2FfmF.b2843f79d90f.js +1 -0
- codex/static_root/assets/VDialog-DbN2FfmF.b2843f79d90f.js.br +0 -0
- codex/static_root/assets/VDialog-DbN2FfmF.b2843f79d90f.js.gz +0 -0
- codex/static_root/assets/VDialog-DbN2FfmF.js +1 -0
- codex/static_root/assets/VDialog-DbN2FfmF.js.br +0 -0
- codex/static_root/assets/VDialog-DbN2FfmF.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CBPnoy7O.212f4cad7d78.js +1 -0
- codex/static_root/assets/VExpansionPanels-CBPnoy7O.212f4cad7d78.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CBPnoy7O.212f4cad7d78.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CBPnoy7O.js +1 -0
- codex/static_root/assets/VExpansionPanels-CBPnoy7O.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CBPnoy7O.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-C7Zu_Eup.b42735892aec.js +1 -0
- codex/static_root/assets/VRadioGroup-C7Zu_Eup.b42735892aec.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C7Zu_Eup.b42735892aec.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-C7Zu_Eup.js +1 -0
- codex/static_root/assets/VRadioGroup-C7Zu_Eup.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C7Zu_Eup.js.gz +0 -0
- codex/static_root/assets/VSelect-BTy7SKk0.dee138de5dd0.js +1 -0
- codex/static_root/assets/VSelect-BTy7SKk0.dee138de5dd0.js.br +0 -0
- codex/static_root/assets/VSelect-BTy7SKk0.dee138de5dd0.js.gz +0 -0
- codex/static_root/assets/VSelect-BTy7SKk0.js +1 -0
- codex/static_root/assets/VSelect-BTy7SKk0.js.br +0 -0
- codex/static_root/assets/VSelect-BTy7SKk0.js.gz +0 -0
- codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css +1 -0
- 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 +1 -0
- 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-B42mmzLA.bf64d259ddef.js +1 -0
- codex/static_root/assets/VSelectionControl-B42mmzLA.bf64d259ddef.js.br +0 -0
- codex/static_root/assets/VSelectionControl-B42mmzLA.bf64d259ddef.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-B42mmzLA.js +1 -0
- codex/static_root/assets/VSelectionControl-B42mmzLA.js.br +0 -0
- codex/static_root/assets/VSelectionControl-B42mmzLA.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css +1 -0
- 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 +1 -0
- 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-BtfHoWCn.33e2bd94e34a.js +1 -0
- codex/static_root/assets/VSlideGroup-BtfHoWCn.33e2bd94e34a.js.br +0 -0
- codex/static_root/assets/VSlideGroup-BtfHoWCn.33e2bd94e34a.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-BtfHoWCn.js +1 -0
- codex/static_root/assets/VSlideGroup-BtfHoWCn.js.br +0 -0
- codex/static_root/assets/VSlideGroup-BtfHoWCn.js.gz +0 -0
- codex/static_root/assets/VTable-R3X0iPjV.ee07d9c2ca5f.js +1 -0
- codex/static_root/assets/VTable-R3X0iPjV.ee07d9c2ca5f.js.br +0 -0
- codex/static_root/assets/VTable-R3X0iPjV.ee07d9c2ca5f.js.gz +0 -0
- codex/static_root/assets/VTable-R3X0iPjV.js +1 -0
- codex/static_root/assets/VTable-R3X0iPjV.js.br +0 -0
- codex/static_root/assets/VTable-R3X0iPjV.js.gz +0 -0
- codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css +1 -0
- 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 +1 -0
- 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-D9pqgz_R.47beb9e7e547.js +1 -0
- codex/static_root/assets/VTextField-D9pqgz_R.47beb9e7e547.js.br +0 -0
- codex/static_root/assets/VTextField-D9pqgz_R.47beb9e7e547.js.gz +0 -0
- codex/static_root/assets/VTextField-D9pqgz_R.js +1 -0
- codex/static_root/assets/VTextField-D9pqgz_R.js.br +0 -0
- codex/static_root/assets/VTextField-D9pqgz_R.js.gz +0 -0
- codex/static_root/assets/VWindowItem-DHTDdYfK.f6bf6b893442.js +1 -0
- codex/static_root/assets/VWindowItem-DHTDdYfK.f6bf6b893442.js.br +0 -0
- codex/static_root/assets/VWindowItem-DHTDdYfK.f6bf6b893442.js.gz +0 -0
- codex/static_root/assets/VWindowItem-DHTDdYfK.js +1 -0
- codex/static_root/assets/VWindowItem-DHTDdYfK.js.br +0 -0
- codex/static_root/assets/VWindowItem-DHTDdYfK.js.gz +0 -0
- codex/static_root/assets/admin-CIWyRJnH.dde0ee029f08.js +1 -0
- codex/static_root/assets/admin-CIWyRJnH.dde0ee029f08.js.br +0 -0
- codex/static_root/assets/admin-CIWyRJnH.dde0ee029f08.js.gz +0 -0
- codex/static_root/assets/admin-CIWyRJnH.js +1 -0
- codex/static_root/assets/admin-CIWyRJnH.js.br +0 -0
- codex/static_root/assets/admin-CIWyRJnH.js.gz +0 -0
- codex/static_root/assets/{admin-CWEjaFr5.7d4523387384.css → admin-DJ7pIXDQ.css} +1 -1
- 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-CWEjaFr5.css → admin-DJ7pIXDQ.e2a77af73ef2.css} +1 -1
- 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 +1 -0
- 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 +1 -0
- 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-BHOUwxhf.1ed26b58fb9f.js +1 -0
- codex/static_root/assets/admin-menu-BHOUwxhf.1ed26b58fb9f.js.br +0 -0
- codex/static_root/assets/admin-menu-BHOUwxhf.1ed26b58fb9f.js.gz +0 -0
- codex/static_root/assets/admin-menu-BHOUwxhf.js +1 -0
- codex/static_root/assets/admin-menu-BHOUwxhf.js.br +0 -0
- codex/static_root/assets/admin-menu-BHOUwxhf.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-DHIl0C8k.85a213c96423.js +1 -0
- codex/static_root/assets/admin-settings-button-progress-DHIl0C8k.85a213c96423.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-DHIl0C8k.85a213c96423.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-DHIl0C8k.js +1 -0
- codex/static_root/assets/admin-settings-button-progress-DHIl0C8k.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-DHIl0C8k.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-DUUQfVsZ.2548c49a853e.css +1 -0
- codex/static_root/assets/admin-settings-button-progress-DUUQfVsZ.css +1 -0
- codex/static_root/assets/browser-6T4uwUjJ.7dd4a7f74df6.js +1 -0
- codex/static_root/assets/browser-6T4uwUjJ.7dd4a7f74df6.js.br +0 -0
- codex/static_root/assets/browser-6T4uwUjJ.7dd4a7f74df6.js.gz +0 -0
- codex/static_root/assets/browser-6T4uwUjJ.js +1 -0
- codex/static_root/assets/browser-6T4uwUjJ.js.br +0 -0
- codex/static_root/assets/browser-6T4uwUjJ.js.gz +0 -0
- codex/static_root/assets/browser-Br5CorL4.6e2a416309d5.css +1 -0
- codex/static_root/assets/browser-Br5CorL4.6e2a416309d5.css.br +0 -0
- codex/static_root/assets/browser-Br5CorL4.6e2a416309d5.css.gz +0 -0
- codex/static_root/assets/browser-Br5CorL4.css +1 -0
- codex/static_root/assets/browser-Br5CorL4.css.br +0 -0
- codex/static_root/assets/browser-Br5CorL4.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BfiQBpUn.48db4c906920.js +1 -0
- codex/static_root/assets/change-password-dialog-BfiQBpUn.48db4c906920.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BfiQBpUn.48db4c906920.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BfiQBpUn.js +1 -0
- codex/static_root/assets/change-password-dialog-BfiQBpUn.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BfiQBpUn.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css +1 -0
- 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 +1 -0
- 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 +1 -0
- codex/static_root/assets/confirm-dialog-B7w6z2rf.css +1 -0
- codex/static_root/assets/confirm-dialog-Iyuhxfyi.cf28325e58b7.js +1 -0
- codex/static_root/assets/confirm-dialog-Iyuhxfyi.cf28325e58b7.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Iyuhxfyi.cf28325e58b7.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-Iyuhxfyi.js +1 -0
- codex/static_root/assets/confirm-dialog-Iyuhxfyi.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Iyuhxfyi.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dnxh9sRh.b6a92a6bcce9.js +1 -0
- codex/static_root/assets/datetime-column-Dnxh9sRh.b6a92a6bcce9.js.br +0 -0
- codex/static_root/assets/datetime-column-Dnxh9sRh.b6a92a6bcce9.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dnxh9sRh.js +1 -0
- codex/static_root/assets/datetime-column-Dnxh9sRh.js.br +0 -0
- codex/static_root/assets/datetime-column-Dnxh9sRh.js.gz +0 -0
- codex/static_root/assets/filter-CmOjoKbH.ea79c4727d92.js +1 -0
- codex/static_root/assets/filter-CmOjoKbH.ea79c4727d92.js.br +0 -0
- codex/static_root/assets/filter-CmOjoKbH.ea79c4727d92.js.gz +0 -0
- codex/static_root/assets/filter-CmOjoKbH.js +1 -0
- codex/static_root/assets/filter-CmOjoKbH.js.br +0 -0
- codex/static_root/assets/filter-CmOjoKbH.js.gz +0 -0
- codex/static_root/assets/flag-tab-1PeNvDA9.2fc60db57915.js +1 -0
- codex/static_root/assets/flag-tab-1PeNvDA9.2fc60db57915.js.br +0 -0
- codex/static_root/assets/flag-tab-1PeNvDA9.2fc60db57915.js.gz +0 -0
- codex/static_root/assets/flag-tab-1PeNvDA9.js +1 -0
- codex/static_root/assets/flag-tab-1PeNvDA9.js.br +0 -0
- codex/static_root/assets/flag-tab-1PeNvDA9.js.gz +0 -0
- codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css +1 -0
- 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 +1 -0
- 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/group-tab-DkIN1Heu.948e907b1e6b.css +1 -0
- 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 +1 -0
- 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/group-tab-Zl_C7M7Z.dbeed7f2d08f.js +1 -0
- codex/static_root/assets/group-tab-Zl_C7M7Z.dbeed7f2d08f.js.br +0 -0
- codex/static_root/assets/group-tab-Zl_C7M7Z.dbeed7f2d08f.js.gz +0 -0
- codex/static_root/assets/group-tab-Zl_C7M7Z.js +1 -0
- codex/static_root/assets/group-tab-Zl_C7M7Z.js.br +0 -0
- codex/static_root/assets/group-tab-Zl_C7M7Z.js.gz +0 -0
- codex/static_root/assets/http-error-YNrrsTPg.73e9978dc0ce.js +1 -0
- codex/static_root/assets/http-error-YNrrsTPg.73e9978dc0ce.js.br +0 -0
- codex/static_root/assets/http-error-YNrrsTPg.73e9978dc0ce.js.gz +0 -0
- codex/static_root/assets/http-error-YNrrsTPg.js +1 -0
- codex/static_root/assets/http-error-YNrrsTPg.js.br +0 -0
- codex/static_root/assets/http-error-YNrrsTPg.js.gz +0 -0
- codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css +1 -0
- 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 +1 -0
- 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-j5K-C_JC.65c2dc5af141.js +1 -0
- codex/static_root/assets/index-j5K-C_JC.65c2dc5af141.js.br +0 -0
- codex/static_root/assets/index-j5K-C_JC.65c2dc5af141.js.gz +0 -0
- codex/static_root/assets/index-j5K-C_JC.js +1 -0
- codex/static_root/assets/index-j5K-C_JC.js.br +0 -0
- codex/static_root/assets/index-j5K-C_JC.js.gz +0 -0
- codex/static_root/assets/library-tab-CxKs8G-K.c6bfd6d53b17.js +1 -0
- codex/static_root/assets/library-tab-CxKs8G-K.c6bfd6d53b17.js.br +0 -0
- codex/static_root/assets/library-tab-CxKs8G-K.c6bfd6d53b17.js.gz +0 -0
- codex/static_root/assets/library-tab-CxKs8G-K.js +1 -0
- codex/static_root/assets/library-tab-CxKs8G-K.js.br +0 -0
- codex/static_root/assets/library-tab-CxKs8G-K.js.gz +0 -0
- codex/static_root/assets/{library-tab-Bh7ErAk7.7c44200ee727.css → library-tab-Wew8YOLv.0c1780a6b68e.css} +1 -1
- 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-Bh7ErAk7.css → library-tab-Wew8YOLv.css} +1 -1
- 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-H14uUxuN.cc1511f0f146.js +35 -0
- codex/static_root/assets/main-H14uUxuN.cc1511f0f146.js.br +0 -0
- codex/static_root/assets/main-H14uUxuN.cc1511f0f146.js.gz +0 -0
- codex/static_root/assets/main-H14uUxuN.js +35 -0
- codex/static_root/assets/main-H14uUxuN.js.br +0 -0
- codex/static_root/assets/main-H14uUxuN.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-DcRg28qt.ca9cdb9619ff.js +1 -0
- codex/static_root/assets/pager-full-pdf-DcRg28qt.ca9cdb9619ff.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-DcRg28qt.ca9cdb9619ff.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-DcRg28qt.js +1 -0
- codex/static_root/assets/pager-full-pdf-DcRg28qt.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-DcRg28qt.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BgoV4zqc.ab5fa1be17a3.js +1 -0
- codex/static_root/assets/pagination-toolbar-BgoV4zqc.ab5fa1be17a3.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-BgoV4zqc.ab5fa1be17a3.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BgoV4zqc.js +1 -0
- codex/static_root/assets/pagination-toolbar-BgoV4zqc.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-BgoV4zqc.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BcRNpoaW.882f6e967869.js → pdf-doc-BbuJd_VS.225d8590d020.js} +3 -3
- codex/static_root/assets/pdf-doc-BbuJd_VS.225d8590d020.js.br +0 -0
- codex/static_root/assets/pdf-doc-BbuJd_VS.225d8590d020.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BcRNpoaW.js → pdf-doc-BbuJd_VS.js} +3 -3
- codex/static_root/assets/pdf-doc-BbuJd_VS.js.br +0 -0
- codex/static_root/assets/pdf-doc-BbuJd_VS.js.gz +0 -0
- codex/static_root/assets/reader-DORea4Al.d8e756bb0027.js +2 -0
- codex/static_root/assets/reader-DORea4Al.d8e756bb0027.js.br +0 -0
- codex/static_root/assets/reader-DORea4Al.d8e756bb0027.js.gz +0 -0
- codex/static_root/assets/reader-DORea4Al.js +2 -0
- codex/static_root/assets/reader-DORea4Al.js.br +0 -0
- codex/static_root/assets/reader-DORea4Al.js.gz +0 -0
- codex/static_root/assets/{reader-DveZZr9x.7b21b9f8f628.css → reader-OpKdTZ_B.css} +1 -1
- codex/static_root/assets/reader-OpKdTZ_B.css.br +0 -0
- codex/static_root/assets/reader-OpKdTZ_B.css.gz +0 -0
- codex/static_root/assets/{reader-DveZZr9x.css → reader-OpKdTZ_B.ebd1512b7014.css} +1 -1
- codex/static_root/assets/reader-OpKdTZ_B.ebd1512b7014.css.br +0 -0
- codex/static_root/assets/reader-OpKdTZ_B.ebd1512b7014.css.gz +0 -0
- codex/static_root/assets/relation-chips-kqofGdNg.0c7a53962f2b.js +1 -0
- codex/static_root/assets/relation-chips-kqofGdNg.0c7a53962f2b.js.br +0 -0
- codex/static_root/assets/relation-chips-kqofGdNg.0c7a53962f2b.js.gz +0 -0
- codex/static_root/assets/relation-chips-kqofGdNg.js +1 -0
- codex/static_root/assets/relation-chips-kqofGdNg.js.br +0 -0
- codex/static_root/assets/relation-chips-kqofGdNg.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C-hgtHJt.0eaf7e00ee9b.js +2 -0
- codex/static_root/assets/settings-drawer-C-hgtHJt.0eaf7e00ee9b.js.br +0 -0
- codex/static_root/assets/settings-drawer-C-hgtHJt.0eaf7e00ee9b.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C-hgtHJt.js +2 -0
- codex/static_root/assets/settings-drawer-C-hgtHJt.js.br +0 -0
- codex/static_root/assets/settings-drawer-C-hgtHJt.js.gz +0 -0
- codex/static_root/assets/{stats-tab-DpfGsVX3.1cb66f2649a9.css → stats-tab-BAYTqQNg.5f715428c394.css} +1 -1
- 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-DpfGsVX3.css → stats-tab-BAYTqQNg.css} +1 -1
- 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-C7iJCFa5.f1039eba03c2.js +1 -0
- codex/static_root/assets/stats-tab-C7iJCFa5.f1039eba03c2.js.br +0 -0
- codex/static_root/assets/stats-tab-C7iJCFa5.f1039eba03c2.js.gz +0 -0
- codex/static_root/assets/stats-tab-C7iJCFa5.js +1 -0
- codex/static_root/assets/stats-tab-C7iJCFa5.js.br +0 -0
- codex/static_root/assets/stats-tab-C7iJCFa5.js.gz +0 -0
- codex/static_root/assets/task-tab--cXi48kg.5dfd6890a1c2.css +1 -0
- codex/static_root/assets/{task-tab-1XiWn9xP.06cb8e1b794d.css.br → task-tab--cXi48kg.5dfd6890a1c2.css.br} +1 -1
- codex/static_root/assets/task-tab--cXi48kg.5dfd6890a1c2.css.gz +0 -0
- codex/static_root/assets/task-tab--cXi48kg.css +1 -0
- codex/static_root/assets/{task-tab-1XiWn9xP.css.br → task-tab--cXi48kg.css.br} +1 -1
- codex/static_root/assets/task-tab--cXi48kg.css.gz +0 -0
- codex/static_root/assets/task-tab-Drfz5uaT.0b387dcd4f5a.js +1 -0
- codex/static_root/assets/task-tab-Drfz5uaT.0b387dcd4f5a.js.br +0 -0
- codex/static_root/assets/task-tab-Drfz5uaT.0b387dcd4f5a.js.gz +0 -0
- codex/static_root/assets/task-tab-Drfz5uaT.js +1 -0
- codex/static_root/assets/task-tab-Drfz5uaT.js.br +0 -0
- codex/static_root/assets/task-tab-Drfz5uaT.js.gz +0 -0
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css +1 -0
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.br +4 -0
- codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.gz +0 -0
- codex/static_root/assets/transition-5LRFTPo5.css +1 -0
- codex/static_root/assets/transition-5LRFTPo5.css.br +4 -0
- codex/static_root/assets/transition-5LRFTPo5.css.gz +0 -0
- codex/static_root/assets/transition-CQVFPpmZ.ba35e475243a.js +1 -0
- codex/static_root/assets/transition-CQVFPpmZ.ba35e475243a.js.br +0 -0
- codex/static_root/assets/transition-CQVFPpmZ.ba35e475243a.js.gz +0 -0
- codex/static_root/assets/transition-CQVFPpmZ.js +1 -0
- codex/static_root/assets/transition-CQVFPpmZ.js.br +0 -0
- codex/static_root/assets/transition-CQVFPpmZ.js.gz +0 -0
- codex/static_root/assets/unauthorized-Cfxq6rru.968d6e6e57bd.js +1 -0
- codex/static_root/assets/unauthorized-Cfxq6rru.968d6e6e57bd.js.br +0 -0
- codex/static_root/assets/unauthorized-Cfxq6rru.968d6e6e57bd.js.gz +0 -0
- codex/static_root/assets/unauthorized-Cfxq6rru.js +1 -0
- codex/static_root/assets/unauthorized-Cfxq6rru.js.br +0 -0
- codex/static_root/assets/unauthorized-Cfxq6rru.js.gz +0 -0
- codex/static_root/assets/user-tab-icKcK_Qd.c29142ed5212.js +1 -0
- codex/static_root/assets/user-tab-icKcK_Qd.c29142ed5212.js.br +0 -0
- codex/static_root/assets/user-tab-icKcK_Qd.c29142ed5212.js.gz +0 -0
- codex/static_root/assets/user-tab-icKcK_Qd.js +1 -0
- codex/static_root/assets/user-tab-icKcK_Qd.js.br +0 -0
- codex/static_root/assets/user-tab-icKcK_Qd.js.gz +0 -0
- codex/static_root/manifest.3eee2b4822b7.json +712 -0
- codex/static_root/manifest.3eee2b4822b7.json.br +0 -0
- codex/static_root/manifest.3eee2b4822b7.json.gz +0 -0
- codex/static_root/manifest.json +377 -300
- 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 +5 -6
- codex/threads.py +1 -1
- codex/urls/api/auth.py +1 -1
- codex/urls/api/browser.py +1 -1
- codex/views/admin/library.py +18 -13
- codex/views/admin/stats.py +23 -16
- codex/views/auth.py +42 -77
- codex/views/bookmark.py +16 -153
- codex/views/browser/annotate/__init__.py +1 -0
- codex/views/browser/annotate/bookmark.py +112 -0
- codex/views/browser/annotate/card.py +61 -0
- codex/views/browser/annotate/order.py +247 -0
- codex/views/browser/breadcrumbs.py +90 -11
- codex/views/browser/browser.py +44 -110
- codex/views/browser/choices.py +15 -18
- codex/views/browser/cover.py +10 -20
- codex/views/browser/filters/bookmark.py +18 -19
- codex/views/browser/filters/field.py +8 -3
- codex/views/browser/filters/filter.py +69 -0
- codex/views/browser/filters/group.py +9 -6
- codex/views/browser/filters/search/fts.py +5 -19
- codex/views/browser/filters/search/parse.py +54 -24
- codex/views/browser/group_mtime.py +83 -0
- codex/views/browser/metadata/__init__.py +1 -0
- codex/views/browser/metadata/annotate.py +109 -0
- codex/views/browser/metadata/copy_intersections.py +90 -0
- codex/views/browser/metadata/metadata.py +83 -0
- codex/views/browser/metadata/query_intersections.py +112 -0
- codex/views/browser/mtime.py +2 -10
- codex/views/browser/order_by.py +22 -22
- codex/views/browser/page_in_bounds.py +2 -2
- codex/views/browser/paginate.py +9 -15
- codex/views/browser/params.py +64 -0
- codex/views/browser/title.py +1 -6
- codex/views/browser/validate.py +68 -33
- codex/views/download.py +1 -1
- codex/views/mixins.py +27 -15
- codex/views/opds/const.py +2 -3
- codex/views/opds/urls.py +2 -2
- codex/views/opds/util.py +2 -3
- codex/views/opds/v1/entry/links.py +3 -1
- codex/views/opds/v1/facets.py +65 -12
- codex/views/opds/v1/feed.py +5 -64
- codex/views/opds/v1/links.py +5 -4
- codex/views/opds/v2/feed.py +1 -28
- codex/views/opds/v2/links.py +26 -1
- codex/views/opds/v2/publications.py +5 -6
- codex/views/opds/v2/top_links.py +3 -6
- codex/views/public.py +2 -2
- codex/views/reader/arcs.py +0 -3
- codex/views/reader/books.py +57 -41
- codex/views/reader/page.py +10 -4
- codex/views/reader/{init.py → params.py} +37 -31
- codex/views/reader/reader.py +1 -2
- codex/views/session.py +43 -54
- codex/views/timezone.py +54 -0
- codex/views/version.py +1 -1
- {codex-1.7.0a0.dist-info → codex-1.7.0a2.dist-info}/METADATA +1 -1
- {codex-1.7.0a0.dist-info → codex-1.7.0a2.dist-info}/RECORD +417 -383
- codex/serializers/choices.py +0 -196
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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-CWEjaFr5.7d4523387384.css.br +0 -0
- codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.gz +0 -0
- 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 +0 -1
- 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 +0 -1
- 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 +0 -30
- 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 +0 -30
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- codex/static_root/assets/confirm-dialog-BmhedLCx.css +0 -1
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.br +0 -4
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.gz +0 -0
- codex/static_root/assets/http-error-C6yuLApo.js +0 -1
- codex/static_root/assets/http-error-C6yuLApo.js.br +0 -4
- codex/static_root/assets/http-error-C6yuLApo.js.gz +0 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.br +0 -2
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.gz +0 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.css.br +0 -2
- codex/static_root/assets/library-tab-Bh7ErAk7.css.gz +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js +0 -1
- 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 +0 -1
- 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 +0 -6
- 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 +0 -6
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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-BcRNpoaW.882f6e967869.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.gz +0 -0
- 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 +0 -2
- 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 +0 -2
- 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-DveZZr9x.7b21b9f8f628.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.gz +0 -0
- 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 +0 -1
- 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 +0 -1
- 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 +0 -2
- 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 +0 -2
- 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 +0 -1
- 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 +0 -1
- 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-DpfGsVX3.1cb66f2649a9.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.gz +0 -0
- 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 +0 -1
- codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css.gz +0 -0
- codex/static_root/assets/task-tab-1XiWn9xP.css +0 -1
- codex/static_root/assets/task-tab-1XiWn9xP.css.gz +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -1
- 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 +0 -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.2f6b0359e1ef.json +0 -1
- codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
- codex/static_root/js/choices.json +0 -1
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/manifest.45cb46e19686.json +0 -635
- codex/static_root/manifest.45cb46e19686.json.br +0 -0
- codex/static_root/manifest.45cb46e19686.json.gz +0 -0
- codex/views/browser/annotations.py +0 -339
- codex/views/browser/base.py +0 -139
- codex/views/browser/filters/annotations.py +0 -125
- codex/views/browser/metadata.py +0 -332
- {codex-1.7.0a0.dist-info → codex-1.7.0a2.dist-info}/LICENSE +0 -0
- {codex-1.7.0a0.dist-info → codex-1.7.0a2.dist-info}/WHEEL +0 -0
- {codex-1.7.0a0.dist-info → codex-1.7.0a2.dist-info}/entry_points.txt +0 -0
codex/views/browser/mtime.py
CHANGED
|
@@ -7,13 +7,13 @@ from codex.logger.logging import get_logger
|
|
|
7
7
|
from codex.models.groups import Publisher
|
|
8
8
|
from codex.serializers.browser.mtime import GroupsMtimeSerializer, MtimeSerializer
|
|
9
9
|
from codex.util import max_none
|
|
10
|
-
from codex.views.browser.
|
|
10
|
+
from codex.views.browser.group_mtime import BrowserGroupMtimeView
|
|
11
11
|
from codex.views.const import GROUP_MODEL_MAP
|
|
12
12
|
|
|
13
13
|
LOG = get_logger(__name__)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class MtimeView(
|
|
16
|
+
class MtimeView(BrowserGroupMtimeView):
|
|
17
17
|
"""Get the mtimes for the submitted groups."""
|
|
18
18
|
|
|
19
19
|
input_serializer_class = GroupsMtimeSerializer
|
|
@@ -22,11 +22,6 @@ class MtimeView(BrowserAnnotationsFilterView):
|
|
|
22
22
|
REPARSE_JSON_FIELDS = frozenset({"groups", "filters"})
|
|
23
23
|
TARGET = "mtime"
|
|
24
24
|
|
|
25
|
-
def __init__(self, *args, **kwargs) -> None:
|
|
26
|
-
"""Initialize."""
|
|
27
|
-
super().__init__(*args, **kwargs)
|
|
28
|
-
self.init_bookmark_data()
|
|
29
|
-
|
|
30
25
|
def _get_group_mtime(self, item):
|
|
31
26
|
"""Get one group's mtimes."""
|
|
32
27
|
group = item["group"]
|
|
@@ -50,9 +45,6 @@ class MtimeView(BrowserAnnotationsFilterView):
|
|
|
50
45
|
@extend_schema(parameters=[GroupsMtimeSerializer])
|
|
51
46
|
def get(self, *args, **kwargs):
|
|
52
47
|
"""Get the mtimes for the submitted groups."""
|
|
53
|
-
# Parse Request
|
|
54
|
-
self.parse_params()
|
|
55
|
-
|
|
56
48
|
max_mtime = self.get_max_groups_mtime()
|
|
57
49
|
|
|
58
50
|
# Serialize Response
|
codex/views/browser/order_by.py
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
"""Base view for ordering the query."""
|
|
2
2
|
|
|
3
|
-
from django.db.models.aggregates import Max, Min
|
|
4
|
-
|
|
5
3
|
from codex.models import Comic
|
|
6
|
-
from codex.views.browser.
|
|
7
|
-
BrowserAnnotationsFilterView,
|
|
8
|
-
)
|
|
4
|
+
from codex.views.browser.group_mtime import BrowserGroupMtimeView
|
|
9
5
|
|
|
10
6
|
|
|
11
|
-
class BrowserOrderByView(
|
|
7
|
+
class BrowserOrderByView(BrowserGroupMtimeView):
|
|
12
8
|
"""Base class for views that need ordering."""
|
|
13
9
|
|
|
14
|
-
def
|
|
10
|
+
def __init__(self, *args, **kwargs):
|
|
11
|
+
"""Initialize memoized vars."""
|
|
12
|
+
super().__init__(*args, **kwargs)
|
|
13
|
+
self._order_key: str = ""
|
|
14
|
+
self._comic_sort_names: tuple[str, ...] = ()
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def order_key(self):
|
|
15
18
|
"""Get the default order key for the view."""
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self.
|
|
24
|
-
self.order_agg_func = Max if order_reverse else Min
|
|
19
|
+
if not self._order_key:
|
|
20
|
+
order_key: str = self.params["order_by"]
|
|
21
|
+
if (order_key == "search_score" and not self.fts_mode) or (
|
|
22
|
+
order_key == "filename" and not self.admin_flags["folder_view"]
|
|
23
|
+
):
|
|
24
|
+
order_key = "sort_name"
|
|
25
|
+
self._order_key = order_key
|
|
26
|
+
return self._order_key
|
|
25
27
|
|
|
26
28
|
def _add_comic_order_by(self, order_key, comic_sort_names):
|
|
27
29
|
"""Order by for comics (and covers)."""
|
|
@@ -29,7 +31,7 @@ class BrowserOrderByView(BrowserAnnotationsFilterView):
|
|
|
29
31
|
order_key = self.order_key
|
|
30
32
|
if order_key == "sort_name":
|
|
31
33
|
if not comic_sort_names:
|
|
32
|
-
comic_sort_names = self.
|
|
34
|
+
comic_sort_names = self._comic_sort_names
|
|
33
35
|
order_fields_head = [
|
|
34
36
|
*comic_sort_names,
|
|
35
37
|
"issue_number",
|
|
@@ -45,15 +47,13 @@ class BrowserOrderByView(BrowserAnnotationsFilterView):
|
|
|
45
47
|
if order_key == "story_arc_number":
|
|
46
48
|
order_fields_head += ["date"]
|
|
47
49
|
elif order_key == "bookmark_updated_at":
|
|
48
|
-
order_fields_head += ["
|
|
50
|
+
order_fields_head += ["bookmark_updated_at"]
|
|
49
51
|
return order_fields_head
|
|
50
52
|
|
|
51
|
-
def add_order_by(
|
|
52
|
-
self, qs, model, order_key="", do_reverse=True, comic_sort_names=None
|
|
53
|
-
):
|
|
53
|
+
def add_order_by(self, qs, order_key="", do_reverse=True, comic_sort_names=None):
|
|
54
54
|
"""Create the order_by list."""
|
|
55
55
|
order_fields_head = ()
|
|
56
|
-
if model
|
|
56
|
+
if qs.model is Comic:
|
|
57
57
|
order_fields_head = self._add_comic_order_by(order_key, comic_sort_names)
|
|
58
58
|
else:
|
|
59
59
|
order_fields_head = ["order_value"]
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""Browser Page Bounds Checking."""
|
|
2
2
|
|
|
3
3
|
from codex.logger.logging import get_logger
|
|
4
|
-
from codex.views.browser.
|
|
4
|
+
from codex.views.browser.annotate.card import BrowserAnnotateCardView
|
|
5
5
|
from codex.views.util import Route
|
|
6
6
|
|
|
7
7
|
LOG = get_logger(__name__)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class BrowserPageInBoundsView(
|
|
10
|
+
class BrowserPageInBoundsView(BrowserAnnotateCardView):
|
|
11
11
|
"""Browser Page Bounds Checking."""
|
|
12
12
|
|
|
13
13
|
def _get_back_one_page_route(self, num_pages):
|
codex/views/browser/paginate.py
CHANGED
|
@@ -5,14 +5,8 @@ from math import ceil
|
|
|
5
5
|
from django.core.paginator import EmptyPage, Paginator
|
|
6
6
|
|
|
7
7
|
from codex.logger.logging import get_logger
|
|
8
|
-
from codex.models import (
|
|
9
|
-
Comic,
|
|
10
|
-
Folder,
|
|
11
|
-
)
|
|
12
8
|
from codex.views.browser.page_in_bounds import BrowserPageInBoundsView
|
|
13
|
-
from codex.views.const import
|
|
14
|
-
MAX_OBJ_PER_PAGE,
|
|
15
|
-
)
|
|
9
|
+
from codex.views.const import MAX_OBJ_PER_PAGE
|
|
16
10
|
|
|
17
11
|
LOG = get_logger(__name__)
|
|
18
12
|
|
|
@@ -20,7 +14,7 @@ LOG = get_logger(__name__)
|
|
|
20
14
|
class BrowserPaginateView(BrowserPageInBoundsView):
|
|
21
15
|
"""Paginate Groups and Books."""
|
|
22
16
|
|
|
23
|
-
def _paginate_section(self,
|
|
17
|
+
def _paginate_section(self, qs, page):
|
|
24
18
|
"""Paginate a group or Comic section."""
|
|
25
19
|
orphans = 0 if self.model_group == "f" or self.params.get("q") else 5
|
|
26
20
|
paginator = Paginator(qs, MAX_OBJ_PER_PAGE, orphans=orphans)
|
|
@@ -28,21 +22,21 @@ class BrowserPaginateView(BrowserPageInBoundsView):
|
|
|
28
22
|
paginator_page = paginator.page(page)
|
|
29
23
|
qs = paginator_page.object_list
|
|
30
24
|
except EmptyPage:
|
|
31
|
-
if
|
|
32
|
-
model_name =
|
|
25
|
+
if self.model_group != "f":
|
|
26
|
+
model_name = qs.model.__name__ if qs.model else "UnknownGroup"
|
|
33
27
|
LOG.warning(f"No {model_name}s on page {page}")
|
|
34
|
-
qs = model.objects.none()
|
|
28
|
+
qs = qs.model.objects.none()
|
|
35
29
|
|
|
36
30
|
return qs
|
|
37
31
|
|
|
38
32
|
def _paginate_groups(self, group_qs):
|
|
39
33
|
"""Paginate the group object list before books."""
|
|
40
34
|
page = self.kwargs.get("page", 1)
|
|
41
|
-
return self._paginate_section(
|
|
35
|
+
return self._paginate_section(group_qs, page)
|
|
42
36
|
|
|
43
37
|
def _paginate_books(self, book_qs, total_group_count, page_group_count):
|
|
44
38
|
"""Paginate the book object list based on how many group/folders are showing."""
|
|
45
|
-
group_remainder =
|
|
39
|
+
group_remainder = total_group_count % MAX_OBJ_PER_PAGE
|
|
46
40
|
num_books_on_mixed_page = MAX_OBJ_PER_PAGE - group_remainder
|
|
47
41
|
if page_group_count:
|
|
48
42
|
# There are books after the groups on the same page
|
|
@@ -50,7 +44,7 @@ class BrowserPaginateView(BrowserPageInBoundsView):
|
|
|
50
44
|
page_book_qs = book_qs[:num_books_on_mixed_page]
|
|
51
45
|
else:
|
|
52
46
|
# There are books after the groups on a new page
|
|
53
|
-
book_offset = 0 if group_remainder else num_books_on_mixed_page
|
|
47
|
+
book_offset = 0 if not group_remainder else num_books_on_mixed_page
|
|
54
48
|
page_book_qs = book_qs[book_offset:]
|
|
55
49
|
|
|
56
50
|
# Which book page are we on after groups?
|
|
@@ -58,7 +52,7 @@ class BrowserPaginateView(BrowserPageInBoundsView):
|
|
|
58
52
|
num_group_and_mixed_pages = ceil(total_group_count / MAX_OBJ_PER_PAGE)
|
|
59
53
|
book_only_page = page - num_group_and_mixed_pages
|
|
60
54
|
|
|
61
|
-
page_book_qs = self._paginate_section(
|
|
55
|
+
page_book_qs = self._paginate_section(page_book_qs, book_only_page)
|
|
62
56
|
return page_book_qs
|
|
63
57
|
|
|
64
58
|
def paginate(self, group_qs, book_qs, group_count):
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Parse browser params."""
|
|
2
|
+
|
|
3
|
+
from types import MappingProxyType
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from codex.serializers.browser.settings import BrowserSettingsSerializer
|
|
7
|
+
from codex.views.const import FOLDER_GROUP, STORY_ARC_GROUP
|
|
8
|
+
from codex.views.session import SessionView
|
|
9
|
+
from codex.views.util import reparse_json_query_params
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BrowserParamsView(SessionView):
|
|
13
|
+
"""Browser Params Parsing."""
|
|
14
|
+
|
|
15
|
+
input_serializer_class = BrowserSettingsSerializer
|
|
16
|
+
REPARSE_JSON_FIELDS = frozenset({"breadcrumbs", "filters", "show"})
|
|
17
|
+
|
|
18
|
+
def __init__(self, *args, **kwargs):
|
|
19
|
+
"""Initialize properties."""
|
|
20
|
+
super().__init__(*args, **kwargs)
|
|
21
|
+
self._params: MappingProxyType[str, Any] | None = None
|
|
22
|
+
|
|
23
|
+
def _parse_query_params(self):
|
|
24
|
+
"""Parse GET query parameters: filter object & snake case."""
|
|
25
|
+
query_params = reparse_json_query_params(
|
|
26
|
+
self.request.GET, self.REPARSE_JSON_FIELDS
|
|
27
|
+
)
|
|
28
|
+
if "q" not in query_params and (query := query_params.get("query")):
|
|
29
|
+
# parse query param for opds v2
|
|
30
|
+
query_params["q"] = query
|
|
31
|
+
return query_params
|
|
32
|
+
|
|
33
|
+
def validate_settings(self):
|
|
34
|
+
"""Not used until browser/validate.py."""
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def params(self):
|
|
38
|
+
"""Validate sbmitted settings and apply them over the session settings."""
|
|
39
|
+
if self._params is None:
|
|
40
|
+
data = self._parse_query_params()
|
|
41
|
+
serializer = self.input_serializer_class(data=data)
|
|
42
|
+
serializer.is_valid(raise_exception=True)
|
|
43
|
+
|
|
44
|
+
params: dict[str, Any] = {}
|
|
45
|
+
defaults = self.SESSION_DEFAULTS[self.SESSION_KEY]
|
|
46
|
+
params.update(defaults)
|
|
47
|
+
group = self.kwargs.get("group")
|
|
48
|
+
if group:
|
|
49
|
+
# order_by has a dynamic group based default
|
|
50
|
+
order_defaults = {
|
|
51
|
+
"order_by": "filename"
|
|
52
|
+
if group == FOLDER_GROUP
|
|
53
|
+
else "story_arc_number"
|
|
54
|
+
if group == STORY_ARC_GROUP
|
|
55
|
+
else "sort_name"
|
|
56
|
+
}
|
|
57
|
+
params.update(order_defaults)
|
|
58
|
+
validated_data = serializer.validated_data
|
|
59
|
+
|
|
60
|
+
if validated_data:
|
|
61
|
+
params.update(validated_data) # type: ignore
|
|
62
|
+
self._params = MappingProxyType(params)
|
|
63
|
+
self.validate_settings()
|
|
64
|
+
return self._params
|
codex/views/browser/title.py
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
"""Browser title."""
|
|
2
2
|
|
|
3
|
-
from codex.models import
|
|
3
|
+
from codex.models import Comic, Volume
|
|
4
4
|
from codex.views.browser.breadcrumbs import BrowserBreadcrumbsView
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class BrowserTitleView(BrowserBreadcrumbsView):
|
|
8
8
|
"""Browser title methods."""
|
|
9
9
|
|
|
10
|
-
def __init__(self, *args, **kwargs):
|
|
11
|
-
"""Set params for the type checker."""
|
|
12
|
-
super().__init__(*args, **kwargs)
|
|
13
|
-
self.group_instance: BrowserGroupModel | None = None
|
|
14
|
-
|
|
15
10
|
def _get_root_group_name(self):
|
|
16
11
|
if not self.model:
|
|
17
12
|
reason = "No model set in browser"
|
codex/views/browser/validate.py
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
from copy import deepcopy
|
|
4
4
|
from types import MappingProxyType
|
|
5
5
|
|
|
6
|
+
from rest_framework.exceptions import NotFound
|
|
7
|
+
|
|
8
|
+
from codex.choices import DEFAULT_BROWSER_ROUTE, mapping_to_dict
|
|
6
9
|
from codex.exceptions import SeeOtherRedirectError
|
|
7
10
|
from codex.logger.logging import get_logger
|
|
8
|
-
from codex.
|
|
9
|
-
from codex.views.browser.
|
|
11
|
+
from codex.models.groups import BrowserGroupModel
|
|
12
|
+
from codex.views.browser.filters.search.parse import SearchFilterView
|
|
10
13
|
from codex.views.const import (
|
|
11
14
|
COMIC_GROUP,
|
|
12
15
|
FOLDER_GROUP,
|
|
16
|
+
GROUP_MODEL_MAP,
|
|
13
17
|
ROOT_GROUP,
|
|
14
18
|
STORY_ARC_GROUP,
|
|
15
19
|
)
|
|
@@ -17,19 +21,55 @@ from codex.views.const import (
|
|
|
17
21
|
LOG = get_logger(__name__)
|
|
18
22
|
|
|
19
23
|
|
|
20
|
-
class BrowserValidateView(
|
|
24
|
+
class BrowserValidateView(SearchFilterView):
|
|
21
25
|
"""Browser Settings and URL Validation."""
|
|
22
26
|
|
|
23
27
|
DEFAULT_ROUTE = MappingProxyType(
|
|
24
|
-
{
|
|
25
|
-
"name": "browser",
|
|
26
|
-
"params": deepcopy(DEFAULTS["breadcrumbs"][0]),
|
|
27
|
-
}
|
|
28
|
+
{"name": "browser", "params": DEFAULT_BROWSER_ROUTE}
|
|
28
29
|
)
|
|
29
30
|
|
|
31
|
+
def __init__(self, *args, **kwargs):
|
|
32
|
+
"""Initialize properties."""
|
|
33
|
+
super().__init__(*args, **kwargs)
|
|
34
|
+
self._is_admin: bool | None = None
|
|
35
|
+
self._model_group: str = ""
|
|
36
|
+
self._model: type[BrowserGroupModel] | None = None
|
|
37
|
+
self._rel_prefix: str | None = None
|
|
38
|
+
self._valid_nav_groups: tuple[str, ...] | None = None
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def model_group(self):
|
|
42
|
+
"""Memoize the model group."""
|
|
43
|
+
if not self._model_group:
|
|
44
|
+
group = self.kwargs["group"]
|
|
45
|
+
if group == ROOT_GROUP:
|
|
46
|
+
group = self.params["top_group"]
|
|
47
|
+
self._model_group = group
|
|
48
|
+
return self._model_group
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def model(self) -> type[BrowserGroupModel] | None:
|
|
52
|
+
"""Memoize the model for the browse list."""
|
|
53
|
+
if not self._model:
|
|
54
|
+
model = GROUP_MODEL_MAP.get(self.model_group)
|
|
55
|
+
if model is None:
|
|
56
|
+
group = self.kwargs["group"]
|
|
57
|
+
detail = f"Cannot browse {group=}"
|
|
58
|
+
LOG.debug(detail)
|
|
59
|
+
raise NotFound(detail=detail)
|
|
60
|
+
self._model = model
|
|
61
|
+
return self._model
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def rel_prefix(self):
|
|
65
|
+
"""Memoize model rel prefix."""
|
|
66
|
+
if self._rel_prefix is None:
|
|
67
|
+
self._rel_prefix = self.get_rel_prefix(self.model)
|
|
68
|
+
return self._rel_prefix
|
|
69
|
+
|
|
30
70
|
def raise_redirect(self, reason, route_mask=None, settings_mask=None):
|
|
31
71
|
"""Redirect the client to a valid group url."""
|
|
32
|
-
route =
|
|
72
|
+
route = mapping_to_dict(self.DEFAULT_ROUTE)
|
|
33
73
|
if route_mask:
|
|
34
74
|
route["params"].update(route_mask) # type: ignore
|
|
35
75
|
settings = deepcopy(dict(self.params))
|
|
@@ -73,7 +113,7 @@ class BrowserValidateView(BrowserBaseView):
|
|
|
73
113
|
settings_mask = {"top_group": valid_top_group, "breadcrumbs": breadcrumbs}
|
|
74
114
|
self.raise_redirect(reason, route, settings_mask)
|
|
75
115
|
|
|
76
|
-
def
|
|
116
|
+
def _get_valid_browse_nav_groups(self, valid_top_groups):
|
|
77
117
|
"""Get valid nav groups for the current settings.
|
|
78
118
|
|
|
79
119
|
Valid nav groups are the top group and below that are also
|
|
@@ -96,7 +136,7 @@ class BrowserValidateView(BrowserBaseView):
|
|
|
96
136
|
reason = f"Nav group {nav_group} unavailable, redirect to {ROOT_GROUP}"
|
|
97
137
|
self.raise_redirect(reason)
|
|
98
138
|
|
|
99
|
-
|
|
139
|
+
return tuple(valid_nav_groups)
|
|
100
140
|
|
|
101
141
|
def _validate_folder_settings(self):
|
|
102
142
|
"""Check that all the view variables for folder mode are set right."""
|
|
@@ -113,7 +153,7 @@ class BrowserValidateView(BrowserBaseView):
|
|
|
113
153
|
|
|
114
154
|
valid_top_groups = (FOLDER_GROUP,)
|
|
115
155
|
self._validate_top_group(valid_top_groups)
|
|
116
|
-
|
|
156
|
+
return valid_top_groups
|
|
117
157
|
|
|
118
158
|
def _validate_browser_group_settings(self):
|
|
119
159
|
"""Check that all the view variables for browser mode are set right."""
|
|
@@ -132,29 +172,24 @@ class BrowserValidateView(BrowserBaseView):
|
|
|
132
172
|
|
|
133
173
|
# Validate Browser nav_group
|
|
134
174
|
# Redirect if nav group is wrong
|
|
135
|
-
self.
|
|
175
|
+
return self._get_valid_browse_nav_groups(valid_top_groups)
|
|
136
176
|
|
|
137
|
-
def _validate_story_arc_settings(self):
|
|
177
|
+
def _validate_story_arc_settings(self) -> tuple[str, ...]:
|
|
138
178
|
"""Validate story arc settings."""
|
|
139
179
|
valid_top_groups = (STORY_ARC_GROUP,)
|
|
140
180
|
self._validate_top_group(valid_top_groups)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if
|
|
147
|
-
self.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
page = self.kwargs["page"]
|
|
157
|
-
route_changes = {"group": group, "pks": pks, "page": page}
|
|
158
|
-
reason = "order by filename not allowed by admin flag."
|
|
159
|
-
settings_mask = {"order_by": "sort_name"}
|
|
160
|
-
self.raise_redirect(reason, route_changes, settings_mask)
|
|
181
|
+
return valid_top_groups
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def valid_nav_groups(self) -> tuple[str, ...]:
|
|
185
|
+
"""Memoize valid nav groups."""
|
|
186
|
+
if self._valid_nav_groups is None:
|
|
187
|
+
group = self.kwargs["group"]
|
|
188
|
+
if group == FOLDER_GROUP:
|
|
189
|
+
vng = self._validate_folder_settings()
|
|
190
|
+
elif group == STORY_ARC_GROUP:
|
|
191
|
+
vng = self._validate_story_arc_settings()
|
|
192
|
+
else:
|
|
193
|
+
vng = self._validate_browser_group_settings()
|
|
194
|
+
self._valid_nav_groups = vng
|
|
195
|
+
return self._valid_nav_groups
|
codex/views/download.py
CHANGED
|
@@ -22,7 +22,7 @@ class DownloadView(AuthFilterAPIView):
|
|
|
22
22
|
"""Download a comic archive."""
|
|
23
23
|
pk = kwargs.get("pk")
|
|
24
24
|
try:
|
|
25
|
-
group_acl_filter = self.get_group_acl_filter(Comic)
|
|
25
|
+
group_acl_filter = self.get_group_acl_filter(Comic, self.request.user)
|
|
26
26
|
comic = (
|
|
27
27
|
Comic.objects.filter(group_acl_filter)
|
|
28
28
|
.only("path", "file_type")
|
codex/views/mixins.py
CHANGED
|
@@ -12,7 +12,7 @@ class SharedAnnotationsMixin:
|
|
|
12
12
|
"""Cross view annotation methods."""
|
|
13
13
|
|
|
14
14
|
@staticmethod
|
|
15
|
-
def _get_order_groups(
|
|
15
|
+
def _get_order_groups(model_group, pks):
|
|
16
16
|
"""Annotate sort_name."""
|
|
17
17
|
if not pks or len(pks) > 1:
|
|
18
18
|
order_groups = _SHOW_GROUPS
|
|
@@ -28,12 +28,11 @@ class SharedAnnotationsMixin:
|
|
|
28
28
|
return order_groups
|
|
29
29
|
|
|
30
30
|
@classmethod
|
|
31
|
-
def
|
|
31
|
+
def get_sort_name_annotations(cls, model, model_group, pks, show):
|
|
32
32
|
"""Annotate sort names for browser subclasses and reader."""
|
|
33
33
|
sort_name_annotations = {}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
order_groups = cls._get_order_groups(pks, model_group)
|
|
34
|
+
if model is Comic:
|
|
35
|
+
order_groups = cls._get_order_groups(model_group, pks)
|
|
37
36
|
for order_group in order_groups:
|
|
38
37
|
if show and not show.get(order_group):
|
|
39
38
|
continue
|
|
@@ -42,23 +41,19 @@ class SharedAnnotationsMixin:
|
|
|
42
41
|
name_field = "name" if group_name == "volume" else "sort_name"
|
|
43
42
|
sort_name = F(f"{group_name}__{name_field}")
|
|
44
43
|
sort_name_annotations[ann_name] = sort_name
|
|
45
|
-
|
|
46
|
-
elif model == Volume:
|
|
44
|
+
elif model is Volume:
|
|
47
45
|
sort_name_annotations["sort_name"] = F("name")
|
|
48
|
-
|
|
49
|
-
if sort_name_annotations:
|
|
50
|
-
qs = qs.alias(**sort_name_annotations)
|
|
51
|
-
return qs, comic_sort_names
|
|
46
|
+
return sort_name_annotations
|
|
52
47
|
|
|
53
48
|
@classmethod
|
|
54
|
-
def annotate_group_names(cls, qs
|
|
49
|
+
def annotate_group_names(cls, qs):
|
|
55
50
|
"""Annotate name fields by hoisting them up."""
|
|
56
51
|
# Optimized to only lookup what is used on the frontend
|
|
57
52
|
target = cls.TARGET # type: ignore
|
|
58
53
|
if target not in frozenset({"browser", "opds1", "opds2", "reader"}):
|
|
59
54
|
return qs
|
|
60
55
|
group_names = {}
|
|
61
|
-
if model
|
|
56
|
+
if qs.model is Comic:
|
|
62
57
|
if target != "reader":
|
|
63
58
|
group_names["publisher_name"] = F("publisher__name")
|
|
64
59
|
if target == "opds2":
|
|
@@ -69,8 +64,25 @@ class SharedAnnotationsMixin:
|
|
|
69
64
|
"volume_name": F("volume__name"),
|
|
70
65
|
}
|
|
71
66
|
)
|
|
72
|
-
elif model
|
|
67
|
+
elif qs.model is Volume:
|
|
73
68
|
group_names["series_name"] = F("series__name")
|
|
74
|
-
elif model
|
|
69
|
+
elif qs.model is Imprint:
|
|
75
70
|
group_names["publisher_name"] = F("publisher__name")
|
|
76
71
|
return qs.annotate(**group_names)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class BookmarkSearchMixin:
|
|
75
|
+
"""Create Bookmark Search kwargs."""
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def get_bookmark_search_kwargs(auth_filter, comic_filter=None):
|
|
79
|
+
"""Get the search kwargs for a user's authentication state."""
|
|
80
|
+
# search kwargs are relative to the bookmark object.
|
|
81
|
+
search_kwargs = {}
|
|
82
|
+
search_kwargs.update(auth_filter)
|
|
83
|
+
|
|
84
|
+
if comic_filter:
|
|
85
|
+
for key, value in comic_filter.items():
|
|
86
|
+
search_kwargs[f"comic__{key}"] = value
|
|
87
|
+
|
|
88
|
+
return search_kwargs
|
codex/views/opds/const.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""OPDS Common consts."""
|
|
2
2
|
|
|
3
|
-
from collections.abc import Mapping
|
|
4
3
|
from types import MappingProxyType
|
|
5
4
|
|
|
6
5
|
from codex.models import (
|
|
@@ -63,7 +62,7 @@ class MimeType:
|
|
|
63
62
|
HTML = "text/html"
|
|
64
63
|
AUTH_BASIC = "http://opds-spec.org/auth/basic"
|
|
65
64
|
COOKIE = "cookie"
|
|
66
|
-
FILE_TYPE_MAP:
|
|
65
|
+
FILE_TYPE_MAP: MappingProxyType[str, str] = MappingProxyType(
|
|
67
66
|
{
|
|
68
67
|
"CBZ": "application/vnd.comicbook+zip",
|
|
69
68
|
"CBR": "application/vnd.comicbook+rar",
|
|
@@ -71,7 +70,7 @@ class MimeType:
|
|
|
71
70
|
"PDF": "application/pdf",
|
|
72
71
|
}
|
|
73
72
|
)
|
|
74
|
-
SIMPLE_FILE_TYPE_MAP:
|
|
73
|
+
SIMPLE_FILE_TYPE_MAP: MappingProxyType[str, str] = MappingProxyType(
|
|
75
74
|
{
|
|
76
75
|
# PocketBooks needs app/zip
|
|
77
76
|
"CBZ": "application/zip",
|
codex/views/opds/urls.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from django.urls import reverse
|
|
4
4
|
from rest_framework.response import Response
|
|
5
5
|
|
|
6
|
-
from codex.
|
|
6
|
+
from codex.choices import DEFAULT_BROWSER_ROUTE
|
|
7
7
|
from codex.serializers.opds.urls import OPDSURLsSerializer
|
|
8
8
|
from codex.views.auth import AuthGenericAPIView
|
|
9
9
|
from codex.views.util import pop_name
|
|
@@ -19,7 +19,7 @@ class OPDSURLsView(AuthGenericAPIView):
|
|
|
19
19
|
def get(self, *args, **kwargs):
|
|
20
20
|
"""Resolve the urls."""
|
|
21
21
|
obj = {}
|
|
22
|
-
route =
|
|
22
|
+
route = DEFAULT_BROWSER_ROUTE
|
|
23
23
|
route = pop_name(route)
|
|
24
24
|
for version in _OPDS_VERSIONS:
|
|
25
25
|
key = f"v{version}"
|
codex/views/opds/util.py
CHANGED
|
@@ -5,11 +5,11 @@ from django.http.response import HttpResponseRedirect
|
|
|
5
5
|
from django.urls import reverse
|
|
6
6
|
from django.utils.http import urlencode
|
|
7
7
|
|
|
8
|
+
from codex.choices import DEFAULT_BROWSER_ROUTE
|
|
8
9
|
from codex.models import (
|
|
9
10
|
Contributor,
|
|
10
11
|
ContributorPerson,
|
|
11
12
|
)
|
|
12
|
-
from codex.serializers.choices import DEFAULTS
|
|
13
13
|
from codex.views.auth import GroupACLMixin
|
|
14
14
|
from codex.views.opds.const import OPDS_M2M_MODELS
|
|
15
15
|
|
|
@@ -71,8 +71,7 @@ def full_redirect_view(url_name):
|
|
|
71
71
|
|
|
72
72
|
def func(request):
|
|
73
73
|
"""Redirect to view, forwarding query strings and auth."""
|
|
74
|
-
kwargs =
|
|
75
|
-
kwargs.pop("name", None)
|
|
74
|
+
kwargs = dict(DEFAULT_BROWSER_ROUTE)
|
|
76
75
|
url = reverse(url_name, kwargs=kwargs)
|
|
77
76
|
|
|
78
77
|
# Forward the query string.
|
|
@@ -82,7 +82,9 @@ class OPDS1EntryLinksMixin:
|
|
|
82
82
|
else:
|
|
83
83
|
mime_type = MimeType.NAV
|
|
84
84
|
|
|
85
|
-
thr_count =
|
|
85
|
+
thr_count = (
|
|
86
|
+
0 if self.fake else 1 if self.obj.group == "c" else self.obj.child_count
|
|
87
|
+
)
|
|
86
88
|
rel = Rel.ALTERNATE if metadata else "subsection"
|
|
87
89
|
|
|
88
90
|
return OPDS1Link(rel, href, mime_type, thr_count=thr_count)
|