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.

Files changed (576) hide show
  1. codex/db.py +119 -0
  2. codex/librarian/covers/purge.py +1 -1
  3. codex/librarian/importer/const.py +7 -1
  4. codex/librarian/importer/create_fks.py +5 -2
  5. codex/librarian/importer/importerd.py +17 -12
  6. codex/librarian/importer/moved.py +44 -13
  7. codex/librarian/importer/tasks.py +2 -0
  8. codex/librarian/janitor/cleanup.py +21 -8
  9. codex/librarian/janitor/integrity.py +369 -0
  10. codex/librarian/janitor/janitor.py +50 -15
  11. codex/librarian/janitor/latest_version.py +2 -1
  12. codex/librarian/janitor/status.py +5 -0
  13. codex/librarian/janitor/tasks.py +37 -0
  14. codex/librarian/janitor/update.py +1 -1
  15. codex/librarian/librariand.py +32 -24
  16. codex/librarian/search/optimize.py +43 -0
  17. codex/librarian/search/remove.py +18 -46
  18. codex/librarian/search/searchd.py +14 -17
  19. codex/librarian/search/status.py +2 -1
  20. codex/librarian/search/tasks.py +4 -9
  21. codex/librarian/search/update.py +223 -301
  22. codex/librarian/watchdog/observers.py +2 -1
  23. codex/migrations/0029_comicfts.py +58 -0
  24. codex/migrations/0030_nocase_collation_day_month_indexes_status_types.py +198 -0
  25. codex/models/admin.py +15 -6
  26. codex/models/base.py +9 -0
  27. codex/models/bookmark.py +3 -3
  28. codex/models/comic.py +68 -16
  29. codex/models/functions.py +59 -2
  30. codex/models/groups.py +6 -1
  31. codex/models/library.py +1 -3
  32. codex/models/paths.py +9 -3
  33. codex/serializers/browser/page.py +2 -0
  34. codex/settings/settings.py +16 -17
  35. codex/signals/django_signals.py +4 -10
  36. codex/startup.py +1 -35
  37. codex/static_root/assets/{VCheckbox-Cko1sQK-.170726b3b8a1.js → VCheckbox-CWiqcZ7a.3feebd47daab.js} +1 -1
  38. codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.br +0 -0
  39. codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.gz +0 -0
  40. codex/static_root/assets/{VCheckbox-Cko1sQK-.js → VCheckbox-CWiqcZ7a.js} +1 -1
  41. codex/static_root/assets/VCheckbox-CWiqcZ7a.js.br +0 -0
  42. codex/static_root/assets/VCheckbox-CWiqcZ7a.js.gz +0 -0
  43. codex/static_root/assets/{VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js → VCheckboxBtn-Dk3iB62X.bc66d5351baa.js} +1 -1
  44. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.br +0 -0
  45. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.gz +0 -0
  46. codex/static_root/assets/{VCheckboxBtn-CO3-aqQL.js → VCheckboxBtn-Dk3iB62X.js} +1 -1
  47. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.br +0 -0
  48. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.gz +0 -0
  49. codex/static_root/assets/{VCombobox-Bjp6lffE.js → VCombobox-D1_KU27A.b1d30587a973.js} +1 -1
  50. codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.br +0 -0
  51. codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.gz +0 -0
  52. codex/static_root/assets/{VCombobox-Bjp6lffE.caf59fba486e.js → VCombobox-D1_KU27A.js} +1 -1
  53. codex/static_root/assets/VCombobox-D1_KU27A.js.br +0 -0
  54. codex/static_root/assets/VCombobox-D1_KU27A.js.gz +0 -0
  55. codex/static_root/assets/{VDialog-BkpVGB70.6eea3ca762a5.js → VDialog-GbxvFMMN.594fb3a39ced.js} +1 -1
  56. codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.br +0 -0
  57. codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.gz +0 -0
  58. codex/static_root/assets/{VDialog-BkpVGB70.js → VDialog-GbxvFMMN.js} +1 -1
  59. codex/static_root/assets/VDialog-GbxvFMMN.js.br +0 -0
  60. codex/static_root/assets/VDialog-GbxvFMMN.js.gz +0 -0
  61. codex/static_root/assets/{VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js → VExpansionPanels-Cywv_bEj.a762e63858e5.js} +1 -1
  62. codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.br +0 -0
  63. codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.gz +0 -0
  64. codex/static_root/assets/{VExpansionPanels-Ci2-j8XX.js → VExpansionPanels-Cywv_bEj.js} +1 -1
  65. codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.br +0 -0
  66. codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.gz +0 -0
  67. codex/static_root/assets/{VRadioGroup-D3Py5BfQ.a4f25edb86d3.js → VRadioGroup-C0etmxpZ.0eb3009a6300.js} +1 -1
  68. codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.br +0 -0
  69. codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.gz +0 -0
  70. codex/static_root/assets/{VRadioGroup-D3Py5BfQ.js → VRadioGroup-C0etmxpZ.js} +1 -1
  71. codex/static_root/assets/VRadioGroup-C0etmxpZ.js.br +0 -0
  72. codex/static_root/assets/VRadioGroup-C0etmxpZ.js.gz +0 -0
  73. codex/static_root/assets/{VSelect-DtvZsYZz.e98745d858eb.js → VSelect-txrnRiNJ.a5625760018c.js} +1 -1
  74. codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.br +0 -0
  75. codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.gz +0 -0
  76. codex/static_root/assets/{VSelect-DtvZsYZz.js → VSelect-txrnRiNJ.js} +1 -1
  77. codex/static_root/assets/VSelect-txrnRiNJ.js.br +0 -0
  78. codex/static_root/assets/VSelect-txrnRiNJ.js.gz +0 -0
  79. codex/static_root/assets/{VSelectionControl-D6kmykQW.1e1fda62ceba.js → VSelectionControl-CY6RmcvW.7e8824224f6c.js} +1 -1
  80. codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.br +0 -0
  81. codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.gz +0 -0
  82. codex/static_root/assets/{VSelectionControl-D6kmykQW.js → VSelectionControl-CY6RmcvW.js} +1 -1
  83. codex/static_root/assets/VSelectionControl-CY6RmcvW.js.br +0 -0
  84. codex/static_root/assets/VSelectionControl-CY6RmcvW.js.gz +0 -0
  85. codex/static_root/assets/{VSlideGroup-UGZWxpF5.360bdefcd215.js → VSlideGroup-DtEQMwY1.6c72325635ed.js} +1 -1
  86. codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.br +0 -0
  87. codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.gz +0 -0
  88. codex/static_root/assets/{VSlideGroup-UGZWxpF5.js → VSlideGroup-DtEQMwY1.js} +1 -1
  89. codex/static_root/assets/VSlideGroup-DtEQMwY1.js.br +0 -0
  90. codex/static_root/assets/VSlideGroup-DtEQMwY1.js.gz +0 -0
  91. codex/static_root/assets/{VTable-DXH_yQ0o.1149f56ad566.js → VTable-2af995xo.6c5bf36631b2.js} +1 -1
  92. codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.br +0 -0
  93. codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.gz +0 -0
  94. codex/static_root/assets/{VTable-DXH_yQ0o.js → VTable-2af995xo.js} +1 -1
  95. codex/static_root/assets/VTable-2af995xo.js.br +0 -0
  96. codex/static_root/assets/VTable-2af995xo.js.gz +0 -0
  97. codex/static_root/assets/{VTextField-Blqy56_L.26d8761e0155.js → VTextField-G6CMj7yO.657a130dd302.js} +1 -1
  98. codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.br +0 -0
  99. codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.gz +0 -0
  100. codex/static_root/assets/{VTextField-Blqy56_L.js → VTextField-G6CMj7yO.js} +1 -1
  101. codex/static_root/assets/VTextField-G6CMj7yO.js.br +0 -0
  102. codex/static_root/assets/VTextField-G6CMj7yO.js.gz +0 -0
  103. codex/static_root/assets/{VWindowItem-C8_ovV7e.afbc08414765.js → VWindowItem-CzoGd6Ul.721e5da11ad4.js} +1 -1
  104. codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.br +0 -0
  105. codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.gz +0 -0
  106. codex/static_root/assets/{VWindowItem-C8_ovV7e.js → VWindowItem-CzoGd6Ul.js} +1 -1
  107. codex/static_root/assets/VWindowItem-CzoGd6Ul.js.br +0 -0
  108. codex/static_root/assets/VWindowItem-CzoGd6Ul.js.gz +0 -0
  109. codex/static_root/assets/{admin-DTRn8bBs.d031ea42d8d0.js → admin-CsPAIQeG.5175467148a3.js} +1 -1
  110. codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.br +0 -0
  111. codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.gz +0 -0
  112. codex/static_root/assets/{admin-DTRn8bBs.js → admin-CsPAIQeG.js} +1 -1
  113. codex/static_root/assets/admin-CsPAIQeG.js.br +0 -0
  114. codex/static_root/assets/admin-CsPAIQeG.js.gz +0 -0
  115. codex/static_root/assets/{admin-drawer-panel-BaXFOcru.b1a44b05f482.js → admin-drawer-panel-BAT7GA64.73a064628c19.js} +11 -11
  116. codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.br +0 -0
  117. codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.gz +0 -0
  118. codex/static_root/assets/{admin-drawer-panel-BaXFOcru.js → admin-drawer-panel-BAT7GA64.js} +11 -11
  119. codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.br +0 -0
  120. codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.gz +0 -0
  121. codex/static_root/assets/browser-C18l93yc.923860009ef4.js +1 -0
  122. codex/static_root/assets/browser-C18l93yc.923860009ef4.js.br +0 -0
  123. codex/static_root/assets/browser-C18l93yc.923860009ef4.js.gz +0 -0
  124. codex/static_root/assets/browser-C18l93yc.js +1 -0
  125. codex/static_root/assets/browser-C18l93yc.js.br +0 -0
  126. codex/static_root/assets/browser-C18l93yc.js.gz +0 -0
  127. codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css +1 -0
  128. codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.br +0 -0
  129. codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.gz +0 -0
  130. codex/static_root/assets/browser-DaAqlBYO.css +1 -0
  131. codex/static_root/assets/browser-DaAqlBYO.css.br +0 -0
  132. codex/static_root/assets/browser-DaAqlBYO.css.gz +0 -0
  133. codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js +1 -0
  134. codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.br +0 -0
  135. codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.gz +0 -0
  136. codex/static_root/assets/change-password-dialog-BazJdNs9.js +1 -0
  137. codex/static_root/assets/change-password-dialog-BazJdNs9.js.br +0 -0
  138. codex/static_root/assets/change-password-dialog-BazJdNs9.js.gz +0 -0
  139. codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css +1 -0
  140. codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.br +0 -0
  141. codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.gz +0 -0
  142. codex/static_root/assets/change-password-dialog-Mlmvz8_4.css +1 -0
  143. codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.br +0 -0
  144. codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.gz +0 -0
  145. codex/static_root/assets/{confirm-dialog-D3_s2DQy.9dde66036c72.js → confirm-dialog-CbW3hlOp.9dd39b443c31.js} +1 -1
  146. codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.br +0 -0
  147. codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.gz +0 -0
  148. codex/static_root/assets/{confirm-dialog-D3_s2DQy.js → confirm-dialog-CbW3hlOp.js} +1 -1
  149. codex/static_root/assets/confirm-dialog-CbW3hlOp.js.br +0 -0
  150. codex/static_root/assets/confirm-dialog-CbW3hlOp.js.gz +0 -0
  151. codex/static_root/assets/{datetime-column-BxC1Li9e.3dc314b3295d.js → datetime-column-uSqKZU37.b157b0743db5.js} +1 -1
  152. codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.br +0 -0
  153. codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.gz +0 -0
  154. codex/static_root/assets/{datetime-column-BxC1Li9e.js → datetime-column-uSqKZU37.js} +1 -1
  155. codex/static_root/assets/datetime-column-uSqKZU37.js.br +0 -0
  156. codex/static_root/assets/datetime-column-uSqKZU37.js.gz +0 -0
  157. codex/static_root/assets/{filter-Bzk7x841.d0c3b602e62d.js → filter-CBl5KEsB.845a81573c2e.js} +1 -1
  158. codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.br +0 -0
  159. codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.gz +0 -0
  160. codex/static_root/assets/{filter-Bzk7x841.js → filter-CBl5KEsB.js} +1 -1
  161. codex/static_root/assets/filter-CBl5KEsB.js.br +0 -0
  162. codex/static_root/assets/filter-CBl5KEsB.js.gz +0 -0
  163. codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js +1 -0
  164. codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.br +0 -0
  165. codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.gz +0 -0
  166. codex/static_root/assets/flag-tab-CT8pqM4r.js +1 -0
  167. codex/static_root/assets/flag-tab-CT8pqM4r.js.br +0 -0
  168. codex/static_root/assets/flag-tab-CT8pqM4r.js.gz +0 -0
  169. codex/static_root/assets/{group-tab-5esH4lwX.a81fdd258117.js → group-tab-BOW9kigw.7e5007596367.js} +1 -1
  170. codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.br +0 -0
  171. codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.gz +0 -0
  172. codex/static_root/assets/{group-tab-5esH4lwX.js → group-tab-BOW9kigw.js} +1 -1
  173. codex/static_root/assets/group-tab-BOW9kigw.js.br +0 -0
  174. codex/static_root/assets/group-tab-BOW9kigw.js.gz +0 -0
  175. codex/static_root/assets/{http-error-BTOBfcGY.52ced6f28420.js → http-error-C6yuLApo.45cf99eb7a70.js} +1 -1
  176. codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.br +4 -0
  177. codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.gz +0 -0
  178. codex/static_root/assets/{http-error-BTOBfcGY.js → http-error-C6yuLApo.js} +1 -1
  179. codex/static_root/assets/http-error-C6yuLApo.js.br +4 -0
  180. codex/static_root/assets/http-error-C6yuLApo.js.gz +0 -0
  181. codex/static_root/assets/{library-tab-DiPEdOF7.ece2d642a9dc.js → library-tab-D3WOtpE0.9af34e6e7235.js} +1 -1
  182. codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.br +0 -0
  183. codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.gz +0 -0
  184. codex/static_root/assets/{library-tab-DiPEdOF7.js → library-tab-D3WOtpE0.js} +1 -1
  185. codex/static_root/assets/library-tab-D3WOtpE0.js.br +0 -0
  186. codex/static_root/assets/library-tab-D3WOtpE0.js.gz +0 -0
  187. codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js +6 -0
  188. codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.br +0 -0
  189. codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.gz +0 -0
  190. codex/static_root/assets/main-CmjU2oTp.js +6 -0
  191. codex/static_root/assets/main-CmjU2oTp.js.br +0 -0
  192. codex/static_root/assets/main-CmjU2oTp.js.gz +0 -0
  193. codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js +1 -0
  194. codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.br +0 -0
  195. codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.gz +0 -0
  196. codex/static_root/assets/pager-full-pdf-BH54_5f6.js +1 -0
  197. codex/static_root/assets/pager-full-pdf-BH54_5f6.js.br +0 -0
  198. codex/static_root/assets/pager-full-pdf-BH54_5f6.js.gz +0 -0
  199. codex/static_root/assets/{pagination-toolbar-C45-onwe.4722195bb60b.css → pagination-toolbar-DGrxp2OD.css} +1 -1
  200. codex/static_root/assets/pagination-toolbar-DGrxp2OD.css.br +0 -0
  201. codex/static_root/assets/pagination-toolbar-DGrxp2OD.css.gz +0 -0
  202. codex/static_root/assets/{pagination-toolbar-C45-onwe.css → pagination-toolbar-DGrxp2OD.f960a624f04f.css} +1 -1
  203. codex/static_root/assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css.br +0 -0
  204. codex/static_root/assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css.gz +0 -0
  205. codex/static_root/assets/{pagination-toolbar-CkIzuyx1.00c354a82e6e.js → pagination-toolbar-JH3Kbffr.451171f7ac33.js} +1 -1
  206. codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.br +0 -0
  207. codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.gz +0 -0
  208. codex/static_root/assets/{pagination-toolbar-CkIzuyx1.js → pagination-toolbar-JH3Kbffr.js} +1 -1
  209. codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.br +0 -0
  210. codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.gz +0 -0
  211. codex/static_root/assets/{pdf-doc-BjlRPuf4.0f056fa321b6.js → pdf-doc-BcRNpoaW.882f6e967869.js} +1 -1
  212. codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.br +0 -0
  213. codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.gz +0 -0
  214. codex/static_root/assets/{pdf-doc-BjlRPuf4.js → pdf-doc-BcRNpoaW.js} +1 -1
  215. codex/static_root/assets/pdf-doc-BcRNpoaW.js.br +0 -0
  216. codex/static_root/assets/pdf-doc-BcRNpoaW.js.gz +0 -0
  217. codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js +2 -0
  218. codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.br +0 -0
  219. codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.gz +0 -0
  220. codex/static_root/assets/reader-DIPRg2VB.js +2 -0
  221. codex/static_root/assets/reader-DIPRg2VB.js.br +0 -0
  222. codex/static_root/assets/reader-DIPRg2VB.js.gz +0 -0
  223. codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css +1 -0
  224. codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.br +0 -0
  225. codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.gz +0 -0
  226. codex/static_root/assets/reader-DveZZr9x.css +1 -0
  227. codex/static_root/assets/reader-DveZZr9x.css.br +0 -0
  228. codex/static_root/assets/reader-DveZZr9x.css.gz +0 -0
  229. codex/static_root/assets/{relation-chips-CUSatZET.js → relation-chips-3tTlR5QH.71f18bc92adc.js} +1 -1
  230. codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.br +0 -0
  231. codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.gz +0 -0
  232. codex/static_root/assets/{relation-chips-CUSatZET.a58031ff8141.js → relation-chips-3tTlR5QH.js} +1 -1
  233. codex/static_root/assets/relation-chips-3tTlR5QH.js.br +0 -0
  234. codex/static_root/assets/relation-chips-3tTlR5QH.js.gz +0 -0
  235. codex/static_root/assets/{settings-drawer-BSHTAsJ9.js → settings-drawer-BdyFhA_r.151f4c3061ed.js} +2 -2
  236. codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.br +0 -0
  237. codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.gz +0 -0
  238. codex/static_root/assets/{settings-drawer-BSHTAsJ9.1d07ce084fc9.js → settings-drawer-BdyFhA_r.js} +2 -2
  239. codex/static_root/assets/settings-drawer-BdyFhA_r.js.br +0 -0
  240. codex/static_root/assets/settings-drawer-BdyFhA_r.js.gz +0 -0
  241. codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js +1 -0
  242. codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.br +0 -0
  243. codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.gz +0 -0
  244. codex/static_root/assets/stats-tab-DeKjwvmh.js +1 -0
  245. codex/static_root/assets/stats-tab-DeKjwvmh.js.br +0 -0
  246. codex/static_root/assets/stats-tab-DeKjwvmh.js.gz +0 -0
  247. codex/static_root/assets/{stats-tab-BXGisn5N.9b7a71e7fe28.css → stats-tab-DpfGsVX3.1cb66f2649a9.css} +1 -1
  248. codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.br +0 -0
  249. codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.gz +0 -0
  250. codex/static_root/assets/{stats-tab-BXGisn5N.css → stats-tab-DpfGsVX3.css} +1 -1
  251. codex/static_root/assets/stats-tab-DpfGsVX3.css.br +0 -0
  252. codex/static_root/assets/stats-tab-DpfGsVX3.css.gz +0 -0
  253. codex/static_root/assets/{task-tab-NORFAa9p.97e07e366ddd.js → task-tab-C8D0qRP_.d4ab174c69a3.js} +1 -1
  254. codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.br +0 -0
  255. codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.gz +0 -0
  256. codex/static_root/assets/{task-tab-NORFAa9p.js → task-tab-C8D0qRP_.js} +1 -1
  257. codex/static_root/assets/task-tab-C8D0qRP_.js.br +0 -0
  258. codex/static_root/assets/task-tab-C8D0qRP_.js.gz +0 -0
  259. codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js +1 -0
  260. codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.br +0 -0
  261. codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.gz +0 -0
  262. codex/static_root/assets/unauthorized-BHgBRF4z.js +1 -0
  263. codex/static_root/assets/unauthorized-BHgBRF4z.js.br +0 -0
  264. codex/static_root/assets/unauthorized-BHgBRF4z.js.gz +0 -0
  265. codex/static_root/assets/{unauthorized-muDsv-rC.css → unauthorized-BjhIc97q.728511c92cd0.css} +1 -1
  266. codex/static_root/assets/unauthorized-BjhIc97q.728511c92cd0.css.br +0 -0
  267. codex/static_root/assets/unauthorized-BjhIc97q.728511c92cd0.css.gz +0 -0
  268. codex/static_root/assets/{unauthorized-muDsv-rC.2166ec8b3a32.css → unauthorized-BjhIc97q.css} +1 -1
  269. codex/static_root/assets/unauthorized-BjhIc97q.css.br +0 -0
  270. codex/static_root/assets/unauthorized-BjhIc97q.css.gz +0 -0
  271. codex/static_root/assets/{user-tab-CHbyN1EW.e7206db08680.js → user-tab-BU31Z92y.35613c713faf.js} +1 -1
  272. codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.br +0 -0
  273. codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.gz +0 -0
  274. codex/static_root/assets/{user-tab-CHbyN1EW.js → user-tab-BU31Z92y.js} +1 -1
  275. codex/static_root/assets/user-tab-BU31Z92y.js.br +0 -0
  276. codex/static_root/assets/user-tab-BU31Z92y.js.gz +0 -0
  277. codex/static_root/js/choices-admin.8aa64c911203.json +1 -0
  278. codex/static_root/js/choices-admin.8aa64c911203.json.br +0 -0
  279. codex/static_root/js/choices-admin.8aa64c911203.json.gz +0 -0
  280. codex/static_root/js/choices-admin.json +1 -1
  281. codex/static_root/js/choices-admin.json.br +0 -0
  282. codex/static_root/js/choices-admin.json.gz +0 -0
  283. codex/static_root/manifest.45cb46e19686.json +635 -0
  284. codex/static_root/manifest.45cb46e19686.json.br +0 -0
  285. codex/static_root/manifest.45cb46e19686.json.gz +0 -0
  286. codex/static_root/manifest.json +248 -248
  287. codex/static_root/manifest.json.br +0 -0
  288. codex/static_root/manifest.json.gz +0 -0
  289. codex/static_root/staticfiles.json +1 -1
  290. codex/status_controller.py +10 -4
  291. codex/views/admin/tasks.py +46 -40
  292. codex/views/auth.py +15 -4
  293. codex/views/browser/annotations.py +94 -51
  294. codex/views/browser/base.py +1 -2
  295. codex/views/browser/browser.py +52 -21
  296. codex/views/browser/cover.py +8 -4
  297. codex/views/browser/filters/annotations.py +28 -6
  298. codex/views/browser/filters/search/__init__.py +1 -0
  299. codex/views/browser/filters/search/aliases.py +81 -0
  300. codex/views/browser/filters/search/field/__init__.py +1 -0
  301. codex/views/browser/filters/search/field/column.py +53 -0
  302. codex/views/browser/filters/search/field/expression.py +177 -0
  303. codex/views/browser/filters/search/field/filter.py +70 -0
  304. codex/views/browser/filters/search/field/optimize.py +71 -0
  305. codex/views/browser/filters/search/field/parse.py +187 -0
  306. codex/views/browser/filters/search/fts.py +38 -0
  307. codex/views/browser/filters/search/parse.py +220 -0
  308. codex/views/browser/order_by.py +8 -2
  309. codex/views/browser/page_in_bounds.py +88 -0
  310. codex/views/browser/paginate.py +28 -113
  311. codex/views/const.py +0 -1
  312. codex/views/opds/v1/feed.py +2 -1
  313. codex/views/opds/v2/feed.py +2 -1
  314. codex/views/opds/v2/links.py +1 -1
  315. codex/views/reader/books.py +1 -1
  316. codex/views/reader/page.py +3 -3
  317. {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/METADATA +18 -10
  318. {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/RECORD +321 -372
  319. codex/_vendor/__init__.py +0 -0
  320. codex/_vendor/django_haystack-3.2.1.dist-info/AUTHORS +0 -127
  321. codex/_vendor/django_haystack-3.2.1.dist-info/INSTALLER +0 -1
  322. codex/_vendor/django_haystack-3.2.1.dist-info/LICENSE +0 -31
  323. codex/_vendor/django_haystack-3.2.1.dist-info/METADATA +0 -98
  324. codex/_vendor/django_haystack-3.2.1.dist-info/RECORD +0 -97
  325. codex/_vendor/django_haystack-3.2.1.dist-info/REQUESTED +0 -0
  326. codex/_vendor/django_haystack-3.2.1.dist-info/WHEEL +0 -5
  327. codex/_vendor/django_haystack-3.2.1.dist-info/top_level.txt +0 -1
  328. codex/_vendor/haystack/__init__.py +0 -80
  329. codex/_vendor/haystack/admin.py +0 -163
  330. codex/_vendor/haystack/apps.py +0 -31
  331. codex/_vendor/haystack/backends/__init__.py +0 -1109
  332. codex/_vendor/haystack/backends/elasticsearch2_backend.py +0 -390
  333. codex/_vendor/haystack/backends/elasticsearch5_backend.py +0 -483
  334. codex/_vendor/haystack/backends/elasticsearch7_backend.py +0 -586
  335. codex/_vendor/haystack/backends/elasticsearch_backend.py +0 -1141
  336. codex/_vendor/haystack/backends/simple_backend.py +0 -132
  337. codex/_vendor/haystack/backends/solr_backend.py +0 -991
  338. codex/_vendor/haystack/backends/whoosh_backend.py +0 -1104
  339. codex/_vendor/haystack/constants.py +0 -57
  340. codex/_vendor/haystack/exceptions.py +0 -57
  341. codex/_vendor/haystack/fields.py +0 -562
  342. codex/_vendor/haystack/forms.py +0 -135
  343. codex/_vendor/haystack/generic_views.py +0 -141
  344. codex/_vendor/haystack/indexes.py +0 -548
  345. codex/_vendor/haystack/inputs.py +0 -169
  346. codex/_vendor/haystack/management/__init__.py +0 -0
  347. codex/_vendor/haystack/management/commands/__init__.py +0 -0
  348. codex/_vendor/haystack/management/commands/build_solr_schema.py +0 -187
  349. codex/_vendor/haystack/management/commands/clear_index.py +0 -67
  350. codex/_vendor/haystack/management/commands/haystack_info.py +0 -22
  351. codex/_vendor/haystack/management/commands/rebuild_index.py +0 -65
  352. codex/_vendor/haystack/management/commands/update_index.py +0 -438
  353. codex/_vendor/haystack/manager.py +0 -105
  354. codex/_vendor/haystack/models.py +0 -257
  355. codex/_vendor/haystack/panels.py +0 -89
  356. codex/_vendor/haystack/query.py +0 -771
  357. codex/_vendor/haystack/routers.py +0 -14
  358. codex/_vendor/haystack/signals.py +0 -88
  359. codex/_vendor/haystack/templates/panels/haystack.html +0 -33
  360. codex/_vendor/haystack/templates/search_configuration/schema.xml +0 -1056
  361. codex/_vendor/haystack/templates/search_configuration/solrconfig.xml +0 -1446
  362. codex/_vendor/haystack/templatetags/__init__.py +0 -0
  363. codex/_vendor/haystack/templatetags/highlight.py +0 -131
  364. codex/_vendor/haystack/templatetags/more_like_this.py +0 -119
  365. codex/_vendor/haystack/urls.py +0 -5
  366. codex/_vendor/haystack/utils/__init__.py +0 -84
  367. codex/_vendor/haystack/utils/app_loading.py +0 -34
  368. codex/_vendor/haystack/utils/geo.py +0 -71
  369. codex/_vendor/haystack/utils/highlighting.py +0 -165
  370. codex/_vendor/haystack/utils/loading.py +0 -374
  371. codex/_vendor/haystack/utils/log.py +0 -21
  372. codex/_vendor/haystack/version.py +0 -16
  373. codex/_vendor/haystack/views.py +0 -253
  374. codex/integrity.py +0 -492
  375. codex/librarian/search/merge.py +0 -86
  376. codex/librarian/search/version.py +0 -63
  377. codex/search/__init__.py +0 -1
  378. codex/search/backend.py +0 -524
  379. codex/search/backend_search.py +0 -229
  380. codex/search/engine.py +0 -33
  381. codex/search/indexes.py +0 -79
  382. codex/search/query.py +0 -67
  383. codex/search/writing.py +0 -180
  384. codex/static_root/assets/VCheckbox-Cko1sQK-.170726b3b8a1.js.br +0 -0
  385. codex/static_root/assets/VCheckbox-Cko1sQK-.170726b3b8a1.js.gz +0 -0
  386. codex/static_root/assets/VCheckbox-Cko1sQK-.js.br +0 -0
  387. codex/static_root/assets/VCheckbox-Cko1sQK-.js.gz +0 -0
  388. codex/static_root/assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js.br +0 -0
  389. codex/static_root/assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js.gz +0 -0
  390. codex/static_root/assets/VCheckboxBtn-CO3-aqQL.js.br +0 -0
  391. codex/static_root/assets/VCheckboxBtn-CO3-aqQL.js.gz +0 -0
  392. codex/static_root/assets/VCombobox-Bjp6lffE.caf59fba486e.js.br +0 -0
  393. codex/static_root/assets/VCombobox-Bjp6lffE.caf59fba486e.js.gz +0 -0
  394. codex/static_root/assets/VCombobox-Bjp6lffE.js.br +0 -0
  395. codex/static_root/assets/VCombobox-Bjp6lffE.js.gz +0 -0
  396. codex/static_root/assets/VDialog-BkpVGB70.6eea3ca762a5.js.br +0 -0
  397. codex/static_root/assets/VDialog-BkpVGB70.6eea3ca762a5.js.gz +0 -0
  398. codex/static_root/assets/VDialog-BkpVGB70.js.br +0 -0
  399. codex/static_root/assets/VDialog-BkpVGB70.js.gz +0 -0
  400. codex/static_root/assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js.br +0 -0
  401. codex/static_root/assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js.gz +0 -0
  402. codex/static_root/assets/VExpansionPanels-Ci2-j8XX.js.br +0 -0
  403. codex/static_root/assets/VExpansionPanels-Ci2-j8XX.js.gz +0 -0
  404. codex/static_root/assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js.br +0 -0
  405. codex/static_root/assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js.gz +0 -0
  406. codex/static_root/assets/VRadioGroup-D3Py5BfQ.js.br +0 -0
  407. codex/static_root/assets/VRadioGroup-D3Py5BfQ.js.gz +0 -0
  408. codex/static_root/assets/VSelect-DtvZsYZz.e98745d858eb.js.br +0 -0
  409. codex/static_root/assets/VSelect-DtvZsYZz.e98745d858eb.js.gz +0 -0
  410. codex/static_root/assets/VSelect-DtvZsYZz.js.br +0 -0
  411. codex/static_root/assets/VSelect-DtvZsYZz.js.gz +0 -0
  412. codex/static_root/assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js.br +0 -0
  413. codex/static_root/assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js.gz +0 -0
  414. codex/static_root/assets/VSelectionControl-D6kmykQW.js.br +0 -0
  415. codex/static_root/assets/VSelectionControl-D6kmykQW.js.gz +0 -0
  416. codex/static_root/assets/VSlideGroup-UGZWxpF5.360bdefcd215.js.br +0 -0
  417. codex/static_root/assets/VSlideGroup-UGZWxpF5.360bdefcd215.js.gz +0 -0
  418. codex/static_root/assets/VSlideGroup-UGZWxpF5.js.br +0 -0
  419. codex/static_root/assets/VSlideGroup-UGZWxpF5.js.gz +0 -0
  420. codex/static_root/assets/VTable-DXH_yQ0o.1149f56ad566.js.br +0 -0
  421. codex/static_root/assets/VTable-DXH_yQ0o.1149f56ad566.js.gz +0 -0
  422. codex/static_root/assets/VTable-DXH_yQ0o.js.br +0 -0
  423. codex/static_root/assets/VTable-DXH_yQ0o.js.gz +0 -0
  424. codex/static_root/assets/VTextField-Blqy56_L.26d8761e0155.js.br +0 -0
  425. codex/static_root/assets/VTextField-Blqy56_L.26d8761e0155.js.gz +0 -0
  426. codex/static_root/assets/VTextField-Blqy56_L.js.br +0 -0
  427. codex/static_root/assets/VTextField-Blqy56_L.js.gz +0 -0
  428. codex/static_root/assets/VWindowItem-C8_ovV7e.afbc08414765.js.br +0 -0
  429. codex/static_root/assets/VWindowItem-C8_ovV7e.afbc08414765.js.gz +0 -0
  430. codex/static_root/assets/VWindowItem-C8_ovV7e.js.br +0 -0
  431. codex/static_root/assets/VWindowItem-C8_ovV7e.js.gz +0 -0
  432. codex/static_root/assets/admin-DTRn8bBs.d031ea42d8d0.js.br +0 -0
  433. codex/static_root/assets/admin-DTRn8bBs.d031ea42d8d0.js.gz +0 -0
  434. codex/static_root/assets/admin-DTRn8bBs.js.br +0 -0
  435. codex/static_root/assets/admin-DTRn8bBs.js.gz +0 -0
  436. codex/static_root/assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js.br +0 -0
  437. codex/static_root/assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js.gz +0 -0
  438. codex/static_root/assets/admin-drawer-panel-BaXFOcru.js.br +0 -0
  439. codex/static_root/assets/admin-drawer-panel-BaXFOcru.js.gz +0 -0
  440. codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js +0 -1
  441. codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js.br +0 -0
  442. codex/static_root/assets/browser-BBpUyGmz.c1cb9e2d463b.js.gz +0 -0
  443. codex/static_root/assets/browser-BBpUyGmz.js +0 -1
  444. codex/static_root/assets/browser-BBpUyGmz.js.br +0 -0
  445. codex/static_root/assets/browser-BBpUyGmz.js.gz +0 -0
  446. codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css +0 -1
  447. codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.br +0 -0
  448. codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.gz +0 -0
  449. codex/static_root/assets/browser-Binc3n9H.css +0 -1
  450. codex/static_root/assets/browser-Binc3n9H.css.br +0 -0
  451. codex/static_root/assets/browser-Binc3n9H.css.gz +0 -0
  452. codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css +0 -1
  453. codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.br +0 -0
  454. codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.gz +0 -0
  455. codex/static_root/assets/change-password-dialog-Cebek1-c.css +0 -1
  456. codex/static_root/assets/change-password-dialog-Cebek1-c.css.br +0 -0
  457. codex/static_root/assets/change-password-dialog-Cebek1-c.css.gz +0 -0
  458. codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js +0 -1
  459. codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js.br +0 -0
  460. codex/static_root/assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js.gz +0 -0
  461. codex/static_root/assets/change-password-dialog-DlHoLhDF.js +0 -1
  462. codex/static_root/assets/change-password-dialog-DlHoLhDF.js.br +0 -0
  463. codex/static_root/assets/change-password-dialog-DlHoLhDF.js.gz +0 -0
  464. codex/static_root/assets/confirm-dialog-D3_s2DQy.9dde66036c72.js.br +0 -0
  465. codex/static_root/assets/confirm-dialog-D3_s2DQy.9dde66036c72.js.gz +0 -0
  466. codex/static_root/assets/confirm-dialog-D3_s2DQy.js.br +0 -0
  467. codex/static_root/assets/confirm-dialog-D3_s2DQy.js.gz +0 -0
  468. codex/static_root/assets/datetime-column-BxC1Li9e.3dc314b3295d.js.br +0 -0
  469. codex/static_root/assets/datetime-column-BxC1Li9e.3dc314b3295d.js.gz +0 -0
  470. codex/static_root/assets/datetime-column-BxC1Li9e.js.br +0 -0
  471. codex/static_root/assets/datetime-column-BxC1Li9e.js.gz +0 -0
  472. codex/static_root/assets/filter-Bzk7x841.d0c3b602e62d.js.br +0 -0
  473. codex/static_root/assets/filter-Bzk7x841.d0c3b602e62d.js.gz +0 -0
  474. codex/static_root/assets/filter-Bzk7x841.js.br +0 -0
  475. codex/static_root/assets/filter-Bzk7x841.js.gz +0 -0
  476. codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js +0 -1
  477. codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js.br +0 -0
  478. codex/static_root/assets/flag-tab-C2bI5zq3.8f8c297600a6.js.gz +0 -0
  479. codex/static_root/assets/flag-tab-C2bI5zq3.js +0 -1
  480. codex/static_root/assets/flag-tab-C2bI5zq3.js.br +0 -0
  481. codex/static_root/assets/flag-tab-C2bI5zq3.js.gz +0 -0
  482. codex/static_root/assets/group-tab-5esH4lwX.a81fdd258117.js.br +0 -0
  483. codex/static_root/assets/group-tab-5esH4lwX.a81fdd258117.js.gz +0 -0
  484. codex/static_root/assets/group-tab-5esH4lwX.js.br +0 -0
  485. codex/static_root/assets/group-tab-5esH4lwX.js.gz +0 -0
  486. codex/static_root/assets/http-error-BTOBfcGY.52ced6f28420.js.br +0 -0
  487. codex/static_root/assets/http-error-BTOBfcGY.52ced6f28420.js.gz +0 -0
  488. codex/static_root/assets/http-error-BTOBfcGY.js.br +0 -0
  489. codex/static_root/assets/http-error-BTOBfcGY.js.gz +0 -0
  490. codex/static_root/assets/library-tab-DiPEdOF7.ece2d642a9dc.js.br +0 -0
  491. codex/static_root/assets/library-tab-DiPEdOF7.ece2d642a9dc.js.gz +0 -0
  492. codex/static_root/assets/library-tab-DiPEdOF7.js.br +0 -0
  493. codex/static_root/assets/library-tab-DiPEdOF7.js.gz +0 -0
  494. codex/static_root/assets/main-BPIPjzsc.66def8991e38.js +0 -6
  495. codex/static_root/assets/main-BPIPjzsc.66def8991e38.js.br +0 -0
  496. codex/static_root/assets/main-BPIPjzsc.66def8991e38.js.gz +0 -0
  497. codex/static_root/assets/main-BPIPjzsc.js +0 -6
  498. codex/static_root/assets/main-BPIPjzsc.js.br +0 -0
  499. codex/static_root/assets/main-BPIPjzsc.js.gz +0 -0
  500. codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js +0 -1
  501. codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js.br +0 -0
  502. codex/static_root/assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js.gz +0 -0
  503. codex/static_root/assets/pager-full-pdf-CiyC6Al9.js +0 -1
  504. codex/static_root/assets/pager-full-pdf-CiyC6Al9.js.br +0 -0
  505. codex/static_root/assets/pager-full-pdf-CiyC6Al9.js.gz +0 -0
  506. codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.br +0 -0
  507. codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.gz +0 -0
  508. codex/static_root/assets/pagination-toolbar-C45-onwe.css.br +0 -0
  509. codex/static_root/assets/pagination-toolbar-C45-onwe.css.gz +0 -0
  510. codex/static_root/assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js.br +0 -0
  511. codex/static_root/assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js.gz +0 -0
  512. codex/static_root/assets/pagination-toolbar-CkIzuyx1.js.br +0 -0
  513. codex/static_root/assets/pagination-toolbar-CkIzuyx1.js.gz +0 -0
  514. codex/static_root/assets/pdf-doc-BjlRPuf4.0f056fa321b6.js.br +0 -0
  515. codex/static_root/assets/pdf-doc-BjlRPuf4.0f056fa321b6.js.gz +0 -0
  516. codex/static_root/assets/pdf-doc-BjlRPuf4.js.br +0 -0
  517. codex/static_root/assets/pdf-doc-BjlRPuf4.js.gz +0 -0
  518. codex/static_root/assets/reader-_stAWL-4.5089e9144795.css +0 -1
  519. codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.br +0 -0
  520. codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.gz +0 -0
  521. codex/static_root/assets/reader-_stAWL-4.css +0 -1
  522. codex/static_root/assets/reader-_stAWL-4.css.br +0 -0
  523. codex/static_root/assets/reader-_stAWL-4.css.gz +0 -0
  524. codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js +0 -2
  525. codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js.br +0 -0
  526. codex/static_root/assets/reader-ucxnLi2K.d0fbce0f03c1.js.gz +0 -0
  527. codex/static_root/assets/reader-ucxnLi2K.js +0 -2
  528. codex/static_root/assets/reader-ucxnLi2K.js.br +0 -0
  529. codex/static_root/assets/reader-ucxnLi2K.js.gz +0 -0
  530. codex/static_root/assets/relation-chips-CUSatZET.a58031ff8141.js.br +0 -0
  531. codex/static_root/assets/relation-chips-CUSatZET.a58031ff8141.js.gz +0 -0
  532. codex/static_root/assets/relation-chips-CUSatZET.js.br +0 -0
  533. codex/static_root/assets/relation-chips-CUSatZET.js.gz +0 -0
  534. codex/static_root/assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js.br +0 -0
  535. codex/static_root/assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js.gz +0 -0
  536. codex/static_root/assets/settings-drawer-BSHTAsJ9.js.br +0 -0
  537. codex/static_root/assets/settings-drawer-BSHTAsJ9.js.gz +0 -0
  538. codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.br +0 -0
  539. codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.gz +0 -0
  540. codex/static_root/assets/stats-tab-BXGisn5N.css.br +0 -0
  541. codex/static_root/assets/stats-tab-BXGisn5N.css.gz +0 -0
  542. codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js +0 -1
  543. codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js.br +0 -0
  544. codex/static_root/assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js.gz +0 -0
  545. codex/static_root/assets/stats-tab-DKXVB2Cc.js +0 -1
  546. codex/static_root/assets/stats-tab-DKXVB2Cc.js.br +0 -0
  547. codex/static_root/assets/stats-tab-DKXVB2Cc.js.gz +0 -0
  548. codex/static_root/assets/task-tab-NORFAa9p.97e07e366ddd.js.br +0 -0
  549. codex/static_root/assets/task-tab-NORFAa9p.97e07e366ddd.js.gz +0 -0
  550. codex/static_root/assets/task-tab-NORFAa9p.js.br +0 -0
  551. codex/static_root/assets/task-tab-NORFAa9p.js.gz +0 -0
  552. codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js +0 -1
  553. codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js.br +0 -0
  554. codex/static_root/assets/unauthorized-RHaQZRfr.a234e9403f60.js.gz +0 -0
  555. codex/static_root/assets/unauthorized-RHaQZRfr.js +0 -1
  556. codex/static_root/assets/unauthorized-RHaQZRfr.js.br +0 -0
  557. codex/static_root/assets/unauthorized-RHaQZRfr.js.gz +0 -0
  558. codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.br +0 -0
  559. codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.gz +0 -0
  560. codex/static_root/assets/unauthorized-muDsv-rC.css.br +0 -0
  561. codex/static_root/assets/unauthorized-muDsv-rC.css.gz +0 -0
  562. codex/static_root/assets/user-tab-CHbyN1EW.e7206db08680.js.br +0 -0
  563. codex/static_root/assets/user-tab-CHbyN1EW.e7206db08680.js.gz +0 -0
  564. codex/static_root/assets/user-tab-CHbyN1EW.js.br +0 -0
  565. codex/static_root/assets/user-tab-CHbyN1EW.js.gz +0 -0
  566. codex/static_root/js/choices-admin.1a20a5648f20.json +0 -1
  567. codex/static_root/js/choices-admin.1a20a5648f20.json.br +0 -0
  568. codex/static_root/js/choices-admin.1a20a5648f20.json.gz +0 -0
  569. codex/static_root/manifest.0fe31a6cb99f.json +0 -635
  570. codex/static_root/manifest.0fe31a6cb99f.json.br +0 -0
  571. codex/static_root/manifest.0fe31a6cb99f.json.gz +0 -0
  572. codex/templates/search/indexes/codex/comic_text.txt +0 -47
  573. codex/views/browser/filters/search.py +0 -196
  574. {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/LICENSE +0 -0
  575. {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/WHEEL +0 -0
  576. {codex-1.6.19.dist-info → codex-1.7.0a0.dist-info}/entry_points.txt +0 -0
Binary file
Binary file
@@ -1 +1 @@
1
- {"paths": {"rest_framework/docs/img/favicon.ico": "rest_framework/docs/img/favicon.5195b4d0f3eb.ico", "rest_framework/docs/img/grid.png": "rest_framework/docs/img/grid.a4b938cf382b.png", "rest_framework/docs/css/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "rest_framework/docs/css/highlight.css": "rest_framework/docs/css/highlight.e0e4d973c6d7.css", "rest_framework/docs/css/base.css": "rest_framework/docs/css/base.e630f8f4990e.css", "rest_framework/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/js/api.js": "rest_framework/docs/js/api.18a5ba8a1bd8.js", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "rest_framework/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.css", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/bootstrap-theme.min.css.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "rest_framework/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.8851fb9336c9.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/jquery-3.7.1.min.js": "rest_framework/js/jquery-3.7.1.min.2c872dbe60f4.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "img/story-arc.svg": "img/story-arc.ca10014355ef.svg", "img/logo-maskable.svg": "img/logo-maskable.8f7de7f1c6a8.svg", "img/folder.svg": "img/folder.f28936a3701d.svg", "img/missing-cover-165.webp": "img/missing-cover-165.0743171f2148.webp", "img/missing-cover.svg": "img/missing-cover.ddb038cc2c56.svg", "img/publisher.svg": "img/publisher.82d378fcd601.svg", "img/logo-32.webp": "img/logo-32.5688944495bf.webp", "img/imprint.svg": "img/imprint.7c2adebb76d6.svg", "img/logo.svg": "img/logo.0f5344a028ca.svg", "img/volume.svg": "img/volume.814e4c79461a.svg", "img/logo-maskable-180.webp": "img/logo-maskable-180.c58d966aa64e.webp", "img/series.svg": "img/series.ec8a889b8f95.svg", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "assets/VCheckboxBtn-CO3-aqQL.js": "assets/VCheckboxBtn-CO3-aqQL.c9b8062fd66f.js", "assets/main-BZ73p_WW.css": "assets/main-BZ73p_WW.d529e8b3f52a.css", "assets/relation-chips-CUSatZET.js": "assets/relation-chips-CUSatZET.a58031ff8141.js", "assets/user-tab-CHbyN1EW.js": "assets/user-tab-CHbyN1EW.e7206db08680.js", "assets/stats-tab-BXGisn5N.css": "assets/stats-tab-BXGisn5N.9b7a71e7fe28.css", "assets/admin-drawer-panel-BaXFOcru.js": "assets/admin-drawer-panel-BaXFOcru.b1a44b05f482.js", "assets/datetime-column-DeCthByU.css": "assets/datetime-column-DeCthByU.1f3bf499e063.css", "assets/settings-drawer-DKncO_vX.css": "assets/settings-drawer-DKncO_vX.0a2cd24cd904.css", "assets/VDialog-BkpVGB70.js": "assets/VDialog-BkpVGB70.6eea3ca762a5.js", "assets/VSlideGroup-UGZWxpF5.js": "assets/VSlideGroup-UGZWxpF5.360bdefcd215.js", "assets/datetime-column-BxC1Li9e.js": "assets/datetime-column-BxC1Li9e.3dc314b3295d.js", "assets/browser-Binc3n9H.css": "assets/browser-Binc3n9H.313722d27ca3.css", "assets/VCheckbox-Cko1sQK-.js": "assets/VCheckbox-Cko1sQK-.170726b3b8a1.js", "assets/relation-chips-ByfWwuQc.css": "assets/relation-chips-ByfWwuQc.01269a188e01.css", "assets/flag-tab-C2bI5zq3.js": "assets/flag-tab-C2bI5zq3.8f8c297600a6.js", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/filter-Bzk7x841.js": "assets/filter-Bzk7x841.d0c3b602e62d.js", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/VTextField-Blqy56_L.js": "assets/VTextField-Blqy56_L.26d8761e0155.js", "assets/VWindowItem-C8_ovV7e.js": "assets/VWindowItem-C8_ovV7e.afbc08414765.js", "assets/VExpansionPanels-Ci2-j8XX.js": "assets/VExpansionPanels-Ci2-j8XX.9c55db4ef49e.js", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/VCombobox-bwEYrXCg.css": "assets/VCombobox-bwEYrXCg.9f25a34994e2.css", "assets/unauthorized-muDsv-rC.css": "assets/unauthorized-muDsv-rC.2166ec8b3a32.css", "assets/group-tab-5esH4lwX.js": "assets/group-tab-5esH4lwX.a81fdd258117.js", "assets/task-tab-1XiWn9xP.css": "assets/task-tab-1XiWn9xP.06cb8e1b794d.css", "assets/VTextField-Crc3gdNh.css": "assets/VTextField-Crc3gdNh.39995ccf4d52.css", "assets/VTable-Y5Sc3hJd.css": "assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css", "assets/VExpansionPanels-6G1AUajL.css": "assets/VExpansionPanels-6G1AUajL.5b17872981df.css", "assets/stats-tab-DKXVB2Cc.js": "assets/stats-tab-DKXVB2Cc.0a3de58bf5ee.js", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/confirm-dialog-D3_s2DQy.js": "assets/confirm-dialog-D3_s2DQy.9dde66036c72.js", "assets/VSelectionControl-D6kmykQW.js": "assets/VSelectionControl-D6kmykQW.1e1fda62ceba.js", "assets/reader-ucxnLi2K.js": "assets/reader-ucxnLi2K.d0fbce0f03c1.js", "assets/VSelectionControl-CiViEYoC.css": "assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css", "assets/copy-to-clipboard-DoUvDJVx.js": "assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js", "assets/reader-_stAWL-4.css": "assets/reader-_stAWL-4.5089e9144795.css", "assets/admin-CWEjaFr5.css": "assets/admin-CWEjaFr5.7d4523387384.css", "assets/task-tab-NORFAa9p.js": "assets/task-tab-NORFAa9p.97e07e366ddd.js", "assets/VSelect-DtvZsYZz.js": "assets/VSelect-DtvZsYZz.e98745d858eb.js", "assets/admin-drawer-panel-Bs2wgKj3.css": "assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css", "assets/http-error-DKulXuTl.css": "assets/http-error-DKulXuTl.95e42988d5d5.css", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/library-tab-DiPEdOF7.js": "assets/library-tab-DiPEdOF7.ece2d642a9dc.js", "assets/pdf-doc-BjlRPuf4.js": "assets/pdf-doc-BjlRPuf4.0f056fa321b6.js", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/main-BPIPjzsc.js": "assets/main-BPIPjzsc.66def8991e38.js", "assets/change-password-dialog-Cebek1-c.css": "assets/change-password-dialog-Cebek1-c.aff2975e31f8.css", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/VSelect-Ca2bIOso.css": "assets/VSelect-Ca2bIOso.6613989e4c45.css", "assets/VRadioGroup-D3Py5BfQ.js": "assets/VRadioGroup-D3Py5BfQ.a4f25edb86d3.js", "assets/library-tab-Bh7ErAk7.css": "assets/library-tab-Bh7ErAk7.7c44200ee727.css", "assets/group-tab-CBKuS3Iv.css": "assets/group-tab-CBKuS3Iv.7e4746634782.css", "assets/admin-DTRn8bBs.js": "assets/admin-DTRn8bBs.d031ea42d8d0.js", "assets/pagination-toolbar-C45-onwe.css": "assets/pagination-toolbar-C45-onwe.4722195bb60b.css", "assets/VCombobox-Bjp6lffE.js": "assets/VCombobox-Bjp6lffE.caf59fba486e.js", "assets/pager-full-pdf-CiyC6Al9.js": "assets/pager-full-pdf-CiyC6Al9.278b9978f36f.js", "assets/unauthorized-RHaQZRfr.js": "assets/unauthorized-RHaQZRfr.a234e9403f60.js", "assets/browser-BBpUyGmz.js": "assets/browser-BBpUyGmz.c1cb9e2d463b.js", "assets/change-password-dialog-DlHoLhDF.js": "assets/change-password-dialog-DlHoLhDF.76ab4a29bb9b.js", "assets/VTable-DXH_yQ0o.js": "assets/VTable-DXH_yQ0o.1149f56ad566.js", "assets/flag-tab-DBqpLKi7.css": "assets/flag-tab-DBqpLKi7.dcfef39f541e.css", "assets/VSlideGroup-Dk_1upHt.css": "assets/VSlideGroup-Dk_1upHt.2c836070ec75.css", "assets/http-error-BTOBfcGY.js": "assets/http-error-BTOBfcGY.52ced6f28420.js", "assets/VDialog-38np3TSA.css": "assets/VDialog-38np3TSA.5a0cb7a9e56d.css", "assets/settings-drawer-BSHTAsJ9.js": "assets/settings-drawer-BSHTAsJ9.1d07ce084fc9.js", "assets/pagination-toolbar-CkIzuyx1.js": "assets/pagination-toolbar-CkIzuyx1.00c354a82e6e.js", "assets/pdf-doc-CG5-AZ2M.css": "assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css", "js/choices.json": "js/choices.2f6b0359e1ef.json", "js/choices-admin.json": "js/choices-admin.1a20a5648f20.json", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.0fe31a6cb99f.json"}, "version": "1.1", "hash": "476282ec9876"}
1
+ {"paths": {"rest_framework/docs/img/favicon.ico": "rest_framework/docs/img/favicon.5195b4d0f3eb.ico", "rest_framework/docs/img/grid.png": "rest_framework/docs/img/grid.a4b938cf382b.png", "rest_framework/docs/css/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "rest_framework/docs/css/highlight.css": "rest_framework/docs/css/highlight.e0e4d973c6d7.css", "rest_framework/docs/css/base.css": "rest_framework/docs/css/base.e630f8f4990e.css", "rest_framework/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/js/api.js": "rest_framework/docs/js/api.18a5ba8a1bd8.js", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "rest_framework/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.css", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/bootstrap-theme.min.css.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "rest_framework/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.8851fb9336c9.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/jquery-3.7.1.min.js": "rest_framework/js/jquery-3.7.1.min.2c872dbe60f4.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "img/story-arc.svg": "img/story-arc.ca10014355ef.svg", "img/logo-maskable.svg": "img/logo-maskable.8f7de7f1c6a8.svg", "img/folder.svg": "img/folder.f28936a3701d.svg", "img/missing-cover-165.webp": "img/missing-cover-165.0743171f2148.webp", "img/missing-cover.svg": "img/missing-cover.ddb038cc2c56.svg", "img/publisher.svg": "img/publisher.82d378fcd601.svg", "img/logo-32.webp": "img/logo-32.5688944495bf.webp", "img/imprint.svg": "img/imprint.7c2adebb76d6.svg", "img/logo.svg": "img/logo.0f5344a028ca.svg", "img/volume.svg": "img/volume.814e4c79461a.svg", "img/logo-maskable-180.webp": "img/logo-maskable-180.c58d966aa64e.webp", "img/series.svg": "img/series.ec8a889b8f95.svg", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "assets/main-BZ73p_WW.css": "assets/main-BZ73p_WW.d529e8b3f52a.css", "assets/VSelect-txrnRiNJ.js": "assets/VSelect-txrnRiNJ.a5625760018c.js", "assets/browser-C18l93yc.js": "assets/browser-C18l93yc.923860009ef4.js", "assets/http-error-C6yuLApo.js": "assets/http-error-C6yuLApo.45cf99eb7a70.js", "assets/VTable-2af995xo.js": "assets/VTable-2af995xo.6c5bf36631b2.js", "assets/datetime-column-DeCthByU.css": "assets/datetime-column-DeCthByU.1f3bf499e063.css", "assets/unauthorized-BjhIc97q.css": "assets/unauthorized-BjhIc97q.728511c92cd0.css", "assets/VExpansionPanels-Cywv_bEj.js": "assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js", "assets/pager-full-pdf-BH54_5f6.js": "assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js", "assets/settings-drawer-DKncO_vX.css": "assets/settings-drawer-DKncO_vX.0a2cd24cd904.css", "assets/filter-CBl5KEsB.js": "assets/filter-CBl5KEsB.845a81573c2e.js", "assets/relation-chips-ByfWwuQc.css": "assets/relation-chips-ByfWwuQc.01269a188e01.css", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/VSlideGroup-DtEQMwY1.js": "assets/VSlideGroup-DtEQMwY1.6c72325635ed.js", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/change-password-dialog-Mlmvz8_4.css": "assets/change-password-dialog-Mlmvz8_4.111421f51e79.css", "assets/pagination-toolbar-DGrxp2OD.css": "assets/pagination-toolbar-DGrxp2OD.f960a624f04f.css", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/VCombobox-bwEYrXCg.css": "assets/VCombobox-bwEYrXCg.9f25a34994e2.css", "assets/VCombobox-D1_KU27A.js": "assets/VCombobox-D1_KU27A.b1d30587a973.js", "assets/task-tab-1XiWn9xP.css": "assets/task-tab-1XiWn9xP.06cb8e1b794d.css", "assets/main-CmjU2oTp.js": "assets/main-CmjU2oTp.6a471f6900e5.js", "assets/VTextField-Crc3gdNh.css": "assets/VTextField-Crc3gdNh.39995ccf4d52.css", "assets/VWindowItem-CzoGd6Ul.js": "assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js", "assets/VTable-Y5Sc3hJd.css": "assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css", "assets/VExpansionPanels-6G1AUajL.css": "assets/VExpansionPanels-6G1AUajL.5b17872981df.css", "assets/browser-DaAqlBYO.css": "assets/browser-DaAqlBYO.708ecf0904bb.css", "assets/group-tab-BOW9kigw.js": "assets/group-tab-BOW9kigw.7e5007596367.js", "assets/pagination-toolbar-JH3Kbffr.js": "assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/task-tab-C8D0qRP_.js": "assets/task-tab-C8D0qRP_.d4ab174c69a3.js", "assets/VCheckboxBtn-Dk3iB62X.js": "assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js", "assets/VSelectionControl-CiViEYoC.css": "assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css", "assets/copy-to-clipboard-DoUvDJVx.js": "assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js", "assets/admin-CsPAIQeG.js": "assets/admin-CsPAIQeG.5175467148a3.js", "assets/admin-CWEjaFr5.css": "assets/admin-CWEjaFr5.7d4523387384.css", "assets/datetime-column-uSqKZU37.js": "assets/datetime-column-uSqKZU37.b157b0743db5.js", "assets/admin-drawer-panel-Bs2wgKj3.css": "assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css", "assets/http-error-DKulXuTl.css": "assets/http-error-DKulXuTl.95e42988d5d5.css", "assets/admin-drawer-panel-BAT7GA64.js": "assets/admin-drawer-panel-BAT7GA64.73a064628c19.js", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/stats-tab-DpfGsVX3.css": "assets/stats-tab-DpfGsVX3.1cb66f2649a9.css", "assets/VRadioGroup-C0etmxpZ.js": "assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js", "assets/VSelectionControl-CY6RmcvW.js": "assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/VSelect-Ca2bIOso.css": "assets/VSelect-Ca2bIOso.6613989e4c45.css", "assets/user-tab-BU31Z92y.js": "assets/user-tab-BU31Z92y.35613c713faf.js", "assets/flag-tab-CT8pqM4r.js": "assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js", "assets/pdf-doc-BcRNpoaW.js": "assets/pdf-doc-BcRNpoaW.882f6e967869.js", "assets/unauthorized-BHgBRF4z.js": "assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js", "assets/reader-DIPRg2VB.js": "assets/reader-DIPRg2VB.900370e097aa.js", "assets/VTextField-G6CMj7yO.js": "assets/VTextField-G6CMj7yO.657a130dd302.js", "assets/library-tab-Bh7ErAk7.css": "assets/library-tab-Bh7ErAk7.7c44200ee727.css", "assets/VCheckbox-CWiqcZ7a.js": "assets/VCheckbox-CWiqcZ7a.3feebd47daab.js", "assets/group-tab-CBKuS3Iv.css": "assets/group-tab-CBKuS3Iv.7e4746634782.css", "assets/stats-tab-DeKjwvmh.js": "assets/stats-tab-DeKjwvmh.7557c71b4cfa.js", "assets/VDialog-GbxvFMMN.js": "assets/VDialog-GbxvFMMN.594fb3a39ced.js", "assets/flag-tab-DBqpLKi7.css": "assets/flag-tab-DBqpLKi7.dcfef39f541e.css", "assets/library-tab-D3WOtpE0.js": "assets/library-tab-D3WOtpE0.9af34e6e7235.js", "assets/reader-DveZZr9x.css": "assets/reader-DveZZr9x.7b21b9f8f628.css", "assets/change-password-dialog-BazJdNs9.js": "assets/change-password-dialog-BazJdNs9.0a4e02656a35.js", "assets/VSlideGroup-Dk_1upHt.css": "assets/VSlideGroup-Dk_1upHt.2c836070ec75.css", "assets/relation-chips-3tTlR5QH.js": "assets/relation-chips-3tTlR5QH.71f18bc92adc.js", "assets/VDialog-38np3TSA.css": "assets/VDialog-38np3TSA.5a0cb7a9e56d.css", "assets/confirm-dialog-CbW3hlOp.js": "assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js", "assets/pdf-doc-CG5-AZ2M.css": "assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css", "assets/settings-drawer-BdyFhA_r.js": "assets/settings-drawer-BdyFhA_r.151f4c3061ed.js", "js/choices.json": "js/choices.2f6b0359e1ef.json", "js/choices-admin.json": "js/choices-admin.8aa64c911203.json", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.45cb46e19686.json"}, "version": "1.1", "hash": "9f6e07e1740e"}
@@ -47,10 +47,16 @@ class StatusController(LoggerBaseMixin):
47
47
  def _loggit(self, level, status):
48
48
  """Log with a ? in place of none."""
49
49
  type_title = STATUS_TITLES[status.status_type]
50
- title = " ".join((type_title, status.subtitle)).strip()
51
- count = "?" if status.complete is None else status.complete
52
- total = "?" if status.total is None else status.total
53
- self.log.log(level, f"{title}: {count}/{total}")
50
+ msg = " ".join((type_title, status.subtitle)).strip()
51
+ msg += ": "
52
+ if status.complete is None and status.total is None:
53
+ msg += "In progress"
54
+ else:
55
+ count = "?" if status.complete is None else status.complete
56
+ total = "?" if status.total is None else status.total
57
+ msg += f"{count}/{total}"
58
+
59
+ self.log.log(level, msg)
54
60
 
55
61
  @staticmethod
56
62
  def _to_status_type_value(status):
@@ -20,8 +20,13 @@ from codex.librarian.janitor.tasks import (
20
20
  JanitorBackupTask,
21
21
  JanitorCleanCoversTask,
22
22
  JanitorCleanFKsTask,
23
+ JanitorCleanupBookmarksTask,
23
24
  JanitorCleanupSessionsTask,
24
25
  JanitorClearStatusTask,
26
+ JanitorForeignKeyCheck,
27
+ JanitorFTSIntegrityCheck,
28
+ JanitorFTSRebuildTask,
29
+ JanitorIntegrityCheck,
25
30
  JanitorLatestVersionTask,
26
31
  JanitorNightlyTask,
27
32
  JanitorRestartTask,
@@ -34,8 +39,7 @@ from codex.librarian.notifier.tasks import LIBRARIAN_STATUS_TASK, LIBRARY_CHANGE
34
39
  from codex.librarian.search.tasks import (
35
40
  SearchIndexAbortTask,
36
41
  SearchIndexClearTask,
37
- SearchIndexMergeTask,
38
- SearchIndexRebuildIfDBChangedTask,
42
+ SearchIndexOptimizeTask,
39
43
  SearchIndexRemoveStaleTask,
40
44
  SearchIndexUpdateTask,
41
45
  )
@@ -56,6 +60,45 @@ if TYPE_CHECKING:
56
60
 
57
61
  LOG = get_logger(__name__)
58
62
 
63
+ _TASK_MAP = MappingProxyType(
64
+ {
65
+ "purge_comic_covers": CoverRemoveAllTask(),
66
+ "create_all_comic_covers": CoverCreateAllTask(),
67
+ "search_index_update": SearchIndexUpdateTask(False),
68
+ "search_index_rebuild": SearchIndexUpdateTask(True),
69
+ "search_index_remove_stale": SearchIndexRemoveStaleTask(),
70
+ "search_index_abort": SearchIndexAbortTask(),
71
+ "search_index_optimize": SearchIndexOptimizeTask(True),
72
+ "search_index_clear": SearchIndexClearTask(),
73
+ "db_vacuum": JanitorVacuumTask(),
74
+ "db_backup": JanitorBackupTask(),
75
+ "db_foreign_key_check": JanitorForeignKeyCheck(),
76
+ "db_integrity_check_quick": JanitorIntegrityCheck(),
77
+ "db_integrity_check_long": JanitorIntegrityCheck(True),
78
+ "db_fts_integrity_check": JanitorFTSIntegrityCheck(),
79
+ "db_fts_rebuild": JanitorFTSRebuildTask(),
80
+ "watchdog_sync": WatchdogSyncTask(),
81
+ "codex_latest_version": JanitorLatestVersionTask(True),
82
+ "codex_update": JanitorUpdateTask(False),
83
+ "codex_shutdown": JanitorShutdownTask(),
84
+ "codex_restart": JanitorRestartTask(),
85
+ "notify_library_changed": LIBRARY_CHANGED_TASK,
86
+ "notify_librarian_status": LIBRARIAN_STATUS_TASK,
87
+ "cleanup_fks": JanitorCleanFKsTask(),
88
+ "cleanup_db_custom_covers": JanitorCleanCoversTask(),
89
+ "cleanup_sessions": JanitorCleanupSessionsTask(),
90
+ "cleanup_bookmarks": JanitorCleanupBookmarksTask(),
91
+ "cleanup_covers": CoverRemoveOrphansTask(),
92
+ "librarian_clear_status": JanitorClearStatusTask(),
93
+ "force_update_all_failed_imports": ForceUpdateAllFailedImportsTask(),
94
+ "poll": WatchdogPollLibrariesTask(frozenset(), False),
95
+ "poll_force": WatchdogPollLibrariesTask(frozenset(), True),
96
+ "janitor_nightly": JanitorNightlyTask(),
97
+ "force_update_groups": UpdateGroupsTask(start_time=EPOCH_START),
98
+ "adopt_folders": AdoptOrphanFoldersTask(),
99
+ }
100
+ )
101
+
59
102
 
60
103
  class AdminLibrarianStatusViewSet(AdminReadOnlyModelViewSet):
61
104
  """Librarian Task Statuses."""
@@ -72,47 +115,10 @@ class AdminLibrarianTaskView(AdminAPIView):
72
115
  input_serializer_class = AdminLibrarianTaskSerializer
73
116
  serializer_class = OKSerializer
74
117
 
75
- _TASK_MAP = MappingProxyType(
76
- {
77
- "purge_comic_covers": CoverRemoveAllTask(),
78
- "create_all_comic_covers": CoverCreateAllTask(),
79
- "search_index_update": SearchIndexUpdateTask(False),
80
- "search_index_rebuild": SearchIndexUpdateTask(True),
81
- "search_index_remove_stale": SearchIndexRemoveStaleTask(),
82
- "search_index_merge_small": SearchIndexMergeTask(
83
- False,
84
- ),
85
- "search_index_abort": SearchIndexAbortTask(),
86
- "search_index_optimize": SearchIndexMergeTask(True),
87
- "search_index_clear": SearchIndexClearTask(),
88
- "db_vacuum": JanitorVacuumTask(),
89
- "db_backup": JanitorBackupTask(),
90
- "db_search_sync": SearchIndexRebuildIfDBChangedTask(),
91
- "watchdog_sync": WatchdogSyncTask(),
92
- "codex_latest_version": JanitorLatestVersionTask(True),
93
- "codex_update": JanitorUpdateTask(False),
94
- "codex_shutdown": JanitorShutdownTask(),
95
- "codex_restart": JanitorRestartTask(),
96
- "notify_library_changed": LIBRARY_CHANGED_TASK,
97
- "notify_librarian_status": LIBRARIAN_STATUS_TASK,
98
- "cleanup_fks": JanitorCleanFKsTask(),
99
- "cleanup_db_custom_covers": JanitorCleanCoversTask(),
100
- "cleanup_sessions": JanitorCleanupSessionsTask(),
101
- "cleanup_covers": CoverRemoveOrphansTask(),
102
- "librarian_clear_status": JanitorClearStatusTask(),
103
- "force_update_all_failed_imports": ForceUpdateAllFailedImportsTask(),
104
- "poll": WatchdogPollLibrariesTask(frozenset(), False),
105
- "poll_force": WatchdogPollLibrariesTask(frozenset(), True),
106
- "janitor_nightly": JanitorNightlyTask(),
107
- "force_update_groups": UpdateGroupsTask(start_time=EPOCH_START),
108
- "adopt_folders": AdoptOrphanFoldersTask(),
109
- }
110
- )
111
-
112
118
  @classmethod
113
119
  def _get_task(cls, name, pk):
114
120
  """Stuff library ids into tasks."""
115
- task = cls._TASK_MAP.get(name)
121
+ task = _TASK_MAP.get(name)
116
122
  if pk and isinstance(
117
123
  task, WatchdogPollLibrariesTask | WatchdogPollLibrariesTask
118
124
  ):
codex/views/auth.py CHANGED
@@ -107,7 +107,8 @@ class GroupACLMixin:
107
107
  groups_rel = f"{prefix}library__groups"
108
108
 
109
109
  # Libraries with no groups are always visible
110
- query = Q(**{f"{groups_rel}__isnull": True})
110
+ ungrouped_filter = {f"{groups_rel}__isnull": True}
111
+ query = Q(**ungrouped_filter)
111
112
 
112
113
  user = self.request.user # type: ignore
113
114
 
@@ -115,10 +116,20 @@ class GroupACLMixin:
115
116
  # Include groups are visible to users in the group
116
117
  user_filter = {f"{groups_rel}__user": user}
117
118
  exclude_rel = f"{groups_rel}__groupauth__exclude"
118
- exclude_query = ~Q(**user_filter, **{exclude_rel: True})
119
- include_query = Q(**user_filter, **{exclude_rel: False}) | ~Q(
120
- **{exclude_rel: False}
119
+
120
+ # Exclude Query
121
+ exclude_filter = {exclude_rel: True}
122
+ exclude_filter.update(user_filter)
123
+ exclude_query = ~Q(**exclude_filter)
124
+
125
+ # Include Query
126
+ include_filter = {exclude_rel: False}
127
+ include_filter.update(user_filter)
128
+ not_member_of_exclude_group_filter = {exclude_rel: False}
129
+ include_query = Q(**include_filter) | ~Q(
130
+ **not_member_of_exclude_group_filter
121
131
  )
132
+
122
133
  auth_query = exclude_query & include_query
123
134
  query |= auth_query
124
135
 
@@ -5,18 +5,20 @@ from types import MappingProxyType
5
5
 
6
6
  from django.db.models import (
7
7
  Avg,
8
- BooleanField,
9
8
  Case,
10
9
  F,
11
10
  FilteredRelation,
12
11
  Min,
12
+ OuterRef,
13
13
  Q,
14
14
  Sum,
15
15
  Value,
16
16
  When,
17
17
  )
18
+ from django.db.models.expressions import Subquery
18
19
  from django.db.models.fields import CharField, PositiveSmallIntegerField
19
20
  from django.db.models.functions import Least, Reverse, Right, StrIndex
21
+ from django.db.models.functions.comparison import Coalesce
20
22
 
21
23
  from codex.logger.logging import get_logger
22
24
  from codex.models import (
@@ -26,7 +28,7 @@ from codex.models import (
26
28
  StoryArc,
27
29
  Volume,
28
30
  )
29
- from codex.models.functions import JsonGroupArray
31
+ from codex.models.functions import ComicFTSRank, JsonGroupArray
30
32
  from codex.models.groups import Imprint, Publisher, Series
31
33
  from codex.views.browser.order_by import (
32
34
  BrowserOrderByView,
@@ -192,9 +194,20 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
192
194
  qs = qs.alias(order_value=order_value)
193
195
  return qs
194
196
 
197
+ def _annotate_search_scores(self, qs):
198
+ """Annotate Search Scores."""
199
+ if (
200
+ self.TARGET not in frozenset({"browser", "cover"})
201
+ or self.order_key != "search_score" # type: ignore
202
+ ):
203
+ return qs
204
+ return qs.annotate(search_score=ComicFTSRank())
205
+
195
206
  def annotate_order_aggregates(self, qs, model):
196
207
  """Annotate common aggregates between browser and metadata."""
208
+ self.set_order_key()
197
209
  qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
210
+ qs = self._annotate_search_scores(qs)
198
211
  qs = self._alias_sort_names(qs, model)
199
212
  qs = self._alias_filename(qs, model)
200
213
  qs = self._alias_story_arc_number(qs)
@@ -210,66 +223,96 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
210
223
  value = "c" if model == Comic else self.model_group # type: ignore
211
224
  return qs.annotate(group=Value(value, CharField(max_length=1)))
212
225
 
226
+ @staticmethod
227
+ def _get_bookmark_page_and_finished_counts(
228
+ bm_filter, page_rel, finished_rel, page_count
229
+ ):
230
+ bookmark_page_case = Case(
231
+ # When(**{bm_rel: None}, then=0),
232
+ When(**{finished_rel: True}, then=page_count),
233
+ default=page_rel,
234
+ output_field=PositiveSmallIntegerField(),
235
+ )
236
+
237
+ bookmark_page = Sum(
238
+ bookmark_page_case,
239
+ default=0,
240
+ filter=bm_filter,
241
+ output_field=PositiveSmallIntegerField(),
242
+ distinct=True,
243
+ )
244
+
245
+ finished_count = Sum(
246
+ finished_rel,
247
+ default=0,
248
+ filter=bm_filter,
249
+ output_field=PositiveSmallIntegerField(),
250
+ distinct=True,
251
+ )
252
+ finished_aggregate = Q(child_count=finished_count)
253
+
254
+ return bookmark_page, finished_aggregate
255
+
256
+ def _annotate_group_bookmarks(self):
257
+ """Aggregate bookmark and finished states for groups using subqueries to not break with the FTS match filter."""
258
+ # Using these subqueries is faster even though it's not neccissary for non-fts queries.
259
+ if not self.model:
260
+ return 0, 0
261
+
262
+ comic_qs = self.get_filtered_queryset(Comic)
263
+ # Must group by the outer ref or it only does aggregates for one comic.
264
+ group_rel = (
265
+ "parent_folder"
266
+ if self.model == Folder
267
+ else "story_arc_numbers__story_arc"
268
+ if self.model == StoryArc
269
+ else self.model.__name__.lower()
270
+ )
271
+ group_rel_suffix = "name" if self.model == Volume else "sort_name"
272
+ group_rel += "__" + group_rel_suffix
273
+ comic_qs = comic_qs.filter(**{group_rel: OuterRef("pk")}).values(group_rel)
274
+
275
+ bm_rel, bm_filter = self.get_bookmark_rel_and_filter(Comic)
276
+ page_rel = f"{bm_rel}__page"
277
+ finished_rel = f"{bm_rel}__finished"
278
+ page_count = "page_count"
279
+ bookmark_page, finished_aggregate = self._get_bookmark_page_and_finished_counts(
280
+ bm_filter, page_rel, finished_rel, page_count
281
+ )
282
+
283
+ comic_qs = comic_qs.annotate(page=bookmark_page, finished=finished_aggregate)
284
+ bookmark_page = Subquery(comic_qs.values("page"))
285
+ finished_aggregate = Subquery(comic_qs.values("finished"))
286
+
287
+ return bookmark_page, finished_aggregate
288
+
213
289
  def _annotate_bookmarks(self, qs, model):
214
290
  """Hoist up bookmark annotations."""
215
- bm_rel, bm_filter = self.get_bookmark_rel_and_filter(model)
216
-
291
+ # XXX These are slow for large groups.
292
+ bm_rel, _bm_filter = self.get_bookmark_rel_and_filter(model)
217
293
  page_rel = f"{bm_rel}__page"
218
294
  finished_rel = f"{bm_rel}__finished"
219
295
 
220
296
  if model == Comic:
221
- # Hoist up the bookmark and finished states
222
- bookmark_page = Sum(
223
- page_rel,
224
- default=0,
225
- filter=bm_filter,
226
- output_field=PositiveSmallIntegerField(),
227
- )
228
- finished_aggregate = Sum(
229
- finished_rel,
230
- default=False,
231
- filter=bm_filter,
232
- output_field=BooleanField(),
233
- )
297
+ # Hoist comic bookmark and finished states
298
+ bookmark_page = F(page_rel)
299
+ finished_aggregate = F(finished_rel)
234
300
  else:
235
- # Aggregate bookmark and finished states
236
- bookmark_page = Sum(
237
- Case(
238
- When(**{bm_rel: None}, then=0),
239
- When(**{finished_rel: True}, then=f"{self.rel_prefix}page_count"),
240
- default=page_rel,
241
- output_field=PositiveSmallIntegerField(),
242
- ),
243
- default=0,
244
- filter=bm_filter,
245
- output_field=PositiveSmallIntegerField(),
246
- distinct=True,
247
- )
301
+ # FTS mode is faster?
302
+ # Aggregate bookmark and finished states for groups using subqueries to
303
+ # not break with FTS match filter.
304
+ bookmark_page, finished_aggregate = self._annotate_group_bookmarks()
248
305
 
249
- finished_count = Sum(
250
- finished_rel,
251
- default=0,
252
- filter=bm_filter,
253
- output_field=PositiveSmallIntegerField(),
254
- distinct=True,
255
- )
256
- qs = qs.annotate(finished_count=finished_count)
257
- finished_aggregate = Case(
258
- When(finished_count=F("child_count"), then=True),
259
- When(finished_count=0, then=False),
260
- default=None,
261
- output_field=BooleanField(),
262
- )
263
-
264
- if self.is_opds_1_acquisition or (
265
- self.is_model_comic and self.TARGET == "browser"
306
+ if (
307
+ self.is_opds_1_acquisition
308
+ or (self.is_model_comic and self.TARGET == "browser")
309
+ or self.TARGET in frozenset({"metadata", "browser"})
266
310
  ):
267
311
  qs = qs.annotate(page=bookmark_page)
268
- elif self.TARGET in frozenset({"metadata", "browser"}):
269
- qs = qs.alias(page=bookmark_page)
270
312
 
271
313
  if self.TARGET in frozenset({"metadata", "browser"}):
272
314
  qs = qs.annotate(finished=finished_aggregate)
315
+
273
316
  return qs
274
317
 
275
318
  def _annotate_progress(self, qs):
@@ -278,10 +321,10 @@ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
278
321
  return qs
279
322
  # Requires bookmark and annotation hoisted from bookmarks.
280
323
  # Requires page_count native to comic or aggregated
324
+ # Page counts can be null with metadata turned off.
281
325
  # Least guard is for rare instances when bookmarks are set to
282
326
  # invalid high values
283
- then = Least(F("page") * 100.0 / F("page_count"), Value(100.0))
284
- progress = Case(When(page_count__gt=0, then=then), default=0.0)
327
+ progress = Least(Coalesce(F("page"), 0) * 100.0 / F("page_count"), Value(100.0))
285
328
  return qs.annotate(progress=progress)
286
329
 
287
330
  def annotate_card_aggregates(self, qs, model):
@@ -13,7 +13,7 @@ from codex.models import (
13
13
  Comic,
14
14
  )
15
15
  from codex.serializers.browser.settings import BrowserSettingsSerializer
16
- from codex.views.browser.filters.search import SearchFilterView
16
+ from codex.views.browser.filters.search.parse import SearchFilterView
17
17
  from codex.views.const import FOLDER_GROUP, GROUP_MODEL_MAP, ROOT_GROUP, STORY_ARC_GROUP
18
18
  from codex.views.util import reparse_json_query_params
19
19
 
@@ -134,7 +134,6 @@ class BrowserBaseView(SearchFilterView):
134
134
  """Initialize request."""
135
135
  self.set_admin_flags()
136
136
  self.parse_params()
137
- self.set_order_key()
138
137
  self.validate_settings()
139
138
  self.set_model()
140
139
  self.set_rel_prefix()
@@ -1,10 +1,12 @@
1
1
  """Views for browsing comic library."""
2
2
 
3
- from math import floor, log10
3
+ from math import ceil, floor, log10
4
4
  from types import MappingProxyType
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- from django.db.models import Max
7
+ from django.db.models import Max, Subquery
8
+ from django.db.models.expressions import OuterRef
9
+ from django.db.utils import OperationalError
8
10
  from drf_spectacular.utils import extend_schema
9
11
  from rest_framework.response import Response
10
12
 
@@ -23,6 +25,7 @@ from codex.views.browser.title import BrowserTitleView
23
25
  from codex.views.const import (
24
26
  COMIC_GROUP,
25
27
  FOLDER_GROUP,
28
+ MAX_OBJ_PER_PAGE,
26
29
  NONE_DATETIMEFIELD,
27
30
  STORY_ARC_GROUP,
28
31
  )
@@ -115,19 +118,40 @@ class BrowserView(BrowserTitleView):
115
118
  ################
116
119
  # MAIN QUERIES #
117
120
  ################
121
+
122
+ def _get_limit(self):
123
+ """Get the limit for the query."""
124
+ # query_limit only is set by some opds views
125
+ query_limit = self.params.get("limit", 0)
126
+ search_limit = self.get_search_limit()
127
+ if query_limit and search_limit:
128
+ limit = min(query_limit, search_limit)
129
+ else:
130
+ limit = max(query_limit, search_limit)
131
+ return limit
132
+
118
133
  def _get_common_queryset(self, model):
119
134
  """Create queryset common to group & books."""
120
135
  qs = self.get_filtered_queryset(model)
136
+ limit = self._get_limit()
121
137
  count_qs = self.add_group_by(qs, model)
122
- count = count_qs.count()
138
+ try:
139
+ if limit:
140
+ count_qs = count_qs[:limit]
141
+ # Get count after filters and before any annotations or orders
142
+ # because it's faster. These counts are used by
143
+ # is_page_in_bounds(), num_pages for the nav bar, and paginate()
144
+ count = count_qs.count()
145
+ except OperationalError as exc:
146
+ self._handle_operational_error(exc)
147
+ count = 0
148
+ qs = model.objects.none()
123
149
 
124
150
  if count:
125
151
  qs = self.annotate_order_aggregates(qs, model)
126
152
  qs = self.add_order_by(qs, model)
127
- if limit := self.params.get("limit"):
128
- # limit only is set by some opds views
153
+ if limit:
129
154
  qs = qs[:limit]
130
- count = min(count, limit) # type: ignore
131
155
 
132
156
  return qs, count
133
157
 
@@ -136,7 +160,7 @@ class BrowserView(BrowserTitleView):
136
160
  if not self.model:
137
161
  reason = "Model not set for browser queryset."
138
162
  raise ValueError(reason)
139
- elif self.is_model_comic: # noqa: RET506
163
+ if self.is_model_comic:
140
164
  qs = self.model.objects.none()
141
165
  count = 0
142
166
  else:
@@ -157,19 +181,19 @@ class BrowserView(BrowserTitleView):
157
181
  """Get max updated at without bookmark filter and aware of multi-groups."""
158
182
  if not self.is_bookmark_filtered:
159
183
  return group_qs
184
+ if not self.model:
185
+ reason = "No model to compute max bookmark updated_at"
186
+ raise ValueError(reason)
187
+
188
+ qs = self.model.objects.filter(pk=OuterRef("pk"))
160
189
 
161
190
  bm_rel, bm_filter = self.get_bookmark_rel_and_filter(self.model)
162
191
  bm_updated_at_rel = f"{bm_rel}__updated_at"
163
192
  max_bmua = Max(bm_updated_at_rel, default=NONE_DATETIMEFIELD, filter=bm_filter)
164
193
 
165
- group_list = []
166
- for group in group_qs:
167
- qs = self.get_filtered_queryset(
168
- self.model, bookmark_filter=False, group_filter=False
169
- )
170
- group.max_bookmark_updated_at = qs.aggregate(max=max_bmua)["max"]
171
- group_list.append(group)
172
- return group_list
194
+ mbua = qs.aggregate(max=max_bmua)["max"]
195
+
196
+ return group_qs.annotate(max_bookmark_updated_at=Subquery(mbua))
173
197
 
174
198
  @staticmethod
175
199
  def _get_zero_pad(book_qs):
@@ -194,10 +218,15 @@ class BrowserView(BrowserTitleView):
194
218
  """Create the main queries with filters, annotation and pagination."""
195
219
  group_qs, group_count = self._get_group_queryset()
196
220
  book_qs, book_count = self._get_book_queryset()
221
+
197
222
  # Paginate
198
- group_qs, book_qs, num_pages, page_group_count, page_book_count = self.paginate(
199
- group_qs, book_qs, group_count, book_count
223
+ num_pages = ceil((group_count + book_count) / MAX_OBJ_PER_PAGE)
224
+ self.check_page_in_bounds(num_pages)
225
+ group_qs, book_qs, page_group_count, page_book_count = self.paginate(
226
+ group_qs, book_qs, group_count
200
227
  )
228
+
229
+ # Annotate
201
230
  if page_group_count:
202
231
  group_qs = self.annotate_card_aggregates(group_qs, self.model)
203
232
  group_qs = self._requery_max_bookmark_updated_at(group_qs)
@@ -207,12 +236,12 @@ class BrowserView(BrowserTitleView):
207
236
  else:
208
237
  zero_pad = 1
209
238
 
210
- # print(group_qs.explain())
211
- # print(group_qs.query)
239
+ # print(book_qs.explain())
240
+ # print(book_qs.query)
212
241
 
213
- total_count = page_group_count + page_book_count
242
+ total_page_count = page_group_count + page_book_count
214
243
  mtime = self._get_page_mtime()
215
- return group_qs, book_qs, num_pages, total_count, zero_pad, mtime
244
+ return group_qs, book_qs, num_pages, total_page_count, zero_pad, mtime
216
245
 
217
246
  def get_object(self):
218
247
  """Validate settings and get the querysets."""
@@ -241,6 +270,8 @@ class BrowserView(BrowserTitleView):
241
270
  "admin_flags": self.admin_flags,
242
271
  "libraries_exist": libraries_exist,
243
272
  "mtime": mtime,
273
+ "search_error": self.search_error,
274
+ "fts": self.fts_mode,
244
275
  }
245
276
  )
246
277
 
@@ -1,5 +1,6 @@
1
1
  """Comic cover thumbnail view."""
2
2
 
3
+ from django.db import OperationalError
3
4
  from django.db.models.query import Q
4
5
  from django.http.response import StreamingHttpResponse
5
6
  from drf_spectacular.types import OpenApiTypes
@@ -97,7 +98,6 @@ class CoverView(BrowserAnnotationsView):
97
98
 
98
99
  def _get_dynamic_cover(self):
99
100
  """Get dynamic cover."""
100
- self.set_order_key()
101
101
  comic_qs = self.get_filtered_queryset(Comic)
102
102
  comic_qs = self.annotate_order_aggregates(comic_qs, Comic)
103
103
  comic_qs = self.add_order_by(comic_qs, Comic)
@@ -155,9 +155,13 @@ class CoverView(BrowserAnnotationsView):
155
155
  """Get comic cover."""
156
156
  try:
157
157
  self.init_request()
158
- pk, custom = self._get_cover_pk()
158
+ try:
159
+ pk, custom = self._get_cover_pk()
160
+ except OperationalError as exc:
161
+ self._handle_operational_error(exc)
162
+ pk = 0
163
+ custom = False
159
164
  cover_file, content_type = self._get_cover_data(pk, custom)
160
-
161
165
  return StreamingHttpResponse(chunker(cover_file), content_type=content_type)
162
166
  except Exception:
163
- LOG.exception("get")
167
+ LOG.exception("Get cover")