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
|
File without changes
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
from django import template
|
|
2
|
-
from django.conf import settings
|
|
3
|
-
from django.core.exceptions import ImproperlyConfigured
|
|
4
|
-
|
|
5
|
-
from ...haystack.utils import importlib
|
|
6
|
-
|
|
7
|
-
register = template.Library()
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class HighlightNode(template.Node):
|
|
11
|
-
def __init__(
|
|
12
|
-
self, text_block, query, html_tag=None, css_class=None, max_length=None
|
|
13
|
-
):
|
|
14
|
-
self.text_block = template.Variable(text_block)
|
|
15
|
-
self.query = template.Variable(query)
|
|
16
|
-
self.html_tag = html_tag
|
|
17
|
-
self.css_class = css_class
|
|
18
|
-
self.max_length = max_length
|
|
19
|
-
|
|
20
|
-
if html_tag is not None:
|
|
21
|
-
self.html_tag = template.Variable(html_tag)
|
|
22
|
-
|
|
23
|
-
if css_class is not None:
|
|
24
|
-
self.css_class = template.Variable(css_class)
|
|
25
|
-
|
|
26
|
-
if max_length is not None:
|
|
27
|
-
self.max_length = template.Variable(max_length)
|
|
28
|
-
|
|
29
|
-
def render(self, context):
|
|
30
|
-
text_block = self.text_block.resolve(context)
|
|
31
|
-
query = self.query.resolve(context)
|
|
32
|
-
kwargs = {}
|
|
33
|
-
|
|
34
|
-
if self.html_tag is not None:
|
|
35
|
-
kwargs["html_tag"] = self.html_tag.resolve(context)
|
|
36
|
-
|
|
37
|
-
if self.css_class is not None:
|
|
38
|
-
kwargs["css_class"] = self.css_class.resolve(context)
|
|
39
|
-
|
|
40
|
-
if self.max_length is not None:
|
|
41
|
-
kwargs["max_length"] = self.max_length.resolve(context)
|
|
42
|
-
|
|
43
|
-
# Handle a user-defined highlighting function.
|
|
44
|
-
if (
|
|
45
|
-
hasattr(settings, "HAYSTACK_CUSTOM_HIGHLIGHTER")
|
|
46
|
-
and settings.HAYSTACK_CUSTOM_HIGHLIGHTER
|
|
47
|
-
):
|
|
48
|
-
# Do the import dance.
|
|
49
|
-
try:
|
|
50
|
-
path_bits = settings.HAYSTACK_CUSTOM_HIGHLIGHTER.split(".")
|
|
51
|
-
highlighter_path, highlighter_classname = (
|
|
52
|
-
".".join(path_bits[:-1]),
|
|
53
|
-
path_bits[-1],
|
|
54
|
-
)
|
|
55
|
-
highlighter_module = importlib.import_module(highlighter_path)
|
|
56
|
-
highlighter_class = getattr(highlighter_module, highlighter_classname)
|
|
57
|
-
except (ImportError, AttributeError) as e:
|
|
58
|
-
raise ImproperlyConfigured(
|
|
59
|
-
"The highlighter '%s' could not be imported: %s"
|
|
60
|
-
% (settings.HAYSTACK_CUSTOM_HIGHLIGHTER, e)
|
|
61
|
-
)
|
|
62
|
-
else:
|
|
63
|
-
from ...haystack.utils.highlighting import Highlighter
|
|
64
|
-
|
|
65
|
-
highlighter_class = Highlighter
|
|
66
|
-
|
|
67
|
-
highlighter = highlighter_class(query, **kwargs)
|
|
68
|
-
highlighted_text = highlighter.highlight(text_block)
|
|
69
|
-
return highlighted_text
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@register.tag
|
|
73
|
-
def highlight(parser, token):
|
|
74
|
-
"""
|
|
75
|
-
Takes a block of text and highlights words from a provided query within that
|
|
76
|
-
block of text. Optionally accepts arguments to provide the HTML tag to wrap
|
|
77
|
-
highlighted word in, a CSS class to use with the tag and a maximum length of
|
|
78
|
-
the blurb in characters.
|
|
79
|
-
|
|
80
|
-
Syntax::
|
|
81
|
-
|
|
82
|
-
{% highlight <text_block> with <query> [css_class "class_name"] [html_tag "span"] [max_length 200] %}
|
|
83
|
-
|
|
84
|
-
Example::
|
|
85
|
-
|
|
86
|
-
# Highlight summary with default behavior.
|
|
87
|
-
{% highlight result.summary with request.query %}
|
|
88
|
-
|
|
89
|
-
# Highlight summary but wrap highlighted words with a div and the
|
|
90
|
-
# following CSS class.
|
|
91
|
-
{% highlight result.summary with request.query html_tag "div" css_class "highlight_me_please" %}
|
|
92
|
-
|
|
93
|
-
# Highlight summary but only show 40 characters.
|
|
94
|
-
{% highlight result.summary with request.query max_length 40 %}
|
|
95
|
-
"""
|
|
96
|
-
bits = token.split_contents()
|
|
97
|
-
tag_name = bits[0]
|
|
98
|
-
|
|
99
|
-
if not len(bits) % 2 == 0:
|
|
100
|
-
raise template.TemplateSyntaxError(
|
|
101
|
-
"'%s' tag requires valid pairings arguments." % tag_name
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
text_block = bits[1]
|
|
105
|
-
|
|
106
|
-
if len(bits) < 4:
|
|
107
|
-
raise template.TemplateSyntaxError(
|
|
108
|
-
"'%s' tag requires an object and a query provided by 'with'." % tag_name
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
if bits[2] != "with":
|
|
112
|
-
raise template.TemplateSyntaxError(
|
|
113
|
-
"'%s' tag's second argument should be 'with'." % tag_name
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
query = bits[3]
|
|
117
|
-
|
|
118
|
-
arg_bits = iter(bits[4:])
|
|
119
|
-
kwargs = {}
|
|
120
|
-
|
|
121
|
-
for bit in arg_bits:
|
|
122
|
-
if bit == "css_class":
|
|
123
|
-
kwargs["css_class"] = next(arg_bits)
|
|
124
|
-
|
|
125
|
-
if bit == "html_tag":
|
|
126
|
-
kwargs["html_tag"] = next(arg_bits)
|
|
127
|
-
|
|
128
|
-
if bit == "max_length":
|
|
129
|
-
kwargs["max_length"] = next(arg_bits)
|
|
130
|
-
|
|
131
|
-
return HighlightNode(text_block, query, **kwargs)
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from django import template
|
|
4
|
-
|
|
5
|
-
from ...haystack.query import SearchQuerySet
|
|
6
|
-
from ...haystack.utils.app_loading import haystack_get_model
|
|
7
|
-
|
|
8
|
-
register = template.Library()
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class MoreLikeThisNode(template.Node):
|
|
12
|
-
def __init__(self, model, varname, for_types=None, limit=None):
|
|
13
|
-
self.model = template.Variable(model)
|
|
14
|
-
self.varname = varname
|
|
15
|
-
self.for_types = for_types
|
|
16
|
-
self.limit = limit
|
|
17
|
-
|
|
18
|
-
if self.limit is not None:
|
|
19
|
-
self.limit = int(self.limit)
|
|
20
|
-
|
|
21
|
-
def render(self, context):
|
|
22
|
-
try:
|
|
23
|
-
model_instance = self.model.resolve(context)
|
|
24
|
-
sqs = SearchQuerySet()
|
|
25
|
-
|
|
26
|
-
if self.for_types is not None:
|
|
27
|
-
intermediate = template.Variable(self.for_types)
|
|
28
|
-
for_types = intermediate.resolve(context).split(",")
|
|
29
|
-
search_models = []
|
|
30
|
-
|
|
31
|
-
for model in for_types:
|
|
32
|
-
model_class = haystack_get_model(*model.split("."))
|
|
33
|
-
|
|
34
|
-
if model_class:
|
|
35
|
-
search_models.append(model_class)
|
|
36
|
-
|
|
37
|
-
sqs = sqs.models(*search_models)
|
|
38
|
-
|
|
39
|
-
sqs = sqs.more_like_this(model_instance)
|
|
40
|
-
|
|
41
|
-
if self.limit is not None:
|
|
42
|
-
sqs = sqs[: self.limit]
|
|
43
|
-
|
|
44
|
-
context[self.varname] = sqs
|
|
45
|
-
except Exception as exc:
|
|
46
|
-
logging.warning(
|
|
47
|
-
"Unhandled exception rendering %r: %s", self, exc, exc_info=True
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
return ""
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@register.tag
|
|
54
|
-
def more_like_this(parser, token):
|
|
55
|
-
"""
|
|
56
|
-
Fetches similar items from the search index to find content that is similar
|
|
57
|
-
to the provided model's content.
|
|
58
|
-
|
|
59
|
-
Syntax::
|
|
60
|
-
|
|
61
|
-
{% more_like_this model_instance as varname [for app_label.model_name,app_label.model_name,...] [limit n] %}
|
|
62
|
-
|
|
63
|
-
Example::
|
|
64
|
-
|
|
65
|
-
# Pull a full SearchQuerySet (lazy loaded) of similar content.
|
|
66
|
-
{% more_like_this entry as related_content %}
|
|
67
|
-
|
|
68
|
-
# Pull just the top 5 similar pieces of content.
|
|
69
|
-
{% more_like_this entry as related_content limit 5 %}
|
|
70
|
-
|
|
71
|
-
# Pull just the top 5 similar entries or comments.
|
|
72
|
-
{% more_like_this entry as related_content for "blog.entry,comments.comment" limit 5 %}
|
|
73
|
-
"""
|
|
74
|
-
bits = token.split_contents()
|
|
75
|
-
|
|
76
|
-
if not len(bits) in (4, 6, 8):
|
|
77
|
-
raise template.TemplateSyntaxError(
|
|
78
|
-
"'%s' tag requires either 3, 5 or 7 arguments." % bits[0]
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
model = bits[1]
|
|
82
|
-
|
|
83
|
-
if bits[2] != "as":
|
|
84
|
-
raise template.TemplateSyntaxError(
|
|
85
|
-
"'%s' tag's second argument should be 'as'." % bits[0]
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
varname = bits[3]
|
|
89
|
-
limit = None
|
|
90
|
-
for_types = None
|
|
91
|
-
|
|
92
|
-
if len(bits) == 6:
|
|
93
|
-
if bits[4] != "limit" and bits[4] != "for":
|
|
94
|
-
raise template.TemplateSyntaxError(
|
|
95
|
-
"'%s' tag's fourth argument should be either 'limit' or 'for'."
|
|
96
|
-
% bits[0]
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
if bits[4] == "limit":
|
|
100
|
-
limit = bits[5]
|
|
101
|
-
else:
|
|
102
|
-
for_types = bits[5]
|
|
103
|
-
|
|
104
|
-
if len(bits) == 8:
|
|
105
|
-
if bits[4] != "for":
|
|
106
|
-
raise template.TemplateSyntaxError(
|
|
107
|
-
"'%s' tag's fourth argument should be 'for'." % bits[0]
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
for_types = bits[5]
|
|
111
|
-
|
|
112
|
-
if bits[6] != "limit":
|
|
113
|
-
raise template.TemplateSyntaxError(
|
|
114
|
-
"'%s' tag's sixth argument should be 'limit'." % bits[0]
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
limit = bits[7]
|
|
118
|
-
|
|
119
|
-
return MoreLikeThisNode(model, varname, for_types, limit)
|
codex/_vendor/haystack/urls.py
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import importlib
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
from django.conf import settings
|
|
5
|
-
|
|
6
|
-
from ...haystack.constants import DJANGO_CT, DJANGO_ID, ID
|
|
7
|
-
from ...haystack.utils.highlighting import Highlighter # noqa=F401
|
|
8
|
-
|
|
9
|
-
IDENTIFIER_REGEX = re.compile(r"^[\w\d_]+\.[\w\d_]+\.[\w\d-]+$")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def default_get_identifier(obj_or_string):
|
|
13
|
-
"""
|
|
14
|
-
Get an unique identifier for the object or a string representing the
|
|
15
|
-
object.
|
|
16
|
-
|
|
17
|
-
If not overridden, uses <app_label>.<object_name>.<pk>.
|
|
18
|
-
"""
|
|
19
|
-
if isinstance(obj_or_string, str):
|
|
20
|
-
if not IDENTIFIER_REGEX.match(obj_or_string):
|
|
21
|
-
raise AttributeError(
|
|
22
|
-
"Provided string '%s' is not a valid identifier." % obj_or_string
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
return obj_or_string
|
|
26
|
-
|
|
27
|
-
return "%s.%s" % (get_model_ct(obj_or_string), obj_or_string._get_pk_val())
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def _lookup_identifier_method():
|
|
31
|
-
"""
|
|
32
|
-
If the user has set HAYSTACK_IDENTIFIER_METHOD, import it and return the method uncalled.
|
|
33
|
-
If HAYSTACK_IDENTIFIER_METHOD is not defined, return haystack.utils.default_get_identifier.
|
|
34
|
-
|
|
35
|
-
This always runs at module import time. We keep the code in a function
|
|
36
|
-
so that it can be called from unit tests, in order to simulate the re-loading
|
|
37
|
-
of this module.
|
|
38
|
-
"""
|
|
39
|
-
if not hasattr(settings, "HAYSTACK_IDENTIFIER_METHOD"):
|
|
40
|
-
return default_get_identifier
|
|
41
|
-
|
|
42
|
-
module_path, method_name = settings.HAYSTACK_IDENTIFIER_METHOD.rsplit(".", 1)
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
module = importlib.import_module(module_path)
|
|
46
|
-
except ImportError:
|
|
47
|
-
raise ImportError(
|
|
48
|
-
"Unable to import module '%s' provided for HAYSTACK_IDENTIFIER_METHOD."
|
|
49
|
-
% module_path
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
identifier_method = getattr(module, method_name, None)
|
|
53
|
-
|
|
54
|
-
if not identifier_method:
|
|
55
|
-
raise AttributeError(
|
|
56
|
-
"Provided method '%s' for HAYSTACK_IDENTIFIER_METHOD does not exist in '%s'."
|
|
57
|
-
% (method_name, module_path)
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
return identifier_method
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
get_identifier = _lookup_identifier_method()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def get_model_ct_tuple(model):
|
|
67
|
-
# Deferred models should be identified as if they were the underlying model.
|
|
68
|
-
model_name = (
|
|
69
|
-
model._meta.concrete_model._meta.model_name
|
|
70
|
-
if hasattr(model, "_deferred") and model._deferred
|
|
71
|
-
else model._meta.model_name
|
|
72
|
-
)
|
|
73
|
-
return (model._meta.app_label, model_name)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def get_model_ct(model):
|
|
77
|
-
return "%s.%s" % get_model_ct_tuple(model)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def get_facet_field_name(fieldname):
|
|
81
|
-
if fieldname in [ID, DJANGO_ID, DJANGO_CT]:
|
|
82
|
-
return fieldname
|
|
83
|
-
|
|
84
|
-
return "%s_exact" % fieldname
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from django.apps import apps
|
|
2
|
-
from django.core.exceptions import ImproperlyConfigured
|
|
3
|
-
|
|
4
|
-
__all__ = ["haystack_get_models", "haystack_load_apps"]
|
|
5
|
-
|
|
6
|
-
APP = "app"
|
|
7
|
-
MODEL = "model"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def haystack_get_app_modules():
|
|
11
|
-
"""Return the Python module for each installed app"""
|
|
12
|
-
return [i.module for i in apps.get_app_configs()]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def haystack_load_apps():
|
|
16
|
-
"""Return a list of app labels for all installed applications which have models"""
|
|
17
|
-
return [i.label for i in apps.get_app_configs() if i.models_module is not None]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def haystack_get_models(label):
|
|
21
|
-
try:
|
|
22
|
-
app_mod = apps.get_app_config(label)
|
|
23
|
-
return app_mod.get_models()
|
|
24
|
-
except LookupError:
|
|
25
|
-
if "." not in label:
|
|
26
|
-
raise ImproperlyConfigured("Unknown application label {}".format(label))
|
|
27
|
-
app_label, model_name = label.rsplit(".", 1)
|
|
28
|
-
return [apps.get_model(app_label, model_name)]
|
|
29
|
-
except ImproperlyConfigured:
|
|
30
|
-
pass
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def haystack_get_model(app_label, model_name):
|
|
34
|
-
return apps.get_model(app_label, model_name)
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
from ...haystack.constants import WGS_84_SRID
|
|
2
|
-
from ...haystack.exceptions import SpatialError
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def ensure_geometry(geom):
|
|
6
|
-
"""
|
|
7
|
-
Makes sure the parameter passed in looks like a GEOS ``GEOSGeometry``.
|
|
8
|
-
"""
|
|
9
|
-
if not hasattr(geom, "geom_type"):
|
|
10
|
-
raise SpatialError("Point '%s' doesn't appear to be a GEOS geometry." % geom)
|
|
11
|
-
|
|
12
|
-
return geom
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def ensure_point(geom):
|
|
16
|
-
"""
|
|
17
|
-
Makes sure the parameter passed in looks like a GEOS ``Point``.
|
|
18
|
-
"""
|
|
19
|
-
ensure_geometry(geom)
|
|
20
|
-
|
|
21
|
-
if geom.geom_type != "Point":
|
|
22
|
-
raise SpatialError("Provided geometry '%s' is not a 'Point'." % geom)
|
|
23
|
-
|
|
24
|
-
return geom
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def ensure_wgs84(point):
|
|
28
|
-
"""
|
|
29
|
-
Ensures the point passed in is a GEOS ``Point`` & returns that point's
|
|
30
|
-
data is in the WGS-84 spatial reference.
|
|
31
|
-
"""
|
|
32
|
-
ensure_point(point)
|
|
33
|
-
# Clone it so we don't alter the original, in case they're using it for
|
|
34
|
-
# something else.
|
|
35
|
-
new_point = point.clone()
|
|
36
|
-
|
|
37
|
-
if not new_point.srid:
|
|
38
|
-
# It has no spatial reference id. Assume WGS-84.
|
|
39
|
-
new_point.srid = WGS_84_SRID
|
|
40
|
-
elif new_point.srid != WGS_84_SRID:
|
|
41
|
-
# Transform it to get to the right system.
|
|
42
|
-
new_point.transform(WGS_84_SRID)
|
|
43
|
-
|
|
44
|
-
return new_point
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def ensure_distance(dist):
|
|
48
|
-
"""
|
|
49
|
-
Makes sure the parameter passed in is a 'Distance' object.
|
|
50
|
-
"""
|
|
51
|
-
try:
|
|
52
|
-
# Since we mostly only care about the ``.km`` attribute, make sure
|
|
53
|
-
# it's there.
|
|
54
|
-
dist.km
|
|
55
|
-
except AttributeError:
|
|
56
|
-
raise SpatialError("'%s' does not appear to be a 'Distance' object." % dist)
|
|
57
|
-
|
|
58
|
-
return dist
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def generate_bounding_box(bottom_left, top_right):
|
|
62
|
-
"""
|
|
63
|
-
Takes two opposite corners of a bounding box (order matters!) & generates
|
|
64
|
-
a two-tuple of the correct coordinates for the bounding box.
|
|
65
|
-
|
|
66
|
-
The two-tuple is in the form ``((min_lat, min_lng), (max_lat, max_lng))``.
|
|
67
|
-
"""
|
|
68
|
-
west, lat_1 = bottom_left.coords
|
|
69
|
-
east, lat_2 = top_right.coords
|
|
70
|
-
min_lat, max_lat = min(lat_1, lat_2), max(lat_1, lat_2)
|
|
71
|
-
return ((min_lat, west), (max_lat, east))
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
from django.utils.html import strip_tags
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Highlighter:
|
|
5
|
-
css_class = "highlighted"
|
|
6
|
-
html_tag = "span"
|
|
7
|
-
max_length = 200
|
|
8
|
-
text_block = ""
|
|
9
|
-
|
|
10
|
-
def __init__(self, query, **kwargs):
|
|
11
|
-
self.query = query
|
|
12
|
-
|
|
13
|
-
if "max_length" in kwargs:
|
|
14
|
-
self.max_length = int(kwargs["max_length"])
|
|
15
|
-
|
|
16
|
-
if "html_tag" in kwargs:
|
|
17
|
-
self.html_tag = kwargs["html_tag"]
|
|
18
|
-
|
|
19
|
-
if "css_class" in kwargs:
|
|
20
|
-
self.css_class = kwargs["css_class"]
|
|
21
|
-
|
|
22
|
-
self.query_words = {
|
|
23
|
-
word.lower() for word in self.query.split() if not word.startswith("-")
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
def highlight(self, text_block):
|
|
27
|
-
self.text_block = strip_tags(text_block)
|
|
28
|
-
highlight_locations = self.find_highlightable_words()
|
|
29
|
-
start_offset, end_offset = self.find_window(highlight_locations)
|
|
30
|
-
return self.render_html(highlight_locations, start_offset, end_offset)
|
|
31
|
-
|
|
32
|
-
def find_highlightable_words(self):
|
|
33
|
-
# Use a set so we only do this once per unique word.
|
|
34
|
-
word_positions = {}
|
|
35
|
-
|
|
36
|
-
# Pre-compute the length.
|
|
37
|
-
end_offset = len(self.text_block)
|
|
38
|
-
lower_text_block = self.text_block.lower()
|
|
39
|
-
|
|
40
|
-
for word in self.query_words:
|
|
41
|
-
if word not in word_positions:
|
|
42
|
-
word_positions[word] = []
|
|
43
|
-
|
|
44
|
-
start_offset = 0
|
|
45
|
-
|
|
46
|
-
while start_offset < end_offset:
|
|
47
|
-
next_offset = lower_text_block.find(word, start_offset, end_offset)
|
|
48
|
-
|
|
49
|
-
# If we get a -1 out of find, it wasn't found. Bomb out and
|
|
50
|
-
# start the next word.
|
|
51
|
-
if next_offset == -1:
|
|
52
|
-
break
|
|
53
|
-
|
|
54
|
-
word_positions[word].append(next_offset)
|
|
55
|
-
start_offset = next_offset + len(word)
|
|
56
|
-
|
|
57
|
-
return word_positions
|
|
58
|
-
|
|
59
|
-
def find_window(self, highlight_locations):
|
|
60
|
-
best_start = 0
|
|
61
|
-
best_end = self.max_length
|
|
62
|
-
|
|
63
|
-
# First, make sure we have words.
|
|
64
|
-
if not len(highlight_locations):
|
|
65
|
-
return (best_start, best_end)
|
|
66
|
-
|
|
67
|
-
words_found = []
|
|
68
|
-
|
|
69
|
-
# Next, make sure we found any words at all.
|
|
70
|
-
for _, offset_list in highlight_locations.items():
|
|
71
|
-
if len(offset_list):
|
|
72
|
-
# Add all of the locations to the list.
|
|
73
|
-
words_found.extend(offset_list)
|
|
74
|
-
|
|
75
|
-
if not len(words_found):
|
|
76
|
-
return (best_start, best_end)
|
|
77
|
-
|
|
78
|
-
if len(words_found) == 1:
|
|
79
|
-
return (words_found[0], words_found[0] + self.max_length)
|
|
80
|
-
|
|
81
|
-
# Sort the list so it's in ascending order.
|
|
82
|
-
words_found = sorted(words_found)
|
|
83
|
-
|
|
84
|
-
# We now have a denormalized list of all positions were a word was
|
|
85
|
-
# found. We'll iterate through and find the densest window we can by
|
|
86
|
-
# counting the number of found offsets (-1 to fit in the window).
|
|
87
|
-
highest_density = 0
|
|
88
|
-
|
|
89
|
-
if words_found[:-1][0] > self.max_length:
|
|
90
|
-
best_start = words_found[:-1][0]
|
|
91
|
-
best_end = best_start + self.max_length
|
|
92
|
-
|
|
93
|
-
for count, start in enumerate(words_found[:-1]):
|
|
94
|
-
current_density = 1
|
|
95
|
-
|
|
96
|
-
for end in words_found[count + 1 :]:
|
|
97
|
-
if end - start < self.max_length:
|
|
98
|
-
current_density += 1
|
|
99
|
-
else:
|
|
100
|
-
current_density = 0
|
|
101
|
-
|
|
102
|
-
# Only replace if we have a bigger (not equal density) so we
|
|
103
|
-
# give deference to windows earlier in the document.
|
|
104
|
-
if current_density > highest_density:
|
|
105
|
-
best_start = start
|
|
106
|
-
best_end = start + self.max_length
|
|
107
|
-
highest_density = current_density
|
|
108
|
-
|
|
109
|
-
return (best_start, best_end)
|
|
110
|
-
|
|
111
|
-
def render_html(self, highlight_locations=None, start_offset=None, end_offset=None):
|
|
112
|
-
# Start by chopping the block down to the proper window.
|
|
113
|
-
text = self.text_block[start_offset:end_offset]
|
|
114
|
-
|
|
115
|
-
# Invert highlight_locations to a location -> term list
|
|
116
|
-
term_list = []
|
|
117
|
-
|
|
118
|
-
for term, locations in highlight_locations.items():
|
|
119
|
-
term_list += [(loc - start_offset, term) for loc in locations]
|
|
120
|
-
|
|
121
|
-
loc_to_term = sorted(term_list)
|
|
122
|
-
|
|
123
|
-
# Prepare the highlight template
|
|
124
|
-
if self.css_class:
|
|
125
|
-
hl_start = '<%s class="%s">' % (self.html_tag, self.css_class)
|
|
126
|
-
else:
|
|
127
|
-
hl_start = "<%s>" % (self.html_tag)
|
|
128
|
-
|
|
129
|
-
hl_end = "</%s>" % self.html_tag
|
|
130
|
-
|
|
131
|
-
# Copy the part from the start of the string to the first match,
|
|
132
|
-
# and there replace the match with a highlighted version.
|
|
133
|
-
highlighted_chunk = ""
|
|
134
|
-
matched_so_far = 0
|
|
135
|
-
prev = 0
|
|
136
|
-
prev_str = ""
|
|
137
|
-
|
|
138
|
-
for cur, cur_str in loc_to_term:
|
|
139
|
-
# This can be in a different case than cur_str
|
|
140
|
-
actual_term = text[cur : cur + len(cur_str)]
|
|
141
|
-
|
|
142
|
-
# Handle incorrect highlight_locations by first checking for the term
|
|
143
|
-
if actual_term.lower() == cur_str:
|
|
144
|
-
if cur < prev + len(prev_str):
|
|
145
|
-
continue
|
|
146
|
-
|
|
147
|
-
highlighted_chunk += (
|
|
148
|
-
text[prev + len(prev_str) : cur] + hl_start + actual_term + hl_end
|
|
149
|
-
)
|
|
150
|
-
prev = cur
|
|
151
|
-
prev_str = cur_str
|
|
152
|
-
|
|
153
|
-
# Keep track of how far we've copied so far, for the last step
|
|
154
|
-
matched_so_far = cur + len(actual_term)
|
|
155
|
-
|
|
156
|
-
# Don't forget the chunk after the last term
|
|
157
|
-
highlighted_chunk += text[matched_so_far:]
|
|
158
|
-
|
|
159
|
-
if start_offset > 0:
|
|
160
|
-
highlighted_chunk = "...%s" % highlighted_chunk
|
|
161
|
-
|
|
162
|
-
if end_offset < len(self.text_block):
|
|
163
|
-
highlighted_chunk = "%s..." % highlighted_chunk
|
|
164
|
-
|
|
165
|
-
return highlighted_chunk
|