codex 1.6.8__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/__init__.py +1 -0
- codex/librarian/cron/crond.py +87 -0
- 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/time.py +12 -0
- codex/librarian/janitor/update.py +21 -2
- codex/librarian/janitor/vacuum.py +1 -2
- codex/librarian/librariand.py +9 -3
- codex/librarian/tasks.py +5 -1
- codex/librarian/telemeter/__init__.py +1 -0
- codex/librarian/{stats.py → telemeter/stats.py} +21 -16
- codex/librarian/telemeter/tasks.py +8 -0
- codex/librarian/telemeter/telemeter.py +80 -0
- codex/librarian/telemeter/time.py +61 -0
- 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/migrations/0028_telemeter.py +48 -0
- codex/models/admin.py +2 -0
- codex/models/comic.py +1 -1
- codex/search/backend.py +10 -3
- codex/serializers/browser/mixins.py +1 -2
- codex/settings/settings.py +6 -2
- 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-CiViEYoC.c77bb19a4a06.css +1 -0
- 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-CiViEYoC.css +1 -0
- 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-KhqCfdKQ.984b37842a8b.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-KhqCfdKQ.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-CJJzxZcw.edb1bb582e5c.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-CJJzxZcw.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-DqSk_iUb.260bcbb4c8e2.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-DqSk_iUb.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-CvqVrkAJ.8996d1b1db2a.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-CvqVrkAJ.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-CI0u-X7Y.2d1b6a0d4139.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-CI0u-X7Y.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/flag.py +10 -1
- codex/views/admin/stats.py +1 -1
- 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/reader/page.py +3 -4
- codex/views/version.py +10 -2
- {codex-1.6.8.dist-info → codex-1.6.9a0.dist-info}/METADATA +47 -51
- {codex-1.6.8.dist-info → codex-1.6.9a0.dist-info}/RECORD +394 -380
- codex/librarian/janitor/janitord.py +0 -76
- codex/static_root/assets/VCheckbox-DE3METqJ.947b68d61150.js +0 -1
- codex/static_root/assets/VCheckbox-DE3METqJ.947b68d61150.js.br +0 -0
- codex/static_root/assets/VCheckbox-DE3METqJ.947b68d61150.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DE3METqJ.js +0 -1
- codex/static_root/assets/VCheckbox-DE3METqJ.js.br +0 -0
- codex/static_root/assets/VCheckbox-DE3METqJ.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DetOzpfX.e4293f13e16f.js +0 -1
- codex/static_root/assets/VCheckboxBtn-DetOzpfX.e4293f13e16f.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DetOzpfX.e4293f13e16f.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DetOzpfX.js +0 -1
- codex/static_root/assets/VCheckboxBtn-DetOzpfX.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DetOzpfX.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/VCombobox-D2XmpiHi.3427b660b686.js +0 -1
- codex/static_root/assets/VCombobox-D2XmpiHi.3427b660b686.js.br +0 -0
- codex/static_root/assets/VCombobox-D2XmpiHi.3427b660b686.js.gz +0 -0
- codex/static_root/assets/VCombobox-D2XmpiHi.js +0 -1
- codex/static_root/assets/VCombobox-D2XmpiHi.js.br +0 -0
- codex/static_root/assets/VCombobox-D2XmpiHi.js.gz +0 -0
- codex/static_root/assets/VDialog-BXw6XFu7.38cde34df95a.js +0 -1
- codex/static_root/assets/VDialog-BXw6XFu7.38cde34df95a.js.br +0 -0
- codex/static_root/assets/VDialog-BXw6XFu7.38cde34df95a.js.gz +0 -0
- codex/static_root/assets/VDialog-BXw6XFu7.js +0 -1
- codex/static_root/assets/VDialog-BXw6XFu7.js.br +0 -0
- codex/static_root/assets/VDialog-BXw6XFu7.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-cdsBiWJW.289873cadd1b.js +0 -1
- codex/static_root/assets/VExpansionPanels-cdsBiWJW.289873cadd1b.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-cdsBiWJW.289873cadd1b.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-cdsBiWJW.js +0 -1
- codex/static_root/assets/VExpansionPanels-cdsBiWJW.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-cdsBiWJW.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DPsPhrgJ.2232643fa4b2.js +0 -1
- codex/static_root/assets/VRadioGroup-DPsPhrgJ.2232643fa4b2.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DPsPhrgJ.2232643fa4b2.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DPsPhrgJ.js +0 -1
- codex/static_root/assets/VRadioGroup-DPsPhrgJ.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DPsPhrgJ.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-wpuzLrPv.f46f25efca5b.js +0 -1
- codex/static_root/assets/VSelect-wpuzLrPv.f46f25efca5b.js.br +0 -0
- codex/static_root/assets/VSelect-wpuzLrPv.f46f25efca5b.js.gz +0 -0
- codex/static_root/assets/VSelect-wpuzLrPv.js +0 -1
- codex/static_root/assets/VSelect-wpuzLrPv.js.br +0 -0
- codex/static_root/assets/VSelect-wpuzLrPv.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-AnuOsZjr.0ca164ae628f.css +0 -1
- codex/static_root/assets/VSelectionControl-AnuOsZjr.0ca164ae628f.css.br +0 -0
- codex/static_root/assets/VSelectionControl-AnuOsZjr.0ca164ae628f.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-AnuOsZjr.css +0 -1
- codex/static_root/assets/VSelectionControl-AnuOsZjr.css.br +0 -0
- codex/static_root/assets/VSelectionControl-AnuOsZjr.css.gz +0 -0
- codex/static_root/assets/VSelectionControl-DTgtf_T6.bbb99484db2e.js +0 -1
- codex/static_root/assets/VSelectionControl-DTgtf_T6.bbb99484db2e.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DTgtf_T6.bbb99484db2e.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DTgtf_T6.js +0 -1
- codex/static_root/assets/VSelectionControl-DTgtf_T6.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DTgtf_T6.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-KhqCfdKQ.984b37842a8b.js.br +0 -0
- codex/static_root/assets/VSlideGroup-KhqCfdKQ.984b37842a8b.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-KhqCfdKQ.js.br +0 -0
- codex/static_root/assets/VSlideGroup-KhqCfdKQ.js.gz +0 -0
- codex/static_root/assets/VTable-CJJzxZcw.edb1bb582e5c.js.br +0 -0
- codex/static_root/assets/VTable-CJJzxZcw.edb1bb582e5c.js.gz +0 -0
- codex/static_root/assets/VTable-CJJzxZcw.js.br +0 -0
- codex/static_root/assets/VTable-CJJzxZcw.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/VTextField-BJxb_QuX.79ef5fb485f2.js +0 -1
- codex/static_root/assets/VTextField-BJxb_QuX.79ef5fb485f2.js.br +0 -0
- codex/static_root/assets/VTextField-BJxb_QuX.79ef5fb485f2.js.gz +0 -0
- codex/static_root/assets/VTextField-BJxb_QuX.js +0 -1
- codex/static_root/assets/VTextField-BJxb_QuX.js.br +0 -0
- codex/static_root/assets/VTextField-BJxb_QuX.js.gz +0 -0
- codex/static_root/assets/VTextField-Bc1tD4nS.2026682c8437.css +0 -1
- codex/static_root/assets/VTextField-Bc1tD4nS.2026682c8437.css.br +0 -0
- codex/static_root/assets/VTextField-Bc1tD4nS.2026682c8437.css.gz +0 -0
- codex/static_root/assets/VTextField-Bc1tD4nS.css +0 -1
- codex/static_root/assets/VTextField-Bc1tD4nS.css.br +0 -0
- codex/static_root/assets/VTextField-Bc1tD4nS.css.gz +0 -0
- codex/static_root/assets/VWindowItem-BWJJgE5I.8daf8cde5589.js +0 -1
- codex/static_root/assets/VWindowItem-BWJJgE5I.8daf8cde5589.js.br +0 -0
- codex/static_root/assets/VWindowItem-BWJJgE5I.8daf8cde5589.js.gz +0 -0
- codex/static_root/assets/VWindowItem-BWJJgE5I.js +0 -1
- codex/static_root/assets/VWindowItem-BWJJgE5I.js.br +0 -0
- codex/static_root/assets/VWindowItem-BWJJgE5I.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-DrXckZDB.5feb8fde2d3a.js +0 -1
- codex/static_root/assets/admin-DrXckZDB.5feb8fde2d3a.js.br +0 -0
- codex/static_root/assets/admin-DrXckZDB.5feb8fde2d3a.js.gz +0 -0
- codex/static_root/assets/admin-DrXckZDB.js +0 -1
- codex/static_root/assets/admin-DrXckZDB.js.br +0 -0
- codex/static_root/assets/admin-DrXckZDB.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Crj9u2NV.6b03e2a2ae14.js +0 -56
- codex/static_root/assets/admin-drawer-panel-Crj9u2NV.6b03e2a2ae14.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Crj9u2NV.6b03e2a2ae14.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Crj9u2NV.js +0 -56
- codex/static_root/assets/admin-drawer-panel-Crj9u2NV.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Crj9u2NV.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Dm28c2tG.css +0 -1
- codex/static_root/assets/admin-drawer-panel-Dm28c2tG.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Dm28c2tG.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-Dm28c2tG.fb4e885ba7c4.css +0 -1
- codex/static_root/assets/admin-drawer-panel-Dm28c2tG.fb4e885ba7c4.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-Dm28c2tG.fb4e885ba7c4.css.gz +0 -0
- codex/static_root/assets/browser-C50xLvMD.ebeac0d428d2.js +0 -1
- codex/static_root/assets/browser-C50xLvMD.ebeac0d428d2.js.br +0 -0
- codex/static_root/assets/browser-C50xLvMD.ebeac0d428d2.js.gz +0 -0
- codex/static_root/assets/browser-C50xLvMD.js +0 -1
- codex/static_root/assets/browser-C50xLvMD.js.br +0 -0
- codex/static_root/assets/browser-C50xLvMD.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/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-Cm6AnxGf.66f4e604144b.js +0 -1
- codex/static_root/assets/change-password-dialog-Cm6AnxGf.66f4e604144b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Cm6AnxGf.66f4e604144b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Cm6AnxGf.js +0 -1
- codex/static_root/assets/change-password-dialog-Cm6AnxGf.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Cm6AnxGf.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-Mznixjrb.927eaa9bea39.js +0 -1
- codex/static_root/assets/confirm-dialog-Mznixjrb.927eaa9bea39.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Mznixjrb.927eaa9bea39.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-Mznixjrb.js +0 -1
- codex/static_root/assets/confirm-dialog-Mznixjrb.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Mznixjrb.js.gz +0 -0
- codex/static_root/assets/datetime-column-DqSk_iUb.260bcbb4c8e2.js.br +0 -0
- codex/static_root/assets/datetime-column-DqSk_iUb.260bcbb4c8e2.js.gz +0 -0
- codex/static_root/assets/datetime-column-DqSk_iUb.js.br +0 -0
- codex/static_root/assets/datetime-column-DqSk_iUb.js.gz +0 -0
- codex/static_root/assets/filter-CvqVrkAJ.8996d1b1db2a.js.br +0 -0
- codex/static_root/assets/filter-CvqVrkAJ.8996d1b1db2a.js.gz +0 -0
- codex/static_root/assets/filter-CvqVrkAJ.js.br +0 -0
- codex/static_root/assets/filter-CvqVrkAJ.js.gz +0 -0
- codex/static_root/assets/flag-tab-Bdh3jAbG.7731e3229cca.js +0 -1
- codex/static_root/assets/flag-tab-Bdh3jAbG.7731e3229cca.js.br +0 -0
- codex/static_root/assets/flag-tab-Bdh3jAbG.7731e3229cca.js.gz +0 -0
- codex/static_root/assets/flag-tab-Bdh3jAbG.js +0 -1
- codex/static_root/assets/flag-tab-Bdh3jAbG.js.br +0 -0
- codex/static_root/assets/flag-tab-Bdh3jAbG.js.gz +0 -0
- codex/static_root/assets/group-tab-89t0MyqI.1c743da230be.js +0 -1
- codex/static_root/assets/group-tab-89t0MyqI.1c743da230be.js.br +0 -0
- codex/static_root/assets/group-tab-89t0MyqI.1c743da230be.js.gz +0 -0
- codex/static_root/assets/group-tab-89t0MyqI.js +0 -1
- codex/static_root/assets/group-tab-89t0MyqI.js.br +0 -0
- codex/static_root/assets/group-tab-89t0MyqI.js.gz +0 -0
- codex/static_root/assets/http-error-BJTIqHG7.68507d59e73a.js +0 -1
- codex/static_root/assets/http-error-BJTIqHG7.68507d59e73a.js.br +0 -3
- codex/static_root/assets/http-error-BJTIqHG7.68507d59e73a.js.gz +0 -0
- codex/static_root/assets/http-error-BJTIqHG7.js +0 -1
- codex/static_root/assets/http-error-BJTIqHG7.js.br +0 -3
- codex/static_root/assets/http-error-BJTIqHG7.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-DoebKL9-.6eef922082b3.js +0 -1
- codex/static_root/assets/library-tab-DoebKL9-.6eef922082b3.js.br +0 -0
- codex/static_root/assets/library-tab-DoebKL9-.6eef922082b3.js.gz +0 -0
- codex/static_root/assets/library-tab-DoebKL9-.js +0 -1
- codex/static_root/assets/library-tab-DoebKL9-.js.br +0 -0
- codex/static_root/assets/library-tab-DoebKL9-.js.gz +0 -0
- codex/static_root/assets/main-DArXKRWW.47a8064195fe.js +0 -6
- codex/static_root/assets/main-DArXKRWW.47a8064195fe.js.br +0 -0
- codex/static_root/assets/main-DArXKRWW.47a8064195fe.js.gz +0 -0
- codex/static_root/assets/main-DArXKRWW.js +0 -6
- codex/static_root/assets/main-DArXKRWW.js.br +0 -0
- codex/static_root/assets/main-DArXKRWW.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-m9-HzvQE.178146e802bf.js +0 -1
- codex/static_root/assets/pagination-toolbar-m9-HzvQE.178146e802bf.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-m9-HzvQE.178146e802bf.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-m9-HzvQE.js +0 -1
- codex/static_root/assets/pagination-toolbar-m9-HzvQE.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-m9-HzvQE.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-xL1qP1mK.643e4fab2b8d.js +0 -305
- codex/static_root/assets/pdf-doc-xL1qP1mK.643e4fab2b8d.js.br +0 -0
- codex/static_root/assets/pdf-doc-xL1qP1mK.643e4fab2b8d.js.gz +0 -0
- codex/static_root/assets/pdf-doc-xL1qP1mK.js +0 -305
- codex/static_root/assets/pdf-doc-xL1qP1mK.js.br +0 -0
- codex/static_root/assets/pdf-doc-xL1qP1mK.js.gz +0 -0
- codex/static_root/assets/reader-DwHuZOrV.b6067501f379.js +0 -2
- codex/static_root/assets/reader-DwHuZOrV.b6067501f379.js.br +0 -0
- codex/static_root/assets/reader-DwHuZOrV.b6067501f379.js.gz +0 -0
- codex/static_root/assets/reader-DwHuZOrV.js +0 -2
- codex/static_root/assets/reader-DwHuZOrV.js.br +0 -0
- codex/static_root/assets/reader-DwHuZOrV.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-CCg5vRIV.59c17fb77441.js +0 -1
- codex/static_root/assets/relation-chips-CCg5vRIV.59c17fb77441.js.br +0 -0
- codex/static_root/assets/relation-chips-CCg5vRIV.59c17fb77441.js.gz +0 -0
- codex/static_root/assets/relation-chips-CCg5vRIV.js +0 -1
- codex/static_root/assets/relation-chips-CCg5vRIV.js.br +0 -0
- codex/static_root/assets/relation-chips-CCg5vRIV.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CUQX8aKC.82a95dcef142.js +0 -2
- codex/static_root/assets/settings-drawer-CUQX8aKC.82a95dcef142.js.br +0 -0
- codex/static_root/assets/settings-drawer-CUQX8aKC.82a95dcef142.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CUQX8aKC.js +0 -2
- codex/static_root/assets/settings-drawer-CUQX8aKC.js.br +0 -0
- codex/static_root/assets/settings-drawer-CUQX8aKC.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-CI0u-X7Y.2d1b6a0d4139.css.br +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css.gz +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.css.br +0 -0
- codex/static_root/assets/stats-tab-CI0u-X7Y.css.gz +0 -0
- codex/static_root/assets/stats-tab-CyDREZdm.000d9e8e00a1.js +0 -1
- codex/static_root/assets/stats-tab-CyDREZdm.000d9e8e00a1.js.br +0 -0
- codex/static_root/assets/stats-tab-CyDREZdm.000d9e8e00a1.js.gz +0 -0
- codex/static_root/assets/stats-tab-CyDREZdm.js +0 -1
- codex/static_root/assets/stats-tab-CyDREZdm.js.br +0 -0
- codex/static_root/assets/stats-tab-CyDREZdm.js.gz +0 -0
- codex/static_root/assets/task-tab-RfF_m-c2.4ea612865931.js +0 -1
- codex/static_root/assets/task-tab-RfF_m-c2.4ea612865931.js.br +0 -0
- codex/static_root/assets/task-tab-RfF_m-c2.4ea612865931.js.gz +0 -0
- codex/static_root/assets/task-tab-RfF_m-c2.js +0 -1
- codex/static_root/assets/task-tab-RfF_m-c2.js.br +0 -0
- codex/static_root/assets/task-tab-RfF_m-c2.js.gz +0 -0
- codex/static_root/assets/unauthorized-BY4OkaUc.7a55b1214630.js +0 -1
- codex/static_root/assets/unauthorized-BY4OkaUc.7a55b1214630.js.br +0 -0
- codex/static_root/assets/unauthorized-BY4OkaUc.7a55b1214630.js.gz +0 -0
- codex/static_root/assets/unauthorized-BY4OkaUc.js +0 -1
- codex/static_root/assets/unauthorized-BY4OkaUc.js.br +0 -0
- codex/static_root/assets/unauthorized-BY4OkaUc.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/user-tab-tSrCTVFj.6c5b9a53a03a.js +0 -1
- codex/static_root/assets/user-tab-tSrCTVFj.6c5b9a53a03a.js.br +0 -0
- codex/static_root/assets/user-tab-tSrCTVFj.6c5b9a53a03a.js.gz +0 -0
- codex/static_root/assets/user-tab-tSrCTVFj.js +0 -1
- codex/static_root/assets/user-tab-tSrCTVFj.js.br +0 -0
- codex/static_root/assets/user-tab-tSrCTVFj.js.gz +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json +0 -1
- codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
- codex/static_root/manifest.37e83529bf91.json +0 -610
- codex/static_root/manifest.37e83529bf91.json.br +0 -0
- codex/static_root/manifest.37e83529bf91.json.gz +0 -0
- {codex-1.6.8.dist-info → codex-1.6.9a0.dist-info}/LICENSE +0 -0
- {codex-1.6.8.dist-info → codex-1.6.9a0.dist-info}/WHEEL +0 -0
- {codex-1.6.8.dist-info → codex-1.6.9a0.dist-info}/entry_points.txt +0 -0
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."""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Janitor Scheduled time."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, time, timedelta
|
|
4
|
+
|
|
5
|
+
from django.utils import timezone as django_timezone
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_janitor_time(_log):
|
|
9
|
+
"""Get midnight relative to now."""
|
|
10
|
+
tomorrow = django_timezone.now() + timedelta(days=1)
|
|
11
|
+
tomorrow = tomorrow.astimezone()
|
|
12
|
+
return datetime.combine(tomorrow, time.min).astimezone()
|
|
@@ -5,17 +5,36 @@ import signal
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
|
+
from versio.version import Version
|
|
9
|
+
|
|
8
10
|
from codex.librarian.janitor.status import JanitorStatusTypes
|
|
9
11
|
from codex.librarian.tasks import LibrarianShutdownTask
|
|
10
12
|
from codex.models import AdminFlag
|
|
13
|
+
from codex.models.admin import Timestamp
|
|
11
14
|
from codex.status import Status
|
|
12
|
-
from codex.version import
|
|
15
|
+
from codex.version import VERSION, get_version
|
|
13
16
|
from codex.worker_base import WorkerBaseMixin
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class UpdateMixin(WorkerBaseMixin):
|
|
17
20
|
"""Update codex methods for janitor."""
|
|
18
21
|
|
|
22
|
+
def _is_outdated(self):
|
|
23
|
+
"""Is codex outdated."""
|
|
24
|
+
result = False
|
|
25
|
+
ts = Timestamp.objects.get(key=Timestamp.TimestampChoices.CODEX_VERSION.value)
|
|
26
|
+
latest_version = ts.version
|
|
27
|
+
versio_latest_version = Version(latest_version)
|
|
28
|
+
|
|
29
|
+
installed_versio_version = Version(VERSION)
|
|
30
|
+
if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore
|
|
31
|
+
pre_blurb = "latest version is a prerelease. But installed version is not."
|
|
32
|
+
else:
|
|
33
|
+
result = versio_latest_version > installed_versio_version
|
|
34
|
+
pre_blurb = ""
|
|
35
|
+
self.log.info(f"{latest_version=} > {VERSION=} = {result}{pre_blurb}")
|
|
36
|
+
return result
|
|
37
|
+
|
|
19
38
|
def update_codex(self, force=False):
|
|
20
39
|
"""Update the package and restart everything if the version changed."""
|
|
21
40
|
status = Status(JanitorStatusTypes.CODEX_UPDATE)
|
|
@@ -27,7 +46,7 @@ class UpdateMixin(WorkerBaseMixin):
|
|
|
27
46
|
eau = AdminFlag.objects.only("on").get(
|
|
28
47
|
key=AdminFlag.FlagChoices.AUTO_UPDATE.value
|
|
29
48
|
)
|
|
30
|
-
if not eau.on or not
|
|
49
|
+
if not eau.on or not self._is_outdated():
|
|
31
50
|
self.log.info("Codex is up to date.")
|
|
32
51
|
return
|
|
33
52
|
|
|
@@ -21,8 +21,7 @@ class VacuumMixin(WorkerBaseMixin):
|
|
|
21
21
|
self.status_controller.start(status)
|
|
22
22
|
old_size = DB_PATH.stat().st_size
|
|
23
23
|
with connection.cursor() as cursor:
|
|
24
|
-
cursor.execute("PRAGMA
|
|
25
|
-
cursor.execute("PRAGMA optimize;")
|
|
24
|
+
cursor.execute("PRAGMA optimize")
|
|
26
25
|
cursor.execute("VACUUM")
|
|
27
26
|
cursor.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
|
28
27
|
new_size = DB_PATH.stat().st_size
|
codex/librarian/librariand.py
CHANGED
|
@@ -9,6 +9,7 @@ from caseconverter import snakecase
|
|
|
9
9
|
|
|
10
10
|
from codex.librarian.covers.coverd import CoverThread
|
|
11
11
|
from codex.librarian.covers.tasks import CoverTask
|
|
12
|
+
from codex.librarian.cron.crond import CronThread
|
|
12
13
|
from codex.librarian.delayed_taskd import DelayedTasksThread
|
|
13
14
|
from codex.librarian.importer.importerd import ComicImporterThread
|
|
14
15
|
from codex.librarian.importer.tasks import (
|
|
@@ -16,7 +17,6 @@ from codex.librarian.importer.tasks import (
|
|
|
16
17
|
ImportTask,
|
|
17
18
|
)
|
|
18
19
|
from codex.librarian.janitor.janitor import Janitor
|
|
19
|
-
from codex.librarian.janitor.janitord import JanitorThread
|
|
20
20
|
from codex.librarian.janitor.tasks import JanitorTask
|
|
21
21
|
from codex.librarian.notifier.notifierd import NotifierThread
|
|
22
22
|
from codex.librarian.notifier.tasks import NotifierTask
|
|
@@ -26,7 +26,9 @@ from codex.librarian.search.tasks import (
|
|
|
26
26
|
SearchIndexerTask,
|
|
27
27
|
SearchIndexRebuildIfDBChangedTask,
|
|
28
28
|
)
|
|
29
|
-
from codex.librarian.tasks import DelayedTasks, LibrarianShutdownTask
|
|
29
|
+
from codex.librarian.tasks import DelayedTasks, LibrarianShutdownTask, WakeCronTask
|
|
30
|
+
from codex.librarian.telemeter.tasks import TelemeterTask
|
|
31
|
+
from codex.librarian.telemeter.telemeter import send_telemetry
|
|
30
32
|
from codex.librarian.watchdog.event_batcherd import WatchdogEventBatcherThread
|
|
31
33
|
from codex.librarian.watchdog.observers import (
|
|
32
34
|
LibraryEventObserver,
|
|
@@ -52,7 +54,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
|
|
|
52
54
|
WatchdogEventBatcherThread,
|
|
53
55
|
LibraryEventObserver,
|
|
54
56
|
LibraryPollingObserver,
|
|
55
|
-
|
|
57
|
+
CronThread,
|
|
56
58
|
)
|
|
57
59
|
_THREAD_CLASS_MAP = MappingProxyType(
|
|
58
60
|
{
|
|
@@ -105,6 +107,10 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
|
|
|
105
107
|
self.log.debug("Told search indexers to stop for db updates.")
|
|
106
108
|
else:
|
|
107
109
|
self._threads.search_indexer_thread.queue.put(task)
|
|
110
|
+
case WakeCronTask():
|
|
111
|
+
self._threads.cron_thread.end_timeout()
|
|
112
|
+
case TelemeterTask():
|
|
113
|
+
send_telemetry(self.log)
|
|
108
114
|
case JanitorTask():
|
|
109
115
|
self.janitor.run(task)
|
|
110
116
|
case DelayedTasks():
|
codex/librarian/tasks.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Telemeter."""
|
|
@@ -111,8 +111,10 @@ class CodexStats:
|
|
|
111
111
|
|
|
112
112
|
return user_stats, anon_session_count
|
|
113
113
|
|
|
114
|
-
def
|
|
114
|
+
def _add_platform(self, obj):
|
|
115
115
|
"""Add dict of platform information to object."""
|
|
116
|
+
if self.params and "platform" not in self.params:
|
|
117
|
+
return
|
|
116
118
|
platform = {
|
|
117
119
|
"docker": self._is_docker(),
|
|
118
120
|
"machine": machine(),
|
|
@@ -126,8 +128,10 @@ class CodexStats:
|
|
|
126
128
|
}
|
|
127
129
|
obj["platform"] = platform
|
|
128
130
|
|
|
129
|
-
def
|
|
131
|
+
def _add_config(self, obj):
|
|
130
132
|
"""Add dict of config informaation to object."""
|
|
133
|
+
if self.params and "config" not in self.params:
|
|
134
|
+
return
|
|
131
135
|
config = self._get_model_counts("config")
|
|
132
136
|
sessions, config["user_anonymous_count"] = self._get_session_stats()
|
|
133
137
|
config["user_registered_count"] = config.pop("users_count", 0)
|
|
@@ -135,14 +139,18 @@ class CodexStats:
|
|
|
135
139
|
obj["config"] = config
|
|
136
140
|
obj["sessions"] = sessions
|
|
137
141
|
|
|
138
|
-
def
|
|
142
|
+
def _add_groups(self, obj):
|
|
139
143
|
"""Add dict of groups information to object."""
|
|
144
|
+
if self.params and "groups" not in self.params:
|
|
145
|
+
return
|
|
140
146
|
groups = self._get_model_counts("groups")
|
|
147
|
+
groups["issue_count"] = groups.pop("comic_count", 0)
|
|
141
148
|
obj["groups"] = groups
|
|
142
149
|
|
|
143
|
-
|
|
144
|
-
def _get_file_types(obj):
|
|
150
|
+
def _add_file_types(self, obj):
|
|
145
151
|
"""Query for file types."""
|
|
152
|
+
if self.params and "fileTypes" not in self.params:
|
|
153
|
+
return
|
|
146
154
|
file_types = {}
|
|
147
155
|
qs = (
|
|
148
156
|
Comic.objects.values("file_type")
|
|
@@ -156,22 +164,19 @@ class CodexStats:
|
|
|
156
164
|
sorted_fts = dict(sorted(file_types.items()))
|
|
157
165
|
obj["file_types"] = sorted_fts
|
|
158
166
|
|
|
159
|
-
def
|
|
167
|
+
def _add_metadata(self, obj):
|
|
160
168
|
"""Add dict of metadata counts to object."""
|
|
169
|
+
if self.params and "metadata" not in self.params:
|
|
170
|
+
return
|
|
161
171
|
metadata = self._get_model_counts("metadata")
|
|
162
172
|
obj["metadata"] = metadata
|
|
163
173
|
|
|
164
174
|
def get(self):
|
|
165
175
|
"""Construct the stats object."""
|
|
166
176
|
obj = {}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
self._get_groups(obj)
|
|
173
|
-
if not self.params or "fileTypes" in self.params:
|
|
174
|
-
self._get_file_types(obj)
|
|
175
|
-
if not self.params or "metadata" in self.params:
|
|
176
|
-
self._get_metadata(obj)
|
|
177
|
+
self._add_platform(obj)
|
|
178
|
+
self._add_config(obj)
|
|
179
|
+
self._add_groups(obj)
|
|
180
|
+
self._add_file_types(obj)
|
|
181
|
+
self._add_metadata(obj)
|
|
177
182
|
return obj
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Telemeter job."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from base64 import a85decode
|
|
5
|
+
from logging import getLogger
|
|
6
|
+
from lzma import compress
|
|
7
|
+
from uuid import uuid4
|
|
8
|
+
|
|
9
|
+
from requests import Session
|
|
10
|
+
|
|
11
|
+
from codex.librarian.telemeter.stats import CodexStats
|
|
12
|
+
from codex.models.admin import AdminFlag, Timestamp
|
|
13
|
+
from codex.settings.settings import DEBUG
|
|
14
|
+
|
|
15
|
+
# Version
|
|
16
|
+
_APP_NAME = "codex"
|
|
17
|
+
_VERSION = "1"
|
|
18
|
+
|
|
19
|
+
# Sending
|
|
20
|
+
# this isn't meant to fool you. it's meant to discourage lazy scraper bots.
|
|
21
|
+
_BASE = "".join(
|
|
22
|
+
(
|
|
23
|
+
a85decode(b"BQS?8F#ks-@:XCm@;\\+").decode(),
|
|
24
|
+
a85decode(b"Ea`frF)to6Bk]hRFCB94/c").decode(),
|
|
25
|
+
a85decode(b"@rGmhGV*rI@:Wqi/n&^<").decode(),
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
_HEADERS = {"Content-Type": "application/xz"}
|
|
29
|
+
_POST = _BASE + f"/stats/{_APP_NAME}/{_VERSION}"
|
|
30
|
+
_TIMEOUT = 5
|
|
31
|
+
|
|
32
|
+
LOG = getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_telemeter_timestamp():
|
|
36
|
+
"""Get or create timestamp."""
|
|
37
|
+
key = Timestamp.TimestampChoices.TELEMETER_SENT.value
|
|
38
|
+
defaults = {"key": key}
|
|
39
|
+
ts, _ = Timestamp.objects.get_or_create(defaults=defaults, key=key)
|
|
40
|
+
if not ts.version:
|
|
41
|
+
ts.version = str(uuid4())
|
|
42
|
+
ts.save()
|
|
43
|
+
return ts
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _post_stats(log, data):
|
|
47
|
+
"""Post telemetry to endpoint."""
|
|
48
|
+
data_json = json.dumps(data)
|
|
49
|
+
json_bytes = data_json.encode()
|
|
50
|
+
compressed_data = compress(json_bytes)
|
|
51
|
+
if DEBUG:
|
|
52
|
+
msg = f"{_POST} {len(compressed_data)}, {_HEADERS}, {_TIMEOUT}"
|
|
53
|
+
log.debug(msg)
|
|
54
|
+
else:
|
|
55
|
+
with Session() as session:
|
|
56
|
+
response = session.post(
|
|
57
|
+
_POST, data=compressed_data, headers=_HEADERS, timeout=_TIMEOUT
|
|
58
|
+
)
|
|
59
|
+
response.raise_for_status()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _send_telemetry(log):
|
|
63
|
+
"""Send telemetry to server."""
|
|
64
|
+
if not AdminFlag.objects.get(key=AdminFlag.FlagChoices.SEND_TELEMETRY.value).on:
|
|
65
|
+
reason = "Send Telemetry flag is off."
|
|
66
|
+
raise ValueError(reason)
|
|
67
|
+
ts = get_telemeter_timestamp()
|
|
68
|
+
stats = CodexStats().get()
|
|
69
|
+
data = {"stats": stats, "uuid": ts.version}
|
|
70
|
+
_post_stats(log, data)
|
|
71
|
+
# log.debug("Sent anonymous stats")
|
|
72
|
+
ts.save() # update updated_at
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def send_telemetry(log):
|
|
76
|
+
"""Send anonymous telemetry during one window per week."""
|
|
77
|
+
try:
|
|
78
|
+
_send_telemetry(log)
|
|
79
|
+
except Exception as exc:
|
|
80
|
+
log.debug(f"Failed to send anonyomous stats: {exc}")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Get telemeter send time."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
6
|
+
from codex.librarian.telemeter.telemeter import get_telemeter_timestamp
|
|
7
|
+
from codex.models.admin import AdminFlag
|
|
8
|
+
|
|
9
|
+
# Timing
|
|
10
|
+
_ONE_DAY = 24 * 60 * 60
|
|
11
|
+
_SECS_PER_WEEK = 7 * _ONE_DAY
|
|
12
|
+
_MAX_UUID = 2**128
|
|
13
|
+
_UUID_DIVISOR = _MAX_UUID / _SECS_PER_WEEK
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_utc_start_of_week():
|
|
17
|
+
"""Get timestamp for this Monady 00:00:00."""
|
|
18
|
+
# Monday, now o'clock.
|
|
19
|
+
utc_now = datetime.now(tz=timezone.utc)
|
|
20
|
+
start_of_week = utc_now - timedelta(days=utc_now.weekday())
|
|
21
|
+
# Monday, midnight.
|
|
22
|
+
return start_of_week.replace(hour=0, minute=0, second=0, microsecond=0).astimezone(
|
|
23
|
+
tz=timezone.utc
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _is_created_recently(ts):
|
|
28
|
+
"""Don't send if created recently."""
|
|
29
|
+
now = datetime.now(tz=timezone.utc)
|
|
30
|
+
since = ts.created_at - now
|
|
31
|
+
return abs(since.total_seconds()) < _ONE_DAY
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_scheduled_time(ts):
|
|
35
|
+
"""Compute the time of week to send from the uuid."""
|
|
36
|
+
start_of_week = _get_utc_start_of_week()
|
|
37
|
+
uuid = UUID(ts.version)
|
|
38
|
+
seconds_after_week_start = uuid.int / _UUID_DIVISOR
|
|
39
|
+
time_of_week = timedelta(seconds=seconds_after_week_start)
|
|
40
|
+
telemeter_time = start_of_week + time_of_week
|
|
41
|
+
telemeter_time = telemeter_time.astimezone(tz=timezone.utc)
|
|
42
|
+
if telemeter_time < ts.updated_at:
|
|
43
|
+
# Already ran this week, or created this week after run time.
|
|
44
|
+
telemeter_time = 0
|
|
45
|
+
return telemeter_time
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_telemeter_time(log):
|
|
49
|
+
"""Get the time to send telemetry."""
|
|
50
|
+
# Should we schedule telemeter at all?
|
|
51
|
+
if not AdminFlag.objects.get(key=AdminFlag.FlagChoices.SEND_TELEMETRY.value).on:
|
|
52
|
+
log.debug("Telemeter disabled. Not scheduled.")
|
|
53
|
+
return 0
|
|
54
|
+
|
|
55
|
+
ts = get_telemeter_timestamp()
|
|
56
|
+
|
|
57
|
+
if _is_created_recently(ts):
|
|
58
|
+
log.debug("Telemeter created recently. Not scheduled.")
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
return _get_scheduled_time(ts)
|
|
@@ -21,9 +21,9 @@ class SnapshotDiffData:
|
|
|
21
21
|
class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
|
|
22
22
|
"""Custom Diff allows inode only changes to be 'modified'."""
|
|
23
23
|
|
|
24
|
-
def _get_inode(self,
|
|
24
|
+
def _get_inode(self, snapshot, full_path):
|
|
25
25
|
"""Get inode optionally ignoring device stats."""
|
|
26
|
-
result =
|
|
26
|
+
result = snapshot.inode(full_path)
|
|
27
27
|
if self._ignore_device:
|
|
28
28
|
result = result[0]
|
|
29
29
|
return result
|
|
@@ -33,7 +33,7 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
|
|
|
33
33
|
return self._get_inode(data.ref, path) == self._get_inode(data.snapshot, path)
|
|
34
34
|
|
|
35
35
|
def _is_stats_equal(self, data, old_path, new_path):
|
|
36
|
-
"""Return if the
|
|
36
|
+
"""Return if the mtime and size are equal.
|
|
37
37
|
|
|
38
38
|
For old paths in the ref and new paths in the snapshot.
|
|
39
39
|
"""
|
|
@@ -48,25 +48,27 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
|
|
|
48
48
|
if self._inode_only_modified:
|
|
49
49
|
data.modified.add(path)
|
|
50
50
|
else:
|
|
51
|
+
# Add these to created and deleted. But...
|
|
52
|
+
# The _find_moved_paths() inode tracker should
|
|
53
|
+
# sort them out.
|
|
54
|
+
# This is the same as in upstream dirsnapshot.py
|
|
51
55
|
data.created.add(path)
|
|
52
56
|
data.deleted.add(path)
|
|
53
57
|
|
|
54
58
|
def _find_moved_paths(self, data):
|
|
55
|
-
"""Find moved paths."""
|
|
56
|
-
for
|
|
57
|
-
inode = data.ref
|
|
58
|
-
new_path
|
|
59
|
-
if new_path:
|
|
59
|
+
"""Find moved paths in deleted and created."""
|
|
60
|
+
for old_path in tuple(data.deleted):
|
|
61
|
+
inode = self._get_inode(data.ref, old_path)
|
|
62
|
+
if new_path := data.snapshot.path(inode):
|
|
60
63
|
# file is not deleted but moved
|
|
61
|
-
data.deleted.remove(
|
|
62
|
-
data.moved.add((
|
|
64
|
+
data.deleted.remove(old_path)
|
|
65
|
+
data.moved.add((old_path, new_path))
|
|
63
66
|
|
|
64
|
-
for
|
|
65
|
-
inode = data.snapshot
|
|
66
|
-
old_path
|
|
67
|
-
|
|
68
|
-
data.
|
|
69
|
-
data.moved.add((old_path, path))
|
|
67
|
+
for new_path in tuple(data.created):
|
|
68
|
+
inode = self._get_inode(data.snapshot, new_path)
|
|
69
|
+
if old_path := data.ref.path(inode):
|
|
70
|
+
data.created.remove(new_path)
|
|
71
|
+
data.moved.add((old_path, new_path))
|
|
70
72
|
|
|
71
73
|
def _find_modified_paths(self, data):
|
|
72
74
|
"""Find modified paths."""
|
|
@@ -80,7 +82,7 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
|
|
|
80
82
|
# Check moved paths for modificiations.
|
|
81
83
|
for old_path, new_path in data.moved:
|
|
82
84
|
if not self._is_stats_equal(data, old_path, new_path):
|
|
83
|
-
data.modified.add(
|
|
85
|
+
data.modified.add(new_path)
|
|
84
86
|
|
|
85
87
|
def __init__( # C901, PLR0912
|
|
86
88
|
self, ref, snapshot, ignore_device=False, inode_only_modified=False
|
|
@@ -25,20 +25,18 @@ from codex.models import Library
|
|
|
25
25
|
from codex.status import Status
|
|
26
26
|
from codex.worker_base import WorkerBaseMixin
|
|
27
27
|
|
|
28
|
-
_CODEX_EVENT_FILTER =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
)
|
|
28
|
+
_CODEX_EVENT_FILTER = [
|
|
29
|
+
FileMovedEvent,
|
|
30
|
+
FileModifiedEvent,
|
|
31
|
+
FileCreatedEvent,
|
|
32
|
+
FileDeletedEvent,
|
|
33
|
+
# FileClosedEvent,
|
|
34
|
+
# FileOpenedEvent,
|
|
35
|
+
DirMovedEvent,
|
|
36
|
+
DirModifiedEvent,
|
|
37
|
+
DirDeletedEvent,
|
|
38
|
+
# DirCreatedEvent,
|
|
39
|
+
]
|
|
42
40
|
_DOCKER_UNMOUNTED_FN = "DOCKER_UNMOUNTED_VOLUME"
|
|
43
41
|
|
|
44
42
|
|
|
@@ -234,7 +234,7 @@ class CodexCustomCoverEventHandler(CodexEventHandlerBase):
|
|
|
234
234
|
try:
|
|
235
235
|
if event.is_directory or event.event_type in self.IGNORED_EVENTS:
|
|
236
236
|
return
|
|
237
|
-
src_cover_match = self._match_group_cover_image(event.src_path)
|
|
237
|
+
src_cover_match = self._match_group_cover_image(str(event.src_path))
|
|
238
238
|
if event.event_type == EVENT_TYPE_MOVED:
|
|
239
239
|
send_event = self._transform_event_moved(event, src_cover_match)
|
|
240
240
|
elif src_cover_match:
|
|
@@ -110,15 +110,15 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
|
|
|
110
110
|
https://pythonhosted.org/watchdog/_modules/watchdog/observers/api.html#BaseObserver
|
|
111
111
|
"""
|
|
112
112
|
if self._emitter_class != DatabasePollingEmitter:
|
|
113
|
-
return super().schedule(event_handler, path, recursive)
|
|
113
|
+
return super().schedule(event_handler, path, recursive=recursive)
|
|
114
114
|
with self._lock:
|
|
115
|
-
watch = ObservedWatch(path, recursive)
|
|
115
|
+
watch = ObservedWatch(path, recursive=recursive)
|
|
116
116
|
self._add_handler_for_watch(event_handler, watch)
|
|
117
117
|
|
|
118
118
|
# If we don't have an emitter for this watch already, create it.
|
|
119
119
|
if self._emitter_for_watch.get(watch) is None:
|
|
120
120
|
covers_only = isinstance(event_handler, CodexCustomCoverEventHandler)
|
|
121
|
-
emitter =
|
|
121
|
+
emitter = DatabasePollingEmitter(
|
|
122
122
|
event_queue=self.event_queue,
|
|
123
123
|
watch=watch,
|
|
124
124
|
timeout=self.timeout,
|
|
@@ -166,7 +166,7 @@ class LibraryPollingObserver(UatuMixin):
|
|
|
166
166
|
|
|
167
167
|
for emitter in self.emitters:
|
|
168
168
|
if emitter.watch.path in paths:
|
|
169
|
-
emitter.poll(force)
|
|
169
|
+
emitter.poll(force) # type: ignore
|
|
170
170
|
except Exception:
|
|
171
171
|
self.log.exception(
|
|
172
172
|
f"{self.__class__.__name__}.poll({library_pks}, {force})"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Generated by Django 5.0.6 on 2024-07-02 21:01."""
|
|
2
|
+
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
"""Migrate db."""
|
|
10
|
+
|
|
11
|
+
dependencies: ClassVar[list] = [ # type: ignore
|
|
12
|
+
("codex", "0027_import_order_and_covers"),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.AlterField(
|
|
17
|
+
model_name="adminflag",
|
|
18
|
+
name="key",
|
|
19
|
+
field=models.CharField(
|
|
20
|
+
choices=[
|
|
21
|
+
("FV", "Folder View"),
|
|
22
|
+
("RG", "Registration"),
|
|
23
|
+
("NU", "Non Users"),
|
|
24
|
+
("AU", "Auto Update"),
|
|
25
|
+
("SO", "Search Index Optimize"),
|
|
26
|
+
("IM", "Import Metadata"),
|
|
27
|
+
("ST", "Send Telemetry"),
|
|
28
|
+
],
|
|
29
|
+
db_index=True,
|
|
30
|
+
max_length=2,
|
|
31
|
+
),
|
|
32
|
+
),
|
|
33
|
+
migrations.AlterField(
|
|
34
|
+
model_name="timestamp",
|
|
35
|
+
name="key",
|
|
36
|
+
field=models.CharField(
|
|
37
|
+
choices=[
|
|
38
|
+
("JA", "Janitor"),
|
|
39
|
+
("VR", "Codex Version"),
|
|
40
|
+
("SI", "Search Index UUID"),
|
|
41
|
+
("AP", "API Key"),
|
|
42
|
+
("TS", "Telemeter Sent"),
|
|
43
|
+
],
|
|
44
|
+
db_index=True,
|
|
45
|
+
max_length=2,
|
|
46
|
+
),
|
|
47
|
+
),
|
|
48
|
+
]
|
codex/models/admin.py
CHANGED
|
@@ -39,6 +39,7 @@ class AdminFlag(BaseModel):
|
|
|
39
39
|
AUTO_UPDATE = "AU"
|
|
40
40
|
SEARCH_INDEX_OPTIMIZE = "SO"
|
|
41
41
|
IMPORT_METADATA = "IM"
|
|
42
|
+
SEND_TELEMETRY = "ST"
|
|
42
43
|
|
|
43
44
|
FALSE_DEFAULTS = frozenset({FlagChoices.AUTO_UPDATE})
|
|
44
45
|
|
|
@@ -86,6 +87,7 @@ class Timestamp(BaseModel):
|
|
|
86
87
|
CODEX_VERSION = "VR", _("Codex Version")
|
|
87
88
|
SEARCH_INDEX_UUID = "SI", _("Search Index UUID")
|
|
88
89
|
API_KEY = "AP", _("API Key")
|
|
90
|
+
TELEMETER_SENT = "TS", _("Telemeter Sent")
|
|
89
91
|
|
|
90
92
|
key = CharField(db_index=True, max_length=2, choices=TimestampChoices.choices)
|
|
91
93
|
version = CharField(max_length=MAX_FIELD_LEN, default="")
|
codex/models/comic.py
CHANGED
|
@@ -246,7 +246,7 @@ class Comic(WatchedPathBrowserGroup):
|
|
|
246
246
|
return issue_str
|
|
247
247
|
|
|
248
248
|
@classmethod
|
|
249
|
-
def get_title(
|
|
249
|
+
def get_title(
|
|
250
250
|
cls, obj, volume=True, zero_pad=None, name=True, filename_fallback=False
|
|
251
251
|
):
|
|
252
252
|
"""Create the comic title for display."""
|
codex/search/backend.py
CHANGED
|
@@ -122,7 +122,10 @@ class CodexSearchBackend(CodexBackendSearchMixin, WhooshSearchBackend, WorkerBas
|
|
|
122
122
|
"created_at": ("created",),
|
|
123
123
|
"critical_rating": gen_multipart_field_aliases("critical_rating"),
|
|
124
124
|
"genres": ("genre",),
|
|
125
|
-
"file_type": (
|
|
125
|
+
"file_type": (
|
|
126
|
+
"filetype",
|
|
127
|
+
"type",
|
|
128
|
+
),
|
|
126
129
|
"identifier": ("id", "nss"),
|
|
127
130
|
"identifier_type": ("idtype", "id_type", "nid"),
|
|
128
131
|
"issue_number": ("number",),
|
|
@@ -132,7 +135,11 @@ class CodexSearchBackend(CodexBackendSearchMixin, WhooshSearchBackend, WorkerBas
|
|
|
132
135
|
"original_format": ("format",),
|
|
133
136
|
"page_count": ("pages",),
|
|
134
137
|
"reading_direction": ("direction",),
|
|
135
|
-
"search_path": (
|
|
138
|
+
"search_path": (
|
|
139
|
+
"path",
|
|
140
|
+
"filename",
|
|
141
|
+
*gen_multipart_field_aliases("folders"),
|
|
142
|
+
),
|
|
136
143
|
"series_groups": gen_multipart_field_aliases("series_groups"),
|
|
137
144
|
"scan_info": ("scan",),
|
|
138
145
|
"stories": ("story",),
|
|
@@ -431,7 +438,7 @@ class CodexSearchBackend(CodexBackendSearchMixin, WhooshSearchBackend, WorkerBas
|
|
|
431
438
|
raise
|
|
432
439
|
return count
|
|
433
440
|
|
|
434
|
-
def update( #
|
|
441
|
+
def update( # type: ignore
|
|
435
442
|
self,
|
|
436
443
|
index,
|
|
437
444
|
iterable,
|