codex 1.8.5__py3-none-any.whl → 1.8.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of codex might be problematic. Click here for more details.
- codex/choices/admin.py +1 -0
- codex/librarian/scribe/importer/const.py +7 -3
- codex/librarian/scribe/importer/create/comics.py +0 -3
- codex/librarian/scribe/importer/delete/comics.py +7 -4
- codex/librarian/scribe/importer/delete/folders.py +2 -1
- codex/librarian/scribe/importer/read/aggregate_path.py +53 -13
- codex/librarian/scribe/importer/read/extract.py +16 -13
- codex/librarian/scribe/importer/search/prepare.py +0 -7
- codex/librarian/scribe/importer/search/sync_m2m.py +2 -1
- codex/librarian/scribe/importer/search/update.py +18 -81
- codex/librarian/scribe/scribed.py +2 -13
- codex/librarian/scribe/{importer/search → search}/const.py +1 -13
- codex/librarian/scribe/search/prepare.py +162 -0
- codex/librarian/scribe/search/remove.py +22 -2
- codex/librarian/scribe/search/sync.py +88 -179
- codex/librarian/status_controller.py +25 -27
- codex/librarian/watchdog/event_batcherd.py +1 -1
- codex/migrations/0035_fts_optmize.py +136 -0
- codex/models/comic.py +0 -16
- codex/models/functions.py +3 -1
- codex/serializers/admin/tasks.py +1 -1
- codex/settings/__init__.py +26 -11
- codex/static_root/assets/VCheckbox-MwE_xq11.b2e2086608d9.js +1 -0
- codex/static_root/assets/VCheckbox-MwE_xq11.b2e2086608d9.js.br +0 -0
- codex/static_root/assets/VCheckbox-MwE_xq11.b2e2086608d9.js.gz +0 -0
- codex/static_root/assets/VCheckbox-MwE_xq11.js +1 -0
- codex/static_root/assets/VCheckbox-MwE_xq11.js.br +0 -0
- codex/static_root/assets/VCheckbox-MwE_xq11.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-BE6yrQ80.4a42f2c068f5.js → VCheckboxBtn-HUjl2ti_.f3e7b85503c6.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-HUjl2ti_.f3e7b85503c6.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-HUjl2ti_.f3e7b85503c6.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-BE6yrQ80.js → VCheckboxBtn-HUjl2ti_.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-HUjl2ti_.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-HUjl2ti_.js.gz +0 -0
- codex/static_root/assets/VCombobox-C1Osy24P.73168f28d07b.js +1 -0
- codex/static_root/assets/VCombobox-C1Osy24P.73168f28d07b.js.br +0 -0
- codex/static_root/assets/VCombobox-C1Osy24P.73168f28d07b.js.gz +0 -0
- codex/static_root/assets/VCombobox-C1Osy24P.js +1 -0
- codex/static_root/assets/VCombobox-C1Osy24P.js.br +0 -0
- codex/static_root/assets/VCombobox-C1Osy24P.js.gz +0 -0
- codex/static_root/assets/VDialog-c2XXqNJH.a4156ca20133.js +1 -0
- codex/static_root/assets/VDialog-c2XXqNJH.a4156ca20133.js.br +0 -0
- codex/static_root/assets/VDialog-c2XXqNJH.a4156ca20133.js.gz +0 -0
- codex/static_root/assets/VDialog-c2XXqNJH.js +1 -0
- codex/static_root/assets/VDialog-c2XXqNJH.js.br +0 -0
- codex/static_root/assets/VDialog-c2XXqNJH.js.gz +0 -0
- codex/static_root/assets/{VDivider-BEpvozL4.c9fd95940cb5.js → VDivider-C06z3Tn-.d5e3ed421a68.js} +1 -1
- codex/static_root/assets/VDivider-C06z3Tn-.d5e3ed421a68.js.br +0 -0
- codex/static_root/assets/VDivider-C06z3Tn-.d5e3ed421a68.js.gz +0 -0
- codex/static_root/assets/{VDivider-BEpvozL4.js → VDivider-C06z3Tn-.js} +1 -1
- codex/static_root/assets/VDivider-C06z3Tn-.js.br +0 -0
- codex/static_root/assets/VDivider-C06z3Tn-.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-BzyHQUL4.7e54cedf021c.js → VExpansionPanels-B8gzbonk.3532c6d08817.js} +1 -1
- codex/static_root/assets/VExpansionPanels-B8gzbonk.3532c6d08817.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-B8gzbonk.3532c6d08817.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-BzyHQUL4.js → VExpansionPanels-B8gzbonk.js} +1 -1
- codex/static_root/assets/VExpansionPanels-B8gzbonk.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-B8gzbonk.js.gz +0 -0
- codex/static_root/assets/{VForm-CmVlbmJK.887eb47d3534.js → VForm-Chf0FU2x.3e8425bff3d0.js} +1 -1
- codex/static_root/assets/VForm-Chf0FU2x.3e8425bff3d0.js.br +0 -0
- codex/static_root/assets/VForm-Chf0FU2x.3e8425bff3d0.js.gz +0 -0
- codex/static_root/assets/{VForm-CmVlbmJK.js → VForm-Chf0FU2x.js} +1 -1
- codex/static_root/assets/VForm-Chf0FU2x.js.br +0 -0
- codex/static_root/assets/VForm-Chf0FU2x.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-DWzKkq9W.daaabd353889.js → VRadioGroup-CHteIFqx.23cb51ee43e1.js} +1 -1
- codex/static_root/assets/VRadioGroup-CHteIFqx.23cb51ee43e1.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CHteIFqx.23cb51ee43e1.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-DWzKkq9W.js → VRadioGroup-CHteIFqx.js} +1 -1
- codex/static_root/assets/VRadioGroup-CHteIFqx.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CHteIFqx.js.gz +0 -0
- codex/static_root/assets/VSelect-BqS1Prvd.36bbebae4f0d.js +1 -0
- codex/static_root/assets/VSelect-BqS1Prvd.36bbebae4f0d.js.br +0 -0
- codex/static_root/assets/VSelect-BqS1Prvd.36bbebae4f0d.js.gz +0 -0
- codex/static_root/assets/VSelect-BqS1Prvd.js +1 -0
- codex/static_root/assets/VSelect-BqS1Prvd.js.br +0 -0
- codex/static_root/assets/VSelect-BqS1Prvd.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BRWozKsT.d10531c1a243.js +1 -0
- codex/static_root/assets/VSelectionControl-BRWozKsT.d10531c1a243.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BRWozKsT.d10531c1a243.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-BRWozKsT.js +1 -0
- codex/static_root/assets/VSelectionControl-BRWozKsT.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BRWozKsT.js.gz +0 -0
- codex/static_root/assets/VTable-BT6w_ifJ.ee6b710fb398.js +1 -0
- codex/static_root/assets/VTable-BT6w_ifJ.ee6b710fb398.js.br +0 -0
- codex/static_root/assets/VTable-BT6w_ifJ.ee6b710fb398.js.gz +0 -0
- codex/static_root/assets/VTable-BT6w_ifJ.js +1 -0
- codex/static_root/assets/VTable-BT6w_ifJ.js.br +0 -0
- codex/static_root/assets/VTable-BT6w_ifJ.js.gz +0 -0
- codex/static_root/assets/VTable-BsI6FsWH.css +1 -0
- codex/static_root/assets/VTable-BsI6FsWH.css.br +0 -0
- codex/static_root/assets/VTable-BsI6FsWH.css.gz +0 -0
- codex/static_root/assets/VTable-BsI6FsWH.d4c54d44a5ab.css +1 -0
- codex/static_root/assets/VTable-BsI6FsWH.d4c54d44a5ab.css.br +0 -0
- codex/static_root/assets/VTable-BsI6FsWH.d4c54d44a5ab.css.gz +0 -0
- codex/static_root/assets/VWindowItem-QQWUVqMX.c22e707ac99c.css +1 -0
- codex/static_root/assets/VWindowItem-QQWUVqMX.c22e707ac99c.css.br +5 -0
- codex/static_root/assets/VWindowItem-QQWUVqMX.c22e707ac99c.css.gz +0 -0
- codex/static_root/assets/VWindowItem-QQWUVqMX.css +1 -0
- codex/static_root/assets/VWindowItem-QQWUVqMX.css.br +5 -0
- codex/static_root/assets/VWindowItem-QQWUVqMX.css.gz +0 -0
- codex/static_root/assets/{VWindowItem-DBU8ce0X.4bbcffa75b9a.js → VWindowItem-sfdsG_YK.ee17f6723988.js} +1 -1
- codex/static_root/assets/VWindowItem-sfdsG_YK.ee17f6723988.js.br +0 -0
- codex/static_root/assets/VWindowItem-sfdsG_YK.ee17f6723988.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-DBU8ce0X.js → VWindowItem-sfdsG_YK.js} +1 -1
- codex/static_root/assets/VWindowItem-sfdsG_YK.js.br +0 -0
- codex/static_root/assets/VWindowItem-sfdsG_YK.js.gz +0 -0
- codex/static_root/assets/{admin-CfqMNEyt.bebc609836d0.js → admin-DuEKXO1g.5c6bfe166705.js} +1 -1
- codex/static_root/assets/admin-DuEKXO1g.5c6bfe166705.js.br +0 -0
- codex/static_root/assets/admin-DuEKXO1g.5c6bfe166705.js.gz +0 -0
- codex/static_root/assets/{admin-CfqMNEyt.js → admin-DuEKXO1g.js} +1 -1
- codex/static_root/assets/admin-DuEKXO1g.js.br +0 -0
- codex/static_root/assets/admin-DuEKXO1g.js.gz +0 -0
- codex/static_root/assets/admin-menu-DBce75Uy.be45a0a53c1a.js +1 -0
- codex/static_root/assets/admin-menu-DBce75Uy.be45a0a53c1a.js.br +0 -0
- codex/static_root/assets/admin-menu-DBce75Uy.be45a0a53c1a.js.gz +0 -0
- codex/static_root/assets/admin-menu-DBce75Uy.js +1 -0
- codex/static_root/assets/admin-menu-DBce75Uy.js.br +0 -0
- codex/static_root/assets/admin-menu-DBce75Uy.js.gz +0 -0
- codex/static_root/assets/{admin-Bt81XU3H.ca36b1a1b799.js → admin-pnPIiLQj.b791e7869cf5.js} +1 -1
- codex/static_root/assets/admin-pnPIiLQj.b791e7869cf5.js.br +0 -0
- codex/static_root/assets/admin-pnPIiLQj.b791e7869cf5.js.gz +0 -0
- codex/static_root/assets/{admin-Bt81XU3H.js → admin-pnPIiLQj.js} +1 -1
- codex/static_root/assets/admin-pnPIiLQj.js.br +0 -0
- codex/static_root/assets/admin-pnPIiLQj.js.gz +0 -0
- codex/static_root/assets/{admin-settings-button-progress-BPQGNinn.53e05963f557.js → admin-settings-button-progress-C4VmW80I.c8d9033b8bc5.js} +1 -1
- codex/static_root/assets/admin-settings-button-progress-C4VmW80I.c8d9033b8bc5.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-C4VmW80I.c8d9033b8bc5.js.gz +0 -0
- codex/static_root/assets/{admin-settings-button-progress-BPQGNinn.js → admin-settings-button-progress-C4VmW80I.js} +1 -1
- codex/static_root/assets/admin-settings-button-progress-C4VmW80I.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-C4VmW80I.js.gz +0 -0
- codex/static_root/assets/browser-DOuRKBxy.c9a72e4f9498.js +1 -0
- codex/static_root/assets/browser-DOuRKBxy.c9a72e4f9498.js.br +0 -0
- codex/static_root/assets/browser-DOuRKBxy.c9a72e4f9498.js.gz +0 -0
- codex/static_root/assets/browser-DOuRKBxy.js +1 -0
- codex/static_root/assets/browser-DOuRKBxy.js.br +0 -0
- codex/static_root/assets/browser-DOuRKBxy.js.gz +0 -0
- codex/static_root/assets/{browser-Cdi7U5ku.bab9fccf0326.css → browser-TRzEFz_k.css} +1 -1
- codex/static_root/assets/browser-TRzEFz_k.css.br +0 -0
- codex/static_root/assets/browser-TRzEFz_k.css.gz +0 -0
- codex/static_root/assets/{browser-Cdi7U5ku.css → browser-TRzEFz_k.e8ae750c4a41.css} +1 -1
- codex/static_root/assets/browser-TRzEFz_k.e8ae750c4a41.css.br +0 -0
- codex/static_root/assets/browser-TRzEFz_k.e8ae750c4a41.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-B4tB0bVX.5b476ac6a873.js +1 -0
- codex/static_root/assets/change-password-dialog-B4tB0bVX.5b476ac6a873.js.br +0 -0
- codex/static_root/assets/change-password-dialog-B4tB0bVX.5b476ac6a873.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-B4tB0bVX.js +1 -0
- codex/static_root/assets/change-password-dialog-B4tB0bVX.js.br +0 -0
- codex/static_root/assets/change-password-dialog-B4tB0bVX.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-CgSU27R5.0127ce62d24c.js → confirm-dialog-C0DHEcnB.e2d9968f6fea.js} +1 -1
- codex/static_root/assets/confirm-dialog-C0DHEcnB.e2d9968f6fea.js.br +0 -0
- codex/static_root/assets/confirm-dialog-C0DHEcnB.e2d9968f6fea.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-CgSU27R5.js → confirm-dialog-C0DHEcnB.js} +1 -1
- codex/static_root/assets/confirm-dialog-C0DHEcnB.js.br +0 -0
- codex/static_root/assets/confirm-dialog-C0DHEcnB.js.gz +0 -0
- codex/static_root/assets/{datetime-column-Dw9zxZLR.cd8791cda91b.js → datetime-column-j_CRGEuQ.cc499deb52f7.js} +1 -1
- codex/static_root/assets/datetime-column-j_CRGEuQ.cc499deb52f7.js.br +4 -0
- codex/static_root/assets/datetime-column-j_CRGEuQ.cc499deb52f7.js.gz +0 -0
- codex/static_root/assets/{datetime-column-Dw9zxZLR.js → datetime-column-j_CRGEuQ.js} +1 -1
- codex/static_root/assets/datetime-column-j_CRGEuQ.js.br +4 -0
- codex/static_root/assets/datetime-column-j_CRGEuQ.js.gz +0 -0
- codex/static_root/assets/{filter-BNTCrH8l.c00298a8fccf.js → filter-Baea_DNH.985ef541d6af.js} +1 -1
- codex/static_root/assets/filter-Baea_DNH.985ef541d6af.js.br +0 -0
- codex/static_root/assets/filter-Baea_DNH.985ef541d6af.js.gz +0 -0
- codex/static_root/assets/{filter-BNTCrH8l.js → filter-Baea_DNH.js} +1 -1
- codex/static_root/assets/filter-Baea_DNH.js.br +0 -0
- codex/static_root/assets/filter-Baea_DNH.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Xpq_xAF5.3f14de61afcc.js → flag-tab-DefdNLmr.0cf5a73c1c8c.js} +1 -1
- codex/static_root/assets/flag-tab-DefdNLmr.0cf5a73c1c8c.js.br +0 -0
- codex/static_root/assets/flag-tab-DefdNLmr.0cf5a73c1c8c.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Xpq_xAF5.js → flag-tab-DefdNLmr.js} +1 -1
- codex/static_root/assets/flag-tab-DefdNLmr.js.br +0 -0
- codex/static_root/assets/flag-tab-DefdNLmr.js.gz +0 -0
- codex/static_root/assets/forwardRefs-Bvy6478W.409df679a82f.js +1 -0
- codex/static_root/assets/forwardRefs-Bvy6478W.409df679a82f.js.br +0 -0
- codex/static_root/assets/forwardRefs-Bvy6478W.409df679a82f.js.gz +0 -0
- codex/static_root/assets/forwardRefs-Bvy6478W.js +1 -0
- codex/static_root/assets/forwardRefs-Bvy6478W.js.br +0 -0
- codex/static_root/assets/forwardRefs-Bvy6478W.js.gz +0 -0
- codex/static_root/assets/{forwardRefs-B_sUWZn6.5114f206ca9c.css → forwardRefs-C3Z4qL85.48fb131ece85.css} +1 -1
- codex/static_root/assets/forwardRefs-C3Z4qL85.48fb131ece85.css.br +0 -0
- codex/static_root/assets/forwardRefs-C3Z4qL85.48fb131ece85.css.gz +0 -0
- codex/static_root/assets/{forwardRefs-B_sUWZn6.css → forwardRefs-C3Z4qL85.css} +1 -1
- codex/static_root/assets/forwardRefs-C3Z4qL85.css.br +0 -0
- codex/static_root/assets/forwardRefs-C3Z4qL85.css.gz +0 -0
- codex/static_root/assets/{group-tab-E_R0glEI.82e1d92be747.js → group-tab-C71MBY7x.483482eaaf8c.js} +1 -1
- codex/static_root/assets/group-tab-C71MBY7x.483482eaaf8c.js.br +0 -0
- codex/static_root/assets/group-tab-C71MBY7x.483482eaaf8c.js.gz +0 -0
- codex/static_root/assets/{group-tab-E_R0glEI.js → group-tab-C71MBY7x.js} +1 -1
- codex/static_root/assets/group-tab-C71MBY7x.js.br +0 -0
- codex/static_root/assets/group-tab-C71MBY7x.js.gz +0 -0
- codex/static_root/assets/http-error-mPrHO762.70c41cde5ee1.js +1 -0
- codex/static_root/assets/http-error-mPrHO762.70c41cde5ee1.js.br +0 -0
- codex/static_root/assets/http-error-mPrHO762.70c41cde5ee1.js.gz +0 -0
- codex/static_root/assets/http-error-mPrHO762.js +1 -0
- codex/static_root/assets/http-error-mPrHO762.js.br +0 -0
- codex/static_root/assets/http-error-mPrHO762.js.gz +0 -0
- codex/static_root/assets/{index-j9jeRHe0.294b614eaea1.js → index-Bxqss4CY.5f797cedb404.js} +1 -1
- codex/static_root/assets/index-Bxqss4CY.5f797cedb404.js.br +0 -0
- codex/static_root/assets/index-Bxqss4CY.5f797cedb404.js.gz +0 -0
- codex/static_root/assets/{index-j9jeRHe0.js → index-Bxqss4CY.js} +1 -1
- codex/static_root/assets/index-Bxqss4CY.js.br +0 -0
- codex/static_root/assets/index-Bxqss4CY.js.gz +0 -0
- codex/static_root/assets/library-tab-DG2bhQ_A.e831e11418e2.js +1 -0
- codex/static_root/assets/library-tab-DG2bhQ_A.e831e11418e2.js.br +0 -0
- codex/static_root/assets/library-tab-DG2bhQ_A.e831e11418e2.js.gz +0 -0
- codex/static_root/assets/library-tab-DG2bhQ_A.js +1 -0
- codex/static_root/assets/library-tab-DG2bhQ_A.js.br +0 -0
- codex/static_root/assets/library-tab-DG2bhQ_A.js.gz +0 -0
- codex/static_root/assets/main-RxUHaWll.d7d671319f8d.js +39 -0
- codex/static_root/assets/main-RxUHaWll.d7d671319f8d.js.br +0 -0
- codex/static_root/assets/main-RxUHaWll.d7d671319f8d.js.gz +0 -0
- codex/static_root/assets/main-RxUHaWll.js +39 -0
- codex/static_root/assets/main-RxUHaWll.js.br +0 -0
- codex/static_root/assets/main-RxUHaWll.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-DYbA0-qc.b66019cafd05.js +1 -0
- codex/static_root/assets/pager-full-pdf-DYbA0-qc.b66019cafd05.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-DYbA0-qc.b66019cafd05.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-DYbA0-qc.js +1 -0
- codex/static_root/assets/pager-full-pdf-DYbA0-qc.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-DYbA0-qc.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-ChW-jj1Q.0cf6985b630e.js +1 -0
- codex/static_root/assets/pagination-toolbar-ChW-jj1Q.0cf6985b630e.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-ChW-jj1Q.0cf6985b630e.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-ChW-jj1Q.js +1 -0
- codex/static_root/assets/pagination-toolbar-ChW-jj1Q.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-ChW-jj1Q.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-741OafNw.4f9155469274.css → pagination-toolbar-rUdD4-Qi.3a224c7806a5.css} +1 -1
- codex/static_root/assets/pagination-toolbar-rUdD4-Qi.3a224c7806a5.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-rUdD4-Qi.3a224c7806a5.css.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-741OafNw.css → pagination-toolbar-rUdD4-Qi.css} +1 -1
- codex/static_root/assets/pagination-toolbar-rUdD4-Qi.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-rUdD4-Qi.css.gz +0 -0
- codex/static_root/assets/{pdf-doc-XAg2nqnS.5a33f918a28a.js → pdf-doc-CfiAAYT8.c4fb1945d1c5.js} +1 -1
- codex/static_root/assets/pdf-doc-CfiAAYT8.c4fb1945d1c5.js.br +0 -0
- codex/static_root/assets/{pdf-doc-XAg2nqnS.5a33f918a28a.js.gz → pdf-doc-CfiAAYT8.c4fb1945d1c5.js.gz} +0 -0
- codex/static_root/assets/{pdf-doc-XAg2nqnS.js → pdf-doc-CfiAAYT8.js} +1 -1
- codex/static_root/assets/pdf-doc-CfiAAYT8.js.br +0 -0
- codex/static_root/assets/{pdf-doc-XAg2nqnS.js.gz → pdf-doc-CfiAAYT8.js.gz} +0 -0
- codex/static_root/assets/reader-BNwsMjad.78e4ad034141.js +2 -0
- codex/static_root/assets/reader-BNwsMjad.78e4ad034141.js.br +0 -0
- codex/static_root/assets/reader-BNwsMjad.78e4ad034141.js.gz +0 -0
- codex/static_root/assets/reader-BNwsMjad.js +2 -0
- codex/static_root/assets/reader-BNwsMjad.js.br +0 -0
- codex/static_root/assets/reader-BNwsMjad.js.gz +0 -0
- codex/static_root/assets/relation-chips-Cfk3FVE5.eadbc380a353.js +1 -0
- codex/static_root/assets/relation-chips-Cfk3FVE5.eadbc380a353.js.br +0 -0
- codex/static_root/assets/relation-chips-Cfk3FVE5.eadbc380a353.js.gz +0 -0
- codex/static_root/assets/relation-chips-Cfk3FVE5.js +1 -0
- codex/static_root/assets/relation-chips-Cfk3FVE5.js.br +0 -0
- codex/static_root/assets/relation-chips-Cfk3FVE5.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CcFgPQOM.0b468249b62e.css → relation-chips-CpiT_4k-.b72d78f512e6.css} +1 -1
- codex/static_root/assets/relation-chips-CpiT_4k-.b72d78f512e6.css.br +0 -0
- codex/static_root/assets/relation-chips-CpiT_4k-.b72d78f512e6.css.gz +0 -0
- codex/static_root/assets/{relation-chips-CcFgPQOM.css → relation-chips-CpiT_4k-.css} +1 -1
- codex/static_root/assets/relation-chips-CpiT_4k-.css.br +0 -0
- codex/static_root/assets/relation-chips-CpiT_4k-.css.gz +0 -0
- codex/static_root/assets/{settings-drawer-CpS_aX4Z.9d4d9533d033.css → settings-drawer-B-pwZRSy.8fc8b237bb52.css} +1 -1
- codex/static_root/assets/settings-drawer-B-pwZRSy.8fc8b237bb52.css.br +0 -0
- codex/static_root/assets/settings-drawer-B-pwZRSy.8fc8b237bb52.css.gz +0 -0
- codex/static_root/assets/{settings-drawer-CpS_aX4Z.css → settings-drawer-B-pwZRSy.css} +1 -1
- codex/static_root/assets/settings-drawer-B-pwZRSy.css.br +0 -0
- codex/static_root/assets/settings-drawer-B-pwZRSy.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BvHMmEFL.6b6d9bf0e2ad.js +2 -0
- codex/static_root/assets/settings-drawer-BvHMmEFL.6b6d9bf0e2ad.js.br +0 -0
- codex/static_root/assets/settings-drawer-BvHMmEFL.6b6d9bf0e2ad.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BvHMmEFL.js +2 -0
- codex/static_root/assets/settings-drawer-BvHMmEFL.js.br +0 -0
- codex/static_root/assets/settings-drawer-BvHMmEFL.js.gz +0 -0
- codex/static_root/assets/{ssrBoot-CoIsTnhp.ab2c4669478d.js → ssrBoot-BBuVfjys.ee6df7695a63.js} +1 -1
- codex/static_root/assets/ssrBoot-BBuVfjys.ee6df7695a63.js.br +0 -0
- codex/static_root/assets/ssrBoot-BBuVfjys.ee6df7695a63.js.gz +0 -0
- codex/static_root/assets/{ssrBoot-CoIsTnhp.js → ssrBoot-BBuVfjys.js} +1 -1
- codex/static_root/assets/ssrBoot-BBuVfjys.js.br +0 -0
- codex/static_root/assets/ssrBoot-BBuVfjys.js.gz +0 -0
- codex/static_root/assets/{stats-tab-Bncgetpc.94f90b6a111b.js → stats-tab-BW725H_T.61e34db4cc39.js} +1 -1
- codex/static_root/assets/stats-tab-BW725H_T.61e34db4cc39.js.br +0 -0
- codex/static_root/assets/stats-tab-BW725H_T.61e34db4cc39.js.gz +0 -0
- codex/static_root/assets/{stats-tab-Bncgetpc.js → stats-tab-BW725H_T.js} +1 -1
- codex/static_root/assets/stats-tab-BW725H_T.js.br +0 -0
- codex/static_root/assets/stats-tab-BW725H_T.js.gz +0 -0
- codex/static_root/assets/task-tab-P3x-M3d5.59e0479f2f1e.js +1 -0
- codex/static_root/assets/task-tab-P3x-M3d5.59e0479f2f1e.js.br +0 -0
- codex/static_root/assets/task-tab-P3x-M3d5.59e0479f2f1e.js.gz +0 -0
- codex/static_root/assets/task-tab-P3x-M3d5.js +1 -0
- codex/static_root/assets/task-tab-P3x-M3d5.js.br +0 -0
- codex/static_root/assets/task-tab-P3x-M3d5.js.gz +0 -0
- codex/static_root/assets/{unauthorized-IXAh-oLS.be484d21eb2a.js → unauthorized-CC6sHmUB.394dffd78457.js} +1 -1
- codex/static_root/assets/unauthorized-CC6sHmUB.394dffd78457.js.br +0 -0
- codex/static_root/assets/unauthorized-CC6sHmUB.394dffd78457.js.gz +0 -0
- codex/static_root/assets/{unauthorized-IXAh-oLS.js → unauthorized-CC6sHmUB.js} +1 -1
- codex/static_root/assets/unauthorized-CC6sHmUB.js.br +0 -0
- codex/static_root/assets/unauthorized-CC6sHmUB.js.gz +0 -0
- codex/static_root/assets/user-tab-OaDKeq2f.ac3d682c35f1.js +1 -0
- codex/static_root/assets/user-tab-OaDKeq2f.ac3d682c35f1.js.br +0 -0
- codex/static_root/assets/user-tab-OaDKeq2f.ac3d682c35f1.js.gz +0 -0
- codex/static_root/assets/user-tab-OaDKeq2f.js +1 -0
- codex/static_root/assets/user-tab-OaDKeq2f.js.br +0 -0
- codex/static_root/assets/user-tab-OaDKeq2f.js.gz +0 -0
- codex/static_root/{manifest.92b1b461eee2.json → manifest.d51267c3637d.json} +310 -310
- codex/static_root/manifest.d51267c3637d.json.br +0 -0
- codex/static_root/manifest.d51267c3637d.json.gz +0 -0
- codex/static_root/manifest.json +310 -310
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/views/browser/filters/filter.py +1 -1
- codex/views/browser/filters/search/field/column.py +3 -3
- codex/views/browser/filters/search/parse.py +15 -10
- {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/METADATA +5 -5
- {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/RECORD +314 -312
- codex/static_root/assets/VCheckbox-DxJUMufz.9a175cfa72c8.js +0 -1
- codex/static_root/assets/VCheckbox-DxJUMufz.9a175cfa72c8.js.br +0 -0
- codex/static_root/assets/VCheckbox-DxJUMufz.9a175cfa72c8.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DxJUMufz.js +0 -1
- codex/static_root/assets/VCheckbox-DxJUMufz.js.br +0 -0
- codex/static_root/assets/VCheckbox-DxJUMufz.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BE6yrQ80.4a42f2c068f5.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BE6yrQ80.4a42f2c068f5.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-BE6yrQ80.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-BE6yrQ80.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bfyp0szs.b51553d29c8c.js +0 -1
- codex/static_root/assets/VCombobox-Bfyp0szs.b51553d29c8c.js.br +0 -0
- codex/static_root/assets/VCombobox-Bfyp0szs.b51553d29c8c.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bfyp0szs.js +0 -1
- codex/static_root/assets/VCombobox-Bfyp0szs.js.br +0 -0
- codex/static_root/assets/VCombobox-Bfyp0szs.js.gz +0 -0
- codex/static_root/assets/VDialog-Cy6wFjb9.9253a3f54f22.js +0 -1
- codex/static_root/assets/VDialog-Cy6wFjb9.9253a3f54f22.js.br +0 -0
- codex/static_root/assets/VDialog-Cy6wFjb9.9253a3f54f22.js.gz +0 -0
- codex/static_root/assets/VDialog-Cy6wFjb9.js +0 -1
- codex/static_root/assets/VDialog-Cy6wFjb9.js.br +0 -0
- codex/static_root/assets/VDialog-Cy6wFjb9.js.gz +0 -0
- codex/static_root/assets/VDivider-BEpvozL4.c9fd95940cb5.js.br +0 -0
- codex/static_root/assets/VDivider-BEpvozL4.c9fd95940cb5.js.gz +0 -0
- codex/static_root/assets/VDivider-BEpvozL4.js.br +0 -0
- codex/static_root/assets/VDivider-BEpvozL4.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-BzyHQUL4.7e54cedf021c.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-BzyHQUL4.7e54cedf021c.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-BzyHQUL4.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-BzyHQUL4.js.gz +0 -0
- codex/static_root/assets/VForm-CmVlbmJK.887eb47d3534.js.br +0 -0
- codex/static_root/assets/VForm-CmVlbmJK.887eb47d3534.js.gz +0 -0
- codex/static_root/assets/VForm-CmVlbmJK.js.br +0 -0
- codex/static_root/assets/VForm-CmVlbmJK.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DWzKkq9W.daaabd353889.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DWzKkq9W.daaabd353889.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DWzKkq9W.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DWzKkq9W.js.gz +0 -0
- codex/static_root/assets/VSelect-Cqjdw0PV.ab01020c5ef4.js +0 -1
- codex/static_root/assets/VSelect-Cqjdw0PV.ab01020c5ef4.js.br +0 -0
- codex/static_root/assets/VSelect-Cqjdw0PV.ab01020c5ef4.js.gz +0 -0
- codex/static_root/assets/VSelect-Cqjdw0PV.js +0 -1
- codex/static_root/assets/VSelect-Cqjdw0PV.js.br +0 -0
- codex/static_root/assets/VSelect-Cqjdw0PV.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-Dk52iIPb.fe2504c50db4.js +0 -1
- codex/static_root/assets/VSelectionControl-Dk52iIPb.fe2504c50db4.js.br +0 -0
- codex/static_root/assets/VSelectionControl-Dk52iIPb.fe2504c50db4.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-Dk52iIPb.js +0 -1
- codex/static_root/assets/VSelectionControl-Dk52iIPb.js.br +0 -0
- codex/static_root/assets/VSelectionControl-Dk52iIPb.js.gz +0 -0
- codex/static_root/assets/VTable-CQyv0wyO.1740239d481b.css +0 -1
- codex/static_root/assets/VTable-CQyv0wyO.1740239d481b.css.br +0 -0
- codex/static_root/assets/VTable-CQyv0wyO.1740239d481b.css.gz +0 -0
- codex/static_root/assets/VTable-CQyv0wyO.css +0 -1
- codex/static_root/assets/VTable-CQyv0wyO.css.br +0 -0
- codex/static_root/assets/VTable-CQyv0wyO.css.gz +0 -0
- codex/static_root/assets/VTable-Ch5ZSNsk.c38a2cb589fa.js +0 -1
- codex/static_root/assets/VTable-Ch5ZSNsk.c38a2cb589fa.js.br +0 -0
- codex/static_root/assets/VTable-Ch5ZSNsk.c38a2cb589fa.js.gz +0 -0
- codex/static_root/assets/VTable-Ch5ZSNsk.js +0 -1
- codex/static_root/assets/VTable-Ch5ZSNsk.js.br +0 -0
- codex/static_root/assets/VTable-Ch5ZSNsk.js.gz +0 -0
- codex/static_root/assets/VWindowItem-ChYLiXSE.a9c679a2977e.css +0 -1
- codex/static_root/assets/VWindowItem-ChYLiXSE.a9c679a2977e.css.br +0 -0
- codex/static_root/assets/VWindowItem-ChYLiXSE.a9c679a2977e.css.gz +0 -0
- codex/static_root/assets/VWindowItem-ChYLiXSE.css +0 -1
- codex/static_root/assets/VWindowItem-ChYLiXSE.css.br +0 -0
- codex/static_root/assets/VWindowItem-ChYLiXSE.css.gz +0 -0
- codex/static_root/assets/VWindowItem-DBU8ce0X.4bbcffa75b9a.js.br +0 -0
- codex/static_root/assets/VWindowItem-DBU8ce0X.4bbcffa75b9a.js.gz +0 -0
- codex/static_root/assets/VWindowItem-DBU8ce0X.js.br +0 -0
- codex/static_root/assets/VWindowItem-DBU8ce0X.js.gz +0 -0
- codex/static_root/assets/admin-Bt81XU3H.ca36b1a1b799.js.br +0 -0
- codex/static_root/assets/admin-Bt81XU3H.ca36b1a1b799.js.gz +0 -0
- codex/static_root/assets/admin-Bt81XU3H.js.br +0 -0
- codex/static_root/assets/admin-Bt81XU3H.js.gz +0 -0
- codex/static_root/assets/admin-CfqMNEyt.bebc609836d0.js.br +0 -0
- codex/static_root/assets/admin-CfqMNEyt.bebc609836d0.js.gz +0 -0
- codex/static_root/assets/admin-CfqMNEyt.js.br +0 -0
- codex/static_root/assets/admin-CfqMNEyt.js.gz +0 -0
- codex/static_root/assets/admin-menu-Db5b3nba.09d034934b88.js +0 -1
- codex/static_root/assets/admin-menu-Db5b3nba.09d034934b88.js.br +0 -0
- codex/static_root/assets/admin-menu-Db5b3nba.09d034934b88.js.gz +0 -0
- codex/static_root/assets/admin-menu-Db5b3nba.js +0 -1
- codex/static_root/assets/admin-menu-Db5b3nba.js.br +0 -0
- codex/static_root/assets/admin-menu-Db5b3nba.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BPQGNinn.53e05963f557.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BPQGNinn.53e05963f557.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BPQGNinn.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BPQGNinn.js.gz +0 -0
- codex/static_root/assets/browser-C19hTg9r.9b5d0eadd6d4.js +0 -1
- codex/static_root/assets/browser-C19hTg9r.9b5d0eadd6d4.js.br +0 -0
- codex/static_root/assets/browser-C19hTg9r.9b5d0eadd6d4.js.gz +0 -0
- codex/static_root/assets/browser-C19hTg9r.js +0 -1
- codex/static_root/assets/browser-C19hTg9r.js.br +0 -0
- codex/static_root/assets/browser-C19hTg9r.js.gz +0 -0
- codex/static_root/assets/browser-Cdi7U5ku.bab9fccf0326.css.br +0 -0
- codex/static_root/assets/browser-Cdi7U5ku.bab9fccf0326.css.gz +0 -0
- codex/static_root/assets/browser-Cdi7U5ku.css.br +0 -0
- codex/static_root/assets/browser-Cdi7U5ku.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-DPAIPGJa.7c4c436e259e.js +0 -1
- codex/static_root/assets/change-password-dialog-DPAIPGJa.7c4c436e259e.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DPAIPGJa.7c4c436e259e.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DPAIPGJa.js +0 -1
- codex/static_root/assets/change-password-dialog-DPAIPGJa.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DPAIPGJa.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CgSU27R5.0127ce62d24c.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CgSU27R5.0127ce62d24c.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CgSU27R5.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CgSU27R5.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dw9zxZLR.cd8791cda91b.js.br +0 -0
- codex/static_root/assets/datetime-column-Dw9zxZLR.cd8791cda91b.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dw9zxZLR.js.br +0 -0
- codex/static_root/assets/datetime-column-Dw9zxZLR.js.gz +0 -0
- codex/static_root/assets/filter-BNTCrH8l.c00298a8fccf.js.br +0 -0
- codex/static_root/assets/filter-BNTCrH8l.c00298a8fccf.js.gz +0 -0
- codex/static_root/assets/filter-BNTCrH8l.js.br +0 -0
- codex/static_root/assets/filter-BNTCrH8l.js.gz +0 -0
- codex/static_root/assets/flag-tab-Xpq_xAF5.3f14de61afcc.js.br +0 -0
- codex/static_root/assets/flag-tab-Xpq_xAF5.3f14de61afcc.js.gz +0 -0
- codex/static_root/assets/flag-tab-Xpq_xAF5.js.br +0 -0
- codex/static_root/assets/flag-tab-Xpq_xAF5.js.gz +0 -0
- codex/static_root/assets/forwardRefs-B_sUWZn6.5114f206ca9c.css.br +0 -0
- codex/static_root/assets/forwardRefs-B_sUWZn6.5114f206ca9c.css.gz +0 -0
- codex/static_root/assets/forwardRefs-B_sUWZn6.css.br +0 -0
- codex/static_root/assets/forwardRefs-B_sUWZn6.css.gz +0 -0
- codex/static_root/assets/forwardRefs-fUake9mW.12c1018cd1e3.js +0 -1
- codex/static_root/assets/forwardRefs-fUake9mW.12c1018cd1e3.js.br +0 -0
- codex/static_root/assets/forwardRefs-fUake9mW.12c1018cd1e3.js.gz +0 -0
- codex/static_root/assets/forwardRefs-fUake9mW.js +0 -1
- codex/static_root/assets/forwardRefs-fUake9mW.js.br +0 -0
- codex/static_root/assets/forwardRefs-fUake9mW.js.gz +0 -0
- codex/static_root/assets/group-tab-E_R0glEI.82e1d92be747.js.br +0 -0
- codex/static_root/assets/group-tab-E_R0glEI.82e1d92be747.js.gz +0 -0
- codex/static_root/assets/group-tab-E_R0glEI.js.br +0 -0
- codex/static_root/assets/group-tab-E_R0glEI.js.gz +0 -0
- codex/static_root/assets/http-error-C6bKTzfv.168d047d5219.js +0 -1
- codex/static_root/assets/http-error-C6bKTzfv.168d047d5219.js.br +0 -5
- codex/static_root/assets/http-error-C6bKTzfv.168d047d5219.js.gz +0 -0
- codex/static_root/assets/http-error-C6bKTzfv.js +0 -1
- codex/static_root/assets/http-error-C6bKTzfv.js.br +0 -5
- codex/static_root/assets/http-error-C6bKTzfv.js.gz +0 -0
- codex/static_root/assets/index-j9jeRHe0.294b614eaea1.js.br +0 -0
- codex/static_root/assets/index-j9jeRHe0.294b614eaea1.js.gz +0 -0
- codex/static_root/assets/index-j9jeRHe0.js.br +0 -0
- codex/static_root/assets/index-j9jeRHe0.js.gz +0 -0
- codex/static_root/assets/library-tab-DPYjRIWp.58e434354594.js +0 -1
- codex/static_root/assets/library-tab-DPYjRIWp.58e434354594.js.br +0 -0
- codex/static_root/assets/library-tab-DPYjRIWp.58e434354594.js.gz +0 -0
- codex/static_root/assets/library-tab-DPYjRIWp.js +0 -1
- codex/static_root/assets/library-tab-DPYjRIWp.js.br +0 -0
- codex/static_root/assets/library-tab-DPYjRIWp.js.gz +0 -0
- codex/static_root/assets/main-Cnq2ldv_.1c0766500281.js +0 -39
- codex/static_root/assets/main-Cnq2ldv_.1c0766500281.js.br +0 -0
- codex/static_root/assets/main-Cnq2ldv_.1c0766500281.js.gz +0 -0
- codex/static_root/assets/main-Cnq2ldv_.js +0 -39
- codex/static_root/assets/main-Cnq2ldv_.js.br +0 -0
- codex/static_root/assets/main-Cnq2ldv_.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-CdHAL6P3.cb6bc541ca5c.js +0 -1
- codex/static_root/assets/pager-full-pdf-CdHAL6P3.cb6bc541ca5c.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-CdHAL6P3.cb6bc541ca5c.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-CdHAL6P3.js +0 -1
- codex/static_root/assets/pager-full-pdf-CdHAL6P3.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-CdHAL6P3.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-741OafNw.4f9155469274.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-741OafNw.4f9155469274.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-741OafNw.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-741OafNw.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BFBKOWOc.3355103910ff.js +0 -1
- codex/static_root/assets/pagination-toolbar-BFBKOWOc.3355103910ff.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-BFBKOWOc.3355103910ff.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BFBKOWOc.js +0 -1
- codex/static_root/assets/pagination-toolbar-BFBKOWOc.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-BFBKOWOc.js.gz +0 -0
- codex/static_root/assets/pdf-doc-XAg2nqnS.5a33f918a28a.js.br +0 -0
- codex/static_root/assets/pdf-doc-XAg2nqnS.js.br +0 -0
- codex/static_root/assets/reader-BZ4Dp-_5.ecd9d42077af.js +0 -2
- codex/static_root/assets/reader-BZ4Dp-_5.ecd9d42077af.js.br +0 -0
- codex/static_root/assets/reader-BZ4Dp-_5.ecd9d42077af.js.gz +0 -0
- codex/static_root/assets/reader-BZ4Dp-_5.js +0 -2
- codex/static_root/assets/reader-BZ4Dp-_5.js.br +0 -0
- codex/static_root/assets/reader-BZ4Dp-_5.js.gz +0 -0
- codex/static_root/assets/relation-chips-B_z9ixOK.8f144f1cf119.js +0 -1
- codex/static_root/assets/relation-chips-B_z9ixOK.8f144f1cf119.js.br +0 -0
- codex/static_root/assets/relation-chips-B_z9ixOK.8f144f1cf119.js.gz +0 -0
- codex/static_root/assets/relation-chips-B_z9ixOK.js +0 -1
- codex/static_root/assets/relation-chips-B_z9ixOK.js.br +0 -0
- codex/static_root/assets/relation-chips-B_z9ixOK.js.gz +0 -0
- codex/static_root/assets/relation-chips-CcFgPQOM.0b468249b62e.css.br +0 -0
- codex/static_root/assets/relation-chips-CcFgPQOM.0b468249b62e.css.gz +0 -0
- codex/static_root/assets/relation-chips-CcFgPQOM.css.br +0 -0
- codex/static_root/assets/relation-chips-CcFgPQOM.css.gz +0 -0
- codex/static_root/assets/settings-drawer-Cd3M0s-h.c5216209feb4.js +0 -2
- codex/static_root/assets/settings-drawer-Cd3M0s-h.c5216209feb4.js.br +0 -0
- codex/static_root/assets/settings-drawer-Cd3M0s-h.c5216209feb4.js.gz +0 -0
- codex/static_root/assets/settings-drawer-Cd3M0s-h.js +0 -2
- codex/static_root/assets/settings-drawer-Cd3M0s-h.js.br +0 -0
- codex/static_root/assets/settings-drawer-Cd3M0s-h.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CpS_aX4Z.9d4d9533d033.css.br +0 -0
- codex/static_root/assets/settings-drawer-CpS_aX4Z.9d4d9533d033.css.gz +0 -0
- codex/static_root/assets/settings-drawer-CpS_aX4Z.css.br +0 -0
- codex/static_root/assets/settings-drawer-CpS_aX4Z.css.gz +0 -0
- codex/static_root/assets/ssrBoot-CoIsTnhp.ab2c4669478d.js.br +0 -0
- codex/static_root/assets/ssrBoot-CoIsTnhp.ab2c4669478d.js.gz +0 -0
- codex/static_root/assets/ssrBoot-CoIsTnhp.js.br +0 -0
- codex/static_root/assets/ssrBoot-CoIsTnhp.js.gz +0 -0
- codex/static_root/assets/stats-tab-Bncgetpc.94f90b6a111b.js.br +0 -0
- codex/static_root/assets/stats-tab-Bncgetpc.94f90b6a111b.js.gz +0 -0
- codex/static_root/assets/stats-tab-Bncgetpc.js.br +0 -0
- codex/static_root/assets/stats-tab-Bncgetpc.js.gz +0 -0
- codex/static_root/assets/task-tab-BWkw24-u.0e981a767773.js +0 -1
- codex/static_root/assets/task-tab-BWkw24-u.0e981a767773.js.br +0 -0
- codex/static_root/assets/task-tab-BWkw24-u.0e981a767773.js.gz +0 -0
- codex/static_root/assets/task-tab-BWkw24-u.js +0 -1
- codex/static_root/assets/task-tab-BWkw24-u.js.br +0 -0
- codex/static_root/assets/task-tab-BWkw24-u.js.gz +0 -0
- codex/static_root/assets/unauthorized-IXAh-oLS.be484d21eb2a.js.br +0 -0
- codex/static_root/assets/unauthorized-IXAh-oLS.be484d21eb2a.js.gz +0 -0
- codex/static_root/assets/unauthorized-IXAh-oLS.js.br +0 -0
- codex/static_root/assets/unauthorized-IXAh-oLS.js.gz +0 -0
- codex/static_root/assets/user-tab-CtQh6PJE.6b9a1d0e733a.js +0 -1
- codex/static_root/assets/user-tab-CtQh6PJE.6b9a1d0e733a.js.br +0 -0
- codex/static_root/assets/user-tab-CtQh6PJE.6b9a1d0e733a.js.gz +0 -0
- codex/static_root/assets/user-tab-CtQh6PJE.js +0 -1
- codex/static_root/assets/user-tab-CtQh6PJE.js.br +0 -0
- codex/static_root/assets/user-tab-CtQh6PJE.js.gz +0 -0
- codex/static_root/manifest.92b1b461eee2.json.br +0 -0
- codex/static_root/manifest.92b1b461eee2.json.gz +0 -0
- /codex/librarian/{watchdog/memory.py → memory.py} +0 -0
- {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/WHEEL +0 -0
- {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/entry_points.txt +0 -0
- {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""Search Index cleanup."""
|
|
2
2
|
|
|
3
|
+
from django.db.models import F, Max
|
|
4
|
+
|
|
3
5
|
from codex.librarian.scribe.search.optimize import SearchIndexerOptimize
|
|
4
6
|
from codex.librarian.scribe.search.status import (
|
|
5
7
|
SearchIndexCleanStatus,
|
|
6
8
|
SearchIndexClearStatus,
|
|
7
9
|
)
|
|
8
|
-
from codex.models.comic import ComicFTS
|
|
10
|
+
from codex.models.comic import Comic, ComicFTS
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class SearchIndexerRemove(SearchIndexerOptimize):
|
|
@@ -22,7 +24,9 @@ class SearchIndexerRemove(SearchIndexerOptimize):
|
|
|
22
24
|
"""Remove records not in the database from the index."""
|
|
23
25
|
self.status_controller.start(status)
|
|
24
26
|
self.log.debug("Finding stale records to remove...")
|
|
25
|
-
delete_comicfts = ComicFTS.objects.
|
|
27
|
+
delete_comicfts = ComicFTS.objects.exclude(
|
|
28
|
+
comic_id__in=Comic.objects.only("pk")
|
|
29
|
+
)
|
|
26
30
|
status.total = delete_comicfts.count()
|
|
27
31
|
self.status_controller.update(status)
|
|
28
32
|
if status.total:
|
|
@@ -42,3 +46,19 @@ class SearchIndexerRemove(SearchIndexerOptimize):
|
|
|
42
46
|
finally:
|
|
43
47
|
self.status_controller.finish(status)
|
|
44
48
|
return count
|
|
49
|
+
|
|
50
|
+
def remove_duplicate_records(self) -> int:
|
|
51
|
+
"""Remove duplicate FTS records."""
|
|
52
|
+
self.log.debug("Looking for duplicate search entries...")
|
|
53
|
+
duplicates = ComicFTS.objects.annotate(max_updated_at=Max("updated_at")).filter(
|
|
54
|
+
updated_at__lt=F("max_updated_at")
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
num_dupes = duplicates.count()
|
|
58
|
+
self.log.debug(f"Found {num_dupes} duplicate search entries")
|
|
59
|
+
count = num_dupes
|
|
60
|
+
if num_dupes:
|
|
61
|
+
count, _ = duplicates.delete()
|
|
62
|
+
if count:
|
|
63
|
+
self.log.info(f"Deleted {count} duplicate search entries")
|
|
64
|
+
return count
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
"""Search Index update."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
+
from math import floor
|
|
4
5
|
from time import time
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
5
7
|
from zoneinfo import ZoneInfo
|
|
6
8
|
|
|
7
|
-
from
|
|
9
|
+
from django.db.models import Q
|
|
8
10
|
from django.db.models.aggregates import Max
|
|
9
11
|
from django.db.models.expressions import F, Value
|
|
10
12
|
from django.db.models.functions import Concat
|
|
11
|
-
from django.db.models.functions.datetime import Now
|
|
12
13
|
from django.db.models.query import QuerySet
|
|
13
14
|
from humanize import intcomma, naturaldelta
|
|
14
15
|
|
|
16
|
+
from codex.librarian.memory import get_mem_limit
|
|
17
|
+
from codex.librarian.scribe.search.const import COMICFTS_UPDATE_FIELDS
|
|
18
|
+
from codex.librarian.scribe.search.prepare import SearchEntryPrepare
|
|
15
19
|
from codex.librarian.scribe.search.remove import SearchIndexerRemove
|
|
16
20
|
from codex.librarian.scribe.search.status import (
|
|
17
21
|
SEARCH_INDEX_STATII,
|
|
@@ -20,56 +24,16 @@ from codex.librarian.scribe.search.status import (
|
|
|
20
24
|
SearchIndexSyncCreateStatus,
|
|
21
25
|
SearchIndexSyncUpdateStatus,
|
|
22
26
|
)
|
|
23
|
-
from codex.librarian.status import Status
|
|
24
27
|
from codex.models import Comic
|
|
25
28
|
from codex.models.comic import ComicFTS
|
|
26
29
|
from codex.models.functions import GroupConcat
|
|
27
|
-
from codex.
|
|
28
|
-
|
|
30
|
+
from codex.settings import SEARCH_SYNC_BATCH_MEMORY_RATIO
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from codex.librarian.status import Status
|
|
29
34
|
|
|
30
|
-
_COMICFTS_ATTRIBUTES = (
|
|
31
|
-
"collection_title",
|
|
32
|
-
"file_type",
|
|
33
|
-
"issue",
|
|
34
|
-
"name",
|
|
35
|
-
"notes",
|
|
36
|
-
"reading_direction",
|
|
37
|
-
"review",
|
|
38
|
-
"summary",
|
|
39
|
-
"updated_at",
|
|
40
|
-
)
|
|
41
|
-
_COMICFTS_FKS = (
|
|
42
|
-
"publisher",
|
|
43
|
-
"imprint",
|
|
44
|
-
"series",
|
|
45
|
-
"age_rating",
|
|
46
|
-
"country",
|
|
47
|
-
"language",
|
|
48
|
-
"original_format",
|
|
49
|
-
"scan_info",
|
|
50
|
-
"tagger",
|
|
51
|
-
)
|
|
52
|
-
_COMICFTS_M2MS = (
|
|
53
|
-
"characters",
|
|
54
|
-
"credits",
|
|
55
|
-
"genres",
|
|
56
|
-
"identifiers",
|
|
57
|
-
"locations",
|
|
58
|
-
"series_groups",
|
|
59
|
-
"sources",
|
|
60
|
-
"stories",
|
|
61
|
-
"story_arcs",
|
|
62
|
-
"tags",
|
|
63
|
-
"teams",
|
|
64
|
-
"universes",
|
|
65
|
-
)
|
|
66
|
-
_COMICFTS_UPDATE_FIELDS = (
|
|
67
|
-
*_COMICFTS_ATTRIBUTES,
|
|
68
|
-
*_COMICFTS_FKS,
|
|
69
|
-
*_COMICFTS_M2MS,
|
|
70
|
-
)
|
|
71
35
|
_MIN_UTC_DATE = datetime.min.replace(tzinfo=ZoneInfo("UTC"))
|
|
72
|
-
|
|
36
|
+
_ALL_FTS_COMIC_IDS_QUERY = Q(pk__in=ComicFTS.objects.values_list("comic_id", flat=True))
|
|
73
37
|
|
|
74
38
|
|
|
75
39
|
class SearchIndexerSync(SearchIndexerRemove):
|
|
@@ -167,22 +131,14 @@ class SearchIndexerSync(SearchIndexerRemove):
|
|
|
167
131
|
),
|
|
168
132
|
)
|
|
169
133
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
names = []
|
|
179
|
-
if not sources:
|
|
180
|
-
return None
|
|
181
|
-
for source in sources.split(","):
|
|
182
|
-
names.append(source)
|
|
183
|
-
if long_name := ID_SOURCE_NAME_MAP.get(source):
|
|
184
|
-
names.append(long_name)
|
|
185
|
-
return ",".join(names)
|
|
134
|
+
def _update_search_index_operate_get_status(
|
|
135
|
+
self, total_comics: int, chunk_human_size: str, *, create: bool
|
|
136
|
+
):
|
|
137
|
+
status_class = (
|
|
138
|
+
SearchIndexSyncCreateStatus if create else SearchIndexSyncUpdateStatus
|
|
139
|
+
)
|
|
140
|
+
subtitle = f"Chunks of {chunk_human_size}" if total_comics else ""
|
|
141
|
+
return status_class(total=total_comics, subtitle=subtitle)
|
|
186
142
|
|
|
187
143
|
def _update_search_index_create_or_update(
|
|
188
144
|
self,
|
|
@@ -202,133 +158,75 @@ class SearchIndexerSync(SearchIndexerRemove):
|
|
|
202
158
|
if create:
|
|
203
159
|
ComicFTS.objects.bulk_create(obj_list)
|
|
204
160
|
else:
|
|
205
|
-
ComicFTS.objects.bulk_update(obj_list,
|
|
206
|
-
obj_list.clear()
|
|
207
|
-
|
|
208
|
-
def _create_comicfts_entry(
|
|
209
|
-
self,
|
|
210
|
-
comic,
|
|
211
|
-
obj_list: list[ComicFTS],
|
|
212
|
-
status: Status,
|
|
213
|
-
*,
|
|
214
|
-
create: bool,
|
|
215
|
-
):
|
|
216
|
-
country_field: PyCountryField = CountryField() # pyright: ignore[reportAssignmentType]
|
|
217
|
-
language_field: PyCountryField = LanguageField() # pyright: ignore[reportAssignmentType]
|
|
218
|
-
|
|
219
|
-
country = self._get_pycountry_fts_field(country_field, comic.fts_country)
|
|
220
|
-
language = self._get_pycountry_fts_field(language_field, comic.fts_language)
|
|
221
|
-
sources = self._get_sources_fts_field(comic.fts_sources)
|
|
222
|
-
|
|
223
|
-
now = Now()
|
|
224
|
-
comicfts = ComicFTS(
|
|
225
|
-
comic_id=comic.pk,
|
|
226
|
-
# Attributes
|
|
227
|
-
collection_title=comic.collection_title,
|
|
228
|
-
file_type=comic.file_type,
|
|
229
|
-
issue=comic.issue(),
|
|
230
|
-
name=comic.name,
|
|
231
|
-
notes=comic.notes,
|
|
232
|
-
reading_direction=comic.reading_direction,
|
|
233
|
-
review=comic.review,
|
|
234
|
-
summary=comic.summary,
|
|
235
|
-
updated_at=now,
|
|
236
|
-
# Group FKs
|
|
237
|
-
publisher=comic.fts_publisher,
|
|
238
|
-
imprint=comic.fts_imprint,
|
|
239
|
-
series=comic.fts_series,
|
|
240
|
-
# FKS
|
|
241
|
-
age_rating=comic.fts_age_rating,
|
|
242
|
-
country=country,
|
|
243
|
-
language=language,
|
|
244
|
-
original_format=comic.fts_original_format,
|
|
245
|
-
scan_info=comic.fts_scan_info,
|
|
246
|
-
tagger=comic.fts_tagger,
|
|
247
|
-
# ManyToMany
|
|
248
|
-
characters=comic.fts_characters,
|
|
249
|
-
credits=comic.fts_credits,
|
|
250
|
-
genres=comic.fts_genres,
|
|
251
|
-
identifiers=comic.fts_identifiers,
|
|
252
|
-
sources=sources, # comic.fts_sources,
|
|
253
|
-
locations=comic.fts_locations,
|
|
254
|
-
series_groups=comic.fts_series_groups,
|
|
255
|
-
stories=comic.fts_stories,
|
|
256
|
-
story_arcs=comic.fts_story_arcs,
|
|
257
|
-
tags=comic.fts_tags,
|
|
258
|
-
teams=comic.fts_teams,
|
|
259
|
-
universes=comic.fts_universes,
|
|
260
|
-
)
|
|
261
|
-
if create:
|
|
262
|
-
comicfts.created_at = now
|
|
161
|
+
ComicFTS.objects.bulk_update(obj_list, COMICFTS_UPDATE_FIELDS)
|
|
263
162
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
self.status_controller.update(status)
|
|
163
|
+
status.increment_complete(num_comic_fts)
|
|
164
|
+
self.status_controller.update(status, notify=True)
|
|
267
165
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
)
|
|
274
|
-
subtitle = f"Chunks of {_CHUNK_HUMAN_SIZE}" if total_comics else ""
|
|
275
|
-
return status_class(total=total_comics, subtitle=subtitle)
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _get_operation_comics_query(qs, *, create: bool):
|
|
168
|
+
if create and not ComicFTS.objects.exists():
|
|
169
|
+
qs = Comic.objects.all()
|
|
170
|
+
return qs
|
|
276
171
|
|
|
277
172
|
def _update_search_index_operate(
|
|
278
173
|
self, comics_filtered_qs: QuerySet, *, create: bool
|
|
279
174
|
):
|
|
280
|
-
|
|
175
|
+
# Smaller systems may run out of virtual memory unless this is auto governed.
|
|
176
|
+
mem_limit_gb = get_mem_limit("g")
|
|
177
|
+
search_index_batch_size = floor(
|
|
178
|
+
(mem_limit_gb / SEARCH_SYNC_BATCH_MEMORY_RATIO) * 1000
|
|
179
|
+
)
|
|
180
|
+
chunk_human_size = intcomma(search_index_batch_size)
|
|
281
181
|
|
|
282
182
|
verb = "create" if create else "update"
|
|
183
|
+
self.log.debug(f"Counting total search index entries to {verb}...")
|
|
184
|
+
total_comics = self._get_operation_comics_query(
|
|
185
|
+
comics_filtered_qs, create=create
|
|
186
|
+
)
|
|
187
|
+
total_comics = total_comics.count()
|
|
283
188
|
status = self._update_search_index_operate_get_status(
|
|
284
|
-
total_comics, create=create
|
|
189
|
+
total_comics, chunk_human_size, create=create
|
|
285
190
|
)
|
|
286
191
|
|
|
287
192
|
try:
|
|
288
|
-
if total_comics:
|
|
289
|
-
reason = (
|
|
290
|
-
f"Batching this search engine {verb} operation in to chunks of {_CHUNK_HUMAN_SIZE}."
|
|
291
|
-
f" Search engine {verb}s run much faster as one large batch but then there's no progress updates."
|
|
292
|
-
" You may adjust the batch size with the environment variable CODEX_SEARCH_INDEX_BATCH_SIZE."
|
|
293
|
-
)
|
|
294
|
-
else:
|
|
295
|
-
reason = f"No search entries to {verb}."
|
|
296
|
-
self.log.debug(reason)
|
|
297
193
|
if not total_comics:
|
|
194
|
+
self.log.debug(f"No search entries to {verb}.")
|
|
298
195
|
return total_comics
|
|
299
196
|
|
|
300
|
-
self.status_controller.start(status)
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
comics = comics.order_by("pk")
|
|
305
|
-
|
|
306
|
-
obj_list = []
|
|
307
|
-
prep_num = min(SEARCH_INDEX_BATCH_SIZE, total_comics)
|
|
308
|
-
self.log.debug(f"Preparing {prep_num} comics for search indexing...")
|
|
309
|
-
for comic in comics.iterator(chunk_size=SEARCH_INDEX_BATCH_SIZE):
|
|
197
|
+
self.status_controller.start(status, notify=True)
|
|
198
|
+
start = 0
|
|
199
|
+
while start < total_comics:
|
|
200
|
+
obj_list = []
|
|
310
201
|
if self.abort_event.is_set():
|
|
311
202
|
break
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
203
|
+
# Not using standard iterator chunking to control memory and really
|
|
204
|
+
# do this in batches.
|
|
205
|
+
self.log.debug(
|
|
206
|
+
f"Preparing up to {chunk_human_size} comics for search indexing..."
|
|
207
|
+
)
|
|
208
|
+
# This query is supposed to get only comics that don't need creating but it doesn't
|
|
209
|
+
# So the start total exit assists it with that.
|
|
210
|
+
comics = self._get_operation_comics_query(
|
|
211
|
+
comics_filtered_qs, create=create
|
|
212
|
+
)
|
|
213
|
+
comics = self._prefetch_related_fts_query(comics)
|
|
214
|
+
comics = comics.order_by("pk")
|
|
215
|
+
comics = comics[:search_index_batch_size]
|
|
216
|
+
comics = self._annotate_fts_query(comics)
|
|
217
|
+
for comic in comics.values():
|
|
218
|
+
SearchEntryPrepare.prepare_sync_fts_entry(
|
|
219
|
+
comic, obj_list, create=create
|
|
318
220
|
)
|
|
319
|
-
complete = status.complete or 0
|
|
320
|
-
if prep_num := min(
|
|
321
|
-
SEARCH_INDEX_BATCH_SIZE, total_comics - complete
|
|
322
|
-
):
|
|
323
|
-
self.log.debug(
|
|
324
|
-
f"Preparing {prep_num} comics for search indexing..."
|
|
325
|
-
)
|
|
326
221
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
222
|
+
if not obj_list:
|
|
223
|
+
break
|
|
224
|
+
self._update_search_index_create_or_update(
|
|
225
|
+
obj_list,
|
|
226
|
+
status,
|
|
227
|
+
create=create,
|
|
228
|
+
)
|
|
229
|
+
start += search_index_batch_size
|
|
332
230
|
|
|
333
231
|
finally:
|
|
334
232
|
self.status_controller.finish(status)
|
|
@@ -336,25 +234,36 @@ class SearchIndexerSync(SearchIndexerRemove):
|
|
|
336
234
|
|
|
337
235
|
def _update_search_index_update(self):
|
|
338
236
|
"""Update out of date search entries."""
|
|
237
|
+
out_of_date_comics = Comic.objects.filter(_ALL_FTS_COMIC_IDS_QUERY)
|
|
238
|
+
self.log.debug("Looking for search index watermark...")
|
|
339
239
|
fts_watermark = ComicFTS.objects.aggregate(max=Max("updated_at"))["max"]
|
|
340
|
-
|
|
240
|
+
if fts_watermark:
|
|
241
|
+
since = fts_watermark
|
|
242
|
+
out_of_date_comics = out_of_date_comics.filter(updated_at__gt=fts_watermark)
|
|
243
|
+
else:
|
|
244
|
+
since = "the fracturing of the multiverse"
|
|
245
|
+
fts_watermark = fts_watermark or _MIN_UTC_DATE
|
|
341
246
|
self.log.info(f"Looking for search entries to update since {since}...")
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
.exclude(comicfts__isnull=True)
|
|
346
|
-
.filter(updated_at__gt=fts_watermark)
|
|
347
|
-
)
|
|
247
|
+
count = out_of_date_comics.count()
|
|
248
|
+
self.log.debug(f"Found {count} comics with out of date search entries.")
|
|
249
|
+
|
|
348
250
|
return self._update_search_index_operate(out_of_date_comics, create=False)
|
|
349
251
|
|
|
350
252
|
def _update_search_index_create(self):
|
|
351
253
|
"""Create missing search entries."""
|
|
352
254
|
self.log.info("Looking for missing search entries to create...")
|
|
353
|
-
missing_comics = Comic.objects.
|
|
255
|
+
missing_comics = Comic.objects.all()
|
|
256
|
+
count = None if ComicFTS.objects.exists() else missing_comics.count()
|
|
257
|
+
missing_comics = missing_comics.exclude(_ALL_FTS_COMIC_IDS_QUERY)
|
|
258
|
+
if count is None:
|
|
259
|
+
count = missing_comics.count()
|
|
260
|
+
|
|
261
|
+
self.log.debug(f"Found {count} comics missing from the search index.")
|
|
354
262
|
return self._update_search_index_operate(missing_comics, create=True)
|
|
355
263
|
|
|
356
264
|
def _update_search_index(self, *, rebuild: bool):
|
|
357
265
|
"""Update or Rebuild the search index."""
|
|
266
|
+
self.log.debug("In update search index before init statii.")
|
|
358
267
|
start_time = time()
|
|
359
268
|
self._init_statuses(rebuild)
|
|
360
269
|
|
|
@@ -111,6 +111,29 @@ class StatusController:
|
|
|
111
111
|
return
|
|
112
112
|
self._update(status, notify=notify)
|
|
113
113
|
|
|
114
|
+
@staticmethod
|
|
115
|
+
def _finish_status_prepare(
|
|
116
|
+
positive_statii: MappingProxyType[str, Status | type[Status]],
|
|
117
|
+
updates: dict[str, Any],
|
|
118
|
+
):
|
|
119
|
+
# Filter all active or preactive statii
|
|
120
|
+
ls_filter = Q(active__isnull=False) | Q(preactive__isnull=False)
|
|
121
|
+
if positive_statii:
|
|
122
|
+
ls_filter_dict: dict[str, Any] = {"status_type__in": positive_statii.keys()}
|
|
123
|
+
# Filter on status codes
|
|
124
|
+
ls_filter &= Q(**ls_filter_dict)
|
|
125
|
+
lses = LibrarianStatus.objects.filter(ls_filter)
|
|
126
|
+
update_ls = []
|
|
127
|
+
log_statii = []
|
|
128
|
+
for ls in lses.iterator():
|
|
129
|
+
for key, value in updates.items():
|
|
130
|
+
setattr(ls, key, value)
|
|
131
|
+
update_ls.append(ls)
|
|
132
|
+
status = positive_statii[ls.status_type]
|
|
133
|
+
if isinstance(status, Status):
|
|
134
|
+
log_statii.append(status)
|
|
135
|
+
return update_ls, log_statii
|
|
136
|
+
|
|
114
137
|
def _log_finish(self, status: Status, *, clear_subtitle: bool):
|
|
115
138
|
"""Log finish of status with stats."""
|
|
116
139
|
level = "INFO"
|
|
@@ -140,31 +163,6 @@ class StatusController:
|
|
|
140
163
|
|
|
141
164
|
self.log.log(level, f"{prefix}{suffix}.")
|
|
142
165
|
|
|
143
|
-
@staticmethod
|
|
144
|
-
def _finish_status_prepare(
|
|
145
|
-
positive_statii: MappingProxyType[str, Status | type[Status]],
|
|
146
|
-
updates: dict[str, Any],
|
|
147
|
-
):
|
|
148
|
-
# Filter on submitted codes or all statii if nothing.
|
|
149
|
-
ls_filter_dict: dict[str, Any] = (
|
|
150
|
-
{"status_type__in": positive_statii.keys()} if positive_statii else {}
|
|
151
|
-
)
|
|
152
|
-
# Filter all active or preactive statii
|
|
153
|
-
ls_filter = (Q(active__isnull=False) | Q(preactive__isnull=False)) & Q(
|
|
154
|
-
**ls_filter_dict
|
|
155
|
-
)
|
|
156
|
-
lses = LibrarianStatus.objects.filter(ls_filter)
|
|
157
|
-
update_ls = []
|
|
158
|
-
log_statii = []
|
|
159
|
-
for ls in lses.iterator():
|
|
160
|
-
for key, value in updates.items():
|
|
161
|
-
setattr(ls, key, value)
|
|
162
|
-
update_ls.append(ls)
|
|
163
|
-
status = positive_statii[ls.status_type]
|
|
164
|
-
if isinstance(status, Status):
|
|
165
|
-
log_statii.append(status)
|
|
166
|
-
return update_ls, log_statii
|
|
167
|
-
|
|
168
166
|
def finish_many(
|
|
169
167
|
self,
|
|
170
168
|
statii: Iterable[Status | type[Status] | None],
|
|
@@ -191,8 +189,8 @@ class StatusController:
|
|
|
191
189
|
self.log.info("Cleared all librarian statuses")
|
|
192
190
|
for status in log_statii:
|
|
193
191
|
self._log_finish(status, clear_subtitle=clear_subtitle)
|
|
194
|
-
except Exception
|
|
195
|
-
self.log.
|
|
192
|
+
except Exception:
|
|
193
|
+
self.log.exception(f"Finish status {positive_statii}")
|
|
196
194
|
|
|
197
195
|
def finish(
|
|
198
196
|
self, status: Status | None, *, notify: bool = True, clear_subtitle=True
|
|
@@ -14,6 +14,7 @@ from types import MappingProxyType
|
|
|
14
14
|
from typing_extensions import override
|
|
15
15
|
from watchdog.events import EVENT_TYPE_MOVED, FileSystemEvent
|
|
16
16
|
|
|
17
|
+
from codex.librarian.memory import get_mem_limit
|
|
17
18
|
from codex.librarian.scribe.importer.tasks import ImportTask
|
|
18
19
|
from codex.librarian.threads import AggregateMessageQueuedThread
|
|
19
20
|
from codex.librarian.watchdog.const import EVENT_CLASS_DIFF_ALL_MAP
|
|
@@ -21,7 +22,6 @@ from codex.librarian.watchdog.events import (
|
|
|
21
22
|
EVENT_TYPE_FINISH_POLL,
|
|
22
23
|
EVENT_TYPE_START_POLL,
|
|
23
24
|
)
|
|
24
|
-
from codex.librarian.watchdog.memory import get_mem_limit
|
|
25
25
|
|
|
26
26
|
_IMPORT_TASK_PARAMS = MappingProxyType(
|
|
27
27
|
{
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Generated by Django 5.2.4 on 2025-07-21 15:05."""
|
|
2
|
+
|
|
3
|
+
from itertools import chain
|
|
4
|
+
|
|
5
|
+
import django.db.models.deletion
|
|
6
|
+
from comicbox.enums.maps.identifiers import ID_SOURCE_NAME_MAP, get_id_source_by_alias
|
|
7
|
+
from django.db import migrations, models
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _map_identifiers_to_canonical_names(apps):
|
|
11
|
+
id_source_model = apps.get_model("codex", "identifiersource")
|
|
12
|
+
identifier_source_map = {}
|
|
13
|
+
|
|
14
|
+
for source in id_source_model.objects.all():
|
|
15
|
+
if id_source_enum := get_id_source_by_alias(source.name, default=None):
|
|
16
|
+
canon_name = ID_SOURCE_NAME_MAP.get(id_source_enum, source.name)
|
|
17
|
+
if canon_name != source.name:
|
|
18
|
+
if canon_name not in identifier_source_map:
|
|
19
|
+
identifier_source_map[canon_name] = set()
|
|
20
|
+
identifier_source_map[canon_name] |= set(
|
|
21
|
+
source.identifier_set.values_list("pk", flat=True)
|
|
22
|
+
)
|
|
23
|
+
return identifier_source_map
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _prepare_canonical_id_sources(apps, identifier_source_map):
|
|
27
|
+
id_source_model = apps.get_model("codex", "identifiersource")
|
|
28
|
+
source_names = identifier_source_map.keys()
|
|
29
|
+
existing_id_sources = id_source_model.objects.filter(name__in=source_names).only(
|
|
30
|
+
"name"
|
|
31
|
+
)
|
|
32
|
+
existing_id_source_names = id_source_model.objects.filter(
|
|
33
|
+
name__in=source_names
|
|
34
|
+
).values_list("name", flat=True)
|
|
35
|
+
create_id_source_names = sorted(
|
|
36
|
+
frozenset(set(identifier_source_map.keys()) - set(existing_id_source_names))
|
|
37
|
+
)
|
|
38
|
+
return tuple(
|
|
39
|
+
id_source_model(name=name) for name in create_id_source_names
|
|
40
|
+
), existing_id_sources
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _create_link_map(identifier_source_map, sources):
|
|
44
|
+
link_map = {}
|
|
45
|
+
for id_source in chain(*sources):
|
|
46
|
+
for id_pk in identifier_source_map[id_source.name]:
|
|
47
|
+
link_map[id_pk] = id_source
|
|
48
|
+
return link_map
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _prepare_updatatable_identifiers(apps, identifier_source_map, sources):
|
|
52
|
+
link_map = _create_link_map(identifier_source_map, sources)
|
|
53
|
+
identifier_model = apps.get_model("codex", "identifier")
|
|
54
|
+
updateable_identifiers = identifier_model.objects.filter(pk__in=link_map.keys())
|
|
55
|
+
obj_list = []
|
|
56
|
+
for identifier in updateable_identifiers:
|
|
57
|
+
identifier.source = link_map[identifier.pk]
|
|
58
|
+
obj_list.append(identifier)
|
|
59
|
+
return tuple(obj_list)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _create_canonical_sources(apps):
|
|
63
|
+
print("Examining identifier sources for conversion to canonical sources...")
|
|
64
|
+
identifier_source_map = _map_identifiers_to_canonical_names(apps)
|
|
65
|
+
print("Preparing missing canonical identifiers sources for creation...")
|
|
66
|
+
obj_list, existing_id_sources = _prepare_canonical_id_sources(
|
|
67
|
+
apps, identifier_source_map
|
|
68
|
+
)
|
|
69
|
+
id_source_model = apps.get_model("codex", "identifiersource")
|
|
70
|
+
created_id_sources = id_source_model.objects.bulk_create(obj_list)
|
|
71
|
+
count = len(obj_list)
|
|
72
|
+
print(f"Created {count} canonical identifier sources")
|
|
73
|
+
return identifier_source_map, (existing_id_sources, created_id_sources)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _update_identifiers_with_canonical_sources(apps, identifier_source_map, sources):
|
|
77
|
+
print("Preparing identifiers for update with canonical sources...")
|
|
78
|
+
obj_list = _prepare_updatatable_identifiers(apps, identifier_source_map, sources)
|
|
79
|
+
identifier_model = apps.get_model("codex", "identifier")
|
|
80
|
+
identifier_model.objects.bulk_update(obj_list, fields=("source",))
|
|
81
|
+
count = len(obj_list)
|
|
82
|
+
print(f"Updated {count} identifiers with canonical sources.")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _convert_identifier_sources(apps, _schema_editor):
|
|
86
|
+
identifier_source_map, sources = _create_canonical_sources(apps)
|
|
87
|
+
_update_identifiers_with_canonical_sources(apps, identifier_source_map, sources)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Migration(migrations.Migration):
|
|
91
|
+
"""Migrate db."""
|
|
92
|
+
|
|
93
|
+
dependencies = [
|
|
94
|
+
("codex", "0034_comicbox2"),
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
operations = [
|
|
98
|
+
migrations.RunPython(_convert_identifier_sources),
|
|
99
|
+
migrations.SeparateDatabaseAndState(
|
|
100
|
+
database_operations=[
|
|
101
|
+
migrations.RunSQL(
|
|
102
|
+
sql="DROP TABLE IF EXISTS codex_comicfts;",
|
|
103
|
+
),
|
|
104
|
+
migrations.RunSQL(
|
|
105
|
+
sql="CREATE VIRTUAL TABLE codex_comicfts USING fts5(comic_id UNINDEXED, created_at UNINDEXED, updated_at UNINDEXED, publisher, imprint, series, name, collection_title, age_rating, country, language, original_format, review, scan_info, summary, tagger, characters, credits, genres, locations, roles, series_groups, stories, story_arcs, tags, teams, universes, sources)",
|
|
106
|
+
reverse_sql="DROP TABLE IF EXISTS codex_comicfts",
|
|
107
|
+
),
|
|
108
|
+
],
|
|
109
|
+
state_operations=[
|
|
110
|
+
migrations.DeleteModel(
|
|
111
|
+
name="ComicFTS",
|
|
112
|
+
),
|
|
113
|
+
migrations.CreateModel(
|
|
114
|
+
name="ComicFTS",
|
|
115
|
+
fields=[
|
|
116
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
117
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
118
|
+
(
|
|
119
|
+
"comic",
|
|
120
|
+
models.OneToOneField(
|
|
121
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
122
|
+
primary_key=True,
|
|
123
|
+
serialize=False,
|
|
124
|
+
to="codex.comic",
|
|
125
|
+
),
|
|
126
|
+
),
|
|
127
|
+
("body", models.TextField()),
|
|
128
|
+
],
|
|
129
|
+
options={
|
|
130
|
+
"get_latest_by": "updated_at",
|
|
131
|
+
"managed": False,
|
|
132
|
+
},
|
|
133
|
+
),
|
|
134
|
+
],
|
|
135
|
+
),
|
|
136
|
+
]
|
codex/models/comic.py
CHANGED
|
@@ -326,23 +326,12 @@ class Comic(WatchedPathBrowserGroup):
|
|
|
326
326
|
"""Most common text representation for logging."""
|
|
327
327
|
return self.get_title(self, volume=True, name=True, filename_fallback=True)
|
|
328
328
|
|
|
329
|
-
def issue(self) -> str:
|
|
330
|
-
"""Combine issue parts for search."""
|
|
331
|
-
res = ""
|
|
332
|
-
if self.issue_number is not None:
|
|
333
|
-
res += str(self.issue_number.normalize())
|
|
334
|
-
if self.issue_suffix:
|
|
335
|
-
res += self.issue_suffix
|
|
336
|
-
return res
|
|
337
|
-
|
|
338
329
|
|
|
339
330
|
class ComicFTS(BaseModel):
|
|
340
331
|
comic = OneToOneField(primary_key=True, to=Comic, on_delete=CASCADE)
|
|
341
332
|
# Attributes
|
|
342
333
|
collection_title = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
343
|
-
issue = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
344
334
|
name = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
345
|
-
notes = TextField(db_collation="nocase")
|
|
346
335
|
review = TextField(db_collation="nocase")
|
|
347
336
|
summary = TextField(db_collation="nocase")
|
|
348
337
|
# FK groups
|
|
@@ -352,7 +341,6 @@ class ComicFTS(BaseModel):
|
|
|
352
341
|
# FK
|
|
353
342
|
age_rating = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
354
343
|
country = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
355
|
-
file_type = CharField(db_collation="nocase", max_length=max_choices_len(FileType))
|
|
356
344
|
language = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
357
345
|
original_format = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
358
346
|
scan_info = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
@@ -361,12 +349,8 @@ class ComicFTS(BaseModel):
|
|
|
361
349
|
characters = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
362
350
|
credits = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
363
351
|
genres = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
364
|
-
identifiers = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
365
352
|
sources = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
366
353
|
locations = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
367
|
-
reading_direction = CharField(
|
|
368
|
-
db_collation="nocase", max_length=max_choices_len(ReadingDirection)
|
|
369
|
-
)
|
|
370
354
|
series_groups = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
371
355
|
stories = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|
|
372
356
|
story_arcs = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
|