codex 1.6.19__py3-none-any.whl → 1.7.0a0__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/db.py +119 -0
- codex/librarian/covers/purge.py +1 -1
- codex/librarian/importer/const.py +7 -1
- codex/librarian/importer/create_fks.py +5 -2
- codex/librarian/importer/importerd.py +17 -12
- codex/librarian/importer/moved.py +44 -13
- codex/librarian/importer/tasks.py +2 -0
- codex/librarian/janitor/cleanup.py +21 -8
- codex/librarian/janitor/integrity.py +369 -0
- codex/librarian/janitor/janitor.py +50 -15
- codex/librarian/janitor/latest_version.py +2 -1
- codex/librarian/janitor/status.py +5 -0
- codex/librarian/janitor/tasks.py +37 -0
- codex/librarian/janitor/update.py +1 -1
- codex/librarian/librariand.py +32 -24
- codex/librarian/search/optimize.py +43 -0
- codex/librarian/search/remove.py +18 -46
- codex/librarian/search/searchd.py +14 -17
- codex/librarian/search/status.py +2 -1
- codex/librarian/search/tasks.py +4 -9
- codex/librarian/search/update.py +223 -301
- codex/librarian/watchdog/observers.py +2 -1
- codex/migrations/0029_comicfts.py +58 -0
- codex/migrations/0030_nocase_collation_day_month_indexes_status_types.py +198 -0
- codex/models/admin.py +15 -6
- codex/models/base.py +9 -0
- codex/models/bookmark.py +3 -3
- codex/models/comic.py +68 -16
- codex/models/functions.py +59 -2
- codex/models/groups.py +6 -1
- codex/models/library.py +1 -3
- codex/models/paths.py +9 -3
- codex/serializers/browser/page.py +2 -0
- codex/settings/settings.py +16 -17
- codex/signals/django_signals.py +4 -10
- codex/startup.py +1 -35
- codex/static_root/assets/{VCheckbox-Cko1sQK-.170726b3b8a1.js → VCheckbox-CWiqcZ7a.3feebd47daab.js} +1 -1
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-Cko1sQK-.js → VCheckbox-CWiqcZ7a.js} +1 -1
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js.br +0 -0
- codex/static_root/assets/VCheckbox-CWiqcZ7a.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js → VCheckboxBtn-Dk3iB62X.bc66d5351baa.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CO3-aqQL.js → VCheckboxBtn-Dk3iB62X.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.gz +0 -0
- codex/static_root/assets/{VCombobox-Bjp6lffE.js → VCombobox-D1_KU27A.b1d30587a973.js} +1 -1
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.gz +0 -0
- codex/static_root/assets/{VCombobox-Bjp6lffE.caf59fba486e.js → VCombobox-D1_KU27A.js} +1 -1
- codex/static_root/assets/VCombobox-D1_KU27A.js.br +0 -0
- codex/static_root/assets/VCombobox-D1_KU27A.js.gz +0 -0
- codex/static_root/assets/{VDialog-BkpVGB70.6eea3ca762a5.js → VDialog-GbxvFMMN.594fb3a39ced.js} +1 -1
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.br +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.gz +0 -0
- codex/static_root/assets/{VDialog-BkpVGB70.js → VDialog-GbxvFMMN.js} +1 -1
- codex/static_root/assets/VDialog-GbxvFMMN.js.br +0 -0
- codex/static_root/assets/VDialog-GbxvFMMN.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js → VExpansionPanels-Cywv_bEj.a762e63858e5.js} +1 -1
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-Ci2-j8XX.js → VExpansionPanels-Cywv_bEj.js} +1 -1
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-D3Py5BfQ.a4f25edb86d3.js → VRadioGroup-C0etmxpZ.0eb3009a6300.js} +1 -1
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-D3Py5BfQ.js → VRadioGroup-C0etmxpZ.js} +1 -1
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js.br +0 -0
- codex/static_root/assets/VRadioGroup-C0etmxpZ.js.gz +0 -0
- codex/static_root/assets/{VSelect-DtvZsYZz.e98745d858eb.js → VSelect-txrnRiNJ.a5625760018c.js} +1 -1
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.br +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.gz +0 -0
- codex/static_root/assets/{VSelect-DtvZsYZz.js → VSelect-txrnRiNJ.js} +1 -1
- codex/static_root/assets/VSelect-txrnRiNJ.js.br +0 -0
- codex/static_root/assets/VSelect-txrnRiNJ.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-D6kmykQW.1e1fda62ceba.js → VSelectionControl-CY6RmcvW.7e8824224f6c.js} +1 -1
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-D6kmykQW.js → VSelectionControl-CY6RmcvW.js} +1 -1
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CY6RmcvW.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-UGZWxpF5.360bdefcd215.js → VSlideGroup-DtEQMwY1.6c72325635ed.js} +1 -1
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-UGZWxpF5.js → VSlideGroup-DtEQMwY1.js} +1 -1
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DtEQMwY1.js.gz +0 -0
- codex/static_root/assets/{VTable-DXH_yQ0o.1149f56ad566.js → VTable-2af995xo.6c5bf36631b2.js} +1 -1
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.br +0 -0
- codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.gz +0 -0
- codex/static_root/assets/{VTable-DXH_yQ0o.js → VTable-2af995xo.js} +1 -1
- codex/static_root/assets/VTable-2af995xo.js.br +0 -0
- codex/static_root/assets/VTable-2af995xo.js.gz +0 -0
- codex/static_root/assets/{VTextField-Blqy56_L.26d8761e0155.js → VTextField-G6CMj7yO.657a130dd302.js} +1 -1
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.br +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.gz +0 -0
- codex/static_root/assets/{VTextField-Blqy56_L.js → VTextField-G6CMj7yO.js} +1 -1
- codex/static_root/assets/VTextField-G6CMj7yO.js.br +0 -0
- codex/static_root/assets/VTextField-G6CMj7yO.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-C8_ovV7e.afbc08414765.js → VWindowItem-CzoGd6Ul.721e5da11ad4.js} +1 -1
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.br +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-C8_ovV7e.js → VWindowItem-CzoGd6Ul.js} +1 -1
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js.br +0 -0
- codex/static_root/assets/VWindowItem-CzoGd6Ul.js.gz +0 -0
- codex/static_root/assets/{admin-DTRn8bBs.d031ea42d8d0.js → admin-CsPAIQeG.5175467148a3.js} +1 -1
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.br +0 -0
- codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.gz +0 -0
- codex/static_root/assets/{admin-DTRn8bBs.js → admin-CsPAIQeG.js} +1 -1
- codex/static_root/assets/admin-CsPAIQeG.js.br +0 -0
- codex/static_root/assets/admin-CsPAIQeG.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BaXFOcru.b1a44b05f482.js → admin-drawer-panel-BAT7GA64.73a064628c19.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-BaXFOcru.js → admin-drawer-panel-BAT7GA64.js} +11 -11
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.gz +0 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js +1 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js.br +0 -0
- codex/static_root/assets/browser-C18l93yc.923860009ef4.js.gz +0 -0
- codex/static_root/assets/browser-C18l93yc.js +1 -0
- codex/static_root/assets/browser-C18l93yc.js.br +0 -0
- codex/static_root/assets/browser-C18l93yc.js.gz +0 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css +1 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.br +0 -0
- codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.gz +0 -0
- codex/static_root/assets/browser-DaAqlBYO.css +1 -0
- codex/static_root/assets/browser-DaAqlBYO.css.br +0 -0
- codex/static_root/assets/browser-DaAqlBYO.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js +1 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js +1 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BazJdNs9.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css +1 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css +1 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.br +0 -0
- codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.gz +0 -0
- codex/static_root/assets/{confirm-dialog-D3_s2DQy.9dde66036c72.js → confirm-dialog-CbW3hlOp.9dd39b443c31.js} +1 -1
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-D3_s2DQy.js → confirm-dialog-CbW3hlOp.js} +1 -1
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CbW3hlOp.js.gz +0 -0
- codex/static_root/assets/{datetime-column-BxC1Li9e.3dc314b3295d.js → datetime-column-uSqKZU37.b157b0743db5.js} +1 -1
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.br +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.gz +0 -0
- codex/static_root/assets/{datetime-column-BxC1Li9e.js → datetime-column-uSqKZU37.js} +1 -1
- codex/static_root/assets/datetime-column-uSqKZU37.js.br +0 -0
- codex/static_root/assets/datetime-column-uSqKZU37.js.gz +0 -0
- codex/static_root/assets/{filter-Bzk7x841.d0c3b602e62d.js → filter-CBl5KEsB.845a81573c2e.js} +1 -1
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.br +0 -0
- codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.gz +0 -0
- codex/static_root/assets/{filter-Bzk7x841.js → filter-CBl5KEsB.js} +1 -1
- codex/static_root/assets/filter-CBl5KEsB.js.br +0 -0
- codex/static_root/assets/filter-CBl5KEsB.js.gz +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js +1 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.br +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.gz +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js +1 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js.br +0 -0
- codex/static_root/assets/flag-tab-CT8pqM4r.js.gz +0 -0
- codex/static_root/assets/{group-tab-5esH4lwX.a81fdd258117.js → group-tab-BOW9kigw.7e5007596367.js} +1 -1
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.br +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.gz +0 -0
- codex/static_root/assets/{group-tab-5esH4lwX.js → group-tab-BOW9kigw.js} +1 -1
- codex/static_root/assets/group-tab-BOW9kigw.js.br +0 -0
- codex/static_root/assets/group-tab-BOW9kigw.js.gz +0 -0
- codex/static_root/assets/{http-error-BTOBfcGY.52ced6f28420.js → http-error-C6yuLApo.45cf99eb7a70.js} +1 -1
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.br +4 -0
- codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.gz +0 -0
- codex/static_root/assets/{http-error-BTOBfcGY.js → http-error-C6yuLApo.js} +1 -1
- codex/static_root/assets/http-error-C6yuLApo.js.br +4 -0
- codex/static_root/assets/http-error-C6yuLApo.js.gz +0 -0
- codex/static_root/assets/{library-tab-DiPEdOF7.ece2d642a9dc.js → library-tab-D3WOtpE0.9af34e6e7235.js} +1 -1
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.br +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.gz +0 -0
- codex/static_root/assets/{library-tab-DiPEdOF7.js → library-tab-D3WOtpE0.js} +1 -1
- codex/static_root/assets/library-tab-D3WOtpE0.js.br +0 -0
- codex/static_root/assets/library-tab-D3WOtpE0.js.gz +0 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js +6 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.br +0 -0
- codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.gz +0 -0
- codex/static_root/assets/main-CmjU2oTp.js +6 -0
- codex/static_root/assets/main-CmjU2oTp.js.br +0 -0
- codex/static_root/assets/main-CmjU2oTp.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js +1 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js +1 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-BH54_5f6.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-C45-onwe.4722195bb60b.css → pagination-toolbar-DGrxp2OD.css} +1 -1
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.css.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-C45-onwe.css → pagination-toolbar-DGrxp2OD.f960a624f04f.css} +1 -1
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-CkIzuyx1.00c354a82e6e.js → pagination-toolbar-JH3Kbffr.451171f7ac33.js} +1 -1
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-CkIzuyx1.js → pagination-toolbar-JH3Kbffr.js} +1 -1
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BjlRPuf4.0f056fa321b6.js → pdf-doc-BcRNpoaW.882f6e967869.js} +1 -1
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BjlRPuf4.js → pdf-doc-BcRNpoaW.js} +1 -1
- codex/static_root/assets/pdf-doc-BcRNpoaW.js.br +0 -0
- codex/static_root/assets/pdf-doc-BcRNpoaW.js.gz +0 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js +2 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.br +0 -0
- codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.gz +0 -0
- codex/static_root/assets/reader-DIPRg2VB.js +2 -0
- codex/static_root/assets/reader-DIPRg2VB.js.br +0 -0
- codex/static_root/assets/reader-DIPRg2VB.js.gz +0 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css +1 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.gz +0 -0
- codex/static_root/assets/reader-DveZZr9x.css +1 -0
- codex/static_root/assets/reader-DveZZr9x.css.br +0 -0
- codex/static_root/assets/reader-DveZZr9x.css.gz +0 -0
- codex/static_root/assets/{relation-chips-CUSatZET.js → relation-chips-3tTlR5QH.71f18bc92adc.js} +1 -1
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.br +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.gz +0 -0
- codex/static_root/assets/{relation-chips-CUSatZET.a58031ff8141.js → relation-chips-3tTlR5QH.js} +1 -1
- codex/static_root/assets/relation-chips-3tTlR5QH.js.br +0 -0
- codex/static_root/assets/relation-chips-3tTlR5QH.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-BSHTAsJ9.js → settings-drawer-BdyFhA_r.151f4c3061ed.js} +2 -2
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.br +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-BSHTAsJ9.1d07ce084fc9.js → settings-drawer-BdyFhA_r.js} +2 -2
- codex/static_root/assets/settings-drawer-BdyFhA_r.js.br +0 -0
- codex/static_root/assets/settings-drawer-BdyFhA_r.js.gz +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js +1 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.br +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.gz +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js +1 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js.br +0 -0
- codex/static_root/assets/stats-tab-DeKjwvmh.js.gz +0 -0
- codex/static_root/assets/{stats-tab-BXGisn5N.9b7a71e7fe28.css → stats-tab-DpfGsVX3.1cb66f2649a9.css} +1 -1
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.gz +0 -0
- codex/static_root/assets/{stats-tab-BXGisn5N.css → stats-tab-DpfGsVX3.css} +1 -1
- codex/static_root/assets/stats-tab-DpfGsVX3.css.br +0 -0
- codex/static_root/assets/stats-tab-DpfGsVX3.css.gz +0 -0
- codex/static_root/assets/{task-tab-NORFAa9p.97e07e366ddd.js → task-tab-C8D0qRP_.d4ab174c69a3.js} +1 -1
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.br +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.gz +0 -0
- codex/static_root/assets/{task-tab-NORFAa9p.js → task-tab-C8D0qRP_.js} +1 -1
- codex/static_root/assets/task-tab-C8D0qRP_.js.br +0 -0
- codex/static_root/assets/task-tab-C8D0qRP_.js.gz +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js +1 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.br +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.gz +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js +1 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js.br +0 -0
- codex/static_root/assets/unauthorized-BHgBRF4z.js.gz +0 -0
- codex/static_root/assets/{unauthorized-muDsv-rC.css → unauthorized-BjhIc97q.728511c92cd0.css} +1 -1
- codex/static_root/assets/unauthorized-BjhIc97q.728511c92cd0.css.br +0 -0
- codex/static_root/assets/unauthorized-BjhIc97q.728511c92cd0.css.gz +0 -0
- codex/static_root/assets/{unauthorized-muDsv-rC.2166ec8b3a32.css → unauthorized-BjhIc97q.css} +1 -1
- codex/static_root/assets/unauthorized-BjhIc97q.css.br +0 -0
- codex/static_root/assets/unauthorized-BjhIc97q.css.gz +0 -0
- codex/static_root/assets/{user-tab-CHbyN1EW.e7206db08680.js → user-tab-BU31Z92y.35613c713faf.js} +1 -1
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.br +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.gz +0 -0
- codex/static_root/assets/{user-tab-CHbyN1EW.js → user-tab-BU31Z92y.js} +1 -1
- codex/static_root/assets/user-tab-BU31Z92y.js.br +0 -0
- codex/static_root/assets/user-tab-BU31Z92y.js.gz +0 -0
- codex/static_root/js/choices-admin.8aa64c911203.json +1 -0
- codex/static_root/js/choices-admin.8aa64c911203.json.br +0 -0
- codex/static_root/js/choices-admin.8aa64c911203.json.gz +0 -0
- codex/static_root/js/choices-admin.json +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.45cb46e19686.json +635 -0
- codex/static_root/manifest.45cb46e19686.json.br +0 -0
- codex/static_root/manifest.45cb46e19686.json.gz +0 -0
- codex/static_root/manifest.json +248 -248
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/status_controller.py +10 -4
- codex/views/admin/tasks.py +46 -40
- codex/views/auth.py +15 -4
- codex/views/browser/annotations.py +94 -51
- codex/views/browser/base.py +1 -2
- codex/views/browser/browser.py +52 -21
- codex/views/browser/cover.py +8 -4
- codex/views/browser/filters/annotations.py +28 -6
- codex/views/browser/filters/search/__init__.py +1 -0
- codex/views/browser/filters/search/aliases.py +81 -0
- codex/views/browser/filters/search/field/__init__.py +1 -0
- codex/views/browser/filters/search/field/column.py +53 -0
- codex/views/browser/filters/search/field/expression.py +177 -0
- codex/views/browser/filters/search/field/filter.py +70 -0
- codex/views/browser/filters/search/field/optimize.py +71 -0
- codex/views/browser/filters/search/field/parse.py +187 -0
- codex/views/browser/filters/search/fts.py +38 -0
- codex/views/browser/filters/search/parse.py +220 -0
- codex/views/browser/order_by.py +8 -2
- codex/views/browser/page_in_bounds.py +88 -0
- codex/views/browser/paginate.py +28 -113
- codex/views/const.py +0 -1
- codex/views/opds/v1/feed.py +2 -1
- codex/views/opds/v2/feed.py +2 -1
- codex/views/opds/v2/links.py +1 -1
- codex/views/reader/books.py +1 -1
- codex/views/reader/page.py +3 -3
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/METADATA +18 -10
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/RECORD +321 -372
- codex/_vendor/__init__.py +0 -0
- codex/_vendor/django_haystack-3.2.1.dist-info/AUTHORS +0 -127
- codex/_vendor/django_haystack-3.2.1.dist-info/INSTALLER +0 -1
- codex/_vendor/django_haystack-3.2.1.dist-info/LICENSE +0 -31
- codex/_vendor/django_haystack-3.2.1.dist-info/METADATA +0 -98
- codex/_vendor/django_haystack-3.2.1.dist-info/RECORD +0 -97
- codex/_vendor/django_haystack-3.2.1.dist-info/REQUESTED +0 -0
- codex/_vendor/django_haystack-3.2.1.dist-info/WHEEL +0 -5
- codex/_vendor/django_haystack-3.2.1.dist-info/top_level.txt +0 -1
- codex/_vendor/haystack/__init__.py +0 -80
- codex/_vendor/haystack/admin.py +0 -163
- codex/_vendor/haystack/apps.py +0 -31
- codex/_vendor/haystack/backends/__init__.py +0 -1109
- codex/_vendor/haystack/backends/elasticsearch2_backend.py +0 -390
- codex/_vendor/haystack/backends/elasticsearch5_backend.py +0 -483
- codex/_vendor/haystack/backends/elasticsearch7_backend.py +0 -586
- codex/_vendor/haystack/backends/elasticsearch_backend.py +0 -1141
- codex/_vendor/haystack/backends/simple_backend.py +0 -132
- codex/_vendor/haystack/backends/solr_backend.py +0 -991
- codex/_vendor/haystack/backends/whoosh_backend.py +0 -1104
- codex/_vendor/haystack/constants.py +0 -57
- codex/_vendor/haystack/exceptions.py +0 -57
- codex/_vendor/haystack/fields.py +0 -562
- codex/_vendor/haystack/forms.py +0 -135
- codex/_vendor/haystack/generic_views.py +0 -141
- codex/_vendor/haystack/indexes.py +0 -548
- codex/_vendor/haystack/inputs.py +0 -169
- codex/_vendor/haystack/management/__init__.py +0 -0
- codex/_vendor/haystack/management/commands/__init__.py +0 -0
- codex/_vendor/haystack/management/commands/build_solr_schema.py +0 -187
- codex/_vendor/haystack/management/commands/clear_index.py +0 -67
- codex/_vendor/haystack/management/commands/haystack_info.py +0 -22
- codex/_vendor/haystack/management/commands/rebuild_index.py +0 -65
- codex/_vendor/haystack/management/commands/update_index.py +0 -438
- codex/_vendor/haystack/manager.py +0 -105
- codex/_vendor/haystack/models.py +0 -257
- codex/_vendor/haystack/panels.py +0 -89
- codex/_vendor/haystack/query.py +0 -771
- codex/_vendor/haystack/routers.py +0 -14
- codex/_vendor/haystack/signals.py +0 -88
- codex/_vendor/haystack/templates/panels/haystack.html +0 -33
- codex/_vendor/haystack/templates/search_configuration/schema.xml +0 -1056
- codex/_vendor/haystack/templates/search_configuration/solrconfig.xml +0 -1446
- codex/_vendor/haystack/templatetags/__init__.py +0 -0
- codex/_vendor/haystack/templatetags/highlight.py +0 -131
- codex/_vendor/haystack/templatetags/more_like_this.py +0 -119
- codex/_vendor/haystack/urls.py +0 -5
- codex/_vendor/haystack/utils/__init__.py +0 -84
- codex/_vendor/haystack/utils/app_loading.py +0 -34
- codex/_vendor/haystack/utils/geo.py +0 -71
- codex/_vendor/haystack/utils/highlighting.py +0 -165
- codex/_vendor/haystack/utils/loading.py +0 -374
- codex/_vendor/haystack/utils/log.py +0 -21
- codex/_vendor/haystack/version.py +0 -16
- codex/_vendor/haystack/views.py +0 -253
- codex/integrity.py +0 -492
- codex/librarian/search/merge.py +0 -86
- codex/librarian/search/version.py +0 -63
- codex/search/__init__.py +0 -1
- codex/search/backend.py +0 -524
- codex/search/backend_search.py +0 -229
- codex/search/engine.py +0 -33
- codex/search/indexes.py +0 -79
- codex/search/query.py +0 -67
- codex/search/writing.py +0 -180
- codex/static_root/assets/VCheckbox-Cko1sQK-.170726b3b8a1.js.br +0 -0
- codex/static_root/assets/VCheckbox-Cko1sQK-.170726b3b8a1.js.gz +0 -0
- codex/static_root/assets/VCheckbox-Cko1sQK-.js.br +0 -0
- codex/static_root/assets/VCheckbox-Cko1sQK-.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CO3-aqQL.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.caf59fba486e.js.br +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.caf59fba486e.js.gz +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.js.br +0 -0
- codex/static_root/assets/VCombobox-Bjp6lffE.js.gz +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.6eea3ca762a5.js.br +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.6eea3ca762a5.js.gz +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.js.br +0 -0
- codex/static_root/assets/VDialog-BkpVGB70.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Ci2-j8XX.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D3Py5BfQ.js.gz +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.e98745d858eb.js.br +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.e98745d858eb.js.gz +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.js.br +0 -0
- codex/static_root/assets/VSelect-DtvZsYZz.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js.br +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.js.br +0 -0
- codex/static_root/assets/VSelectionControl-D6kmykQW.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.360bdefcd215.js.br +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.360bdefcd215.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.js.br +0 -0
- codex/static_root/assets/VSlideGroup-UGZWxpF5.js.gz +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.1149f56ad566.js.br +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.1149f56ad566.js.gz +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.js.br +0 -0
- codex/static_root/assets/VTable-DXH_yQ0o.js.gz +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.26d8761e0155.js.br +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.26d8761e0155.js.gz +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.js.br +0 -0
- codex/static_root/assets/VTextField-Blqy56_L.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.afbc08414765.js.br +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.afbc08414765.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.js.br +0 -0
- codex/static_root/assets/VWindowItem-C8_ovV7e.js.gz +0 -0
- codex/static_root/assets/admin-DTRn8bBs.d031ea42d8d0.js.br +0 -0
- codex/static_root/assets/admin-DTRn8bBs.d031ea42d8d0.js.gz +0 -0
- codex/static_root/assets/admin-DTRn8bBs.js.br +0 -0
- codex/static_root/assets/admin-DTRn8bBs.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BaXFOcru.js.gz +0 -0
- codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js +0 -1
- codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js.br +0 -0
- codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js.gz +0 -0
- codex/static_root/assets/browser-BBpUyGmz.js +0 -1
- codex/static_root/assets/browser-BBpUyGmz.js.br +0 -0
- codex/static_root/assets/browser-BBpUyGmz.js.gz +0 -0
- codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css +0 -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-Binc3n9H.css +0 -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/change-password-dialog-Cebek1-c.aff2975e31f8.css +0 -1
- 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 +0 -1
- 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-DlHoLhDF.76ab4a29bb9b.js +0 -1
- codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.js +0 -1
- codex/static_root/assets/change-password-dialog-DlHoLhDF.js.br +0 -0
- codex/static_root/assets/change-password-dialog-DlHoLhDF.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.9dde66036c72.js.br +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.9dde66036c72.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.js.br +0 -0
- codex/static_root/assets/confirm-dialog-D3_s2DQy.js.gz +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.3dc314b3295d.js.br +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.3dc314b3295d.js.gz +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.js.br +0 -0
- codex/static_root/assets/datetime-column-BxC1Li9e.js.gz +0 -0
- codex/static_root/assets/filter-Bzk7x841.d0c3b602e62d.js.br +0 -0
- codex/static_root/assets/filter-Bzk7x841.d0c3b602e62d.js.gz +0 -0
- codex/static_root/assets/filter-Bzk7x841.js.br +0 -0
- codex/static_root/assets/filter-Bzk7x841.js.gz +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js +0 -1
- codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js.br +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js.gz +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.js +0 -1
- codex/static_root/assets/flag-tab-C2bI5zq3.js.br +0 -0
- codex/static_root/assets/flag-tab-C2bI5zq3.js.gz +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.a81fdd258117.js.br +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.a81fdd258117.js.gz +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.js.br +0 -0
- codex/static_root/assets/group-tab-5esH4lwX.js.gz +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.52ced6f28420.js.br +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.52ced6f28420.js.gz +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.js.br +0 -0
- codex/static_root/assets/http-error-BTOBfcGY.js.gz +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.ece2d642a9dc.js.br +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.ece2d642a9dc.js.gz +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.js.br +0 -0
- codex/static_root/assets/library-tab-DiPEdOF7.js.gz +0 -0
- codex/static_root/assets/main-BPIPjzsc.66def8991e38.js +0 -6
- codex/static_root/assets/main-BPIPjzsc.66def8991e38.js.br +0 -0
- codex/static_root/assets/main-BPIPjzsc.66def8991e38.js.gz +0 -0
- codex/static_root/assets/main-BPIPjzsc.js +0 -6
- codex/static_root/assets/main-BPIPjzsc.js.br +0 -0
- codex/static_root/assets/main-BPIPjzsc.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js +0 -1
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.js +0 -1
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-CiyC6Al9.js.gz +0 -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.br +0 -0
- codex/static_root/assets/pagination-toolbar-C45-onwe.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CkIzuyx1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.0f056fa321b6.js.br +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.0f056fa321b6.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.js.br +0 -0
- codex/static_root/assets/pdf-doc-BjlRPuf4.js.gz +0 -0
- codex/static_root/assets/reader-_stAWL-4.5089e9144795.css +0 -1
- 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 +0 -1
- 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/reader-ucxnLi2K.d0fbce0f03c1.js +0 -2
- codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js.br +0 -0
- codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js.gz +0 -0
- codex/static_root/assets/reader-ucxnLi2K.js +0 -2
- codex/static_root/assets/reader-ucxnLi2K.js.br +0 -0
- codex/static_root/assets/reader-ucxnLi2K.js.gz +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.a58031ff8141.js.br +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.a58031ff8141.js.gz +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.js.br +0 -0
- codex/static_root/assets/relation-chips-CUSatZET.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js.br +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js.gz +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.js.br +0 -0
- codex/static_root/assets/settings-drawer-BSHTAsJ9.js.gz +0 -0
- 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-BXGisn5N.css.br +0 -0
- codex/static_root/assets/stats-tab-BXGisn5N.css.gz +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js +0 -1
- codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js.br +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js.gz +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.js +0 -1
- codex/static_root/assets/stats-tab-DKXVB2Cc.js.br +0 -0
- codex/static_root/assets/stats-tab-DKXVB2Cc.js.gz +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.97e07e366ddd.js.br +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.97e07e366ddd.js.gz +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.js.br +0 -0
- codex/static_root/assets/task-tab-NORFAa9p.js.gz +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js +0 -1
- codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js.br +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js.gz +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.js +0 -1
- codex/static_root/assets/unauthorized-RHaQZRfr.js.br +0 -0
- codex/static_root/assets/unauthorized-RHaQZRfr.js.gz +0 -0
- 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-muDsv-rC.css.br +0 -0
- codex/static_root/assets/unauthorized-muDsv-rC.css.gz +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.e7206db08680.js.br +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.e7206db08680.js.gz +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.js.br +0 -0
- codex/static_root/assets/user-tab-CHbyN1EW.js.gz +0 -0
- codex/static_root/js/choices-admin.1a20a5648f20.json +0 -1
- 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/manifest.0fe31a6cb99f.json +0 -635
- codex/static_root/manifest.0fe31a6cb99f.json.br +0 -0
- codex/static_root/manifest.0fe31a6cb99f.json.gz +0 -0
- codex/templates/search/indexes/codex/comic_text.txt +0 -47
- codex/views/browser/filters/search.py +0 -196
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/LICENSE +0 -0
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/WHEEL +0 -0
- {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/entry_points.txt +0 -0
codex/search/backend_search.py
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
"""Backend search that gets the reverse variable as a parameter."""
|
|
2
|
-
|
|
3
|
-
import warnings
|
|
4
|
-
from datetime import date, datetime
|
|
5
|
-
|
|
6
|
-
from django.conf import settings
|
|
7
|
-
from django.utils.encoding import force_str
|
|
8
|
-
|
|
9
|
-
# Bubble up the correct error.
|
|
10
|
-
from whoosh.sorting import Count, DateRangeFacet, FieldFacet
|
|
11
|
-
from whoosh.support.relativedelta import relativedelta as RelativeDelta
|
|
12
|
-
|
|
13
|
-
from codex._vendor.haystack.backends import log_query
|
|
14
|
-
from codex._vendor.haystack.constants import DJANGO_CT
|
|
15
|
-
from codex._vendor.haystack.exceptions import SearchBackendError
|
|
16
|
-
from codex._vendor.haystack.utils import get_model_ct
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class CodexBackendSearchMixin:
|
|
20
|
-
@log_query
|
|
21
|
-
def search(
|
|
22
|
-
self,
|
|
23
|
-
query_string,
|
|
24
|
-
sort_by=None,
|
|
25
|
-
start_offset=0,
|
|
26
|
-
end_offset=None,
|
|
27
|
-
fields="",
|
|
28
|
-
highlight=False,
|
|
29
|
-
facets=None,
|
|
30
|
-
date_facets=None,
|
|
31
|
-
query_facets=None,
|
|
32
|
-
narrow_queries=None,
|
|
33
|
-
spelling_query=None,
|
|
34
|
-
within=None,
|
|
35
|
-
dwithin=None,
|
|
36
|
-
distance_point=None,
|
|
37
|
-
models=None,
|
|
38
|
-
limit_to_registered_models=None,
|
|
39
|
-
result_class=None,
|
|
40
|
-
reverse=False,
|
|
41
|
-
**kwargs,
|
|
42
|
-
):
|
|
43
|
-
if not self.setup_complete:
|
|
44
|
-
self.setup()
|
|
45
|
-
|
|
46
|
-
# A zero length query should return no results.
|
|
47
|
-
if len(query_string) == 0:
|
|
48
|
-
return {"results": [], "hits": 0}
|
|
49
|
-
|
|
50
|
-
query_string = force_str(query_string)
|
|
51
|
-
|
|
52
|
-
# A one-character query (non-wildcard) gets nabbed by a stopwords
|
|
53
|
-
# filter and should yield zero results.
|
|
54
|
-
if len(query_string) <= 1 and query_string != "*":
|
|
55
|
-
return {"results": [], "hits": 0}
|
|
56
|
-
|
|
57
|
-
# reverse = False # old reverse flag
|
|
58
|
-
|
|
59
|
-
if sort_by is not None:
|
|
60
|
-
# Determine if we need to reverse the results and if Whoosh can
|
|
61
|
-
# handle what it's being asked to sort by. Reversing is an
|
|
62
|
-
# all-or-nothing action, unfortunately.
|
|
63
|
-
sort_by_list = []
|
|
64
|
-
reverse_counter = 0
|
|
65
|
-
|
|
66
|
-
for order_by in sort_by:
|
|
67
|
-
if order_by.startswith("-"):
|
|
68
|
-
reverse_counter += 1
|
|
69
|
-
if reverse_counter and reverse_counter != len(sort_by):
|
|
70
|
-
raise SearchBackendError(
|
|
71
|
-
"Whoosh requires all order_by fields"
|
|
72
|
-
" to use the same sort direction"
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
for order_by in sort_by:
|
|
76
|
-
if order_by.startswith("-"):
|
|
77
|
-
sort_by_list.append(order_by[1:])
|
|
78
|
-
|
|
79
|
-
if len(sort_by_list) == 1:
|
|
80
|
-
reverse = True
|
|
81
|
-
else:
|
|
82
|
-
sort_by_list.append(order_by)
|
|
83
|
-
|
|
84
|
-
if len(sort_by_list) == 1:
|
|
85
|
-
reverse = False
|
|
86
|
-
|
|
87
|
-
sort_by = sort_by_list
|
|
88
|
-
|
|
89
|
-
group_by = []
|
|
90
|
-
facet_types = {}
|
|
91
|
-
if facets is not None:
|
|
92
|
-
group_by += [
|
|
93
|
-
FieldFacet(facet, allow_overlap=True, maptype=Count) for facet in facets
|
|
94
|
-
]
|
|
95
|
-
facet_types.update({facet: "fields" for facet in facets})
|
|
96
|
-
|
|
97
|
-
if date_facets is not None:
|
|
98
|
-
|
|
99
|
-
def _fixup_datetime(dt):
|
|
100
|
-
if isinstance(dt, datetime):
|
|
101
|
-
return dt
|
|
102
|
-
if isinstance(dt, date):
|
|
103
|
-
return datetime(dt.year, dt.month, dt.day)
|
|
104
|
-
raise ValueError
|
|
105
|
-
|
|
106
|
-
for key, value in date_facets.items():
|
|
107
|
-
start = _fixup_datetime(value["start_date"])
|
|
108
|
-
end = _fixup_datetime(value["end_date"])
|
|
109
|
-
gap_by = value["gap_by"]
|
|
110
|
-
gap_amount = value.get("gap_amount", 1)
|
|
111
|
-
gap = RelativeDelta(**{"%ss" % gap_by: gap_amount})
|
|
112
|
-
group_by.append(DateRangeFacet(key, start, end, gap, maptype=Count))
|
|
113
|
-
facet_types[key] = "dates"
|
|
114
|
-
|
|
115
|
-
if query_facets is not None:
|
|
116
|
-
warnings.warn(
|
|
117
|
-
"Whoosh does not handle query faceting.", Warning, stacklevel=2
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
narrowed_results = None
|
|
121
|
-
self.index = self.index.refresh()
|
|
122
|
-
|
|
123
|
-
if limit_to_registered_models is None:
|
|
124
|
-
limit_to_registered_models = getattr(
|
|
125
|
-
settings, "HAYSTACK_LIMIT_TO_REGISTERED_MODELS", True
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
if models and len(models):
|
|
129
|
-
model_choices = sorted(get_model_ct(model) for model in models)
|
|
130
|
-
elif limit_to_registered_models:
|
|
131
|
-
# Using narrow queries, limit the results to only models handled
|
|
132
|
-
# with the current routers.
|
|
133
|
-
model_choices = self.build_models_list()
|
|
134
|
-
else:
|
|
135
|
-
model_choices = []
|
|
136
|
-
|
|
137
|
-
if len(model_choices) > 0:
|
|
138
|
-
if narrow_queries is None:
|
|
139
|
-
narrow_queries = set()
|
|
140
|
-
|
|
141
|
-
narrow_queries.add(
|
|
142
|
-
" OR ".join(["%s:%s" % (DJANGO_CT, rm) for rm in model_choices])
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
narrow_searcher = None
|
|
146
|
-
|
|
147
|
-
if narrow_queries is not None:
|
|
148
|
-
# Potentially expensive? I don't see another way to do it in Whoosh...
|
|
149
|
-
narrow_searcher = self.index.searcher()
|
|
150
|
-
|
|
151
|
-
for nq in narrow_queries:
|
|
152
|
-
recent_narrowed_results = narrow_searcher.search(
|
|
153
|
-
self.parser.parse(force_str(nq)), limit=None
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
if len(recent_narrowed_results) <= 0:
|
|
157
|
-
return {"results": [], "hits": 0}
|
|
158
|
-
|
|
159
|
-
if narrowed_results is not None:
|
|
160
|
-
narrowed_results.filter(recent_narrowed_results)
|
|
161
|
-
else:
|
|
162
|
-
narrowed_results = recent_narrowed_results
|
|
163
|
-
|
|
164
|
-
self.index = self.index.refresh()
|
|
165
|
-
|
|
166
|
-
if self.index.doc_count():
|
|
167
|
-
searcher = self.index.searcher()
|
|
168
|
-
parsed_query = self.parser.parse(query_string)
|
|
169
|
-
|
|
170
|
-
# In the event of an invalid/stopworded query, recover gracefully.
|
|
171
|
-
if parsed_query is None:
|
|
172
|
-
return {"results": [], "hits": 0}
|
|
173
|
-
|
|
174
|
-
page_num, page_length = self.calculate_page(start_offset, end_offset)
|
|
175
|
-
|
|
176
|
-
search_kwargs = {
|
|
177
|
-
"pagelen": page_length,
|
|
178
|
-
"sortedby": sort_by,
|
|
179
|
-
"reverse": reverse,
|
|
180
|
-
"groupedby": group_by,
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
# Handle the case where the results have been narrowed.
|
|
184
|
-
if narrowed_results is not None:
|
|
185
|
-
search_kwargs["filter"] = narrowed_results
|
|
186
|
-
|
|
187
|
-
try:
|
|
188
|
-
raw_page = searcher.search_page(parsed_query, page_num, **search_kwargs)
|
|
189
|
-
except ValueError:
|
|
190
|
-
if not self.silently_fail:
|
|
191
|
-
raise
|
|
192
|
-
|
|
193
|
-
return {"results": [], "hits": 0, "spelling_suggestion": None}
|
|
194
|
-
|
|
195
|
-
# Because as of Whoosh 2.5.1, it will return the wrong page of
|
|
196
|
-
# results if you request something too high. :(
|
|
197
|
-
if raw_page.pagenum < page_num:
|
|
198
|
-
return {"results": [], "hits": 0, "spelling_suggestion": None}
|
|
199
|
-
|
|
200
|
-
results = self._process_results(
|
|
201
|
-
raw_page,
|
|
202
|
-
highlight=highlight,
|
|
203
|
-
query_string=query_string,
|
|
204
|
-
spelling_query=spelling_query,
|
|
205
|
-
result_class=result_class,
|
|
206
|
-
facet_types=facet_types,
|
|
207
|
-
)
|
|
208
|
-
searcher.close()
|
|
209
|
-
|
|
210
|
-
if hasattr(narrow_searcher, "close"):
|
|
211
|
-
narrow_searcher.close()
|
|
212
|
-
|
|
213
|
-
return results
|
|
214
|
-
else:
|
|
215
|
-
if self.include_spelling:
|
|
216
|
-
if spelling_query:
|
|
217
|
-
spelling_suggestion = self.create_spelling_suggestion(
|
|
218
|
-
spelling_query
|
|
219
|
-
)
|
|
220
|
-
else:
|
|
221
|
-
spelling_suggestion = self.create_spelling_suggestion(query_string)
|
|
222
|
-
else:
|
|
223
|
-
spelling_suggestion = None
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
"results": [],
|
|
227
|
-
"hits": 0,
|
|
228
|
-
"spelling_suggestion": spelling_suggestion,
|
|
229
|
-
}
|
codex/search/engine.py
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"""Custom Haystack Search Engine."""
|
|
2
|
-
|
|
3
|
-
from codex._vendor.haystack.backends import UnifiedIndex
|
|
4
|
-
from codex._vendor.haystack.backends.whoosh_backend import (
|
|
5
|
-
WhooshEngine,
|
|
6
|
-
)
|
|
7
|
-
from codex.search.backend import CodexSearchBackend
|
|
8
|
-
from codex.search.indexes import ComicIndex
|
|
9
|
-
from codex.search.query import CodexSearchQuery
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class CodexUnifiedIndex(UnifiedIndex):
|
|
13
|
-
"""Custom Codex Unified Index."""
|
|
14
|
-
|
|
15
|
-
def collect_indexes(self):
|
|
16
|
-
"""Replace auto app.search_index finding with one exact instance."""
|
|
17
|
-
# Because I moved search_indexes into codex.search
|
|
18
|
-
return [ComicIndex()]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class CodexSearchEngine(WhooshEngine):
|
|
22
|
-
"""A search engine with a locking backend."""
|
|
23
|
-
|
|
24
|
-
backend = CodexSearchBackend
|
|
25
|
-
query = CodexSearchQuery
|
|
26
|
-
unified_index = CodexUnifiedIndex
|
|
27
|
-
|
|
28
|
-
def __init__(self, using=None, queue_kwargs=None):
|
|
29
|
-
"""Initialize options with queue kwargs."""
|
|
30
|
-
super().__init__(using=using)
|
|
31
|
-
if not queue_kwargs:
|
|
32
|
-
queue_kwargs = {}
|
|
33
|
-
self.options.update(queue_kwargs)
|
codex/search/indexes.py
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"""Haystack search indexes."""
|
|
2
|
-
|
|
3
|
-
from codex._vendor.haystack.fields import CharField, FloatField, IntegerField
|
|
4
|
-
from codex._vendor.haystack.indexes import Indexable, ModelSearchIndex
|
|
5
|
-
from codex.models import Comic
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ComicIndex(ModelSearchIndex, Indexable):
|
|
9
|
-
"""Search index for the Comic model."""
|
|
10
|
-
|
|
11
|
-
# Groups
|
|
12
|
-
publisher = CharField(model_attr="publisher__name")
|
|
13
|
-
imprint = CharField(model_attr="imprint__name")
|
|
14
|
-
series = CharField(model_attr="series__name", boost=1.125)
|
|
15
|
-
volume = IntegerField(model_attr="volume__name", null=True)
|
|
16
|
-
|
|
17
|
-
# Related Fields
|
|
18
|
-
age_rating = CharField(model_attr="age_rating__name", null=True)
|
|
19
|
-
country = CharField(model_attr="country__name", null=True)
|
|
20
|
-
language = CharField(model_attr="language__name", null=True)
|
|
21
|
-
original_format = CharField(model_attr="original_format__name", null=True)
|
|
22
|
-
scan_info = CharField(model_attr="scan_info__name", null=True)
|
|
23
|
-
tagger = CharField(model_attr="tagger__name", null=True)
|
|
24
|
-
|
|
25
|
-
# Many to Many Fields
|
|
26
|
-
characters = CharField(model_attr="characters__name", null=True)
|
|
27
|
-
contributors = CharField(model_attr="contributors__person__name", null=True)
|
|
28
|
-
genres = CharField(model_attr="genres__name", null=True)
|
|
29
|
-
locations = CharField(model_attr="locations__name", null=True)
|
|
30
|
-
identifier = CharField(model_attr="identifiers__nss", null=True)
|
|
31
|
-
identifier_type = CharField(
|
|
32
|
-
model_attr="identifiers__identifier_type__name", null=True
|
|
33
|
-
)
|
|
34
|
-
series_groups = CharField(model_attr="series_groups__name", null=True)
|
|
35
|
-
stories = CharField(model_attr="stories__name", null=True)
|
|
36
|
-
story_arcs = CharField(
|
|
37
|
-
model_attr="story_arc_numbers__story_arc__name",
|
|
38
|
-
null=True,
|
|
39
|
-
)
|
|
40
|
-
tags = CharField(model_attr="tags__name", null=True)
|
|
41
|
-
teams = CharField(model_attr="teams__name", null=True)
|
|
42
|
-
|
|
43
|
-
# This makes issue field searching exact rather than fuzzy
|
|
44
|
-
issue = CharField(model_attr="issue", null=True)
|
|
45
|
-
|
|
46
|
-
# Floats are a better search experience than fixed decimals
|
|
47
|
-
issue_number = FloatField(model_attr="issue_number", null=True)
|
|
48
|
-
community_rating = FloatField(model_attr="community_rating", null=True)
|
|
49
|
-
critical_rating = FloatField(model_attr="critical_rating", null=True)
|
|
50
|
-
|
|
51
|
-
search_path = CharField(model_attr="search_path", null=True)
|
|
52
|
-
|
|
53
|
-
class Meta:
|
|
54
|
-
"""Model & field include list."""
|
|
55
|
-
|
|
56
|
-
model = Comic
|
|
57
|
-
fields = (
|
|
58
|
-
"created_at",
|
|
59
|
-
"day",
|
|
60
|
-
"date",
|
|
61
|
-
"decade",
|
|
62
|
-
"issue_suffix",
|
|
63
|
-
"month",
|
|
64
|
-
"monochrome",
|
|
65
|
-
"name",
|
|
66
|
-
"notes",
|
|
67
|
-
"page_count",
|
|
68
|
-
"reading_direction",
|
|
69
|
-
"review",
|
|
70
|
-
"size",
|
|
71
|
-
"summary",
|
|
72
|
-
"updated_at",
|
|
73
|
-
"user_rating",
|
|
74
|
-
"year",
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
def get_updated_field(self): # type: ignore
|
|
78
|
-
"""To only update models that have changed."""
|
|
79
|
-
return "updated_at"
|
codex/search/query.py
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"""Custom Codex search query classes."""
|
|
2
|
-
|
|
3
|
-
from codex._vendor.haystack.backends.whoosh_backend import (
|
|
4
|
-
WhooshSearchQuery,
|
|
5
|
-
)
|
|
6
|
-
from codex._vendor.haystack.constants import DJANGO_ID
|
|
7
|
-
from codex._vendor.haystack.query import SearchQuerySet
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class CodexSearchQuery(WhooshSearchQuery):
|
|
11
|
-
"""Custom search qeuery."""
|
|
12
|
-
|
|
13
|
-
def __init__(self, *args, **kwargs):
|
|
14
|
-
"""Give CodexSearchQuery a reverse attribute."""
|
|
15
|
-
super().__init__(*args, **kwargs)
|
|
16
|
-
self._reverse: bool = False
|
|
17
|
-
self._filter_comic_ids: frozenset | None = None
|
|
18
|
-
|
|
19
|
-
def clean(self, query_fragment):
|
|
20
|
-
"""Optimize to noop because RESERVED_ consts are empty in the backend."""
|
|
21
|
-
return query_fragment
|
|
22
|
-
|
|
23
|
-
def order_reverse(self):
|
|
24
|
-
"""Invert the reverse attribute."""
|
|
25
|
-
self._reverse = not self._reverse
|
|
26
|
-
|
|
27
|
-
def filter_comic_ids(self, ids):
|
|
28
|
-
"""Set a filter for the query."""
|
|
29
|
-
self._filter_comic_ids = frozenset(ids)
|
|
30
|
-
|
|
31
|
-
def get_results(self, **kwargs):
|
|
32
|
-
"""Send custom get results the reverse flag."""
|
|
33
|
-
custom_params = {}
|
|
34
|
-
if self._reverse:
|
|
35
|
-
custom_params["reverse"] = self._reverse
|
|
36
|
-
|
|
37
|
-
if self._filter_comic_ids:
|
|
38
|
-
filter_query = " OR ".join(
|
|
39
|
-
[f"{DJANGO_ID}:{pk}" for pk in self._filter_comic_ids]
|
|
40
|
-
)
|
|
41
|
-
custom_params["narrow_queries"] = {filter_query}
|
|
42
|
-
|
|
43
|
-
return super().get_results(**custom_params, **kwargs)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class CodexSearchQuerySet(SearchQuerySet):
|
|
47
|
-
"""SearchQuerySet with order_reverse method."""
|
|
48
|
-
|
|
49
|
-
def order_reverse(self):
|
|
50
|
-
"""Alters the order in which the results should appear."""
|
|
51
|
-
clone = self._clone()
|
|
52
|
-
clone.query.order_reverse() # type: ignore
|
|
53
|
-
return clone
|
|
54
|
-
|
|
55
|
-
def filter_comic_ids(self, ids):
|
|
56
|
-
"""Filter search by comic ids."""
|
|
57
|
-
clone = self._clone()
|
|
58
|
-
clone.query.filter_comic_ids(ids) # type: ignore
|
|
59
|
-
return clone
|
|
60
|
-
|
|
61
|
-
def values_list(self, *args, **kwargs):
|
|
62
|
-
"""Copy reverse setting into CodexSearchQuery."""
|
|
63
|
-
clone = super().values_list(*args, **kwargs)
|
|
64
|
-
if clone.query and self.query:
|
|
65
|
-
clone.query._reverse = self.query._reverse # noqa: SLF001
|
|
66
|
-
clone.query._filter_comic_ids = self.query._filter_comic_ids # noqa: SLF001
|
|
67
|
-
return clone
|
codex/search/writing.py
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
"""Custom Codex Writer."""
|
|
2
|
-
|
|
3
|
-
from threading import RLock
|
|
4
|
-
from time import sleep
|
|
5
|
-
|
|
6
|
-
from whoosh.index import FileIndex, LockError
|
|
7
|
-
from whoosh.reading import MultiReader
|
|
8
|
-
from whoosh.writing import BufferedWriter
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class AbortOperationError(Exception):
|
|
12
|
-
"""Interrupt the operation because something more important happened."""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class CodexWriter(BufferedWriter):
|
|
16
|
-
"""MP safe Buffered Writer that locks the index writer much more granularly.
|
|
17
|
-
|
|
18
|
-
XXX The period and limit args are ignored and this writer behaves more like the
|
|
19
|
-
AsycWriter. The AsyncWriter could possibly be extended to not lock until commit
|
|
20
|
-
time like this one and use less code.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
# For waiting for the writer lock
|
|
24
|
-
_DELAY = 0.25
|
|
25
|
-
|
|
26
|
-
def __init__(self, index, writerargs=None, commitargs=None):
|
|
27
|
-
"""Initialize with special values."""
|
|
28
|
-
# Same as BufferedWriter init without caching the index.writer() as self.writer
|
|
29
|
-
self.index = index
|
|
30
|
-
self.writerargs = writerargs or {}
|
|
31
|
-
self.commitargs = commitargs or {}
|
|
32
|
-
|
|
33
|
-
self.lock = RLock()
|
|
34
|
-
|
|
35
|
-
self._make_ram_index()
|
|
36
|
-
|
|
37
|
-
# Caching the schema so it doesn't need to derive from a writer every time.
|
|
38
|
-
self._schema = None
|
|
39
|
-
# Debug dict to print on close to diagnose what's waiting for locks.
|
|
40
|
-
self._time_sleeping = {}
|
|
41
|
-
|
|
42
|
-
def get_writer(self, caller="unknown"):
|
|
43
|
-
"""Wait for the lock to be available and furnish the writer."""
|
|
44
|
-
writer = None
|
|
45
|
-
while writer is None:
|
|
46
|
-
try:
|
|
47
|
-
writer = self.index.refresh().writer(**self.writerargs)
|
|
48
|
-
except LockError:
|
|
49
|
-
sleep(self._DELAY)
|
|
50
|
-
if caller not in self._time_sleeping:
|
|
51
|
-
self._time_sleeping[caller] = 0
|
|
52
|
-
self._time_sleeping[caller] += self._DELAY
|
|
53
|
-
return writer
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def schema(self):
|
|
57
|
-
"""Get a cached schema."""
|
|
58
|
-
if not self._schema:
|
|
59
|
-
info = self.index.refresh()._read_toc() # noqa SLF001
|
|
60
|
-
self._schema = info.schema
|
|
61
|
-
return self._schema
|
|
62
|
-
|
|
63
|
-
def _get_non_locking_writer_reader(self):
|
|
64
|
-
"""Get self.writer.reader() without locking.
|
|
65
|
-
|
|
66
|
-
Copied from SegmentWriter.reader()
|
|
67
|
-
|
|
68
|
-
XXX Runs the risk of being slightly out of date if it's actively being
|
|
69
|
-
written by other procs.
|
|
70
|
-
"""
|
|
71
|
-
with self.lock:
|
|
72
|
-
ramreader = self._get_ram_reader()
|
|
73
|
-
|
|
74
|
-
index = self.index.refresh()
|
|
75
|
-
info = index._read_toc() # noqa SLF001
|
|
76
|
-
return FileIndex._reader( # noqa SLF001
|
|
77
|
-
index.storage,
|
|
78
|
-
info.schema,
|
|
79
|
-
info.segments,
|
|
80
|
-
info.generation + 1,
|
|
81
|
-
# using the ram index for reuse massively reduces
|
|
82
|
-
# duplication, but is a hack.
|
|
83
|
-
reuse=ramreader,
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
def reader(self, **_kwargs):
|
|
87
|
-
"""Get the reader without locking the writer."""
|
|
88
|
-
reader = self._get_non_locking_writer_reader()
|
|
89
|
-
|
|
90
|
-
with self.lock:
|
|
91
|
-
ramreader = self._get_ram_reader()
|
|
92
|
-
|
|
93
|
-
# If there are in-memory docs, combine the readers
|
|
94
|
-
if ramreader.doc_count():
|
|
95
|
-
if reader.is_atomic():
|
|
96
|
-
reader = MultiReader([reader, ramreader])
|
|
97
|
-
else:
|
|
98
|
-
reader.add_reader(ramreader) # type: ignore
|
|
99
|
-
|
|
100
|
-
return reader
|
|
101
|
-
|
|
102
|
-
def commit(self, restart=True, reader=None): # noqa: ARG002
|
|
103
|
-
"""Commit with a writer we get now."""
|
|
104
|
-
with self.lock:
|
|
105
|
-
writer = self.get_writer("commit")
|
|
106
|
-
if reader:
|
|
107
|
-
writer.add_reader(reader)
|
|
108
|
-
ramreader = self._get_ram_reader()
|
|
109
|
-
writer.add_reader(ramreader)
|
|
110
|
-
writer.commit(**self.commitargs)
|
|
111
|
-
|
|
112
|
-
def add_reader(self, reader):
|
|
113
|
-
"""Do a commit with the supplied reader."""
|
|
114
|
-
# Pass through to the underlying on-disk index
|
|
115
|
-
self.commit(reader=reader)
|
|
116
|
-
|
|
117
|
-
def add_document(self, **fields):
|
|
118
|
-
"""Add a document with the cached schema."""
|
|
119
|
-
with self.lock, self.codec.writer(self.schema) as w:
|
|
120
|
-
# Hijack a writer to make the calls into the ram index.
|
|
121
|
-
w.add_document(**fields)
|
|
122
|
-
|
|
123
|
-
def _stage_segment_deletes(self, writer, docnums):
|
|
124
|
-
"""Get the deletes to perform."""
|
|
125
|
-
base = self.index.refresh().doc_count_all()
|
|
126
|
-
count = 0
|
|
127
|
-
with self.lock:
|
|
128
|
-
for docnum in sorted(docnums):
|
|
129
|
-
if docnum < base:
|
|
130
|
-
segment, segdocnum = writer._segment_and_docnum( # noqa SLF001
|
|
131
|
-
docnum
|
|
132
|
-
)
|
|
133
|
-
else:
|
|
134
|
-
segment = self.codec.segment
|
|
135
|
-
segdocnum = docnum - base
|
|
136
|
-
segment.delete_document(segdocnum, delete=True)
|
|
137
|
-
count += 1
|
|
138
|
-
return count
|
|
139
|
-
|
|
140
|
-
def delete_batch_documents(self, docnums):
|
|
141
|
-
"""Delete multiple documents very quickly."""
|
|
142
|
-
count = 0
|
|
143
|
-
writer = self.get_writer("delete_batch_docnums")
|
|
144
|
-
count = self._stage_segment_deletes(writer, docnums)
|
|
145
|
-
|
|
146
|
-
# Commit
|
|
147
|
-
try:
|
|
148
|
-
writer.commit(**self.commitargs)
|
|
149
|
-
except Exception:
|
|
150
|
-
with self.lock:
|
|
151
|
-
writer.cancel()
|
|
152
|
-
raise
|
|
153
|
-
return count
|
|
154
|
-
|
|
155
|
-
def is_deleted(self, docnum):
|
|
156
|
-
"""Check if document is deleted with temporary writer."""
|
|
157
|
-
base = self.index.refresh().doc_count_all()
|
|
158
|
-
if docnum < base:
|
|
159
|
-
writer = self.get_writer("is_deleted")
|
|
160
|
-
is_deleted = writer.is_deleted(docnum)
|
|
161
|
-
writer.cancel()
|
|
162
|
-
return is_deleted
|
|
163
|
-
return self._get_ram_reader().is_deleted(docnum - base)
|
|
164
|
-
|
|
165
|
-
def delete_by_query(self, q, searcher=None):
|
|
166
|
-
"""Delete any documents matching a query object.
|
|
167
|
-
|
|
168
|
-
:returns: the number of documents deleted.
|
|
169
|
-
Called by multiprocessing.
|
|
170
|
-
"""
|
|
171
|
-
s = searcher if searcher else self.searcher()
|
|
172
|
-
|
|
173
|
-
count = 0
|
|
174
|
-
try:
|
|
175
|
-
docnums = s.docs_for_query(q, for_deletion=True)
|
|
176
|
-
count = self.delete_batch_documents(docnums)
|
|
177
|
-
finally:
|
|
178
|
-
if not searcher:
|
|
179
|
-
s.close()
|
|
180
|
-
return count
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|