codex 1.7.0__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 (717) hide show
  1. codex/db.py +17 -32
  2. codex/exceptions.py +3 -2
  3. codex/librarian/covers/coverd.py +4 -3
  4. codex/librarian/covers/create.py +7 -8
  5. codex/librarian/covers/path.py +2 -1
  6. codex/librarian/covers/purge.py +2 -2
  7. codex/librarian/janitor/integrity.py +10 -12
  8. codex/librarian/janitor/tasks.py +1 -1
  9. codex/librarian/librariand.py +0 -5
  10. codex/librarian/watchdog/emitter.py +19 -21
  11. codex/librarian/watchdog/event_batcherd.py +1 -0
  12. codex/migrations/0030_nocase_collation_day_month_indexes_status_types.py +0 -4
  13. codex/models/admin.py +5 -0
  14. codex/models/functions.py +5 -6
  15. codex/models/query.py +0 -6
  16. codex/run.py +9 -11
  17. codex/serializers/admin/tasks.py +2 -2
  18. codex/serializers/browser/mixins.py +14 -18
  19. codex/serializers/browser/settings.py +4 -4
  20. codex/serializers/choices.py +196 -0
  21. codex/serializers/fields.py +5 -9
  22. codex/startup.py +4 -6
  23. codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js +1 -0
  24. codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.br +0 -0
  25. codex/static_root/assets/VCheckbox-CWiqcZ7a.3feebd47daab.js.gz +0 -0
  26. codex/static_root/assets/VCheckbox-CWiqcZ7a.js +1 -0
  27. codex/static_root/assets/VCheckbox-CWiqcZ7a.js.br +0 -0
  28. codex/static_root/assets/VCheckbox-CWiqcZ7a.js.gz +0 -0
  29. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js +1 -0
  30. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.br +0 -0
  31. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.bc66d5351baa.js.gz +0 -0
  32. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js +1 -0
  33. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.br +0 -0
  34. codex/static_root/assets/VCheckboxBtn-Dk3iB62X.js.gz +0 -0
  35. codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js +1 -0
  36. codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.br +0 -0
  37. codex/static_root/assets/VCombobox-D1_KU27A.b1d30587a973.js.gz +0 -0
  38. codex/static_root/assets/VCombobox-D1_KU27A.js +1 -0
  39. codex/static_root/assets/VCombobox-D1_KU27A.js.br +0 -0
  40. codex/static_root/assets/VCombobox-D1_KU27A.js.gz +0 -0
  41. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css +1 -0
  42. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.br +0 -0
  43. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.gz +0 -0
  44. codex/static_root/assets/VDialog-38np3TSA.css +1 -0
  45. codex/static_root/assets/VDialog-38np3TSA.css.br +0 -0
  46. codex/static_root/assets/VDialog-38np3TSA.css.gz +0 -0
  47. codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js +1 -0
  48. codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.br +0 -0
  49. codex/static_root/assets/VDialog-GbxvFMMN.594fb3a39ced.js.gz +0 -0
  50. codex/static_root/assets/VDialog-GbxvFMMN.js +1 -0
  51. codex/static_root/assets/VDialog-GbxvFMMN.js.br +0 -0
  52. codex/static_root/assets/VDialog-GbxvFMMN.js.gz +0 -0
  53. codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js +1 -0
  54. codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.br +0 -0
  55. codex/static_root/assets/VExpansionPanels-Cywv_bEj.a762e63858e5.js.gz +0 -0
  56. codex/static_root/assets/VExpansionPanels-Cywv_bEj.js +1 -0
  57. codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.br +0 -0
  58. codex/static_root/assets/VExpansionPanels-Cywv_bEj.js.gz +0 -0
  59. codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js +1 -0
  60. codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.br +0 -0
  61. codex/static_root/assets/VRadioGroup-C0etmxpZ.0eb3009a6300.js.gz +0 -0
  62. codex/static_root/assets/VRadioGroup-C0etmxpZ.js +1 -0
  63. codex/static_root/assets/VRadioGroup-C0etmxpZ.js.br +0 -0
  64. codex/static_root/assets/VRadioGroup-C0etmxpZ.js.gz +0 -0
  65. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css +1 -0
  66. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.br +0 -0
  67. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.gz +0 -0
  68. codex/static_root/assets/VSelect-Ca2bIOso.css +1 -0
  69. codex/static_root/assets/VSelect-Ca2bIOso.css.br +0 -0
  70. codex/static_root/assets/VSelect-Ca2bIOso.css.gz +0 -0
  71. codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js +1 -0
  72. codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.br +0 -0
  73. codex/static_root/assets/VSelect-txrnRiNJ.a5625760018c.js.gz +0 -0
  74. codex/static_root/assets/VSelect-txrnRiNJ.js +1 -0
  75. codex/static_root/assets/VSelect-txrnRiNJ.js.br +0 -0
  76. codex/static_root/assets/VSelect-txrnRiNJ.js.gz +0 -0
  77. codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js +1 -0
  78. codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.br +0 -0
  79. codex/static_root/assets/VSelectionControl-CY6RmcvW.7e8824224f6c.js.gz +0 -0
  80. codex/static_root/assets/VSelectionControl-CY6RmcvW.js +1 -0
  81. codex/static_root/assets/VSelectionControl-CY6RmcvW.js.br +0 -0
  82. codex/static_root/assets/VSelectionControl-CY6RmcvW.js.gz +0 -0
  83. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css +1 -0
  84. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.br +0 -0
  85. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.gz +0 -0
  86. codex/static_root/assets/VSlideGroup-Dk_1upHt.css +1 -0
  87. codex/static_root/assets/VSlideGroup-Dk_1upHt.css.br +0 -0
  88. codex/static_root/assets/VSlideGroup-Dk_1upHt.css.gz +0 -0
  89. codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js +1 -0
  90. codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.br +0 -0
  91. codex/static_root/assets/VSlideGroup-DtEQMwY1.6c72325635ed.js.gz +0 -0
  92. codex/static_root/assets/VSlideGroup-DtEQMwY1.js +1 -0
  93. codex/static_root/assets/VSlideGroup-DtEQMwY1.js.br +0 -0
  94. codex/static_root/assets/VSlideGroup-DtEQMwY1.js.gz +0 -0
  95. codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js +1 -0
  96. codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.br +0 -0
  97. codex/static_root/assets/VTable-2af995xo.6c5bf36631b2.js.gz +0 -0
  98. codex/static_root/assets/VTable-2af995xo.js +1 -0
  99. codex/static_root/assets/VTable-2af995xo.js.br +0 -0
  100. codex/static_root/assets/VTable-2af995xo.js.gz +0 -0
  101. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +1 -0
  102. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
  103. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
  104. codex/static_root/assets/VTextField-Crc3gdNh.css +1 -0
  105. codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
  106. codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
  107. codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js +1 -0
  108. codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.br +0 -0
  109. codex/static_root/assets/VTextField-G6CMj7yO.657a130dd302.js.gz +0 -0
  110. codex/static_root/assets/VTextField-G6CMj7yO.js +1 -0
  111. codex/static_root/assets/VTextField-G6CMj7yO.js.br +0 -0
  112. codex/static_root/assets/VTextField-G6CMj7yO.js.gz +0 -0
  113. codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js +1 -0
  114. codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.br +0 -0
  115. codex/static_root/assets/VWindowItem-CzoGd6Ul.721e5da11ad4.js.gz +0 -0
  116. codex/static_root/assets/VWindowItem-CzoGd6Ul.js +1 -0
  117. codex/static_root/assets/VWindowItem-CzoGd6Ul.js.br +0 -0
  118. codex/static_root/assets/VWindowItem-CzoGd6Ul.js.gz +0 -0
  119. codex/static_root/assets/{admin-DJ7pIXDQ.css → admin-CWEjaFr5.7d4523387384.css} +1 -1
  120. codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.br +0 -0
  121. codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.gz +0 -0
  122. codex/static_root/assets/{admin-DJ7pIXDQ.e2a77af73ef2.css → admin-CWEjaFr5.css} +1 -1
  123. codex/static_root/assets/admin-CWEjaFr5.css.br +0 -0
  124. codex/static_root/assets/admin-CWEjaFr5.css.gz +0 -0
  125. codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js +1 -0
  126. codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.br +0 -0
  127. codex/static_root/assets/admin-CsPAIQeG.5175467148a3.js.gz +0 -0
  128. codex/static_root/assets/admin-CsPAIQeG.js +1 -0
  129. codex/static_root/assets/admin-CsPAIQeG.js.br +0 -0
  130. codex/static_root/assets/admin-CsPAIQeG.js.gz +0 -0
  131. codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js +30 -0
  132. codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.br +0 -0
  133. codex/static_root/assets/admin-drawer-panel-BAT7GA64.73a064628c19.js.gz +0 -0
  134. codex/static_root/assets/admin-drawer-panel-BAT7GA64.js +30 -0
  135. codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.br +0 -0
  136. codex/static_root/assets/admin-drawer-panel-BAT7GA64.js.gz +0 -0
  137. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css +1 -0
  138. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.br +0 -0
  139. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.gz +0 -0
  140. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css +1 -0
  141. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.br +0 -0
  142. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.gz +0 -0
  143. codex/static_root/assets/browser-C18l93yc.923860009ef4.js +1 -0
  144. codex/static_root/assets/browser-C18l93yc.923860009ef4.js.br +0 -0
  145. codex/static_root/assets/browser-C18l93yc.923860009ef4.js.gz +0 -0
  146. codex/static_root/assets/browser-C18l93yc.js +1 -0
  147. codex/static_root/assets/browser-C18l93yc.js.br +0 -0
  148. codex/static_root/assets/browser-C18l93yc.js.gz +0 -0
  149. codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css +1 -0
  150. codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.br +0 -0
  151. codex/static_root/assets/browser-DaAqlBYO.708ecf0904bb.css.gz +0 -0
  152. codex/static_root/assets/browser-DaAqlBYO.css +1 -0
  153. codex/static_root/assets/browser-DaAqlBYO.css.br +0 -0
  154. codex/static_root/assets/browser-DaAqlBYO.css.gz +0 -0
  155. codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js +1 -0
  156. codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.br +0 -0
  157. codex/static_root/assets/change-password-dialog-BazJdNs9.0a4e02656a35.js.gz +0 -0
  158. codex/static_root/assets/change-password-dialog-BazJdNs9.js +1 -0
  159. codex/static_root/assets/change-password-dialog-BazJdNs9.js.br +0 -0
  160. codex/static_root/assets/change-password-dialog-BazJdNs9.js.gz +0 -0
  161. codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css +1 -0
  162. codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.br +0 -0
  163. codex/static_root/assets/change-password-dialog-Mlmvz8_4.111421f51e79.css.gz +0 -0
  164. codex/static_root/assets/change-password-dialog-Mlmvz8_4.css +1 -0
  165. codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.br +0 -0
  166. codex/static_root/assets/change-password-dialog-Mlmvz8_4.css.gz +0 -0
  167. codex/static_root/assets/confirm-dialog-BmhedLCx.7c2103d319d4.css +1 -0
  168. codex/static_root/assets/confirm-dialog-BmhedLCx.css +1 -0
  169. codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js +1 -0
  170. codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.br +0 -0
  171. codex/static_root/assets/confirm-dialog-CbW3hlOp.9dd39b443c31.js.gz +0 -0
  172. codex/static_root/assets/confirm-dialog-CbW3hlOp.js +1 -0
  173. codex/static_root/assets/confirm-dialog-CbW3hlOp.js.br +0 -0
  174. codex/static_root/assets/confirm-dialog-CbW3hlOp.js.gz +0 -0
  175. codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js +1 -0
  176. codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.br +0 -0
  177. codex/static_root/assets/datetime-column-uSqKZU37.b157b0743db5.js.gz +0 -0
  178. codex/static_root/assets/datetime-column-uSqKZU37.js +1 -0
  179. codex/static_root/assets/datetime-column-uSqKZU37.js.br +0 -0
  180. codex/static_root/assets/datetime-column-uSqKZU37.js.gz +0 -0
  181. codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js +1 -0
  182. codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.br +0 -0
  183. codex/static_root/assets/filter-CBl5KEsB.845a81573c2e.js.gz +0 -0
  184. codex/static_root/assets/filter-CBl5KEsB.js +1 -0
  185. codex/static_root/assets/filter-CBl5KEsB.js.br +0 -0
  186. codex/static_root/assets/filter-CBl5KEsB.js.gz +0 -0
  187. codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js +1 -0
  188. codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.br +0 -0
  189. codex/static_root/assets/flag-tab-CT8pqM4r.ecb6eee2e7a0.js.gz +0 -0
  190. codex/static_root/assets/flag-tab-CT8pqM4r.js +1 -0
  191. codex/static_root/assets/flag-tab-CT8pqM4r.js.br +0 -0
  192. codex/static_root/assets/flag-tab-CT8pqM4r.js.gz +0 -0
  193. codex/static_root/assets/flag-tab-DBqpLKi7.css +1 -0
  194. codex/static_root/assets/flag-tab-DBqpLKi7.css.br +0 -0
  195. codex/static_root/assets/flag-tab-DBqpLKi7.css.gz +0 -0
  196. codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css +1 -0
  197. codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.br +0 -0
  198. codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.gz +0 -0
  199. codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js +1 -0
  200. codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.br +0 -0
  201. codex/static_root/assets/group-tab-BOW9kigw.7e5007596367.js.gz +0 -0
  202. codex/static_root/assets/group-tab-BOW9kigw.js +1 -0
  203. codex/static_root/assets/group-tab-BOW9kigw.js.br +0 -0
  204. codex/static_root/assets/group-tab-BOW9kigw.js.gz +0 -0
  205. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css +1 -0
  206. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.br +0 -0
  207. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.gz +0 -0
  208. codex/static_root/assets/group-tab-CBKuS3Iv.css +1 -0
  209. codex/static_root/assets/group-tab-CBKuS3Iv.css.br +0 -0
  210. codex/static_root/assets/group-tab-CBKuS3Iv.css.gz +0 -0
  211. codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js +1 -0
  212. codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.br +4 -0
  213. codex/static_root/assets/http-error-C6yuLApo.45cf99eb7a70.js.gz +0 -0
  214. codex/static_root/assets/http-error-C6yuLApo.js +1 -0
  215. codex/static_root/assets/http-error-C6yuLApo.js.br +4 -0
  216. codex/static_root/assets/http-error-C6yuLApo.js.gz +0 -0
  217. codex/static_root/assets/{library-tab-Wew8YOLv.0c1780a6b68e.css → library-tab-Bh7ErAk7.7c44200ee727.css} +1 -1
  218. codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.br +2 -0
  219. codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.gz +0 -0
  220. codex/static_root/assets/{library-tab-Wew8YOLv.css → library-tab-Bh7ErAk7.css} +1 -1
  221. codex/static_root/assets/library-tab-Bh7ErAk7.css.br +2 -0
  222. codex/static_root/assets/library-tab-Bh7ErAk7.css.gz +0 -0
  223. codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js +1 -0
  224. codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.br +0 -0
  225. codex/static_root/assets/library-tab-D3WOtpE0.9af34e6e7235.js.gz +0 -0
  226. codex/static_root/assets/library-tab-D3WOtpE0.js +1 -0
  227. codex/static_root/assets/library-tab-D3WOtpE0.js.br +0 -0
  228. codex/static_root/assets/library-tab-D3WOtpE0.js.gz +0 -0
  229. codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js +6 -0
  230. codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.br +0 -0
  231. codex/static_root/assets/main-CmjU2oTp.6a471f6900e5.js.gz +0 -0
  232. codex/static_root/assets/main-CmjU2oTp.js +6 -0
  233. codex/static_root/assets/main-CmjU2oTp.js.br +0 -0
  234. codex/static_root/assets/main-CmjU2oTp.js.gz +0 -0
  235. codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js +1 -0
  236. codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.br +0 -0
  237. codex/static_root/assets/pager-full-pdf-BH54_5f6.6e92d0555cd7.js.gz +0 -0
  238. codex/static_root/assets/pager-full-pdf-BH54_5f6.js +1 -0
  239. codex/static_root/assets/pager-full-pdf-BH54_5f6.js.br +0 -0
  240. codex/static_root/assets/pager-full-pdf-BH54_5f6.js.gz +0 -0
  241. codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js +1 -0
  242. codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.br +0 -0
  243. codex/static_root/assets/pagination-toolbar-JH3Kbffr.451171f7ac33.js.gz +0 -0
  244. codex/static_root/assets/pagination-toolbar-JH3Kbffr.js +1 -0
  245. codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.br +0 -0
  246. codex/static_root/assets/pagination-toolbar-JH3Kbffr.js.gz +0 -0
  247. codex/static_root/assets/{pdf-doc-ctqZIwOP.7e7b2c815ea4.js → pdf-doc-BcRNpoaW.882f6e967869.js} +3 -3
  248. codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.br +0 -0
  249. codex/static_root/assets/pdf-doc-BcRNpoaW.882f6e967869.js.gz +0 -0
  250. codex/static_root/assets/{pdf-doc-ctqZIwOP.js → pdf-doc-BcRNpoaW.js} +3 -3
  251. codex/static_root/assets/pdf-doc-BcRNpoaW.js.br +0 -0
  252. codex/static_root/assets/pdf-doc-BcRNpoaW.js.gz +0 -0
  253. codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js +2 -0
  254. codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.br +0 -0
  255. codex/static_root/assets/reader-DIPRg2VB.900370e097aa.js.gz +0 -0
  256. codex/static_root/assets/reader-DIPRg2VB.js +2 -0
  257. codex/static_root/assets/reader-DIPRg2VB.js.br +0 -0
  258. codex/static_root/assets/reader-DIPRg2VB.js.gz +0 -0
  259. codex/static_root/assets/{reader-nD70BeEz.06e85e73c391.css → reader-DveZZr9x.7b21b9f8f628.css} +1 -1
  260. codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.br +0 -0
  261. codex/static_root/assets/reader-DveZZr9x.7b21b9f8f628.css.gz +0 -0
  262. codex/static_root/assets/{reader-nD70BeEz.css → reader-DveZZr9x.css} +1 -1
  263. codex/static_root/assets/reader-DveZZr9x.css.br +0 -0
  264. codex/static_root/assets/reader-DveZZr9x.css.gz +0 -0
  265. codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js +1 -0
  266. codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.br +0 -0
  267. codex/static_root/assets/relation-chips-3tTlR5QH.71f18bc92adc.js.gz +0 -0
  268. codex/static_root/assets/relation-chips-3tTlR5QH.js +1 -0
  269. codex/static_root/assets/relation-chips-3tTlR5QH.js.br +0 -0
  270. codex/static_root/assets/relation-chips-3tTlR5QH.js.gz +0 -0
  271. codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js +2 -0
  272. codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.br +0 -0
  273. codex/static_root/assets/settings-drawer-BdyFhA_r.151f4c3061ed.js.gz +0 -0
  274. codex/static_root/assets/settings-drawer-BdyFhA_r.js +2 -0
  275. codex/static_root/assets/settings-drawer-BdyFhA_r.js.br +0 -0
  276. codex/static_root/assets/settings-drawer-BdyFhA_r.js.gz +0 -0
  277. codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js +1 -0
  278. codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.br +0 -0
  279. codex/static_root/assets/stats-tab-DeKjwvmh.7557c71b4cfa.js.gz +0 -0
  280. codex/static_root/assets/stats-tab-DeKjwvmh.js +1 -0
  281. codex/static_root/assets/stats-tab-DeKjwvmh.js.br +0 -0
  282. codex/static_root/assets/stats-tab-DeKjwvmh.js.gz +0 -0
  283. codex/static_root/assets/{stats-tab-BAYTqQNg.5f715428c394.css → stats-tab-DpfGsVX3.1cb66f2649a9.css} +1 -1
  284. codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.br +0 -0
  285. codex/static_root/assets/stats-tab-DpfGsVX3.1cb66f2649a9.css.gz +0 -0
  286. codex/static_root/assets/{stats-tab-BAYTqQNg.css → stats-tab-DpfGsVX3.css} +1 -1
  287. codex/static_root/assets/stats-tab-DpfGsVX3.css.br +0 -0
  288. codex/static_root/assets/stats-tab-DpfGsVX3.css.gz +0 -0
  289. codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css +1 -0
  290. codex/static_root/assets/{task-tab--cXi48kg.5dfd6890a1c2.css.br → task-tab-1XiWn9xP.06cb8e1b794d.css.br} +1 -1
  291. codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css.gz +0 -0
  292. codex/static_root/assets/task-tab-1XiWn9xP.css +1 -0
  293. codex/static_root/assets/{task-tab--cXi48kg.css.br → task-tab-1XiWn9xP.css.br} +1 -1
  294. codex/static_root/assets/task-tab-1XiWn9xP.css.gz +0 -0
  295. codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js +1 -0
  296. codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.br +0 -0
  297. codex/static_root/assets/task-tab-C8D0qRP_.d4ab174c69a3.js.gz +0 -0
  298. codex/static_root/assets/task-tab-C8D0qRP_.js +1 -0
  299. codex/static_root/assets/task-tab-C8D0qRP_.js.br +0 -0
  300. codex/static_root/assets/task-tab-C8D0qRP_.js.gz +0 -0
  301. codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js +1 -0
  302. codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.br +0 -0
  303. codex/static_root/assets/unauthorized-BHgBRF4z.8e5c1b9e52b0.js.gz +0 -0
  304. codex/static_root/assets/unauthorized-BHgBRF4z.js +1 -0
  305. codex/static_root/assets/unauthorized-BHgBRF4z.js.br +0 -0
  306. codex/static_root/assets/unauthorized-BHgBRF4z.js.gz +0 -0
  307. codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js +1 -0
  308. codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.br +0 -0
  309. codex/static_root/assets/user-tab-BU31Z92y.35613c713faf.js.gz +0 -0
  310. codex/static_root/assets/user-tab-BU31Z92y.js +1 -0
  311. codex/static_root/assets/user-tab-BU31Z92y.js.br +0 -0
  312. codex/static_root/assets/user-tab-BU31Z92y.js.gz +0 -0
  313. codex/static_root/js/choices-admin.8aa64c911203.json +1 -0
  314. codex/static_root/js/choices-admin.8aa64c911203.json.br +0 -0
  315. codex/static_root/js/choices-admin.8aa64c911203.json.gz +0 -0
  316. codex/static_root/js/choices-admin.json +1 -0
  317. codex/static_root/js/choices-admin.json.br +0 -0
  318. codex/static_root/js/choices-admin.json.gz +0 -0
  319. codex/static_root/js/choices.2f6b0359e1ef.json +1 -0
  320. codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
  321. codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
  322. codex/static_root/js/choices.json +1 -0
  323. codex/static_root/js/choices.json.br +0 -0
  324. codex/static_root/js/choices.json.gz +0 -0
  325. codex/static_root/manifest.45cb46e19686.json +635 -0
  326. codex/static_root/manifest.45cb46e19686.json.br +0 -0
  327. codex/static_root/manifest.45cb46e19686.json.gz +0 -0
  328. codex/static_root/manifest.json +300 -377
  329. codex/static_root/manifest.json.br +0 -0
  330. codex/static_root/manifest.json.gz +0 -0
  331. codex/static_root/staticfiles.json +1 -1
  332. codex/status_controller.py +6 -5
  333. codex/threads.py +1 -1
  334. codex/urls/api/auth.py +1 -1
  335. codex/urls/api/browser.py +1 -1
  336. codex/views/admin/library.py +13 -18
  337. codex/views/admin/stats.py +16 -23
  338. codex/views/admin/tasks.py +2 -1
  339. codex/views/auth.py +77 -42
  340. codex/views/bookmark.py +153 -16
  341. codex/views/browser/annotations.py +339 -0
  342. codex/views/browser/base.py +139 -0
  343. codex/views/browser/breadcrumbs.py +11 -90
  344. codex/views/browser/browser.py +110 -44
  345. codex/views/browser/choices.py +18 -15
  346. codex/views/browser/cover.py +20 -10
  347. codex/views/browser/filters/annotations.py +125 -0
  348. codex/views/browser/filters/bookmark.py +19 -18
  349. codex/views/browser/filters/field.py +3 -8
  350. codex/views/browser/filters/group.py +6 -9
  351. codex/views/browser/filters/search/fts.py +19 -5
  352. codex/views/browser/filters/search/parse.py +24 -54
  353. codex/views/browser/metadata.py +332 -0
  354. codex/views/browser/mtime.py +10 -2
  355. codex/views/browser/order_by.py +22 -22
  356. codex/views/browser/page_in_bounds.py +2 -2
  357. codex/views/browser/paginate.py +15 -9
  358. codex/views/browser/title.py +6 -1
  359. codex/views/browser/validate.py +33 -68
  360. codex/views/download.py +1 -1
  361. codex/views/mixins.py +15 -27
  362. codex/views/opds/const.py +3 -2
  363. codex/views/opds/urls.py +2 -2
  364. codex/views/opds/util.py +3 -2
  365. codex/views/opds/v1/entry/links.py +1 -3
  366. codex/views/opds/v1/facets.py +12 -65
  367. codex/views/opds/v1/feed.py +64 -5
  368. codex/views/opds/v1/links.py +4 -5
  369. codex/views/opds/v2/feed.py +28 -1
  370. codex/views/opds/v2/links.py +1 -26
  371. codex/views/opds/v2/publications.py +6 -5
  372. codex/views/opds/v2/top_links.py +6 -3
  373. codex/views/public.py +2 -2
  374. codex/views/reader/arcs.py +3 -0
  375. codex/views/reader/books.py +41 -57
  376. codex/views/reader/{params.py → init.py} +31 -37
  377. codex/views/reader/page.py +4 -10
  378. codex/views/reader/reader.py +2 -1
  379. codex/views/session.py +54 -43
  380. codex/views/version.py +1 -1
  381. {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/METADATA +5 -2
  382. {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/RECORD +385 -419
  383. codex/choices.py +0 -419
  384. codex/choices_to_json.py +0 -122
  385. codex/librarian/bookmark/__init__.py +0 -1
  386. codex/librarian/bookmark/bookmarkd.py +0 -38
  387. codex/librarian/bookmark/tasks.py +0 -19
  388. codex/librarian/bookmark/update.py +0 -154
  389. codex/static_root/assets/VCheckbox-D_CWEmqR.84f2b49311e8.js +0 -1
  390. codex/static_root/assets/VCheckbox-D_CWEmqR.84f2b49311e8.js.br +0 -0
  391. codex/static_root/assets/VCheckbox-D_CWEmqR.84f2b49311e8.js.gz +0 -0
  392. codex/static_root/assets/VCheckbox-D_CWEmqR.js +0 -1
  393. codex/static_root/assets/VCheckbox-D_CWEmqR.js.br +0 -0
  394. codex/static_root/assets/VCheckbox-D_CWEmqR.js.gz +0 -0
  395. codex/static_root/assets/VCheckboxBtn-B1BhcFvi.0eaf3c516474.js +0 -1
  396. codex/static_root/assets/VCheckboxBtn-B1BhcFvi.0eaf3c516474.js.br +0 -0
  397. codex/static_root/assets/VCheckboxBtn-B1BhcFvi.0eaf3c516474.js.gz +0 -0
  398. codex/static_root/assets/VCheckboxBtn-B1BhcFvi.js +0 -1
  399. codex/static_root/assets/VCheckboxBtn-B1BhcFvi.js.br +0 -0
  400. codex/static_root/assets/VCheckboxBtn-B1BhcFvi.js.gz +0 -0
  401. codex/static_root/assets/VCombobox-CZAW6_3Q.0e2401e819a5.js +0 -1
  402. codex/static_root/assets/VCombobox-CZAW6_3Q.0e2401e819a5.js.br +0 -0
  403. codex/static_root/assets/VCombobox-CZAW6_3Q.0e2401e819a5.js.gz +0 -0
  404. codex/static_root/assets/VCombobox-CZAW6_3Q.js +0 -1
  405. codex/static_root/assets/VCombobox-CZAW6_3Q.js.br +0 -0
  406. codex/static_root/assets/VCombobox-CZAW6_3Q.js.gz +0 -0
  407. codex/static_root/assets/VDialog-CSHgHIlG.a0bcd336cb7b.js +0 -1
  408. codex/static_root/assets/VDialog-CSHgHIlG.a0bcd336cb7b.js.br +0 -0
  409. codex/static_root/assets/VDialog-CSHgHIlG.a0bcd336cb7b.js.gz +0 -0
  410. codex/static_root/assets/VDialog-CSHgHIlG.js +0 -1
  411. codex/static_root/assets/VDialog-CSHgHIlG.js.br +0 -0
  412. codex/static_root/assets/VDialog-CSHgHIlG.js.gz +0 -0
  413. codex/static_root/assets/VDialog-CSvrKKb2.css +0 -1
  414. codex/static_root/assets/VDialog-CSvrKKb2.css.br +0 -0
  415. codex/static_root/assets/VDialog-CSvrKKb2.css.gz +0 -0
  416. codex/static_root/assets/VDialog-CSvrKKb2.fd4d50904b17.css +0 -1
  417. codex/static_root/assets/VDialog-CSvrKKb2.fd4d50904b17.css.br +0 -0
  418. codex/static_root/assets/VDialog-CSvrKKb2.fd4d50904b17.css.gz +0 -0
  419. codex/static_root/assets/VExpansionPanels-CG6MpdMC.e96ae8afad97.js +0 -1
  420. codex/static_root/assets/VExpansionPanels-CG6MpdMC.e96ae8afad97.js.br +0 -0
  421. codex/static_root/assets/VExpansionPanels-CG6MpdMC.e96ae8afad97.js.gz +0 -0
  422. codex/static_root/assets/VExpansionPanels-CG6MpdMC.js +0 -1
  423. codex/static_root/assets/VExpansionPanels-CG6MpdMC.js.br +0 -0
  424. codex/static_root/assets/VExpansionPanels-CG6MpdMC.js.gz +0 -0
  425. codex/static_root/assets/VRadioGroup-CAgQxkiU.f26ecb0c3e34.js +0 -1
  426. codex/static_root/assets/VRadioGroup-CAgQxkiU.f26ecb0c3e34.js.br +0 -0
  427. codex/static_root/assets/VRadioGroup-CAgQxkiU.f26ecb0c3e34.js.gz +0 -0
  428. codex/static_root/assets/VRadioGroup-CAgQxkiU.js +0 -1
  429. codex/static_root/assets/VRadioGroup-CAgQxkiU.js.br +0 -0
  430. codex/static_root/assets/VRadioGroup-CAgQxkiU.js.gz +0 -0
  431. codex/static_root/assets/VSelect-C1gez8rl.9b11d2f00faf.js +0 -1
  432. codex/static_root/assets/VSelect-C1gez8rl.9b11d2f00faf.js.br +0 -0
  433. codex/static_root/assets/VSelect-C1gez8rl.9b11d2f00faf.js.gz +0 -0
  434. codex/static_root/assets/VSelect-C1gez8rl.js +0 -1
  435. codex/static_root/assets/VSelect-C1gez8rl.js.br +0 -0
  436. codex/static_root/assets/VSelect-C1gez8rl.js.gz +0 -0
  437. codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css +0 -1
  438. codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.br +0 -0
  439. codex/static_root/assets/VSelect-DLu8bpcs.98bdcf35411c.css.gz +0 -0
  440. codex/static_root/assets/VSelect-DLu8bpcs.css +0 -1
  441. codex/static_root/assets/VSelect-DLu8bpcs.css.br +0 -0
  442. codex/static_root/assets/VSelect-DLu8bpcs.css.gz +0 -0
  443. codex/static_root/assets/VSelectionControl-DBqDlKjK.160beaa1b56c.js +0 -1
  444. codex/static_root/assets/VSelectionControl-DBqDlKjK.160beaa1b56c.js.br +0 -0
  445. codex/static_root/assets/VSelectionControl-DBqDlKjK.160beaa1b56c.js.gz +0 -0
  446. codex/static_root/assets/VSelectionControl-DBqDlKjK.js +0 -1
  447. codex/static_root/assets/VSelectionControl-DBqDlKjK.js.br +0 -0
  448. codex/static_root/assets/VSelectionControl-DBqDlKjK.js.gz +0 -0
  449. codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css +0 -1
  450. codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.br +0 -0
  451. codex/static_root/assets/VSlideGroup-BtXPhMOT.830f136ffd41.css.gz +0 -0
  452. codex/static_root/assets/VSlideGroup-BtXPhMOT.css +0 -1
  453. codex/static_root/assets/VSlideGroup-BtXPhMOT.css.br +0 -0
  454. codex/static_root/assets/VSlideGroup-BtXPhMOT.css.gz +0 -0
  455. codex/static_root/assets/VSlideGroup-SqR_NAL1.40ba1d838a6e.js +0 -1
  456. codex/static_root/assets/VSlideGroup-SqR_NAL1.40ba1d838a6e.js.br +0 -0
  457. codex/static_root/assets/VSlideGroup-SqR_NAL1.40ba1d838a6e.js.gz +0 -0
  458. codex/static_root/assets/VSlideGroup-SqR_NAL1.js +0 -1
  459. codex/static_root/assets/VSlideGroup-SqR_NAL1.js.br +0 -0
  460. codex/static_root/assets/VSlideGroup-SqR_NAL1.js.gz +0 -0
  461. codex/static_root/assets/VTable-CYC-wMpk.8f15d66044da.js +0 -1
  462. codex/static_root/assets/VTable-CYC-wMpk.8f15d66044da.js.br +0 -0
  463. codex/static_root/assets/VTable-CYC-wMpk.8f15d66044da.js.gz +0 -0
  464. codex/static_root/assets/VTable-CYC-wMpk.js +0 -1
  465. codex/static_root/assets/VTable-CYC-wMpk.js.br +0 -0
  466. codex/static_root/assets/VTable-CYC-wMpk.js.gz +0 -0
  467. codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css +0 -1
  468. codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css.br +0 -0
  469. codex/static_root/assets/VTextField-BnCEPZIq.b0c32b2c0b14.css.gz +0 -0
  470. codex/static_root/assets/VTextField-BnCEPZIq.css +0 -1
  471. codex/static_root/assets/VTextField-BnCEPZIq.css.br +0 -0
  472. codex/static_root/assets/VTextField-BnCEPZIq.css.gz +0 -0
  473. codex/static_root/assets/VTextField-DMRKRbbT.911ac84d8c9d.js +0 -1
  474. codex/static_root/assets/VTextField-DMRKRbbT.911ac84d8c9d.js.br +0 -0
  475. codex/static_root/assets/VTextField-DMRKRbbT.911ac84d8c9d.js.gz +0 -0
  476. codex/static_root/assets/VTextField-DMRKRbbT.js +0 -1
  477. codex/static_root/assets/VTextField-DMRKRbbT.js.br +0 -0
  478. codex/static_root/assets/VTextField-DMRKRbbT.js.gz +0 -0
  479. codex/static_root/assets/VWindowItem-D0P2xNJl.6a10e3f3a153.js +0 -1
  480. codex/static_root/assets/VWindowItem-D0P2xNJl.6a10e3f3a153.js.br +0 -0
  481. codex/static_root/assets/VWindowItem-D0P2xNJl.6a10e3f3a153.js.gz +0 -0
  482. codex/static_root/assets/VWindowItem-D0P2xNJl.js +0 -1
  483. codex/static_root/assets/VWindowItem-D0P2xNJl.js.br +0 -0
  484. codex/static_root/assets/VWindowItem-D0P2xNJl.js.gz +0 -0
  485. codex/static_root/assets/admin-CD90VMen.4c2b6f2a5111.js +0 -1
  486. codex/static_root/assets/admin-CD90VMen.4c2b6f2a5111.js.br +0 -0
  487. codex/static_root/assets/admin-CD90VMen.4c2b6f2a5111.js.gz +0 -0
  488. codex/static_root/assets/admin-CD90VMen.js +0 -1
  489. codex/static_root/assets/admin-CD90VMen.js.br +0 -0
  490. codex/static_root/assets/admin-CD90VMen.js.gz +0 -0
  491. codex/static_root/assets/admin-DJ7pIXDQ.css.br +0 -0
  492. codex/static_root/assets/admin-DJ7pIXDQ.css.gz +0 -0
  493. codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.br +0 -0
  494. codex/static_root/assets/admin-DJ7pIXDQ.e2a77af73ef2.css.gz +0 -0
  495. codex/static_root/assets/admin-menu-B9bzD6hV.c13b7df2ff57.css +0 -1
  496. codex/static_root/assets/admin-menu-B9bzD6hV.c13b7df2ff57.css.br +0 -0
  497. codex/static_root/assets/admin-menu-B9bzD6hV.c13b7df2ff57.css.gz +0 -0
  498. codex/static_root/assets/admin-menu-B9bzD6hV.css +0 -1
  499. codex/static_root/assets/admin-menu-B9bzD6hV.css.br +0 -0
  500. codex/static_root/assets/admin-menu-B9bzD6hV.css.gz +0 -0
  501. codex/static_root/assets/admin-menu-BpEPGKr_.c9f9178c5a82.js +0 -1
  502. codex/static_root/assets/admin-menu-BpEPGKr_.c9f9178c5a82.js.br +0 -0
  503. codex/static_root/assets/admin-menu-BpEPGKr_.c9f9178c5a82.js.gz +0 -0
  504. codex/static_root/assets/admin-menu-BpEPGKr_.js +0 -1
  505. codex/static_root/assets/admin-menu-BpEPGKr_.js.br +0 -0
  506. codex/static_root/assets/admin-menu-BpEPGKr_.js.gz +0 -0
  507. codex/static_root/assets/admin-settings-button-progress-DDF4rY07.4f1158b650ab.js +0 -1
  508. codex/static_root/assets/admin-settings-button-progress-DDF4rY07.4f1158b650ab.js.br +0 -0
  509. codex/static_root/assets/admin-settings-button-progress-DDF4rY07.4f1158b650ab.js.gz +0 -0
  510. codex/static_root/assets/admin-settings-button-progress-DDF4rY07.js +0 -1
  511. codex/static_root/assets/admin-settings-button-progress-DDF4rY07.js.br +0 -0
  512. codex/static_root/assets/admin-settings-button-progress-DDF4rY07.js.gz +0 -0
  513. codex/static_root/assets/admin-settings-button-progress-DUUQfVsZ.2548c49a853e.css +0 -1
  514. codex/static_root/assets/admin-settings-button-progress-DUUQfVsZ.css +0 -1
  515. codex/static_root/assets/browser-C0KS3g2E.0bf1c9c6319c.js +0 -1
  516. codex/static_root/assets/browser-C0KS3g2E.0bf1c9c6319c.js.br +0 -0
  517. codex/static_root/assets/browser-C0KS3g2E.0bf1c9c6319c.js.gz +0 -0
  518. codex/static_root/assets/browser-C0KS3g2E.js +0 -1
  519. codex/static_root/assets/browser-C0KS3g2E.js.br +0 -0
  520. codex/static_root/assets/browser-C0KS3g2E.js.gz +0 -0
  521. codex/static_root/assets/browser-JFL2hLjt.21cb2f7335f7.css +0 -1
  522. codex/static_root/assets/browser-JFL2hLjt.21cb2f7335f7.css.br +0 -0
  523. codex/static_root/assets/browser-JFL2hLjt.21cb2f7335f7.css.gz +0 -0
  524. codex/static_root/assets/browser-JFL2hLjt.css +0 -1
  525. codex/static_root/assets/browser-JFL2hLjt.css.br +0 -0
  526. codex/static_root/assets/browser-JFL2hLjt.css.gz +0 -0
  527. codex/static_root/assets/change-password-dialog-DXJalgW4.a9f34729599d.js +0 -1
  528. codex/static_root/assets/change-password-dialog-DXJalgW4.a9f34729599d.js.br +0 -0
  529. codex/static_root/assets/change-password-dialog-DXJalgW4.a9f34729599d.js.gz +0 -0
  530. codex/static_root/assets/change-password-dialog-DXJalgW4.js +0 -1
  531. codex/static_root/assets/change-password-dialog-DXJalgW4.js.br +0 -0
  532. codex/static_root/assets/change-password-dialog-DXJalgW4.js.gz +0 -0
  533. codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css +0 -1
  534. codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css.br +0 -0
  535. codex/static_root/assets/change-password-dialog-DrIXdKUC.217574b45b02.css.gz +0 -0
  536. codex/static_root/assets/change-password-dialog-DrIXdKUC.css +0 -1
  537. codex/static_root/assets/change-password-dialog-DrIXdKUC.css.br +0 -0
  538. codex/static_root/assets/change-password-dialog-DrIXdKUC.css.gz +0 -0
  539. codex/static_root/assets/confirm-dialog-B7w6z2rf.472011c06253.css +0 -1
  540. codex/static_root/assets/confirm-dialog-B7w6z2rf.css +0 -1
  541. codex/static_root/assets/confirm-dialog-CqDEpU9k.e1811488121d.js +0 -1
  542. codex/static_root/assets/confirm-dialog-CqDEpU9k.e1811488121d.js.br +0 -0
  543. codex/static_root/assets/confirm-dialog-CqDEpU9k.e1811488121d.js.gz +0 -0
  544. codex/static_root/assets/confirm-dialog-CqDEpU9k.js +0 -1
  545. codex/static_root/assets/confirm-dialog-CqDEpU9k.js.br +0 -0
  546. codex/static_root/assets/confirm-dialog-CqDEpU9k.js.gz +0 -0
  547. codex/static_root/assets/datetime-column-UxNaJr8b.c06a11531af3.js +0 -1
  548. codex/static_root/assets/datetime-column-UxNaJr8b.c06a11531af3.js.br +0 -0
  549. codex/static_root/assets/datetime-column-UxNaJr8b.c06a11531af3.js.gz +0 -0
  550. codex/static_root/assets/datetime-column-UxNaJr8b.js +0 -1
  551. codex/static_root/assets/datetime-column-UxNaJr8b.js.br +0 -0
  552. codex/static_root/assets/datetime-column-UxNaJr8b.js.gz +0 -0
  553. codex/static_root/assets/filter-DskcqZHA.e707b5a05b4b.js +0 -1
  554. codex/static_root/assets/filter-DskcqZHA.e707b5a05b4b.js.br +0 -0
  555. codex/static_root/assets/filter-DskcqZHA.e707b5a05b4b.js.gz +0 -0
  556. codex/static_root/assets/filter-DskcqZHA.js +0 -1
  557. codex/static_root/assets/filter-DskcqZHA.js.br +0 -0
  558. codex/static_root/assets/filter-DskcqZHA.js.gz +0 -0
  559. codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css +0 -1
  560. codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.br +0 -0
  561. codex/static_root/assets/flag-tab-CqWovuBu.4dfc6b251e85.css.gz +0 -0
  562. codex/static_root/assets/flag-tab-CqWovuBu.css +0 -1
  563. codex/static_root/assets/flag-tab-CqWovuBu.css.br +0 -0
  564. codex/static_root/assets/flag-tab-CqWovuBu.css.gz +0 -0
  565. codex/static_root/assets/flag-tab-xBeWEQ0w.582e77d9e885.js +0 -1
  566. codex/static_root/assets/flag-tab-xBeWEQ0w.582e77d9e885.js.br +0 -0
  567. codex/static_root/assets/flag-tab-xBeWEQ0w.582e77d9e885.js.gz +0 -0
  568. codex/static_root/assets/flag-tab-xBeWEQ0w.js +0 -1
  569. codex/static_root/assets/flag-tab-xBeWEQ0w.js.br +0 -0
  570. codex/static_root/assets/flag-tab-xBeWEQ0w.js.gz +0 -0
  571. codex/static_root/assets/group-tab-Czt2x61x.56e2886b1bac.js +0 -1
  572. codex/static_root/assets/group-tab-Czt2x61x.56e2886b1bac.js.br +0 -0
  573. codex/static_root/assets/group-tab-Czt2x61x.56e2886b1bac.js.gz +0 -0
  574. codex/static_root/assets/group-tab-Czt2x61x.js +0 -1
  575. codex/static_root/assets/group-tab-Czt2x61x.js.br +0 -0
  576. codex/static_root/assets/group-tab-Czt2x61x.js.gz +0 -0
  577. codex/static_root/assets/group-tab-DkIN1Heu.948e907b1e6b.css +0 -1
  578. codex/static_root/assets/group-tab-DkIN1Heu.948e907b1e6b.css.br +0 -0
  579. codex/static_root/assets/group-tab-DkIN1Heu.948e907b1e6b.css.gz +0 -0
  580. codex/static_root/assets/group-tab-DkIN1Heu.css +0 -1
  581. codex/static_root/assets/group-tab-DkIN1Heu.css.br +0 -0
  582. codex/static_root/assets/group-tab-DkIN1Heu.css.gz +0 -0
  583. codex/static_root/assets/http-error-NHcwE5e_.d49089e40589.js +0 -1
  584. codex/static_root/assets/http-error-NHcwE5e_.d49089e40589.js.br +0 -0
  585. codex/static_root/assets/http-error-NHcwE5e_.d49089e40589.js.gz +0 -0
  586. codex/static_root/assets/http-error-NHcwE5e_.js +0 -1
  587. codex/static_root/assets/http-error-NHcwE5e_.js.br +0 -0
  588. codex/static_root/assets/http-error-NHcwE5e_.js.gz +0 -0
  589. codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css +0 -1
  590. codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css.br +0 -0
  591. codex/static_root/assets/index-CL_6g4tJ.523d7b3d350f.css.gz +0 -0
  592. codex/static_root/assets/index-CL_6g4tJ.css +0 -1
  593. codex/static_root/assets/index-CL_6g4tJ.css.br +0 -0
  594. codex/static_root/assets/index-CL_6g4tJ.css.gz +0 -0
  595. codex/static_root/assets/index-DYNNO5kn.24a2d270253a.js +0 -1
  596. codex/static_root/assets/index-DYNNO5kn.24a2d270253a.js.br +0 -0
  597. codex/static_root/assets/index-DYNNO5kn.24a2d270253a.js.gz +0 -0
  598. codex/static_root/assets/index-DYNNO5kn.js +0 -1
  599. codex/static_root/assets/index-DYNNO5kn.js.br +0 -0
  600. codex/static_root/assets/index-DYNNO5kn.js.gz +0 -0
  601. codex/static_root/assets/library-tab-CgIqcjvj.60327dfa8ecb.js +0 -1
  602. codex/static_root/assets/library-tab-CgIqcjvj.60327dfa8ecb.js.br +0 -0
  603. codex/static_root/assets/library-tab-CgIqcjvj.60327dfa8ecb.js.gz +0 -0
  604. codex/static_root/assets/library-tab-CgIqcjvj.js +0 -1
  605. codex/static_root/assets/library-tab-CgIqcjvj.js.br +0 -0
  606. codex/static_root/assets/library-tab-CgIqcjvj.js.gz +0 -0
  607. codex/static_root/assets/library-tab-Wew8YOLv.0c1780a6b68e.css.br +0 -0
  608. codex/static_root/assets/library-tab-Wew8YOLv.0c1780a6b68e.css.gz +0 -0
  609. codex/static_root/assets/library-tab-Wew8YOLv.css.br +0 -0
  610. codex/static_root/assets/library-tab-Wew8YOLv.css.gz +0 -0
  611. codex/static_root/assets/main-B3EwXQ3Q.14dfc21e272c.js +0 -35
  612. codex/static_root/assets/main-B3EwXQ3Q.14dfc21e272c.js.br +0 -0
  613. codex/static_root/assets/main-B3EwXQ3Q.14dfc21e272c.js.gz +0 -0
  614. codex/static_root/assets/main-B3EwXQ3Q.js +0 -35
  615. codex/static_root/assets/main-B3EwXQ3Q.js.br +0 -0
  616. codex/static_root/assets/main-B3EwXQ3Q.js.gz +0 -0
  617. codex/static_root/assets/pager-full-pdf-YkzUXx0B.f39d84e2380a.js +0 -1
  618. codex/static_root/assets/pager-full-pdf-YkzUXx0B.f39d84e2380a.js.br +0 -0
  619. codex/static_root/assets/pager-full-pdf-YkzUXx0B.f39d84e2380a.js.gz +0 -0
  620. codex/static_root/assets/pager-full-pdf-YkzUXx0B.js +0 -1
  621. codex/static_root/assets/pager-full-pdf-YkzUXx0B.js.br +0 -0
  622. codex/static_root/assets/pager-full-pdf-YkzUXx0B.js.gz +0 -0
  623. codex/static_root/assets/pagination-toolbar-CrruiVov.3bf9b795350d.js +0 -1
  624. codex/static_root/assets/pagination-toolbar-CrruiVov.3bf9b795350d.js.br +0 -0
  625. codex/static_root/assets/pagination-toolbar-CrruiVov.3bf9b795350d.js.gz +0 -0
  626. codex/static_root/assets/pagination-toolbar-CrruiVov.js +0 -1
  627. codex/static_root/assets/pagination-toolbar-CrruiVov.js.br +0 -0
  628. codex/static_root/assets/pagination-toolbar-CrruiVov.js.gz +0 -0
  629. codex/static_root/assets/pdf-doc-ctqZIwOP.7e7b2c815ea4.js.br +0 -0
  630. codex/static_root/assets/pdf-doc-ctqZIwOP.7e7b2c815ea4.js.gz +0 -0
  631. codex/static_root/assets/pdf-doc-ctqZIwOP.js.br +0 -0
  632. codex/static_root/assets/pdf-doc-ctqZIwOP.js.gz +0 -0
  633. codex/static_root/assets/reader-DPLhdrJD.268417c618c5.js +0 -2
  634. codex/static_root/assets/reader-DPLhdrJD.268417c618c5.js.br +0 -0
  635. codex/static_root/assets/reader-DPLhdrJD.268417c618c5.js.gz +0 -0
  636. codex/static_root/assets/reader-DPLhdrJD.js +0 -2
  637. codex/static_root/assets/reader-DPLhdrJD.js.br +0 -0
  638. codex/static_root/assets/reader-DPLhdrJD.js.gz +0 -0
  639. codex/static_root/assets/reader-nD70BeEz.06e85e73c391.css.br +0 -0
  640. codex/static_root/assets/reader-nD70BeEz.06e85e73c391.css.gz +0 -0
  641. codex/static_root/assets/reader-nD70BeEz.css.br +0 -0
  642. codex/static_root/assets/reader-nD70BeEz.css.gz +0 -0
  643. codex/static_root/assets/relation-chips-DOALMcWL.5d3b8100ba03.js +0 -1
  644. codex/static_root/assets/relation-chips-DOALMcWL.5d3b8100ba03.js.br +0 -0
  645. codex/static_root/assets/relation-chips-DOALMcWL.5d3b8100ba03.js.gz +0 -0
  646. codex/static_root/assets/relation-chips-DOALMcWL.js +0 -1
  647. codex/static_root/assets/relation-chips-DOALMcWL.js.br +0 -0
  648. codex/static_root/assets/relation-chips-DOALMcWL.js.gz +0 -0
  649. codex/static_root/assets/settings-drawer-BVj1U_LC.c68369e0c2de.js +0 -2
  650. codex/static_root/assets/settings-drawer-BVj1U_LC.c68369e0c2de.js.br +0 -0
  651. codex/static_root/assets/settings-drawer-BVj1U_LC.c68369e0c2de.js.gz +0 -0
  652. codex/static_root/assets/settings-drawer-BVj1U_LC.js +0 -2
  653. codex/static_root/assets/settings-drawer-BVj1U_LC.js.br +0 -0
  654. codex/static_root/assets/settings-drawer-BVj1U_LC.js.gz +0 -0
  655. codex/static_root/assets/stats-tab-BAYTqQNg.5f715428c394.css.br +0 -0
  656. codex/static_root/assets/stats-tab-BAYTqQNg.5f715428c394.css.gz +0 -0
  657. codex/static_root/assets/stats-tab-BAYTqQNg.css.br +0 -0
  658. codex/static_root/assets/stats-tab-BAYTqQNg.css.gz +0 -0
  659. codex/static_root/assets/stats-tab-DDLwwwnP.35305258097b.js +0 -1
  660. codex/static_root/assets/stats-tab-DDLwwwnP.35305258097b.js.br +0 -0
  661. codex/static_root/assets/stats-tab-DDLwwwnP.35305258097b.js.gz +0 -0
  662. codex/static_root/assets/stats-tab-DDLwwwnP.js +0 -1
  663. codex/static_root/assets/stats-tab-DDLwwwnP.js.br +0 -0
  664. codex/static_root/assets/stats-tab-DDLwwwnP.js.gz +0 -0
  665. codex/static_root/assets/task-tab--cXi48kg.5dfd6890a1c2.css +0 -1
  666. codex/static_root/assets/task-tab--cXi48kg.5dfd6890a1c2.css.gz +0 -0
  667. codex/static_root/assets/task-tab--cXi48kg.css +0 -1
  668. codex/static_root/assets/task-tab--cXi48kg.css.gz +0 -0
  669. codex/static_root/assets/task-tab-BpP3n2dO.c853eb565200.js +0 -1
  670. codex/static_root/assets/task-tab-BpP3n2dO.c853eb565200.js.br +0 -0
  671. codex/static_root/assets/task-tab-BpP3n2dO.c853eb565200.js.gz +0 -0
  672. codex/static_root/assets/task-tab-BpP3n2dO.js +0 -1
  673. codex/static_root/assets/task-tab-BpP3n2dO.js.br +0 -0
  674. codex/static_root/assets/task-tab-BpP3n2dO.js.gz +0 -0
  675. codex/static_root/assets/transition-3g2arGgl.c6e6f91b7978.js +0 -1
  676. codex/static_root/assets/transition-3g2arGgl.c6e6f91b7978.js.br +0 -0
  677. codex/static_root/assets/transition-3g2arGgl.c6e6f91b7978.js.gz +0 -0
  678. codex/static_root/assets/transition-3g2arGgl.js +0 -1
  679. codex/static_root/assets/transition-3g2arGgl.js.br +0 -0
  680. codex/static_root/assets/transition-3g2arGgl.js.gz +0 -0
  681. codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css +0 -1
  682. codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.br +0 -4
  683. codex/static_root/assets/transition-5LRFTPo5.3d726f9bbcb0.css.gz +0 -0
  684. codex/static_root/assets/transition-5LRFTPo5.css +0 -1
  685. codex/static_root/assets/transition-5LRFTPo5.css.br +0 -4
  686. codex/static_root/assets/transition-5LRFTPo5.css.gz +0 -0
  687. codex/static_root/assets/unauthorized-CdumQ1Vf.79d9a61eb572.js +0 -1
  688. codex/static_root/assets/unauthorized-CdumQ1Vf.79d9a61eb572.js.br +0 -0
  689. codex/static_root/assets/unauthorized-CdumQ1Vf.79d9a61eb572.js.gz +0 -0
  690. codex/static_root/assets/unauthorized-CdumQ1Vf.js +0 -1
  691. codex/static_root/assets/unauthorized-CdumQ1Vf.js.br +0 -0
  692. codex/static_root/assets/unauthorized-CdumQ1Vf.js.gz +0 -0
  693. codex/static_root/assets/user-tab-D6oOKZTP.05944b00c700.js +0 -1
  694. codex/static_root/assets/user-tab-D6oOKZTP.05944b00c700.js.br +0 -0
  695. codex/static_root/assets/user-tab-D6oOKZTP.05944b00c700.js.gz +0 -0
  696. codex/static_root/assets/user-tab-D6oOKZTP.js +0 -1
  697. codex/static_root/assets/user-tab-D6oOKZTP.js.br +0 -0
  698. codex/static_root/assets/user-tab-D6oOKZTP.js.gz +0 -0
  699. codex/static_root/manifest.c5f1de88b1ac.json +0 -712
  700. codex/static_root/manifest.c5f1de88b1ac.json.br +0 -0
  701. codex/static_root/manifest.c5f1de88b1ac.json.gz +0 -0
  702. codex/views/browser/annotate/__init__.py +0 -1
  703. codex/views/browser/annotate/bookmark.py +0 -112
  704. codex/views/browser/annotate/card.py +0 -61
  705. codex/views/browser/annotate/order.py +0 -247
  706. codex/views/browser/filters/filter.py +0 -69
  707. codex/views/browser/group_mtime.py +0 -83
  708. codex/views/browser/metadata/__init__.py +0 -1
  709. codex/views/browser/metadata/annotate.py +0 -109
  710. codex/views/browser/metadata/copy_intersections.py +0 -90
  711. codex/views/browser/metadata/metadata.py +0 -83
  712. codex/views/browser/metadata/query_intersections.py +0 -112
  713. codex/views/browser/params.py +0 -64
  714. codex/views/timezone.py +0 -54
  715. {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/LICENSE +0 -0
  716. {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/WHEEL +0 -0
  717. {codex-1.7.0.dist-info → codex-1.7.0a0.dist-info}/entry_points.txt +0 -0
@@ -2,29 +2,26 @@
2
2
 
3
3
  from django.db.models import Q
4
4
 
5
- from codex.views.browser.params import BrowserParamsView
6
5
  from codex.views.const import (
7
6
  FILTER_ONLY_GROUP_RELATION,
8
7
  FOLDER_GROUP,
9
8
  GROUP_RELATION,
10
9
  )
10
+ from codex.views.session import SessionView
11
11
 
12
- _GROUP_REL_TARGETS = frozenset({"cover", "choices"})
13
- _PK_REL_TARGETS = frozenset({"metadata", "mtime"})
14
12
 
15
-
16
- class GroupFilterView(BrowserParamsView):
13
+ class GroupFilterView(SessionView):
17
14
  """Group Filters."""
18
15
 
19
- SESSION_KEY = BrowserParamsView.BROWSER_SESSION_KEY
16
+ SESSION_KEY = SessionView.BROWSER_SESSION_KEY
20
17
 
21
18
  def _get_rel_for_pks(self, group, page_mtime=False):
22
19
  """Get the relation from the model to the pks."""
23
20
  # XXX these TARGET refs might be better as subclass get rel methods.
24
21
  target: str = self.TARGET # type: ignore
25
- if target in _GROUP_REL_TARGETS:
22
+ if target in ("cover", "choices"):
26
23
  rel = FILTER_ONLY_GROUP_RELATION[group]
27
- elif target in _PK_REL_TARGETS or page_mtime:
24
+ elif target in ("metadata", "mtime") or page_mtime:
28
25
  # metadata, mtime, browser.page_mtime
29
26
  rel = "pk"
30
27
  else:
@@ -45,8 +42,8 @@ class GroupFilterView(BrowserParamsView):
45
42
  rel = self._get_rel_for_pks(group, page_mtime)
46
43
  group_filter_dict = {rel: pks}
47
44
  elif group == FOLDER_GROUP:
48
- # Top folder search
49
45
  group_filter_dict = {"parent_folder": None}
50
46
  else:
51
47
  group_filter_dict = {}
48
+
52
49
  return Q(**group_filter_dict)
@@ -1,6 +1,9 @@
1
1
  """Search Filters Methods."""
2
2
 
3
+ from django.db.models.query import Q
4
+
3
5
  from codex.logger.logging import get_logger
6
+ from codex.models import Comic, StoryArc
4
7
  from codex.views.browser.filters.search.field.filter import BrowserFieldQueryFilter
5
8
 
6
9
  LOG = get_logger(__name__)
@@ -9,16 +12,27 @@ LOG = get_logger(__name__)
9
12
  class BrowserFTSFilter(BrowserFieldQueryFilter):
10
13
  """Search Filters Methods."""
11
14
 
15
+ @staticmethod
16
+ def _get_fts_filter(model, text):
17
+ """Get the filter dict."""
18
+ prefix = (
19
+ ""
20
+ if model == Comic
21
+ else "storyarcnumber__comic__"
22
+ if model == StoryArc
23
+ else "comic__"
24
+ )
25
+ # Custom Lookup defined in codex.models
26
+ rel = prefix + "comicfts__match"
27
+ return {rel: text}
28
+
12
29
  def get_fts_filter(self, model, text):
13
30
  """Perform the search and return the scores as a dict."""
14
31
  fts_filter = {}
15
32
  try:
16
33
  if text:
17
- rel = self.get_rel_prefix(model)
18
- # Custom lookup defined in codex.models
19
- rel += "comicfts__match"
20
- fts_filter[rel] = text
34
+ fts_filter = self._get_fts_filter(model, text)
21
35
  except Exception:
22
36
  LOG.exception("Getting Full Text Search Filter.")
23
37
  self.search_error = "Error creating full text search filter"
24
- return fts_filter
38
+ return Q(**fts_filter)
@@ -1,12 +1,8 @@
1
1
  """Search Filters Methods."""
2
2
 
3
3
  import re
4
- from types import MappingProxyType
5
-
6
- from django.db.models.query import Q
7
4
 
8
5
  from codex.logger.logging import get_logger
9
- from codex.models import AdminFlag
10
6
  from codex.models.comic import ComicFTS
11
7
  from codex.views.browser.filters.search.aliases import ALIAS_FIELD_MAP
12
8
  from codex.views.browser.filters.search.fts import BrowserFTSFilter
@@ -60,43 +56,24 @@ _TOKEN_RE = re.compile(_TOKEN_REXP, flags=re.IGNORECASE)
60
56
  class SearchFilterView(BrowserFTSFilter):
61
57
  """Search Query Parser."""
62
58
 
63
- ADMIN_FLAG_VALUE_KEY_MAP = MappingProxyType(
64
- {
65
- AdminFlag.FlagChoices.FOLDER_VIEW.value: "folder_view",
66
- }
67
- )
68
-
69
59
  def __init__(self, *args, **kwargs):
70
60
  """Initialize search variables."""
71
61
  super().__init__(*args, **kwargs)
72
- self._admin_flags: MappingProxyType[str, bool] | None = None
73
62
  self.fts_mode = False
74
63
  self.search_mode = False
75
64
  self.search_error = ""
76
65
 
77
- @property
78
- def admin_flags(self) -> MappingProxyType[str, bool]:
79
- """Set browser relevant admin flags."""
80
- if self._admin_flags is None:
81
- if self.ADMIN_FLAG_VALUE_KEY_MAP:
82
- admin_pairs = AdminFlag.objects.filter(
83
- key__in=self.ADMIN_FLAG_VALUE_KEY_MAP.keys()
84
- ).values_list("key", "on")
85
- else:
86
- admin_pairs = ()
87
- admin_flags = {}
88
- for key, on in admin_pairs:
89
- export_key = self.ADMIN_FLAG_VALUE_KEY_MAP[key]
90
- admin_flags[export_key] = on
91
- self._admin_flags = MappingProxyType(admin_flags)
92
- return self._admin_flags
93
-
94
66
  def _is_path_column_allowed(self):
95
67
  """Is path column allowed."""
96
- return self.is_admin or bool(self.admin_flags["folder_view"])
68
+ if not self.is_admin(): # type: ignore
69
+ if not "folder_view" not in self.admin_flags: # type: ignore
70
+ # Ensure admin flags for Cover View
71
+ self.set_admin_flags() # type: ignore
72
+ return bool(self.admin_flags.get("folder_view")) # type: ignore
73
+ return True
97
74
 
98
75
  @staticmethod
99
- def _is_column_operators_used(exp) -> bool:
76
+ def _is_column_operators_used(exp):
100
77
  """Detect column expression operators, but not inside quotes."""
101
78
  for match in _COLUMN_EXPRESSION_OPERATORS_RE.finditer(exp):
102
79
  if match.group("star"):
@@ -172,7 +149,7 @@ class SearchFilterView(BrowserFTSFilter):
172
149
  field_exclude_q_list = []
173
150
  for preop, field_token_pairs in field_tokens_dict.items():
174
151
  if preop == "not":
175
- exclude_q_list, include_q_list = self.get_search_field_filters(
152
+ exclude_q_list, filter_q_list = self.get_search_field_filters(
176
153
  model, field_token_pairs
177
154
  )
178
155
  else:
@@ -180,44 +157,37 @@ class SearchFilterView(BrowserFTSFilter):
180
157
  # XXX cannot do OR queries with MATCH, it decontextualizes MATCH somehow.
181
158
  if preop == "or":
182
159
  self.search_error = "OR preceding column tokens with operator expressions will act as AND"
183
- include_q_list, exclude_q_list = self.get_search_field_filters(
160
+ filter_q_list, exclude_q_list = self.get_search_field_filters(
184
161
  model, field_token_pairs
185
162
  )
186
- field_filter_q_list += include_q_list
163
+ field_filter_q_list += filter_q_list
187
164
  field_exclude_q_list += exclude_q_list
165
+ fts_filter = self.get_fts_filter(model, fts_text)
166
+ return field_exclude_q_list, field_filter_q_list, fts_filter
188
167
 
189
- fts_filter_dict = self.get_fts_filter(model, fts_text)
190
- if fts_filter_dict:
191
- self.fts_mode = True
192
- fts_q = Q(**fts_filter_dict)
193
- else:
194
- fts_q = Q()
195
-
196
- return field_filter_q_list, field_exclude_q_list, fts_q
197
-
198
- def _create_search_filter(self, filter_list):
168
+ def _apply_search_filter_list(self, qs, filter_list, exclude):
199
169
  """Apply search filter lists. Separate filter clauses are employed for m2m searches."""
200
- combined_q = Q()
201
170
  for q in filter_list:
202
171
  if not q:
203
172
  continue
204
- combined_q &= q
173
+ qs = qs.exclude(q) if exclude else qs.filter(q)
205
174
  self.search_mode = True
206
175
 
207
- return combined_q
176
+ return qs
208
177
 
209
- def get_search_filters(self, model):
178
+ def apply_search_filter(self, qs, model):
210
179
  """Preparse search, search and return the filter and scores."""
211
- include_q = Q()
212
- exclude_q = Q()
213
- fts_q = Q()
214
180
  try:
215
- field_filter_q_list, field_exclude_q_list, fts_q = (
181
+ field_exclude_q_list, field_filter_q_list, fts_filter = (
216
182
  self._create_search_filters(model)
217
183
  )
184
+
218
185
  # Apply filters
219
- include_q = self._create_search_filter(field_filter_q_list)
220
- exclude_q = self._create_search_filter(field_exclude_q_list)
186
+ qs = self._apply_search_filter_list(qs, field_exclude_q_list, True)
187
+ qs = self._apply_search_filter_list(qs, field_filter_q_list, False)
188
+ if fts_filter:
189
+ qs = qs.filter(fts_filter)
190
+ self.search_mode = self.fts_mode = True
221
191
 
222
192
  except Exception as exc:
223
193
  msg = "Creating search filters"
@@ -225,7 +195,7 @@ class SearchFilterView(BrowserFTSFilter):
225
195
  msg = f"{msg} - {exc}"
226
196
  self.search_error = msg
227
197
 
228
- return include_q, exclude_q, fts_q
198
+ return qs
229
199
 
230
200
  def _is_search_results_limited(self) -> bool:
231
201
  """Get search result limit from params."""
@@ -0,0 +1,332 @@
1
+ """View for marking comics read and unread."""
2
+
3
+ from types import MappingProxyType
4
+
5
+ from django.db.models import Count, IntegerField, Sum, Value
6
+ from drf_spectacular.utils import extend_schema
7
+ from rest_framework.exceptions import NotFound
8
+ from rest_framework.response import Response
9
+
10
+ from codex.librarian.importer.const import (
11
+ COMIC_M2M_FIELD_NAMES,
12
+ )
13
+ from codex.logger.logging import get_logger
14
+ from codex.models import AdminFlag, Comic
15
+ from codex.models.functions import JsonGroupArray
16
+ from codex.serializers.browser.metadata import PREFETCH_PREFIX, MetadataSerializer
17
+ from codex.serializers.browser.settings import BrowserFilterChoicesInputSerilalizer
18
+ from codex.views.browser.annotations import BrowserAnnotationsView
19
+ from codex.views.const import METADATA_GROUP_RELATION
20
+
21
+ LOG = get_logger(__name__)
22
+ _ADMIN_OR_FILE_VIEW_ENABLED_COMIC_VALUE_FIELDS = frozenset({"path"})
23
+ _COMIC_VALUE_FIELDS_CONFLICTING = frozenset(
24
+ {
25
+ "created_at",
26
+ "name",
27
+ "path",
28
+ "updated_at",
29
+ }
30
+ )
31
+ _DISABLED_VALUE_FIELD_NAMES = frozenset(
32
+ {"id", "pk", "sort_name", "stat"} | _COMIC_VALUE_FIELDS_CONFLICTING
33
+ )
34
+ _COMIC_VALUE_FIELD_NAMES = frozenset(
35
+ # contains path
36
+ field.name
37
+ for field in Comic._meta.get_fields()
38
+ if not field.is_relation and field.name not in _DISABLED_VALUE_FIELD_NAMES
39
+ )
40
+ _COMIC_VALUE_FIELDS_CONFLICTING_PREFIX = "conflict_"
41
+ _COMIC_RELATED_VALUE_FIELDS = frozenset({"series__volume_count", "volume__issue_count"})
42
+ _PATH_GROUPS = frozenset({"c", "f"})
43
+ _CONTRIBUTOR_RELATIONS = ("role", "person")
44
+ _SUM_FIELDS = frozenset({"page_count", "size"})
45
+ _GROUP_RELS = MappingProxyType(
46
+ {
47
+ "i": ("publisher",),
48
+ "s": (
49
+ "publisher",
50
+ "imprint",
51
+ ),
52
+ "v": (
53
+ "publisher",
54
+ "imprint",
55
+ "series",
56
+ ),
57
+ "c": ("publisher", "imprint", "series", "volume"),
58
+ }
59
+ )
60
+
61
+
62
+ class MetadataView(BrowserAnnotationsView):
63
+ """Comic metadata."""
64
+
65
+ serializer_class = MetadataSerializer
66
+ input_serializer_class = BrowserFilterChoicesInputSerilalizer
67
+ TARGET = "metadata"
68
+ ADMIN_FLAG_VALUE_KEY_MAP = MappingProxyType(
69
+ {
70
+ AdminFlag.FlagChoices.FOLDER_VIEW.value: "folder_view",
71
+ }
72
+ )
73
+
74
+ def init_request(self):
75
+ """Initialize request."""
76
+ self.set_admin_flags()
77
+ self.parse_params()
78
+ self.set_model()
79
+ self.set_rel_prefix()
80
+
81
+ def set_valid_browse_nav_groups(self, valid_top_groups): # noqa: ARG002
82
+ """Limited allowed nav groups for metadata."""
83
+ group = self.kwargs["group"]
84
+ self.valid_nav_groups = (group,)
85
+
86
+ def _get_comic_value_fields(self):
87
+ """Include the path field for staff."""
88
+ fields = set(_COMIC_VALUE_FIELD_NAMES)
89
+ group = self.kwargs["group"]
90
+ if (
91
+ not (self.admin_flags["folder_view"] and self.is_admin())
92
+ or group not in _PATH_GROUPS
93
+ ):
94
+ fields -= _ADMIN_OR_FILE_VIEW_ENABLED_COMIC_VALUE_FIELDS
95
+ return tuple(fields)
96
+
97
+ def _intersection_annotate(
98
+ self,
99
+ querysets,
100
+ fields,
101
+ related_suffix="",
102
+ annotation_prefix="",
103
+ ):
104
+ """Annotate the intersection of value and fk fields."""
105
+ (filtered_qs, qs) = querysets
106
+
107
+ for field in fields:
108
+ # Annotate variant counts
109
+ # If 1 variant, annotate value, otherwise None
110
+ ann_field = annotation_prefix + field.replace("__", "_")
111
+
112
+ full_field = self.rel_prefix + field
113
+
114
+ if field in _SUM_FIELDS:
115
+ val = Sum(full_field)
116
+ else:
117
+ # group_by makes the filter work, but prevents its
118
+ # use as a subquery in the big query.
119
+ sq = filtered_qs.alias(
120
+ filter_count=Count(full_field, distinct=True)
121
+ ).filter(filter_count=1)
122
+ sq = self.add_group_by(sq)
123
+ sq = sq.values_list(full_field + related_suffix, flat=True)
124
+ try:
125
+ val = sq[0]
126
+ except IndexError:
127
+ val = None
128
+ if field.endswith("count"):
129
+ output_field = IntegerField()
130
+ else:
131
+ output_field = Comic._meta.get_field(field).__class__()
132
+ val = Value(val, output_field)
133
+ qs = qs.annotate(**{ann_field: val})
134
+
135
+ return filtered_qs, qs
136
+
137
+ def _annotate_values_and_fks(self, qs, filtered_qs):
138
+ """Annotate comic values and comic foreign key values."""
139
+ # Simple Values
140
+ querysets = (filtered_qs, qs)
141
+ if not self.is_model_comic:
142
+ comic_value_fields = self._get_comic_value_fields()
143
+ querysets = self._intersection_annotate(querysets, comic_value_fields)
144
+
145
+ # Conflicting Simple Values
146
+ querysets = self._intersection_annotate(
147
+ querysets,
148
+ _COMIC_VALUE_FIELDS_CONFLICTING,
149
+ annotation_prefix=_COMIC_VALUE_FIELDS_CONFLICTING_PREFIX,
150
+ )
151
+
152
+ # Foreign Keys with special count values
153
+ _, qs = self._intersection_annotate(
154
+ querysets,
155
+ _COMIC_RELATED_VALUE_FIELDS,
156
+ )
157
+ return qs
158
+
159
+ def _query_groups(self):
160
+ """Query the through models to show group lists."""
161
+ groups = {}
162
+ if not self.model:
163
+ return groups
164
+ group = self.kwargs["group"]
165
+ rel = METADATA_GROUP_RELATION.get(group)
166
+ if not rel:
167
+ return groups
168
+ rel = rel + "__in"
169
+ pks = self.kwargs["pks"]
170
+ group_filter = {rel: pks}
171
+
172
+ for field_name in _GROUP_RELS.get(group, ()):
173
+ field = self.model._meta.get_field(field_name)
174
+ model = field.related_model
175
+ if not model:
176
+ continue
177
+
178
+ qs = model.objects.filter(**group_filter)
179
+ qs = qs.only("name").distinct()
180
+ qs = qs.group_by("name")
181
+ qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
182
+ qs = qs.values("ids", "name")
183
+ groups[field_name] = qs
184
+ return groups
185
+
186
+ @staticmethod
187
+ def _get_optimized_m2m_query(key, qs):
188
+ if key == "contributors":
189
+ qs = qs.prefetch_related(*_CONTRIBUTOR_RELATIONS)
190
+ qs = qs.only(*_CONTRIBUTOR_RELATIONS)
191
+ elif key == "story_arc_numbers":
192
+ qs = qs.select_related("story_arc")
193
+ qs = qs.only("story_arc", "number")
194
+ elif key == "identifiers":
195
+ qs = qs.select_related("identifier_type")
196
+ qs = qs.only("identifier_type", "nss", "url")
197
+ else:
198
+ qs = qs.only("name")
199
+ return qs
200
+
201
+ def _query_m2m_intersections(self, filtered_qs):
202
+ """Query the through models to figure out m2m intersections."""
203
+ # Speed ok, but still does a query per m2m model
204
+ m2m_intersections = {}
205
+ pk_field = self.rel_prefix + "pk"
206
+ comic_pks = filtered_qs.values_list(pk_field, flat=True)
207
+ comic_pks_count = comic_pks.count()
208
+ for field_name in COMIC_M2M_FIELD_NAMES:
209
+ model = Comic._meta.get_field(field_name).related_model
210
+ if not model:
211
+ reason = f"No model found for comic field: {field_name}"
212
+ raise ValueError(reason)
213
+
214
+ intersection_qs = model.objects.filter(comic__pk__in=comic_pks)
215
+ intersection_qs = intersection_qs.alias(count=Count("comic")).filter(
216
+ count=comic_pks_count
217
+ )
218
+ intersection_qs = self._get_optimized_m2m_query(field_name, intersection_qs)
219
+
220
+ m2m_intersections[field_name] = intersection_qs
221
+ return m2m_intersections
222
+
223
+ def _path_security(self, obj):
224
+ """Secure filesystem information for acl situation."""
225
+ group = self.kwargs["group"]
226
+ is_path_group = group in _PATH_GROUPS
227
+ if is_path_group:
228
+ if self.is_admin():
229
+ return
230
+ if self.admin_flags["folder_view"]:
231
+ obj.path = obj.search_path()
232
+ else:
233
+ obj.path = ""
234
+
235
+ def _highlight_current_group(self, obj):
236
+ """Values for highlighting the current group."""
237
+ if self.model and not self.is_model_comic:
238
+ # move the name of the group to the correct field
239
+ field = self.model.__name__.lower() + "_list"
240
+ group_list = self.model.objects.filter(pk__in=obj.ids).values("name")
241
+ setattr(obj, field, group_list)
242
+ obj.name = None
243
+
244
+ @classmethod
245
+ def _copy_m2m_intersections(cls, obj, m2m_intersections):
246
+ """Copy the m2m intersections into the object."""
247
+ # XXX It might even be faster to copy everything to a dict and not use the obj.
248
+ for key, qs in m2m_intersections.items():
249
+ serializer_key = (
250
+ f"{PREFETCH_PREFIX}{key}"
251
+ if key in ("identifiers", "contributors", "story_arc_numbers")
252
+ else key
253
+ )
254
+ if hasattr(obj, serializer_key):
255
+ # real db fields need to use their special set method.
256
+ field = getattr(obj, serializer_key)
257
+ field.set(
258
+ qs,
259
+ clear=True,
260
+ )
261
+ else:
262
+ # fake db field is just a queryset attached.
263
+ setattr(obj, serializer_key, qs)
264
+
265
+ @staticmethod
266
+ def _copy_groups(obj, groups):
267
+ for field, group_qs in groups.items():
268
+ setattr(obj, field + "_list", group_qs)
269
+
270
+ @staticmethod
271
+ def _copy_conflicting_simple_fields(obj):
272
+ for field in _COMIC_VALUE_FIELDS_CONFLICTING:
273
+ """Copy conflicting fields over naturral fields."""
274
+ conflict_field = _COMIC_VALUE_FIELDS_CONFLICTING_PREFIX + field
275
+ val = getattr(obj, conflict_field)
276
+ setattr(obj, field, val)
277
+
278
+ def _copy_annotations_into_comic_fields(self, obj, groups, m2m_intersections):
279
+ """Copy a bunch of values that i couldn't fit cleanly in the main queryset."""
280
+ self._path_security(obj)
281
+ self._highlight_current_group(obj)
282
+ self._copy_groups(obj, groups)
283
+ self._copy_m2m_intersections(obj, m2m_intersections)
284
+ if not self.is_model_comic:
285
+ self._copy_conflicting_simple_fields(obj)
286
+
287
+ return obj
288
+
289
+ def _raise_not_found(self, exc=None):
290
+ """Raise an exception if the object is not found."""
291
+ pks = self.kwargs["pks"]
292
+ group = self.kwargs["group"]
293
+ detail = f"Filtered metadata for {group}/{pks} not found"
294
+ raise NotFound(detail=detail) from exc
295
+
296
+ def get_object(self):
297
+ """Create a comic-like object from the current browser group."""
298
+ # Comic model goes through the same code path as groups because
299
+ # values dicts don't copy relations to the serializer. The values
300
+ # dict is necessary because of the folders view union in browser.py.
301
+
302
+ qs = self.get_filtered_queryset(self.model)
303
+
304
+ filtered_qs = qs
305
+ qs = self.annotate_order_aggregates(qs, self.model)
306
+ qs = self.annotate_card_aggregates(qs, self.model)
307
+ qs = self._annotate_values_and_fks(qs, filtered_qs)
308
+ qs = self.add_group_by(qs)
309
+
310
+ try:
311
+ obj = qs[0]
312
+ if not obj:
313
+ reason = "Empty obj"
314
+ raise ValueError(reason) # noqa TRY301
315
+ except (IndexError, ValueError) as exc:
316
+ self._raise_not_found(exc)
317
+
318
+ groups = self._query_groups()
319
+ m2m_intersections = self._query_m2m_intersections(filtered_qs)
320
+ return self._copy_annotations_into_comic_fields(obj, groups, m2m_intersections) # type: ignore
321
+
322
+ @extend_schema(parameters=[input_serializer_class])
323
+ def get(self, *_args, **_kwargs):
324
+ """Get metadata for a filtered browse group."""
325
+ # Init
326
+ try:
327
+ self.init_request()
328
+ obj = self.get_object()
329
+ serializer = self.get_serializer(obj)
330
+ return Response(serializer.data)
331
+ except Exception:
332
+ LOG.exception(f"Getting metadata {self.kwargs}")
@@ -7,13 +7,13 @@ from codex.logger.logging import get_logger
7
7
  from codex.models.groups import Publisher
8
8
  from codex.serializers.browser.mtime import GroupsMtimeSerializer, MtimeSerializer
9
9
  from codex.util import max_none
10
- from codex.views.browser.group_mtime import BrowserGroupMtimeView
10
+ from codex.views.browser.filters.annotations import BrowserAnnotationsFilterView
11
11
  from codex.views.const import GROUP_MODEL_MAP
12
12
 
13
13
  LOG = get_logger(__name__)
14
14
 
15
15
 
16
- class MtimeView(BrowserGroupMtimeView):
16
+ class MtimeView(BrowserAnnotationsFilterView):
17
17
  """Get the mtimes for the submitted groups."""
18
18
 
19
19
  input_serializer_class = GroupsMtimeSerializer
@@ -22,6 +22,11 @@ class MtimeView(BrowserGroupMtimeView):
22
22
  REPARSE_JSON_FIELDS = frozenset({"groups", "filters"})
23
23
  TARGET = "mtime"
24
24
 
25
+ def __init__(self, *args, **kwargs) -> None:
26
+ """Initialize."""
27
+ super().__init__(*args, **kwargs)
28
+ self.init_bookmark_data()
29
+
25
30
  def _get_group_mtime(self, item):
26
31
  """Get one group's mtimes."""
27
32
  group = item["group"]
@@ -45,6 +50,9 @@ class MtimeView(BrowserGroupMtimeView):
45
50
  @extend_schema(parameters=[GroupsMtimeSerializer])
46
51
  def get(self, *args, **kwargs):
47
52
  """Get the mtimes for the submitted groups."""
53
+ # Parse Request
54
+ self.parse_params()
55
+
48
56
  max_mtime = self.get_max_groups_mtime()
49
57
 
50
58
  # Serialize Response
@@ -1,29 +1,27 @@
1
1
  """Base view for ordering the query."""
2
2
 
3
+ from django.db.models.aggregates import Max, Min
4
+
3
5
  from codex.models import Comic
4
- from codex.views.browser.group_mtime import BrowserGroupMtimeView
6
+ from codex.views.browser.filters.annotations import (
7
+ BrowserAnnotationsFilterView,
8
+ )
5
9
 
6
10
 
7
- class BrowserOrderByView(BrowserGroupMtimeView):
11
+ class BrowserOrderByView(BrowserAnnotationsFilterView):
8
12
  """Base class for views that need ordering."""
9
13
 
10
- def __init__(self, *args, **kwargs):
11
- """Initialize memoized vars."""
12
- super().__init__(*args, **kwargs)
13
- self._order_key: str = ""
14
- self._comic_sort_names: tuple[str, ...] = ()
15
-
16
- @property
17
- def order_key(self):
14
+ def set_order_key(self):
18
15
  """Get the default order key for the view."""
19
- if not self._order_key:
20
- order_key: str = self.params["order_by"]
21
- if (order_key == "search_score" and not self.fts_mode) or (
22
- order_key == "filename" and not self.admin_flags["folder_view"]
23
- ):
24
- order_key = "sort_name"
25
- self._order_key = order_key
26
- return self._order_key
16
+ order_key: str = self.params["order_by"]
17
+ if order_key == "search_score" and not self.fts_mode:
18
+ # if no search scores, use sort_name, asc
19
+ order_key = "sort_name"
20
+ order_reverse = False
21
+ else:
22
+ order_reverse = self.params.get("order_reverse")
23
+ self.order_key = order_key
24
+ self.order_agg_func = Max if order_reverse else Min
27
25
 
28
26
  def _add_comic_order_by(self, order_key, comic_sort_names):
29
27
  """Order by for comics (and covers)."""
@@ -31,7 +29,7 @@ class BrowserOrderByView(BrowserGroupMtimeView):
31
29
  order_key = self.order_key
32
30
  if order_key == "sort_name":
33
31
  if not comic_sort_names:
34
- comic_sort_names = self._comic_sort_names
32
+ comic_sort_names = self.comic_sort_names # type: ignore
35
33
  order_fields_head = [
36
34
  *comic_sort_names,
37
35
  "issue_number",
@@ -47,13 +45,15 @@ class BrowserOrderByView(BrowserGroupMtimeView):
47
45
  if order_key == "story_arc_number":
48
46
  order_fields_head += ["date"]
49
47
  elif order_key == "bookmark_updated_at":
50
- order_fields_head += ["bookmark_updated_at"]
48
+ order_fields_head += ["updated_at"]
51
49
  return order_fields_head
52
50
 
53
- def add_order_by(self, qs, order_key="", do_reverse=True, comic_sort_names=None):
51
+ def add_order_by(
52
+ self, qs, model, order_key="", do_reverse=True, comic_sort_names=None
53
+ ):
54
54
  """Create the order_by list."""
55
55
  order_fields_head = ()
56
- if qs.model is Comic:
56
+ if model == Comic:
57
57
  order_fields_head = self._add_comic_order_by(order_key, comic_sort_names)
58
58
  else:
59
59
  order_fields_head = ["order_value"]