codex 1.6.9__py3-none-any.whl → 1.6.9a0__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/covers/create.py +1 -1
- codex/librarian/cron/crond.py +13 -10
- codex/librarian/importer/importerd.py +24 -19
- codex/librarian/importer/moved.py +176 -73
- codex/librarian/importer/query_fks.py +18 -12
- codex/librarian/janitor/janitor.py +12 -4
- codex/librarian/janitor/latest_version.py +54 -0
- codex/librarian/janitor/status.py +1 -0
- codex/librarian/janitor/tasks.py +14 -7
- codex/librarian/janitor/update.py +21 -2
- codex/librarian/janitor/vacuum.py +1 -2
- codex/librarian/telemeter/stats.py +20 -16
- codex/librarian/telemeter/telemeter.py +10 -7
- codex/librarian/telemeter/time.py +24 -11
- codex/librarian/watchdog/db_snapshot.py +1 -1
- codex/librarian/watchdog/dir_snapshot_diff.py +19 -17
- codex/librarian/watchdog/emitter.py +12 -14
- codex/librarian/watchdog/events.py +1 -1
- codex/librarian/watchdog/observers.py +4 -4
- codex/models/comic.py +1 -1
- codex/search/backend.py +10 -3
- codex/serializers/browser/mixins.py +1 -2
- codex/settings/settings.py +5 -1
- codex/signals/django_signals.py +0 -9
- codex/startup.py +1 -3
- codex/static_root/assets/VCheckbox-BkQ2enlR.c750c7d8cecc.js +1 -0
- codex/static_root/assets/VCheckbox-BkQ2enlR.c750c7d8cecc.js.br +0 -0
- codex/static_root/assets/VCheckbox-BkQ2enlR.c750c7d8cecc.js.gz +0 -0
- codex/static_root/assets/VCheckbox-BkQ2enlR.js +1 -0
- codex/static_root/assets/VCheckbox-BkQ2enlR.js.br +0 -0
- codex/static_root/assets/VCheckbox-BkQ2enlR.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-C4qxJC_o.ff4628662392.js +1 -0
- codex/static_root/assets/VCheckboxBtn-C4qxJC_o.ff4628662392.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-C4qxJC_o.ff4628662392.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-C4qxJC_o.js +1 -0
- codex/static_root/assets/VCheckboxBtn-C4qxJC_o.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-C4qxJC_o.js.gz +0 -0
- codex/static_root/assets/VCombobox-CJYErhVj.5ae39362d05a.js +1 -0
- codex/static_root/assets/VCombobox-CJYErhVj.5ae39362d05a.js.br +0 -0
- codex/static_root/assets/VCombobox-CJYErhVj.5ae39362d05a.js.gz +0 -0
- codex/static_root/assets/VCombobox-CJYErhVj.js +1 -0
- codex/static_root/assets/VCombobox-CJYErhVj.js.br +0 -0
- codex/static_root/assets/VCombobox-CJYErhVj.js.gz +0 -0
- codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css +1 -0
- codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css.br +0 -0
- codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css.gz +0 -0
- codex/static_root/assets/VCombobox-bwEYrXCg.css +1 -0
- codex/static_root/assets/VCombobox-bwEYrXCg.css.br +0 -0
- codex/static_root/assets/VCombobox-bwEYrXCg.css.gz +0 -0
- codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css +1 -0
- codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.br +0 -0
- codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.gz +0 -0
- codex/static_root/assets/VDialog-38np3TSA.css +1 -0
- codex/static_root/assets/VDialog-38np3TSA.css.br +0 -0
- codex/static_root/assets/VDialog-38np3TSA.css.gz +0 -0
- codex/static_root/assets/VDialog-U73OI2x0.a74b15bb2eb4.js +1 -0
- codex/static_root/assets/VDialog-U73OI2x0.a74b15bb2eb4.js.br +0 -0
- codex/static_root/assets/VDialog-U73OI2x0.a74b15bb2eb4.js.gz +0 -0
- codex/static_root/assets/VDialog-U73OI2x0.js +1 -0
- codex/static_root/assets/VDialog-U73OI2x0.js.br +0 -0
- codex/static_root/assets/VDialog-U73OI2x0.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css +1 -0
- codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css.br +0 -0
- codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-6G1AUajL.css +1 -0
- codex/static_root/assets/VExpansionPanels-6G1AUajL.css.br +0 -0
- codex/static_root/assets/VExpansionPanels-6G1AUajL.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CfbnFRnp.11d5ca74c514.js +1 -0
- codex/static_root/assets/VExpansionPanels-CfbnFRnp.11d5ca74c514.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CfbnFRnp.11d5ca74c514.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-CfbnFRnp.js +1 -0
- codex/static_root/assets/VExpansionPanels-CfbnFRnp.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CfbnFRnp.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-Dk6ucuGh.b364223f1e2b.js +1 -0
- codex/static_root/assets/VRadioGroup-Dk6ucuGh.b364223f1e2b.js.br +3 -0
- codex/static_root/assets/VRadioGroup-Dk6ucuGh.b364223f1e2b.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-Dk6ucuGh.js +1 -0
- codex/static_root/assets/VRadioGroup-Dk6ucuGh.js.br +3 -0
- codex/static_root/assets/VRadioGroup-Dk6ucuGh.js.gz +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css +1 -0
- codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.br +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.gz +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.css +1 -0
- codex/static_root/assets/VSelect-Ca2bIOso.css.br +0 -0
- codex/static_root/assets/VSelect-Ca2bIOso.css.gz +0 -0
- codex/static_root/assets/VSelect-gqsRzrIv.f022ea270144.js +1 -0
- codex/static_root/assets/VSelect-gqsRzrIv.f022ea270144.js.br +0 -0
- codex/static_root/assets/VSelect-gqsRzrIv.f022ea270144.js.gz +0 -0
- codex/static_root/assets/VSelect-gqsRzrIv.js +1 -0
- codex/static_root/assets/VSelect-gqsRzrIv.js.br +0 -0
- codex/static_root/assets/VSelect-gqsRzrIv.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BVJLEOcO.5d398b4b3b1c.js +1 -0
- codex/static_root/assets/VSelectionControl-BVJLEOcO.5d398b4b3b1c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BVJLEOcO.5d398b4b3b1c.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BVJLEOcO.js +1 -0
- codex/static_root/assets/VSelectionControl-BVJLEOcO.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BVJLEOcO.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-BFq--28I.9687c77461fc.css → VSelectionControl-CiViEYoC.c77bb19a4a06.css} +1 -1
- codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.br +0 -0
- codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.gz +0 -0
- codex/static_root/assets/{VSelectionControl-BFq--28I.css → VSelectionControl-CiViEYoC.css} +1 -1
- codex/static_root/assets/VSelectionControl-CiViEYoC.css.br +0 -0
- codex/static_root/assets/VSelectionControl-CiViEYoC.css.gz +0 -0
- codex/static_root/assets/{VSlideGroup-D4BlVRr8.10a79fd58813.js → VSlideGroup-P_U-rmf2.ca3efe698702.js} +1 -1
- codex/static_root/assets/VSlideGroup-P_U-rmf2.ca3efe698702.js.br +0 -0
- codex/static_root/assets/VSlideGroup-P_U-rmf2.ca3efe698702.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-D4BlVRr8.js → VSlideGroup-P_U-rmf2.js} +1 -1
- codex/static_root/assets/VSlideGroup-P_U-rmf2.js.br +0 -0
- codex/static_root/assets/VSlideGroup-P_U-rmf2.js.gz +0 -0
- codex/static_root/assets/{VTable-IQf8Xj4J.2e69b517b235.js → VTable-Coq-QwtQ.2b04104cbf05.js} +1 -1
- codex/static_root/assets/VTable-Coq-QwtQ.2b04104cbf05.js.br +0 -0
- codex/static_root/assets/VTable-Coq-QwtQ.2b04104cbf05.js.gz +0 -0
- codex/static_root/assets/{VTable-IQf8Xj4J.js → VTable-Coq-QwtQ.js} +1 -1
- codex/static_root/assets/VTable-Coq-QwtQ.js.br +0 -0
- codex/static_root/assets/VTable-Coq-QwtQ.js.gz +0 -0
- codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css +1 -0
- codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css.br +0 -0
- codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css.gz +0 -0
- codex/static_root/assets/VTable-Y5Sc3hJd.css +1 -0
- codex/static_root/assets/VTable-Y5Sc3hJd.css.br +0 -0
- codex/static_root/assets/VTable-Y5Sc3hJd.css.gz +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +1 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css +1 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
- codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
- codex/static_root/assets/VTextField-D8612eNs.bff65c141149.js +1 -0
- codex/static_root/assets/VTextField-D8612eNs.bff65c141149.js.br +0 -0
- codex/static_root/assets/VTextField-D8612eNs.bff65c141149.js.gz +0 -0
- codex/static_root/assets/VTextField-D8612eNs.js +1 -0
- codex/static_root/assets/VTextField-D8612eNs.js.br +0 -0
- codex/static_root/assets/VTextField-D8612eNs.js.gz +0 -0
- codex/static_root/assets/VWindowItem-BViiLFXH.70619152a4a5.js +1 -0
- codex/static_root/assets/VWindowItem-BViiLFXH.70619152a4a5.js.br +0 -0
- codex/static_root/assets/VWindowItem-BViiLFXH.70619152a4a5.js.gz +0 -0
- codex/static_root/assets/VWindowItem-BViiLFXH.js +1 -0
- codex/static_root/assets/VWindowItem-BViiLFXH.js.br +0 -0
- codex/static_root/assets/VWindowItem-BViiLFXH.js.gz +0 -0
- codex/static_root/assets/{admin-D4sw3N4T.b590f88a62cf.css → admin-CWEjaFr5.7d4523387384.css} +1 -1
- codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.br +0 -0
- codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.gz +0 -0
- codex/static_root/assets/{admin-D4sw3N4T.css → admin-CWEjaFr5.css} +1 -1
- codex/static_root/assets/admin-CWEjaFr5.css.br +0 -0
- codex/static_root/assets/admin-CWEjaFr5.css.gz +0 -0
- codex/static_root/assets/admin-RVemMSzT.fcbdca9f6b46.js +1 -0
- codex/static_root/assets/admin-RVemMSzT.fcbdca9f6b46.js.br +0 -0
- codex/static_root/assets/admin-RVemMSzT.fcbdca9f6b46.js.gz +0 -0
- codex/static_root/assets/admin-RVemMSzT.js +1 -0
- codex/static_root/assets/admin-RVemMSzT.js.br +0 -0
- codex/static_root/assets/admin-RVemMSzT.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css +1 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css +1 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.0acaf708f24a.js +30 -0
- codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.0acaf708f24a.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.0acaf708f24a.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.js +30 -0
- codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.js.gz +0 -0
- codex/static_root/assets/{browser-Cy0OgQJC.css → browser-Binc3n9H.313722d27ca3.css} +1 -1
- codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.br +0 -0
- codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.gz +0 -0
- codex/static_root/assets/{browser-Cy0OgQJC.fabbd1aaffcf.css → browser-Binc3n9H.css} +1 -1
- codex/static_root/assets/browser-Binc3n9H.css.br +0 -0
- codex/static_root/assets/browser-Binc3n9H.css.gz +0 -0
- codex/static_root/assets/browser-C9pnPT8U.bc239343d007.js +1 -0
- codex/static_root/assets/browser-C9pnPT8U.bc239343d007.js.br +0 -0
- codex/static_root/assets/browser-C9pnPT8U.bc239343d007.js.gz +0 -0
- codex/static_root/assets/browser-C9pnPT8U.js +1 -0
- codex/static_root/assets/browser-C9pnPT8U.js.br +0 -0
- codex/static_root/assets/browser-C9pnPT8U.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css +1 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.css +1 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Cebek1-c.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-DjxFqIRX.1a5ac7100192.js +1 -0
- codex/static_root/assets/change-password-dialog-DjxFqIRX.1a5ac7100192.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DjxFqIRX.1a5ac7100192.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DjxFqIRX.js +1 -0
- codex/static_root/assets/change-password-dialog-DjxFqIRX.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DjxFqIRX.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-B-dLb_50.62d3ea64b93a.js +1 -0
- codex/static_root/assets/confirm-dialog-B-dLb_50.62d3ea64b93a.js.br +0 -0
- codex/static_root/assets/confirm-dialog-B-dLb_50.62d3ea64b93a.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-B-dLb_50.js +1 -0
- codex/static_root/assets/confirm-dialog-B-dLb_50.js.br +0 -0
- codex/static_root/assets/confirm-dialog-B-dLb_50.js.gz +0 -0
- codex/static_root/assets/{datetime-column-D93BsvcX.5caf2255bdc3.js → datetime-column-BRZDPl2k.f6ae3ce94da7.js} +1 -1
- codex/static_root/assets/datetime-column-BRZDPl2k.f6ae3ce94da7.js.br +0 -0
- codex/static_root/assets/datetime-column-BRZDPl2k.f6ae3ce94da7.js.gz +0 -0
- codex/static_root/assets/{datetime-column-D93BsvcX.js → datetime-column-BRZDPl2k.js} +1 -1
- codex/static_root/assets/datetime-column-BRZDPl2k.js.br +0 -0
- codex/static_root/assets/datetime-column-BRZDPl2k.js.gz +0 -0
- codex/static_root/assets/{filter-ji46VTOR.f7b9ed6fec13.js → filter-Tk2N9xgO.02f23827434b.js} +1 -1
- codex/static_root/assets/filter-Tk2N9xgO.02f23827434b.js.br +0 -0
- codex/static_root/assets/filter-Tk2N9xgO.02f23827434b.js.gz +0 -0
- codex/static_root/assets/{filter-ji46VTOR.js → filter-Tk2N9xgO.js} +1 -1
- codex/static_root/assets/filter-Tk2N9xgO.js.br +0 -0
- codex/static_root/assets/filter-Tk2N9xgO.js.gz +0 -0
- codex/static_root/assets/flag-tab-C_v4CugS.928c1035d6ce.js +1 -0
- codex/static_root/assets/flag-tab-C_v4CugS.928c1035d6ce.js.br +0 -0
- codex/static_root/assets/flag-tab-C_v4CugS.928c1035d6ce.js.gz +0 -0
- codex/static_root/assets/flag-tab-C_v4CugS.js +1 -0
- codex/static_root/assets/flag-tab-C_v4CugS.js.br +0 -0
- codex/static_root/assets/flag-tab-C_v4CugS.js.gz +0 -0
- codex/static_root/assets/group-tab-Dn5N4Ebx.d2cf9c300427.js +1 -0
- codex/static_root/assets/group-tab-Dn5N4Ebx.d2cf9c300427.js.br +0 -0
- codex/static_root/assets/group-tab-Dn5N4Ebx.d2cf9c300427.js.gz +0 -0
- codex/static_root/assets/group-tab-Dn5N4Ebx.js +1 -0
- codex/static_root/assets/group-tab-Dn5N4Ebx.js.br +0 -0
- codex/static_root/assets/group-tab-Dn5N4Ebx.js.gz +0 -0
- codex/static_root/assets/http-error-DbRCRWKA.c31ec2f67f3b.js +1 -0
- codex/static_root/assets/http-error-DbRCRWKA.c31ec2f67f3b.js.br +0 -0
- codex/static_root/assets/http-error-DbRCRWKA.c31ec2f67f3b.js.gz +0 -0
- codex/static_root/assets/http-error-DbRCRWKA.js +1 -0
- codex/static_root/assets/http-error-DbRCRWKA.js.br +0 -0
- codex/static_root/assets/http-error-DbRCRWKA.js.gz +0 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css +1 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.br +2 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.gz +0 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.css +1 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.css.br +2 -0
- codex/static_root/assets/library-tab-Bh7ErAk7.css.gz +0 -0
- codex/static_root/assets/library-tab-CnvofvVA.419ab4e21896.js +1 -0
- codex/static_root/assets/library-tab-CnvofvVA.419ab4e21896.js.br +0 -0
- codex/static_root/assets/library-tab-CnvofvVA.419ab4e21896.js.gz +0 -0
- codex/static_root/assets/library-tab-CnvofvVA.js +1 -0
- codex/static_root/assets/library-tab-CnvofvVA.js.br +0 -0
- codex/static_root/assets/library-tab-CnvofvVA.js.gz +0 -0
- codex/static_root/assets/main-BHIo0ep5.6ec78d1380f0.js +6 -0
- codex/static_root/assets/main-BHIo0ep5.6ec78d1380f0.js.br +0 -0
- codex/static_root/assets/main-BHIo0ep5.6ec78d1380f0.js.gz +0 -0
- codex/static_root/assets/main-BHIo0ep5.js +6 -0
- codex/static_root/assets/main-BHIo0ep5.js.br +0 -0
- codex/static_root/assets/main-BHIo0ep5.js.gz +0 -0
- codex/static_root/assets/{main-Ztp855PW.css → main-BZ73p_WW.css} +1 -1
- codex/static_root/assets/main-BZ73p_WW.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.css.gz → main-BZ73p_WW.css.gz} +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-BZ73p_WW.d529e8b3f52a.css} +1 -1
- codex/static_root/assets/main-BZ73p_WW.d529e8b3f52a.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-BZ73p_WW.d529e8b3f52a.css.gz} +0 -0
- codex/static_root/assets/pager-full-pdf-DuOmJXkZ.5eaa11c55b1d.js +1 -0
- codex/static_root/assets/pager-full-pdf-DuOmJXkZ.5eaa11c55b1d.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-DuOmJXkZ.5eaa11c55b1d.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-DuOmJXkZ.js +1 -0
- codex/static_root/assets/pager-full-pdf-DuOmJXkZ.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-DuOmJXkZ.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css +1 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.css +1 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CzvikaTz.a025f20590fa.js +1 -0
- codex/static_root/assets/pagination-toolbar-CzvikaTz.a025f20590fa.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CzvikaTz.a025f20590fa.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CzvikaTz.js +1 -0
- codex/static_root/assets/pagination-toolbar-CzvikaTz.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CzvikaTz.js.gz +0 -0
- codex/static_root/assets/pdf-doc-B5yncCrs.97ba5cf21b63.js +305 -0
- codex/static_root/assets/pdf-doc-B5yncCrs.97ba5cf21b63.js.br +0 -0
- codex/static_root/assets/pdf-doc-B5yncCrs.97ba5cf21b63.js.gz +0 -0
- codex/static_root/assets/pdf-doc-B5yncCrs.js +305 -0
- codex/static_root/assets/pdf-doc-B5yncCrs.js.br +0 -0
- codex/static_root/assets/pdf-doc-B5yncCrs.js.gz +0 -0
- codex/static_root/assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css +1 -0
- codex/static_root/assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css.br +1 -0
- codex/static_root/assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css.gz +0 -0
- codex/static_root/assets/pdf-doc-CG5-AZ2M.css +1 -0
- codex/static_root/assets/pdf-doc-CG5-AZ2M.css.br +1 -0
- codex/static_root/assets/pdf-doc-CG5-AZ2M.css.gz +0 -0
- codex/static_root/assets/reader-BR3TSv3h.b00087748514.js +2 -0
- codex/static_root/assets/reader-BR3TSv3h.b00087748514.js.br +0 -0
- codex/static_root/assets/reader-BR3TSv3h.b00087748514.js.gz +0 -0
- codex/static_root/assets/reader-BR3TSv3h.js +2 -0
- codex/static_root/assets/reader-BR3TSv3h.js.br +0 -0
- codex/static_root/assets/reader-BR3TSv3h.js.gz +0 -0
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css +1 -0
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.br +0 -0
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.gz +0 -0
- codex/static_root/assets/reader-_stAWL-4.css +1 -0
- codex/static_root/assets/reader-_stAWL-4.css.br +0 -0
- codex/static_root/assets/reader-_stAWL-4.css.gz +0 -0
- codex/static_root/assets/{relation-chips-BGitp3yx.b3e7d178a615.css → relation-chips-ByfWwuQc.01269a188e01.css} +1 -1
- codex/static_root/assets/relation-chips-ByfWwuQc.01269a188e01.css.br +0 -0
- codex/static_root/assets/relation-chips-ByfWwuQc.01269a188e01.css.gz +0 -0
- codex/static_root/assets/{relation-chips-BGitp3yx.css → relation-chips-ByfWwuQc.css} +1 -1
- codex/static_root/assets/relation-chips-ByfWwuQc.css.br +0 -0
- codex/static_root/assets/relation-chips-ByfWwuQc.css.gz +0 -0
- codex/static_root/assets/relation-chips-C-62eg7F.d234f5d05881.js +1 -0
- codex/static_root/assets/relation-chips-C-62eg7F.d234f5d05881.js.br +0 -0
- codex/static_root/assets/relation-chips-C-62eg7F.d234f5d05881.js.gz +0 -0
- codex/static_root/assets/relation-chips-C-62eg7F.js +1 -0
- codex/static_root/assets/relation-chips-C-62eg7F.js.br +0 -0
- codex/static_root/assets/relation-chips-C-62eg7F.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CpNFdMBP.e5b1af37e0a0.js +2 -0
- codex/static_root/assets/settings-drawer-CpNFdMBP.e5b1af37e0a0.js.br +0 -0
- codex/static_root/assets/settings-drawer-CpNFdMBP.e5b1af37e0a0.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CpNFdMBP.js +2 -0
- codex/static_root/assets/settings-drawer-CpNFdMBP.js.br +0 -0
- codex/static_root/assets/settings-drawer-CpNFdMBP.js.gz +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css +1 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.br +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.gz +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.css +1 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.css.br +0 -0
- codex/static_root/assets/settings-drawer-DKncO_vX.css.gz +0 -0
- codex/static_root/assets/stats-tab-B0-L7btW.8f7914d5c8ac.js +1 -0
- codex/static_root/assets/stats-tab-B0-L7btW.8f7914d5c8ac.js.br +0 -0
- codex/static_root/assets/stats-tab-B0-L7btW.8f7914d5c8ac.js.gz +0 -0
- codex/static_root/assets/stats-tab-B0-L7btW.js +1 -0
- codex/static_root/assets/stats-tab-B0-L7btW.js.br +0 -0
- codex/static_root/assets/stats-tab-B0-L7btW.js.gz +0 -0
- codex/static_root/assets/{stats-tab-BVZzfwi1.28f07f038e88.css → stats-tab-BXGisn5N.9b7a71e7fe28.css} +1 -1
- codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.br +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.gz +0 -0
- codex/static_root/assets/{stats-tab-BVZzfwi1.css → stats-tab-BXGisn5N.css} +1 -1
- codex/static_root/assets/stats-tab-BXGisn5N.css.br +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.css.gz +0 -0
- codex/static_root/assets/task-tab-CJEMX4mU.31d0755cc8f7.js +1 -0
- codex/static_root/assets/task-tab-CJEMX4mU.31d0755cc8f7.js.br +0 -0
- codex/static_root/assets/task-tab-CJEMX4mU.31d0755cc8f7.js.gz +0 -0
- codex/static_root/assets/task-tab-CJEMX4mU.js +1 -0
- codex/static_root/assets/task-tab-CJEMX4mU.js.br +0 -0
- codex/static_root/assets/task-tab-CJEMX4mU.js.gz +0 -0
- codex/static_root/assets/unauthorized-U1yum5qV.4a44970b19c7.js +1 -0
- codex/static_root/assets/unauthorized-U1yum5qV.4a44970b19c7.js.br +0 -0
- codex/static_root/assets/unauthorized-U1yum5qV.4a44970b19c7.js.gz +0 -0
- codex/static_root/assets/unauthorized-U1yum5qV.js +1 -0
- codex/static_root/assets/unauthorized-U1yum5qV.js.br +0 -0
- codex/static_root/assets/unauthorized-U1yum5qV.js.gz +0 -0
- codex/static_root/assets/{unauthorized-DGIM1VFy.c93d0b3ea5a4.css → unauthorized-muDsv-rC.2166ec8b3a32.css} +1 -1
- codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.br +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.gz +0 -0
- codex/static_root/assets/{unauthorized-DGIM1VFy.css → unauthorized-muDsv-rC.css} +1 -1
- codex/static_root/assets/unauthorized-muDsv-rC.css.br +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.css.gz +0 -0
- codex/static_root/assets/user-tab-8DHYNhT5.3160ac8849a9.js +1 -0
- codex/static_root/assets/user-tab-8DHYNhT5.3160ac8849a9.js.br +0 -0
- codex/static_root/assets/user-tab-8DHYNhT5.3160ac8849a9.js.gz +0 -0
- codex/static_root/assets/user-tab-8DHYNhT5.js +1 -0
- codex/static_root/assets/user-tab-8DHYNhT5.js.br +0 -0
- codex/static_root/assets/user-tab-8DHYNhT5.js.gz +0 -0
- codex/static_root/js/choices-admin.1a20a5648f20.json +1 -0
- codex/static_root/js/choices-admin.1a20a5648f20.json.br +0 -0
- codex/static_root/js/choices-admin.1a20a5648f20.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/manifest.c82a23abe985.json +635 -0
- codex/static_root/manifest.c82a23abe985.json.br +0 -0
- codex/static_root/manifest.c82a23abe985.json.gz +0 -0
- codex/static_root/manifest.json +295 -270
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/templates/pwa/serviceworker.js +4 -4
- codex/urls/root.py +2 -6
- codex/version.py +0 -53
- codex/views/admin/tasks.py +2 -0
- codex/views/admin/user.py +4 -2
- codex/views/bookmark.py +16 -23
- codex/views/browser/filters/annotations.py +5 -4
- codex/views/browser/filters/search.py +1 -1
- codex/views/browser/order_by.py +1 -1
- codex/views/browser/validate.py +4 -7
- codex/views/const.py +1 -2
- codex/views/mixins.py +1 -1
- codex/views/reader/arcs.py +1 -1
- codex/views/reader/books.py +3 -3
- codex/views/reader/init.py +3 -2
- codex/views/version.py +10 -2
- {codex-1.6.9.dist-info → codex-1.6.9a0.dist-info}/METADATA +9 -8
- {codex-1.6.9.dist-info → codex-1.6.9a0.dist-info}/RECORD +383 -376
- codex/static_root/assets/VCheckbox-dIScsEcb.d54e90dd2ea2.js +0 -1
- codex/static_root/assets/VCheckbox-dIScsEcb.d54e90dd2ea2.js.br +0 -0
- codex/static_root/assets/VCheckbox-dIScsEcb.d54e90dd2ea2.js.gz +0 -0
- codex/static_root/assets/VCheckbox-dIScsEcb.js +0 -1
- codex/static_root/assets/VCheckbox-dIScsEcb.js.br +0 -0
- codex/static_root/assets/VCheckbox-dIScsEcb.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BpLxL6DP.a4cfab2286b6.js +0 -1
- codex/static_root/assets/VCheckboxBtn-BpLxL6DP.a4cfab2286b6.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BpLxL6DP.a4cfab2286b6.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BpLxL6DP.js +0 -1
- codex/static_root/assets/VCheckboxBtn-BpLxL6DP.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BpLxL6DP.js.gz +0 -0
- codex/static_root/assets/VCombobox-BiTvNSAL.d1884eaf76e6.js +0 -1
- codex/static_root/assets/VCombobox-BiTvNSAL.d1884eaf76e6.js.br +0 -0
- codex/static_root/assets/VCombobox-BiTvNSAL.d1884eaf76e6.js.gz +0 -0
- codex/static_root/assets/VCombobox-BiTvNSAL.js +0 -1
- codex/static_root/assets/VCombobox-BiTvNSAL.js.br +0 -0
- codex/static_root/assets/VCombobox-BiTvNSAL.js.gz +0 -0
- codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css +0 -1
- codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css.br +0 -0
- codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css.gz +0 -0
- codex/static_root/assets/VCombobox-BksEr4D_.css +0 -1
- codex/static_root/assets/VCombobox-BksEr4D_.css.br +0 -0
- codex/static_root/assets/VCombobox-BksEr4D_.css.gz +0 -0
- codex/static_root/assets/VDialog-DO_IeNl2.1e644515ee26.js +0 -1
- codex/static_root/assets/VDialog-DO_IeNl2.1e644515ee26.js.br +0 -0
- codex/static_root/assets/VDialog-DO_IeNl2.1e644515ee26.js.gz +0 -0
- codex/static_root/assets/VDialog-DO_IeNl2.js +0 -1
- codex/static_root/assets/VDialog-DO_IeNl2.js.br +0 -0
- codex/static_root/assets/VDialog-DO_IeNl2.js.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css +0 -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-lQ13FnRR.css +0 -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-DsEXVcCv.20b475255e66.css +0 -1
- codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css.br +0 -0
- codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DsEXVcCv.css +0 -1
- codex/static_root/assets/VExpansionPanels-DsEXVcCv.css.br +0 -0
- codex/static_root/assets/VExpansionPanels-DsEXVcCv.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-paVX7c3D.3be9407a80fd.js +0 -1
- codex/static_root/assets/VExpansionPanels-paVX7c3D.3be9407a80fd.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-paVX7c3D.3be9407a80fd.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-paVX7c3D.js +0 -1
- codex/static_root/assets/VExpansionPanels-paVX7c3D.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-paVX7c3D.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-CLZGmBdz.1e41ea70fba8.js +0 -1
- codex/static_root/assets/VRadioGroup-CLZGmBdz.1e41ea70fba8.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CLZGmBdz.1e41ea70fba8.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-CLZGmBdz.js +0 -1
- codex/static_root/assets/VRadioGroup-CLZGmBdz.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CLZGmBdz.js.gz +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +0 -1
- codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.css +0 -1
- codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
- codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
- codex/static_root/assets/VSelect-Cwd7Ke2J.fd98b8f830af.js +0 -1
- codex/static_root/assets/VSelect-Cwd7Ke2J.fd98b8f830af.js.br +0 -0
- codex/static_root/assets/VSelect-Cwd7Ke2J.fd98b8f830af.js.gz +0 -0
- codex/static_root/assets/VSelect-Cwd7Ke2J.js +0 -1
- codex/static_root/assets/VSelect-Cwd7Ke2J.js.br +0 -0
- codex/static_root/assets/VSelect-Cwd7Ke2J.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BFq--28I.9687c77461fc.css.br +0 -0
- codex/static_root/assets/VSelectionControl-BFq--28I.9687c77461fc.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-BFq--28I.css.br +0 -0
- codex/static_root/assets/VSelectionControl-BFq--28I.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-BTjn8uTI.3e9f6e646a35.js +0 -1
- codex/static_root/assets/VSelectionControl-BTjn8uTI.3e9f6e646a35.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BTjn8uTI.3e9f6e646a35.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BTjn8uTI.js +0 -1
- codex/static_root/assets/VSelectionControl-BTjn8uTI.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BTjn8uTI.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D4BlVRr8.10a79fd58813.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D4BlVRr8.10a79fd58813.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D4BlVRr8.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D4BlVRr8.js.gz +0 -0
- codex/static_root/assets/VTable-DflzroDq.160504d82195.css +0 -1
- codex/static_root/assets/VTable-DflzroDq.160504d82195.css.br +0 -0
- codex/static_root/assets/VTable-DflzroDq.160504d82195.css.gz +0 -0
- codex/static_root/assets/VTable-DflzroDq.css +0 -1
- codex/static_root/assets/VTable-DflzroDq.css.br +0 -0
- codex/static_root/assets/VTable-DflzroDq.css.gz +0 -0
- codex/static_root/assets/VTable-IQf8Xj4J.2e69b517b235.js.br +0 -0
- codex/static_root/assets/VTable-IQf8Xj4J.2e69b517b235.js.gz +0 -0
- codex/static_root/assets/VTable-IQf8Xj4J.js.br +0 -0
- codex/static_root/assets/VTable-IQf8Xj4J.js.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
- codex/static_root/assets/VTextField-DKXmWQyx.53d9e13b9847.js +0 -1
- codex/static_root/assets/VTextField-DKXmWQyx.53d9e13b9847.js.br +0 -0
- codex/static_root/assets/VTextField-DKXmWQyx.53d9e13b9847.js.gz +0 -0
- codex/static_root/assets/VTextField-DKXmWQyx.js +0 -1
- codex/static_root/assets/VTextField-DKXmWQyx.js.br +0 -0
- codex/static_root/assets/VTextField-DKXmWQyx.js.gz +0 -0
- codex/static_root/assets/VWindowItem-clwRzf0Z.01620e37f020.js +0 -1
- codex/static_root/assets/VWindowItem-clwRzf0Z.01620e37f020.js.br +0 -0
- codex/static_root/assets/VWindowItem-clwRzf0Z.01620e37f020.js.gz +0 -0
- codex/static_root/assets/VWindowItem-clwRzf0Z.js +0 -1
- codex/static_root/assets/VWindowItem-clwRzf0Z.js.br +0 -0
- codex/static_root/assets/VWindowItem-clwRzf0Z.js.gz +0 -0
- codex/static_root/assets/admin-BMM4khnn.eeb686da7f67.js +0 -1
- codex/static_root/assets/admin-BMM4khnn.eeb686da7f67.js.br +0 -0
- codex/static_root/assets/admin-BMM4khnn.eeb686da7f67.js.gz +0 -0
- codex/static_root/assets/admin-BMM4khnn.js +0 -1
- codex/static_root/assets/admin-BMM4khnn.js.br +0 -0
- codex/static_root/assets/admin-BMM4khnn.js.gz +0 -0
- codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.br +0 -0
- codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.gz +0 -0
- codex/static_root/assets/admin-D4sw3N4T.css.br +0 -0
- codex/static_root/assets/admin-D4sw3N4T.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css +0 -1
- codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css +0 -1
- codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-SQytza6T.d94cc92fef08.js +0 -56
- codex/static_root/assets/admin-drawer-panel-SQytza6T.d94cc92fef08.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-SQytza6T.d94cc92fef08.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-SQytza6T.js +0 -56
- codex/static_root/assets/admin-drawer-panel-SQytza6T.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-SQytza6T.js.gz +0 -0
- codex/static_root/assets/browser-Cy0OgQJC.css.br +0 -0
- codex/static_root/assets/browser-Cy0OgQJC.css.gz +0 -0
- codex/static_root/assets/browser-Cy0OgQJC.fabbd1aaffcf.css.br +0 -0
- codex/static_root/assets/browser-Cy0OgQJC.fabbd1aaffcf.css.gz +0 -0
- codex/static_root/assets/browser-D6WDXwID.011e4a20946c.js +0 -1
- codex/static_root/assets/browser-D6WDXwID.011e4a20946c.js.br +0 -0
- codex/static_root/assets/browser-D6WDXwID.011e4a20946c.js.gz +0 -0
- codex/static_root/assets/browser-D6WDXwID.js +0 -1
- codex/static_root/assets/browser-D6WDXwID.js.br +0 -0
- codex/static_root/assets/browser-D6WDXwID.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-CD1pdXK9.8b94f4f171c6.js +0 -1
- codex/static_root/assets/change-password-dialog-CD1pdXK9.8b94f4f171c6.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CD1pdXK9.8b94f4f171c6.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-CD1pdXK9.js +0 -1
- codex/static_root/assets/change-password-dialog-CD1pdXK9.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CD1pdXK9.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-_HotKBZZ.2e586037b3e1.js +0 -1
- codex/static_root/assets/confirm-dialog-_HotKBZZ.2e586037b3e1.js.br +0 -0
- codex/static_root/assets/confirm-dialog-_HotKBZZ.2e586037b3e1.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-_HotKBZZ.js +0 -1
- codex/static_root/assets/confirm-dialog-_HotKBZZ.js.br +0 -0
- codex/static_root/assets/confirm-dialog-_HotKBZZ.js.gz +0 -0
- codex/static_root/assets/datetime-column-D93BsvcX.5caf2255bdc3.js.br +0 -0
- codex/static_root/assets/datetime-column-D93BsvcX.5caf2255bdc3.js.gz +0 -0
- codex/static_root/assets/datetime-column-D93BsvcX.js.br +0 -0
- codex/static_root/assets/datetime-column-D93BsvcX.js.gz +0 -0
- codex/static_root/assets/filter-ji46VTOR.f7b9ed6fec13.js.br +0 -0
- codex/static_root/assets/filter-ji46VTOR.f7b9ed6fec13.js.gz +0 -0
- codex/static_root/assets/filter-ji46VTOR.js.br +0 -0
- codex/static_root/assets/filter-ji46VTOR.js.gz +0 -0
- codex/static_root/assets/flag-tab-CRfBTBsz.0d8f084eea6e.js +0 -1
- codex/static_root/assets/flag-tab-CRfBTBsz.0d8f084eea6e.js.br +0 -0
- codex/static_root/assets/flag-tab-CRfBTBsz.0d8f084eea6e.js.gz +0 -0
- codex/static_root/assets/flag-tab-CRfBTBsz.js +0 -1
- codex/static_root/assets/flag-tab-CRfBTBsz.js.br +0 -0
- codex/static_root/assets/flag-tab-CRfBTBsz.js.gz +0 -0
- codex/static_root/assets/group-tab-CMX07Y1c.98dd5e2d0bfc.js +0 -1
- codex/static_root/assets/group-tab-CMX07Y1c.98dd5e2d0bfc.js.br +0 -0
- codex/static_root/assets/group-tab-CMX07Y1c.98dd5e2d0bfc.js.gz +0 -0
- codex/static_root/assets/group-tab-CMX07Y1c.js +0 -1
- codex/static_root/assets/group-tab-CMX07Y1c.js.br +0 -0
- codex/static_root/assets/group-tab-CMX07Y1c.js.gz +0 -0
- codex/static_root/assets/http-error-CzJHczxG.6ee3173d40a5.js +0 -1
- codex/static_root/assets/http-error-CzJHczxG.6ee3173d40a5.js.br +0 -0
- codex/static_root/assets/http-error-CzJHczxG.6ee3173d40a5.js.gz +0 -0
- codex/static_root/assets/http-error-CzJHczxG.js +0 -1
- codex/static_root/assets/http-error-CzJHczxG.js.br +0 -0
- codex/static_root/assets/http-error-CzJHczxG.js.gz +0 -0
- codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css +0 -1
- codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.br +0 -0
- codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.gz +0 -0
- codex/static_root/assets/library-tab-BygwYPnO.css +0 -1
- codex/static_root/assets/library-tab-BygwYPnO.css.br +0 -0
- codex/static_root/assets/library-tab-BygwYPnO.css.gz +0 -0
- codex/static_root/assets/library-tab-DfZENbbA.1117ad225bde.js +0 -1
- codex/static_root/assets/library-tab-DfZENbbA.1117ad225bde.js.br +0 -0
- codex/static_root/assets/library-tab-DfZENbbA.1117ad225bde.js.gz +0 -0
- codex/static_root/assets/library-tab-DfZENbbA.js +0 -1
- codex/static_root/assets/library-tab-DfZENbbA.js.br +0 -0
- codex/static_root/assets/library-tab-DfZENbbA.js.gz +0 -0
- codex/static_root/assets/main-EAqCu0p6.e749a8bc2923.js +0 -6
- codex/static_root/assets/main-EAqCu0p6.e749a8bc2923.js.br +0 -0
- codex/static_root/assets/main-EAqCu0p6.e749a8bc2923.js.gz +0 -0
- codex/static_root/assets/main-EAqCu0p6.js +0 -6
- codex/static_root/assets/main-EAqCu0p6.js.br +0 -0
- codex/static_root/assets/main-EAqCu0p6.js.gz +0 -0
- codex/static_root/assets/main-Ztp855PW.css.br +0 -0
- codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-m4o9cYJ6.df24ef17e904.js +0 -1
- codex/static_root/assets/pagination-toolbar-m4o9cYJ6.df24ef17e904.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-m4o9cYJ6.df24ef17e904.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-m4o9cYJ6.js +0 -1
- codex/static_root/assets/pagination-toolbar-m4o9cYJ6.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-m4o9cYJ6.js.gz +0 -0
- codex/static_root/assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css +0 -1
- codex/static_root/assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css.br +0 -0
- codex/static_root/assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css.gz +0 -0
- codex/static_root/assets/pdf-doc-CMzaSV_K.css +0 -1
- codex/static_root/assets/pdf-doc-CMzaSV_K.css.br +0 -0
- codex/static_root/assets/pdf-doc-CMzaSV_K.css.gz +0 -0
- codex/static_root/assets/pdf-doc-RJuKdcoH.4be4a68338de.js +0 -305
- codex/static_root/assets/pdf-doc-RJuKdcoH.4be4a68338de.js.br +0 -0
- codex/static_root/assets/pdf-doc-RJuKdcoH.4be4a68338de.js.gz +0 -0
- codex/static_root/assets/pdf-doc-RJuKdcoH.js +0 -305
- codex/static_root/assets/pdf-doc-RJuKdcoH.js.br +0 -0
- codex/static_root/assets/pdf-doc-RJuKdcoH.js.gz +0 -0
- codex/static_root/assets/reader-BYz5uPEm.e2db8893aa10.js +0 -2
- codex/static_root/assets/reader-BYz5uPEm.e2db8893aa10.js.br +0 -0
- codex/static_root/assets/reader-BYz5uPEm.e2db8893aa10.js.gz +0 -0
- codex/static_root/assets/reader-BYz5uPEm.js +0 -2
- codex/static_root/assets/reader-BYz5uPEm.js.br +0 -0
- codex/static_root/assets/reader-BYz5uPEm.js.gz +0 -0
- codex/static_root/assets/reader-YbzUiD0T.css +0 -1
- codex/static_root/assets/reader-YbzUiD0T.css.br +0 -0
- codex/static_root/assets/reader-YbzUiD0T.css.gz +0 -0
- codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css +0 -1
- codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.br +0 -0
- codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.gz +0 -0
- codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.br +0 -0
- codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.gz +0 -0
- codex/static_root/assets/relation-chips-BGitp3yx.css.br +0 -0
- codex/static_root/assets/relation-chips-BGitp3yx.css.gz +0 -0
- codex/static_root/assets/relation-chips-BQX3oeiS.acfd60628c35.js +0 -1
- codex/static_root/assets/relation-chips-BQX3oeiS.acfd60628c35.js.br +0 -0
- codex/static_root/assets/relation-chips-BQX3oeiS.acfd60628c35.js.gz +0 -0
- codex/static_root/assets/relation-chips-BQX3oeiS.js +0 -1
- codex/static_root/assets/relation-chips-BQX3oeiS.js.br +0 -0
- codex/static_root/assets/relation-chips-BQX3oeiS.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C5t3OYPS.3fb97d35ae92.js +0 -2
- codex/static_root/assets/settings-drawer-C5t3OYPS.3fb97d35ae92.js.br +0 -0
- codex/static_root/assets/settings-drawer-C5t3OYPS.3fb97d35ae92.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C5t3OYPS.js +0 -2
- codex/static_root/assets/settings-drawer-C5t3OYPS.js.br +0 -0
- codex/static_root/assets/settings-drawer-C5t3OYPS.js.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
- codex/static_root/assets/stats-tab-BVZzfwi1.28f07f038e88.css.br +0 -0
- codex/static_root/assets/stats-tab-BVZzfwi1.28f07f038e88.css.gz +0 -0
- codex/static_root/assets/stats-tab-BVZzfwi1.css.br +0 -0
- codex/static_root/assets/stats-tab-BVZzfwi1.css.gz +0 -0
- codex/static_root/assets/stats-tab-BZrcMjtP.b8d127a34aa7.js +0 -1
- codex/static_root/assets/stats-tab-BZrcMjtP.b8d127a34aa7.js.br +0 -0
- codex/static_root/assets/stats-tab-BZrcMjtP.b8d127a34aa7.js.gz +0 -0
- codex/static_root/assets/stats-tab-BZrcMjtP.js +0 -1
- codex/static_root/assets/stats-tab-BZrcMjtP.js.br +0 -0
- codex/static_root/assets/stats-tab-BZrcMjtP.js.gz +0 -0
- codex/static_root/assets/task-tab-aLrGKXQT.f8498ed38d40.js +0 -1
- codex/static_root/assets/task-tab-aLrGKXQT.f8498ed38d40.js.br +0 -0
- codex/static_root/assets/task-tab-aLrGKXQT.f8498ed38d40.js.gz +0 -0
- codex/static_root/assets/task-tab-aLrGKXQT.js +0 -1
- codex/static_root/assets/task-tab-aLrGKXQT.js.br +0 -0
- codex/static_root/assets/task-tab-aLrGKXQT.js.gz +0 -0
- codex/static_root/assets/unauthorized-DGIM1VFy.c93d0b3ea5a4.css.br +0 -0
- codex/static_root/assets/unauthorized-DGIM1VFy.c93d0b3ea5a4.css.gz +0 -0
- codex/static_root/assets/unauthorized-DGIM1VFy.css.br +0 -0
- codex/static_root/assets/unauthorized-DGIM1VFy.css.gz +0 -0
- codex/static_root/assets/unauthorized-DpTUrdQV.0c1bfe81f457.js +0 -1
- codex/static_root/assets/unauthorized-DpTUrdQV.0c1bfe81f457.js.br +0 -0
- codex/static_root/assets/unauthorized-DpTUrdQV.0c1bfe81f457.js.gz +0 -0
- codex/static_root/assets/unauthorized-DpTUrdQV.js +0 -1
- codex/static_root/assets/unauthorized-DpTUrdQV.js.br +0 -0
- codex/static_root/assets/unauthorized-DpTUrdQV.js.gz +0 -0
- codex/static_root/assets/user-tab-B_CPCP10.d1a264b97a0f.js +0 -1
- codex/static_root/assets/user-tab-B_CPCP10.d1a264b97a0f.js.br +0 -0
- codex/static_root/assets/user-tab-B_CPCP10.d1a264b97a0f.js.gz +0 -0
- codex/static_root/assets/user-tab-B_CPCP10.js +0 -1
- codex/static_root/assets/user-tab-B_CPCP10.js.br +0 -0
- codex/static_root/assets/user-tab-B_CPCP10.js.gz +0 -0
- codex/static_root/js/choices-admin.1eca6d2c22cf.json +0 -1
- codex/static_root/js/choices-admin.1eca6d2c22cf.json.br +0 -0
- codex/static_root/js/choices-admin.1eca6d2c22cf.json.gz +0 -0
- codex/static_root/manifest.3d6dd67354d1.json +0 -610
- codex/static_root/manifest.3d6dd67354d1.json.br +0 -0
- codex/static_root/manifest.3d6dd67354d1.json.gz +0 -0
- {codex-1.6.9.dist-info → codex-1.6.9a0.dist-info}/LICENSE +0 -0
- {codex-1.6.9.dist-info → codex-1.6.9a0.dist-info}/WHEEL +0 -0
- {codex-1.6.9.dist-info → codex-1.6.9a0.dist-info}/entry_points.txt +0 -0
codex/librarian/covers/create.py
CHANGED
|
@@ -58,7 +58,7 @@ class CoverCreateMixin(CoverPathMixin):
|
|
|
58
58
|
return f.read()
|
|
59
59
|
|
|
60
60
|
@classmethod
|
|
61
|
-
def create_cover_from_path(cls, pk, cover_path, log, librarian_queue, custom=False):
|
|
61
|
+
def create_cover_from_path(cls, pk, cover_path, log, librarian_queue, custom=False):
|
|
62
62
|
"""Create cover for path.
|
|
63
63
|
|
|
64
64
|
Called from views/cover.
|
codex/librarian/cron/crond.py
CHANGED
|
@@ -30,21 +30,23 @@ class CronThread(NamedThread):
|
|
|
30
30
|
self._task_times = ()
|
|
31
31
|
super().__init__(*args, name=self.__class__.__name__, daemon=True, **kwargs)
|
|
32
32
|
|
|
33
|
-
def
|
|
33
|
+
def _create_task_times(self):
|
|
34
34
|
task_times = {}
|
|
35
35
|
for task_class, func in _TASK_TIME_FUNCTION_MAP.items():
|
|
36
36
|
if dttm := func(self.log):
|
|
37
37
|
task_times[dttm] = task_class
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# There's always at least the janitor job
|
|
42
|
-
self._next_time = task_tuple[0][0]
|
|
43
|
-
self._task_times = task_tuple
|
|
39
|
+
self._task_times = tuple(sorted(task_times.items()))
|
|
44
40
|
|
|
45
41
|
def _get_timeout(self):
|
|
42
|
+
if not self._task_times or not self._task_times[0]:
|
|
43
|
+
self.log.warning("No scheduled jobs found. Not normal! Waiting a minute.")
|
|
44
|
+
return 60
|
|
45
|
+
|
|
46
|
+
next_time = self._task_times[0][0]
|
|
46
47
|
now = django_timezone.now()
|
|
47
|
-
delta =
|
|
48
|
+
delta = next_time - now
|
|
49
|
+
self.log.debug(f"Next scheduled job at {next_time} in {delta}.")
|
|
48
50
|
return max(0, int(delta.total_seconds()))
|
|
49
51
|
|
|
50
52
|
def _run_expired_jobs(self):
|
|
@@ -53,6 +55,7 @@ class CronThread(NamedThread):
|
|
|
53
55
|
if dttm < now:
|
|
54
56
|
self.librarian_queue.put(task_class())
|
|
55
57
|
else:
|
|
58
|
+
# Times are always ordered so stop checking at the first future job.
|
|
56
59
|
break
|
|
57
60
|
|
|
58
61
|
def run(self):
|
|
@@ -62,13 +65,13 @@ class CronThread(NamedThread):
|
|
|
62
65
|
with self._cond:
|
|
63
66
|
while not self._stop_event.is_set():
|
|
64
67
|
self._run_expired_jobs()
|
|
65
|
-
self.
|
|
68
|
+
self._create_task_times()
|
|
69
|
+
sleep(2) # try to fix double jobs
|
|
66
70
|
timeout = self._get_timeout()
|
|
67
|
-
self.log.debug(f"Next scheduled job at {self._next_time}.")
|
|
68
71
|
self._cond.wait(timeout=timeout)
|
|
69
72
|
if self._stop_event.is_set():
|
|
70
73
|
break
|
|
71
|
-
sleep(2) #
|
|
74
|
+
sleep(2) # fix time rounding problems
|
|
72
75
|
except Exception:
|
|
73
76
|
self.log.exception(f"In {self.__class__.__name__}")
|
|
74
77
|
self.log.debug(f"Stopped {self.__class__.__name__}.")
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Bulk import and move comics and folders."""
|
|
2
2
|
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
3
5
|
from django.utils import timezone
|
|
4
6
|
|
|
5
7
|
from codex.librarian.importer.importer import ComicImporter
|
|
@@ -46,6 +48,15 @@ class ComicImporterThread(QueuedThread):
|
|
|
46
48
|
)
|
|
47
49
|
self._import(task)
|
|
48
50
|
|
|
51
|
+
def _update_groups(self, task):
|
|
52
|
+
pks = Library.objects.filter(covers_only=False).values_list("pk", flat=True)
|
|
53
|
+
start_time = task.start_time if task.start_time else timezone.now()
|
|
54
|
+
for pk in pks:
|
|
55
|
+
task = ImportDBDiffTask(library_id=pk)
|
|
56
|
+
importer = self._create_importer(task)
|
|
57
|
+
importer.update_all_groups({}, start_time)
|
|
58
|
+
self.librarian_queue.put(LIBRARY_CHANGED_TASK)
|
|
59
|
+
|
|
49
60
|
def _adopt_orphan_folders_for_library(self, library):
|
|
50
61
|
"""Adopt orphan folders for one library."""
|
|
51
62
|
orphan_folder_paths = (
|
|
@@ -53,17 +64,20 @@ class ComicImporterThread(QueuedThread):
|
|
|
53
64
|
.exclude(path=library.path)
|
|
54
65
|
.values_list("path", flat=True)
|
|
55
66
|
)
|
|
56
|
-
|
|
67
|
+
# Move in place
|
|
68
|
+
# Exclude deleted folders
|
|
69
|
+
folders_moved = {
|
|
70
|
+
path: path for path in orphan_folder_paths if Path(path).is_dir()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if folders_moved:
|
|
74
|
+
self.log.debug(
|
|
75
|
+
f"{len(folders_moved)} orphan folders found in {library.path}"
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
57
78
|
self.log.debug(f"No orphan folders in {library.path}")
|
|
58
79
|
return False
|
|
59
80
|
|
|
60
|
-
self.log.debug(
|
|
61
|
-
f"{len(orphan_folder_paths)} orphan folders found in {library.path}"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# Move in place
|
|
65
|
-
folders_moved = {path: path for path in orphan_folder_paths}
|
|
66
|
-
|
|
67
81
|
# An abridged import task.
|
|
68
82
|
task = ImportDBDiffTask(
|
|
69
83
|
library_id=library.pk,
|
|
@@ -89,15 +103,6 @@ class ComicImporterThread(QueuedThread):
|
|
|
89
103
|
|
|
90
104
|
self.status_controller.finish_many((moved_status, status))
|
|
91
105
|
|
|
92
|
-
def _update_groups(self, task):
|
|
93
|
-
pks = Library.objects.filter(covers_only=False).values_list("pk", flat=True)
|
|
94
|
-
start_time = task.start_time if task.start_time else timezone.now()
|
|
95
|
-
for pk in pks:
|
|
96
|
-
task = ImportDBDiffTask(library_id=pk)
|
|
97
|
-
importer = self._create_importer(task)
|
|
98
|
-
importer.update_all_groups({}, start_time)
|
|
99
|
-
self.librarian_queue.put(LIBRARY_CHANGED_TASK)
|
|
100
|
-
|
|
101
106
|
def process_item(self, item):
|
|
102
107
|
"""Run the updater."""
|
|
103
108
|
task = item
|
|
@@ -105,9 +110,9 @@ class ComicImporterThread(QueuedThread):
|
|
|
105
110
|
self._import(task)
|
|
106
111
|
elif isinstance(task, LazyImportComicsTask):
|
|
107
112
|
self._lazy_import_metadata(task)
|
|
108
|
-
elif isinstance(task, AdoptOrphanFoldersTask):
|
|
109
|
-
self._adopt_orphan_folders()
|
|
110
113
|
elif isinstance(task, UpdateGroupsTask):
|
|
111
114
|
self._update_groups(task)
|
|
115
|
+
elif isinstance(task, AdoptOrphanFoldersTask):
|
|
116
|
+
self._adopt_orphan_folders()
|
|
112
117
|
else:
|
|
113
118
|
self.log.warning(f"Bad task sent to library updater {task}")
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
+
from bidict import bidict
|
|
5
6
|
from django.db.models.functions import Now
|
|
6
7
|
|
|
7
8
|
from codex.librarian.importer.aggregate import AggregateMetadataImporter
|
|
@@ -23,20 +24,42 @@ from codex.status import Status
|
|
|
23
24
|
class MovedImporter(AggregateMetadataImporter):
|
|
24
25
|
"""Methods for moving comics and folders."""
|
|
25
26
|
|
|
26
|
-
def
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
if not
|
|
27
|
+
def _bulk_comics_moved_ensure_folders(self):
|
|
28
|
+
"""Ensure folders we're moving to exist."""
|
|
29
|
+
dest_comic_paths = self.task.files_moved.values()
|
|
30
|
+
if not dest_comic_paths:
|
|
30
31
|
return
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
self.
|
|
32
|
+
# Not sending statues to the controller for now.
|
|
33
|
+
status = Status(ImportStatusTypes.QUERY_MISSING_FKS)
|
|
34
|
+
create_folder_paths = self.query_missing_folder_paths(dest_comic_paths, status)
|
|
35
|
+
if not create_folder_paths:
|
|
36
|
+
return
|
|
37
|
+
status = Status(ImportStatusTypes.CREATE_FKS, 0, len(create_folder_paths))
|
|
38
|
+
self.log.debug(
|
|
39
|
+
"Creating {len(create_folder_paths)} folders for {len(dest_comic_paths)} moved comics."
|
|
40
|
+
)
|
|
41
|
+
self.bulk_folders_create(create_folder_paths, status)
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
def _prepare_moved_comic(self, comic, folder_m2m_links, updated_comics):
|
|
44
|
+
"""Prepare one comic for bulk update."""
|
|
45
|
+
try:
|
|
46
|
+
new_path = self.task.files_moved[comic.path]
|
|
47
|
+
comic.path = new_path
|
|
48
|
+
new_path = Path(new_path)
|
|
49
|
+
comic.parent_folder = Folder.objects.get( # type: ignore
|
|
50
|
+
path=new_path.parent
|
|
51
|
+
)
|
|
52
|
+
comic.updated_at = Now()
|
|
53
|
+
comic.presave()
|
|
54
|
+
folder_m2m_links[comic.pk] = Folder.objects.filter(
|
|
55
|
+
path__in=new_path.parents
|
|
56
|
+
).values_list("pk", flat=True)
|
|
57
|
+
updated_comics.append(comic)
|
|
58
|
+
except Exception:
|
|
59
|
+
self.log.exception(f"moving {comic.path}")
|
|
60
|
+
|
|
61
|
+
def _bulk_comics_move_prepare(self):
|
|
62
|
+
"""Prepare Update Comics."""
|
|
40
63
|
comics = Comic.objects.filter(
|
|
41
64
|
library=self.library, path__in=self.task.files_moved.keys()
|
|
42
65
|
).only("pk", "path", PARENT_FOLDER, FOLDERS_FIELD)
|
|
@@ -44,29 +67,29 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
44
67
|
folder_m2m_links = {}
|
|
45
68
|
updated_comics = []
|
|
46
69
|
for comic in comics.iterator():
|
|
47
|
-
|
|
48
|
-
new_path = self.task.files_moved[comic.path]
|
|
49
|
-
comic.path = new_path
|
|
50
|
-
new_path = Path(new_path)
|
|
51
|
-
comic.parent_folder = Folder.objects.get( # type: ignore
|
|
52
|
-
path=new_path.parent
|
|
53
|
-
)
|
|
54
|
-
comic.updated_at = Now()
|
|
55
|
-
comic.presave()
|
|
56
|
-
folder_m2m_links[comic.pk] = Folder.objects.filter(
|
|
57
|
-
path__in=new_path.parents
|
|
58
|
-
).values_list("pk", flat=True)
|
|
59
|
-
updated_comics.append(comic)
|
|
60
|
-
except Exception:
|
|
61
|
-
self.log.exception(f"moving {comic.path}")
|
|
70
|
+
self._prepare_moved_comic(comic, folder_m2m_links, updated_comics)
|
|
62
71
|
self.task.files_moved = {}
|
|
72
|
+
self.log.debug(f"Prepared {len(updated_comics)} for move...")
|
|
73
|
+
return updated_comics, folder_m2m_links
|
|
63
74
|
|
|
64
|
-
|
|
75
|
+
def _bulk_comics_moved(self):
|
|
76
|
+
"""Move comcis."""
|
|
77
|
+
num_files_moved = len(self.task.files_moved)
|
|
78
|
+
if not num_files_moved:
|
|
79
|
+
return
|
|
80
|
+
status = Status(ImportStatusTypes.FILES_MOVED, 0, num_files_moved)
|
|
81
|
+
self.status_controller.start(status)
|
|
65
82
|
|
|
66
|
-
#
|
|
83
|
+
# Prepare
|
|
84
|
+
self._bulk_comics_moved_ensure_folders()
|
|
85
|
+
updated_comics, folder_m2m_links = self._bulk_comics_move_prepare()
|
|
86
|
+
|
|
87
|
+
# Update comics
|
|
88
|
+
Comic.objects.bulk_update(updated_comics, MOVED_BULK_COMIC_UPDATE_FIELDS)
|
|
67
89
|
if folder_m2m_links:
|
|
68
90
|
self.bulk_fix_comic_m2m_field(FOLDERS_FIELD, folder_m2m_links, status)
|
|
69
|
-
|
|
91
|
+
|
|
92
|
+
count = len(updated_comics)
|
|
70
93
|
if count:
|
|
71
94
|
self.log.info(f"Moved {count} comics.")
|
|
72
95
|
|
|
@@ -140,60 +163,32 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
140
163
|
self.changed += count
|
|
141
164
|
self.status_controller.finish(status)
|
|
142
165
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
# Create only intermediate subfolders.
|
|
153
|
-
existing_folder_paths = Folder.objects.filter(
|
|
154
|
-
library=self.library, path__in=dest_parent_folder_paths
|
|
155
|
-
).values_list("path", flat=True)
|
|
156
|
-
create_folder_paths = frozenset(
|
|
157
|
-
dest_parent_folder_paths - frozenset(existing_folder_paths)
|
|
166
|
+
def _bulk_move_folders(
|
|
167
|
+
self, src_folder_paths, dest_parent_folders_map, dirs_moved: bidict, status
|
|
168
|
+
):
|
|
169
|
+
"""Bulk move folders."""
|
|
170
|
+
folders_to_move = (
|
|
171
|
+
Folder.objects.filter(library=self.library, path__in=src_folder_paths)
|
|
172
|
+
.only(*MOVED_BULK_FOLDER_UPDATE_FIELDS)
|
|
173
|
+
.order_by("path")
|
|
158
174
|
)
|
|
159
|
-
self.bulk_folders_create(create_folder_paths, status)
|
|
160
175
|
|
|
161
|
-
#
|
|
162
|
-
dest_parent_folders_objs = Folder.objects.filter(
|
|
163
|
-
path__in=dest_parent_folder_paths
|
|
164
|
-
).only("path", "pk")
|
|
165
|
-
dest_parent_folders = {}
|
|
166
|
-
for folder in dest_parent_folders_objs.iterator():
|
|
167
|
-
dest_parent_folders[folder.path] = folder
|
|
168
|
-
return dest_parent_folders
|
|
169
|
-
|
|
170
|
-
def bulk_folders_moved(self):
|
|
171
|
-
"""Move folders in the database instead of recreating them."""
|
|
172
|
-
num_dirs_moved = len(self.task.dirs_moved)
|
|
173
|
-
if not num_dirs_moved:
|
|
174
|
-
return
|
|
175
|
-
status = Status(ImportStatusTypes.DIRS_MOVED, None, num_dirs_moved)
|
|
176
|
-
self.status_controller.start(status)
|
|
177
|
-
|
|
178
|
-
dest_folder_paths = frozenset(self.task.dirs_moved.values())
|
|
179
|
-
dest_parent_folders = self._get_parent_folders(dest_folder_paths, status)
|
|
180
|
-
|
|
181
|
-
src_folder_paths = frozenset(self.task.dirs_moved.keys())
|
|
182
|
-
folders_to_move = Folder.objects.filter(
|
|
183
|
-
library=self.library, path__in=src_folder_paths
|
|
184
|
-
).order_by("path")
|
|
176
|
+
# TODO what if a folder with that path already exists in the db?
|
|
185
177
|
|
|
178
|
+
new_paths = set()
|
|
186
179
|
update_folders = []
|
|
187
180
|
for folder in folders_to_move.iterator():
|
|
188
|
-
new_path =
|
|
181
|
+
new_path = dirs_moved[folder.path]
|
|
182
|
+
new_paths.add(new_path)
|
|
189
183
|
folder.name = Path(new_path).name
|
|
190
184
|
folder.path = new_path
|
|
191
185
|
parent_path_str = str(Path(new_path).parent)
|
|
192
|
-
folder.parent_folder =
|
|
186
|
+
folder.parent_folder = dest_parent_folders_map.get(parent_path_str)
|
|
193
187
|
folder.presave()
|
|
194
188
|
folder.updated_at = Now()
|
|
195
189
|
update_folders.append(folder)
|
|
196
|
-
|
|
190
|
+
|
|
191
|
+
# delete_folders = Folder.objects.filter(library=self.library, path__in=new_paths)
|
|
197
192
|
|
|
198
193
|
update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
|
|
199
194
|
|
|
@@ -202,6 +197,112 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
202
197
|
if count:
|
|
203
198
|
self.log.info(f"Moved {count} folders.")
|
|
204
199
|
self.changed += count
|
|
200
|
+
status.add_complete(count)
|
|
201
|
+
self.status_controller.update(status, notify=False)
|
|
202
|
+
|
|
203
|
+
def _bulk_move_folders_under_existing_parents(
|
|
204
|
+
self, dest_parent_folder_paths_map, dirs_moved, status
|
|
205
|
+
):
|
|
206
|
+
"""Move folders under existing folders."""
|
|
207
|
+
while True:
|
|
208
|
+
# Get existing parent folders
|
|
209
|
+
# dest_parent_paths = tuple(str(path) for path in dest_parent_folder_paths_map)
|
|
210
|
+
dest_parent_paths = tuple(dest_parent_folder_paths_map.keys())
|
|
211
|
+
extant_parent_folders = Folder.objects.filter(
|
|
212
|
+
library=self.library, path__in=dest_parent_paths
|
|
213
|
+
).only("path")
|
|
214
|
+
|
|
215
|
+
# Create a list of folders than can be moved under existing folders.
|
|
216
|
+
dest_parent_folders_map = {}
|
|
217
|
+
src_folder_paths = []
|
|
218
|
+
for extant_parent_folder in extant_parent_folders:
|
|
219
|
+
dest_parent_folders_map[extant_parent_folder.path] = (
|
|
220
|
+
extant_parent_folder
|
|
221
|
+
)
|
|
222
|
+
if dest_folder_paths := dest_parent_folder_paths_map.pop(
|
|
223
|
+
extant_parent_folder.path
|
|
224
|
+
):
|
|
225
|
+
for dest_folder_path in dest_folder_paths:
|
|
226
|
+
src_path = dirs_moved.inverse[dest_folder_path]
|
|
227
|
+
src_folder_paths.append(src_path)
|
|
228
|
+
|
|
229
|
+
if not src_folder_paths:
|
|
230
|
+
return
|
|
231
|
+
src_folder_paths = sorted(src_folder_paths)
|
|
232
|
+
self.log.debug(f"Moving folders under existing parents: {src_folder_paths}")
|
|
233
|
+
|
|
234
|
+
self._bulk_move_folders(
|
|
235
|
+
src_folder_paths, dest_parent_folders_map, dirs_moved, status
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def _get_move_create_folders_one_layer(self, dest_parent_folder_paths_map):
|
|
239
|
+
"""Find the next layer of folder paths to create."""
|
|
240
|
+
create_folder_paths_one_layer = set()
|
|
241
|
+
library_parts_len = len(Path(self.library.path).parts)
|
|
242
|
+
for parent_path_str in sorted(dest_parent_folder_paths_map.keys()):
|
|
243
|
+
parts = Path(parent_path_str).parts
|
|
244
|
+
# Get one layer of folders from existing layers.
|
|
245
|
+
possible_create_folder_path = ""
|
|
246
|
+
for index in range(library_parts_len, len(parts) + 1):
|
|
247
|
+
layer_parts = parts[:index]
|
|
248
|
+
possible_create_folder_path = str(Path(*layer_parts))
|
|
249
|
+
if (
|
|
250
|
+
possible_create_folder_path in create_folder_paths_one_layer
|
|
251
|
+
or not Folder.objects.filter(
|
|
252
|
+
library=self.library, path=possible_create_folder_path
|
|
253
|
+
).exists()
|
|
254
|
+
):
|
|
255
|
+
break
|
|
256
|
+
else:
|
|
257
|
+
possible_create_folder_path = ""
|
|
258
|
+
if possible_create_folder_path:
|
|
259
|
+
create_folder_paths_one_layer.add(possible_create_folder_path)
|
|
260
|
+
|
|
261
|
+
return frozenset(create_folder_paths_one_layer)
|
|
262
|
+
|
|
263
|
+
def _bulk_move_folders_and_create_parents(self, status):
|
|
264
|
+
"""Find folders that can be moved without creating parents."""
|
|
265
|
+
dirs_moved = bidict(self.task.dirs_moved)
|
|
266
|
+
|
|
267
|
+
dest_parent_folder_paths_map = {}
|
|
268
|
+
for dest_path in sorted(set(dirs_moved.values())):
|
|
269
|
+
parent = str(Path(dest_path).parent)
|
|
270
|
+
if parent not in dest_parent_folder_paths_map:
|
|
271
|
+
dest_parent_folder_paths_map[parent] = set()
|
|
272
|
+
dest_parent_folder_paths_map[parent].add(dest_path)
|
|
273
|
+
|
|
274
|
+
create_status = Status(ImportStatusTypes.CREATE_FKS)
|
|
275
|
+
layer = 1
|
|
276
|
+
while True:
|
|
277
|
+
self._bulk_move_folders_under_existing_parents(
|
|
278
|
+
dest_parent_folder_paths_map, dirs_moved, status
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# All folders movable without creation have moved.
|
|
282
|
+
if not dest_parent_folder_paths_map:
|
|
283
|
+
break
|
|
284
|
+
self.log.debug(
|
|
285
|
+
f"Creating intermediate folder layer {layer} to move folders."
|
|
286
|
+
)
|
|
287
|
+
create_folder_paths_one_layer = self._get_move_create_folders_one_layer(
|
|
288
|
+
dest_parent_folder_paths_map
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Create one layer of folders
|
|
292
|
+
self.bulk_folders_create(create_folder_paths_one_layer, create_status)
|
|
293
|
+
layer += 1
|
|
294
|
+
|
|
295
|
+
def bulk_folders_moved(self):
|
|
296
|
+
"""Move folders in the database instead of recreating them."""
|
|
297
|
+
num_dirs_moved = len(self.task.dirs_moved)
|
|
298
|
+
if not num_dirs_moved:
|
|
299
|
+
return
|
|
300
|
+
status = Status(ImportStatusTypes.DIRS_MOVED, None, num_dirs_moved)
|
|
301
|
+
self.status_controller.start(status)
|
|
302
|
+
|
|
303
|
+
self._bulk_move_folders_and_create_parents(status)
|
|
304
|
+
self.task.dirs_moved = {}
|
|
305
|
+
|
|
205
306
|
self.status_controller.finish(status)
|
|
206
307
|
|
|
207
308
|
def _bulk_folders_modified(self):
|
|
@@ -233,6 +334,8 @@ class MovedImporter(AggregateMetadataImporter):
|
|
|
233
334
|
|
|
234
335
|
def move_and_modify_dirs(self):
|
|
235
336
|
"""Move files and dirs and modify dirs."""
|
|
337
|
+
# It would be nice to move folders instead of recreating them but it would require
|
|
338
|
+
# an inode map from the snapshots to do correctly.
|
|
236
339
|
self.bulk_folders_moved()
|
|
237
340
|
self._bulk_comics_moved()
|
|
238
341
|
self._bulk_covers_moved()
|
|
@@ -159,7 +159,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
|
|
|
159
159
|
|
|
160
160
|
filter_args[key] = group_name
|
|
161
161
|
|
|
162
|
-
def _get_create_group_set(
|
|
162
|
+
def _get_create_group_set(
|
|
163
163
|
self, groups, group_cls, create_group_set, update_group_set, status
|
|
164
164
|
):
|
|
165
165
|
"""Create the create group set."""
|
|
@@ -544,19 +544,22 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
|
|
|
544
544
|
base_cls = Comic
|
|
545
545
|
fk_data = self.metadata[FK_CREATE][FKC_CREATE_FKS], base_cls, fk_field, "name"
|
|
546
546
|
names = self.metadata[FKS].pop(fk_field)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
status,
|
|
552
|
-
)
|
|
547
|
+
count = self._query_missing_simple_models(
|
|
548
|
+
names,
|
|
549
|
+
fk_data,
|
|
550
|
+
status,
|
|
553
551
|
)
|
|
552
|
+
status.add_complete(count)
|
|
554
553
|
self.status_controller.update(status, notify=False)
|
|
555
554
|
|
|
556
555
|
###########
|
|
557
556
|
# FOLDERS #
|
|
558
557
|
###########
|
|
559
|
-
def query_missing_folder_paths(
|
|
558
|
+
def query_missing_folder_paths(
|
|
559
|
+
self,
|
|
560
|
+
comic_paths,
|
|
561
|
+
status,
|
|
562
|
+
):
|
|
560
563
|
"""Find missing folder paths."""
|
|
561
564
|
# Get the proposed folder_paths
|
|
562
565
|
library_path = Path(self.library.path)
|
|
@@ -575,15 +578,18 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
|
|
|
575
578
|
status,
|
|
576
579
|
)
|
|
577
580
|
create_folder_paths = create_folder_paths_dict.get(Folder, set())
|
|
581
|
+
return frozenset(create_folder_paths)
|
|
582
|
+
|
|
583
|
+
def _add_missing_folder_paths(self, comic_paths, status):
|
|
584
|
+
create_folder_paths = self.query_missing_folder_paths(comic_paths, status)
|
|
585
|
+
"""Add missing folder_paths to create fks."""
|
|
578
586
|
if FK_CREATE not in self.metadata:
|
|
579
587
|
self.metadata[FK_CREATE] = {}
|
|
580
588
|
if FKC_FOLDER_PATHS not in self.metadata[FK_CREATE]:
|
|
581
589
|
self.metadata[FK_CREATE][FKC_FOLDER_PATHS] = set()
|
|
582
590
|
self.metadata[FK_CREATE][FKC_FOLDER_PATHS] |= create_folder_paths
|
|
583
|
-
count
|
|
584
|
-
if count:
|
|
591
|
+
if count := len(create_folder_paths):
|
|
585
592
|
self.log.info(f"Prepared {count} new Folders.")
|
|
586
|
-
return count
|
|
587
593
|
|
|
588
594
|
def _get_create_fks_totals(self):
|
|
589
595
|
fkc = self.metadata[FK_CREATE]
|
|
@@ -634,7 +640,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
|
|
|
634
640
|
status,
|
|
635
641
|
)
|
|
636
642
|
|
|
637
|
-
self.
|
|
643
|
+
self._add_missing_folder_paths(
|
|
638
644
|
self.metadata[FKS].pop(COMIC_PATHS, ()), status
|
|
639
645
|
)
|
|
640
646
|
|
|
@@ -8,6 +8,7 @@ from codex.librarian.importer.tasks import (
|
|
|
8
8
|
)
|
|
9
9
|
from codex.librarian.janitor.cleanup import TOTAL_NUM_FK_CLASSES, CleanupMixin
|
|
10
10
|
from codex.librarian.janitor.failed_imports import UpdateFailedImportsMixin
|
|
11
|
+
from codex.librarian.janitor.latest_version import LatestVersionMixin
|
|
11
12
|
from codex.librarian.janitor.status import JanitorStatusTypes
|
|
12
13
|
from codex.librarian.janitor.tasks import (
|
|
13
14
|
ForceUpdateAllFailedImportsTask,
|
|
@@ -16,6 +17,7 @@ from codex.librarian.janitor.tasks import (
|
|
|
16
17
|
JanitorCleanFKsTask,
|
|
17
18
|
JanitorCleanupSessionsTask,
|
|
18
19
|
JanitorClearStatusTask,
|
|
20
|
+
JanitorLatestVersionTask,
|
|
19
21
|
JanitorNightlyTask,
|
|
20
22
|
JanitorRestartTask,
|
|
21
23
|
JanitorShutdownTask,
|
|
@@ -47,10 +49,13 @@ _JANITOR_STATII = (
|
|
|
47
49
|
Status(SearchIndexStatusTypes.SEARCH_INDEX_UPDATE),
|
|
48
50
|
Status(SearchIndexStatusTypes.SEARCH_INDEX_REMOVE),
|
|
49
51
|
Status(SearchIndexStatusTypes.SEARCH_INDEX_MERGE),
|
|
52
|
+
Status(JanitorStatusTypes.CODEX_LATEST_VERSION),
|
|
50
53
|
)
|
|
51
54
|
|
|
52
55
|
|
|
53
|
-
class Janitor(
|
|
56
|
+
class Janitor(
|
|
57
|
+
CleanupMixin, LatestVersionMixin, UpdateMixin, UpdateFailedImportsMixin, VacuumMixin
|
|
58
|
+
):
|
|
54
59
|
"""Janitor inline task runner."""
|
|
55
60
|
|
|
56
61
|
def __init__(self, log_queue, librarian_queue):
|
|
@@ -65,14 +70,15 @@ class Janitor(CleanupMixin, UpdateMixin, VacuumMixin, UpdateFailedImportsMixin):
|
|
|
65
70
|
).on
|
|
66
71
|
self.status_controller.start_many(_JANITOR_STATII)
|
|
67
72
|
tasks = (
|
|
73
|
+
JanitorLatestVersionTask(),
|
|
74
|
+
JanitorUpdateTask(force=False),
|
|
68
75
|
SearchIndexAbortTask(),
|
|
69
76
|
JanitorCleanFKsTask(),
|
|
70
77
|
JanitorCleanCoversTask(),
|
|
71
78
|
JanitorCleanupSessionsTask(),
|
|
79
|
+
AdoptOrphanFoldersTask(),
|
|
72
80
|
JanitorVacuumTask(),
|
|
73
81
|
JanitorBackupTask(),
|
|
74
|
-
JanitorUpdateTask(force=False),
|
|
75
|
-
AdoptOrphanFoldersTask(),
|
|
76
82
|
CoverRemoveOrphansTask(),
|
|
77
83
|
SearchIndexUpdateTask(False),
|
|
78
84
|
SearchIndexMergeTask(optimize),
|
|
@@ -91,8 +97,10 @@ class Janitor(CleanupMixin, UpdateMixin, VacuumMixin, UpdateFailedImportsMixin):
|
|
|
91
97
|
self.vacuum_db()
|
|
92
98
|
case JanitorBackupTask():
|
|
93
99
|
self.backup_db()
|
|
100
|
+
case JanitorLatestVersionTask():
|
|
101
|
+
self.update_latest_version(task.force)
|
|
94
102
|
case JanitorUpdateTask():
|
|
95
|
-
self.update_codex()
|
|
103
|
+
self.update_codex(task.force)
|
|
96
104
|
case JanitorRestartTask():
|
|
97
105
|
self.restart_codex()
|
|
98
106
|
case JanitorShutdownTask():
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Fetch the current codex version."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from django.utils import timezone
|
|
8
|
+
|
|
9
|
+
from codex.librarian.janitor.status import JanitorStatusTypes
|
|
10
|
+
from codex.models import Timestamp
|
|
11
|
+
from codex.status import Status
|
|
12
|
+
from codex.version import PACKAGE_NAME
|
|
13
|
+
from codex.worker_base import WorkerBaseMixin
|
|
14
|
+
|
|
15
|
+
_PYPI_URL_TEMPLATE = "https://pypi.python.org/pypi/%s/json"
|
|
16
|
+
_REPO_URL = _PYPI_URL_TEMPLATE % PACKAGE_NAME
|
|
17
|
+
_CACHE_EXPIRY = timedelta(days=1) - timedelta(minutes=1)
|
|
18
|
+
_REPO_TIMEOUT = 5
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class LatestVersionMixin(WorkerBaseMixin):
|
|
22
|
+
"""Methods for fetching the latest version."""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def _fetch_latest_version():
|
|
26
|
+
"""Fetch Latest Remotely."""
|
|
27
|
+
response = requests.get(_REPO_URL, timeout=_REPO_TIMEOUT)
|
|
28
|
+
return json.loads(response.text)["info"]["version"]
|
|
29
|
+
|
|
30
|
+
def update_latest_version(self, force=False):
|
|
31
|
+
"""Get the latest version from a remote repo using a cache."""
|
|
32
|
+
status = Status(JanitorStatusTypes.CODEX_LATEST_VERSION)
|
|
33
|
+
try:
|
|
34
|
+
self.status_controller.start(status)
|
|
35
|
+
ts = Timestamp.objects.get(
|
|
36
|
+
key=Timestamp.TimestampChoices.CODEX_VERSION.value
|
|
37
|
+
)
|
|
38
|
+
do_fetch = (
|
|
39
|
+
force
|
|
40
|
+
or not ts.version
|
|
41
|
+
or (timezone.now() - ts.updated_at > _CACHE_EXPIRY)
|
|
42
|
+
)
|
|
43
|
+
if do_fetch:
|
|
44
|
+
latest_version = self._fetch_latest_version()
|
|
45
|
+
if not latest_version:
|
|
46
|
+
reason = "Bad latest version fetched."
|
|
47
|
+
raise ValueError(reason)
|
|
48
|
+
ts.version = latest_version
|
|
49
|
+
ts.save()
|
|
50
|
+
self.log.info(f"Saved new latest codex version {latest_version}.")
|
|
51
|
+
else:
|
|
52
|
+
self.log.debug("Not fetching new latest version, not expired.")
|
|
53
|
+
finally:
|
|
54
|
+
self.status_controller.finish(status)
|
codex/librarian/janitor/tasks.py
CHANGED
|
@@ -8,6 +8,20 @@ class JanitorTask:
|
|
|
8
8
|
"""Tasks for the janitor."""
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
@dataclass
|
|
12
|
+
class JanitorLatestVersionTask(JanitorTask):
|
|
13
|
+
"""Get the latest version."""
|
|
14
|
+
|
|
15
|
+
force: bool = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class JanitorUpdateTask(JanitorTask):
|
|
20
|
+
"""Task for updater."""
|
|
21
|
+
|
|
22
|
+
force: bool = False
|
|
23
|
+
|
|
24
|
+
|
|
11
25
|
@dataclass
|
|
12
26
|
class JanitorBackupTask(JanitorTask):
|
|
13
27
|
"""Backup the database."""
|
|
@@ -23,13 +37,6 @@ class JanitorShutdownTask(JanitorTask):
|
|
|
23
37
|
"""for shutdown."""
|
|
24
38
|
|
|
25
39
|
|
|
26
|
-
@dataclass
|
|
27
|
-
class JanitorUpdateTask(JanitorTask):
|
|
28
|
-
"""Task for updater."""
|
|
29
|
-
|
|
30
|
-
force: bool
|
|
31
|
-
|
|
32
|
-
|
|
33
40
|
@dataclass
|
|
34
41
|
class JanitorVacuumTask(JanitorTask):
|
|
35
42
|
"""Vacuum the database."""
|