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
@@ -1,13 +1,13 @@
1
1
  """Browser Page Bounds Checking."""
2
2
 
3
3
  from codex.logger.logging import get_logger
4
- from codex.views.browser.annotate.card import BrowserAnnotateCardView
4
+ from codex.views.browser.annotations import BrowserAnnotationsView
5
5
  from codex.views.util import Route
6
6
 
7
7
  LOG = get_logger(__name__)
8
8
 
9
9
 
10
- class BrowserPageInBoundsView(BrowserAnnotateCardView):
10
+ class BrowserPageInBoundsView(BrowserAnnotationsView):
11
11
  """Browser Page Bounds Checking."""
12
12
 
13
13
  def _get_back_one_page_route(self, num_pages):
@@ -5,8 +5,14 @@ from math import ceil
5
5
  from django.core.paginator import EmptyPage, Paginator
6
6
 
7
7
  from codex.logger.logging import get_logger
8
+ from codex.models import (
9
+ Comic,
10
+ Folder,
11
+ )
8
12
  from codex.views.browser.page_in_bounds import BrowserPageInBoundsView
9
- from codex.views.const import MAX_OBJ_PER_PAGE
13
+ from codex.views.const import (
14
+ MAX_OBJ_PER_PAGE,
15
+ )
10
16
 
11
17
  LOG = get_logger(__name__)
12
18
 
@@ -14,7 +20,7 @@ LOG = get_logger(__name__)
14
20
  class BrowserPaginateView(BrowserPageInBoundsView):
15
21
  """Paginate Groups and Books."""
16
22
 
17
- def _paginate_section(self, qs, page):
23
+ def _paginate_section(self, model, qs, page):
18
24
  """Paginate a group or Comic section."""
19
25
  orphans = 0 if self.model_group == "f" or self.params.get("q") else 5
20
26
  paginator = Paginator(qs, MAX_OBJ_PER_PAGE, orphans=orphans)
@@ -22,21 +28,21 @@ class BrowserPaginateView(BrowserPageInBoundsView):
22
28
  paginator_page = paginator.page(page)
23
29
  qs = paginator_page.object_list
24
30
  except EmptyPage:
25
- if self.model_group != "f":
26
- model_name = qs.model.__name__ if qs.model else "UnknownGroup"
31
+ if model != Folder:
32
+ model_name = self.model.__name__ if self.model else "UnknownGroup"
27
33
  LOG.warning(f"No {model_name}s on page {page}")
28
- qs = qs.model.objects.none()
34
+ qs = model.objects.none()
29
35
 
30
36
  return qs
31
37
 
32
38
  def _paginate_groups(self, group_qs):
33
39
  """Paginate the group object list before books."""
34
40
  page = self.kwargs.get("page", 1)
35
- return self._paginate_section(group_qs, page)
41
+ return self._paginate_section(self.model, group_qs, page)
36
42
 
37
43
  def _paginate_books(self, book_qs, total_group_count, page_group_count):
38
44
  """Paginate the book object list based on how many group/folders are showing."""
39
- group_remainder = total_group_count % MAX_OBJ_PER_PAGE
45
+ group_remainder = page_group_count % MAX_OBJ_PER_PAGE
40
46
  num_books_on_mixed_page = MAX_OBJ_PER_PAGE - group_remainder
41
47
  if page_group_count:
42
48
  # There are books after the groups on the same page
@@ -44,7 +50,7 @@ class BrowserPaginateView(BrowserPageInBoundsView):
44
50
  page_book_qs = book_qs[:num_books_on_mixed_page]
45
51
  else:
46
52
  # There are books after the groups on a new page
47
- book_offset = 0 if not group_remainder else num_books_on_mixed_page
53
+ book_offset = 0 if group_remainder else num_books_on_mixed_page
48
54
  page_book_qs = book_qs[book_offset:]
49
55
 
50
56
  # Which book page are we on after groups?
@@ -52,7 +58,7 @@ class BrowserPaginateView(BrowserPageInBoundsView):
52
58
  num_group_and_mixed_pages = ceil(total_group_count / MAX_OBJ_PER_PAGE)
53
59
  book_only_page = page - num_group_and_mixed_pages
54
60
 
55
- page_book_qs = self._paginate_section(page_book_qs, book_only_page)
61
+ page_book_qs = self._paginate_section(Comic, page_book_qs, book_only_page)
56
62
  return page_book_qs
57
63
 
58
64
  def paginate(self, group_qs, book_qs, group_count):
@@ -1,12 +1,17 @@
1
1
  """Browser title."""
2
2
 
3
- from codex.models import Comic, Volume
3
+ from codex.models import BrowserGroupModel, Comic, Volume
4
4
  from codex.views.browser.breadcrumbs import BrowserBreadcrumbsView
5
5
 
6
6
 
7
7
  class BrowserTitleView(BrowserBreadcrumbsView):
8
8
  """Browser title methods."""
9
9
 
10
+ def __init__(self, *args, **kwargs):
11
+ """Set params for the type checker."""
12
+ super().__init__(*args, **kwargs)
13
+ self.group_instance: BrowserGroupModel | None = None
14
+
10
15
  def _get_root_group_name(self):
11
16
  if not self.model:
12
17
  reason = "No model set in browser"
@@ -3,17 +3,13 @@
3
3
  from copy import deepcopy
4
4
  from types import MappingProxyType
5
5
 
6
- from rest_framework.exceptions import NotFound
7
-
8
- from codex.choices import DEFAULT_BROWSER_ROUTE, mapping_to_dict
9
6
  from codex.exceptions import SeeOtherRedirectError
10
7
  from codex.logger.logging import get_logger
11
- from codex.models.groups import BrowserGroupModel
12
- from codex.views.browser.filters.search.parse import SearchFilterView
8
+ from codex.serializers.choices import DEFAULTS
9
+ from codex.views.browser.base import BrowserBaseView
13
10
  from codex.views.const import (
14
11
  COMIC_GROUP,
15
12
  FOLDER_GROUP,
16
- GROUP_MODEL_MAP,
17
13
  ROOT_GROUP,
18
14
  STORY_ARC_GROUP,
19
15
  )
@@ -21,55 +17,19 @@ from codex.views.const import (
21
17
  LOG = get_logger(__name__)
22
18
 
23
19
 
24
- class BrowserValidateView(SearchFilterView):
20
+ class BrowserValidateView(BrowserBaseView):
25
21
  """Browser Settings and URL Validation."""
26
22
 
27
23
  DEFAULT_ROUTE = MappingProxyType(
28
- {"name": "browser", "params": DEFAULT_BROWSER_ROUTE}
24
+ {
25
+ "name": "browser",
26
+ "params": deepcopy(DEFAULTS["breadcrumbs"][0]),
27
+ }
29
28
  )
30
29
 
31
- def __init__(self, *args, **kwargs):
32
- """Initialize properties."""
33
- super().__init__(*args, **kwargs)
34
- self._is_admin: bool | None = None
35
- self._model_group: str = ""
36
- self._model: type[BrowserGroupModel] | None = None
37
- self._rel_prefix: str | None = None
38
- self._valid_nav_groups: tuple[str, ...] | None = None
39
-
40
- @property
41
- def model_group(self):
42
- """Memoize the model group."""
43
- if not self._model_group:
44
- group = self.kwargs["group"]
45
- if group == ROOT_GROUP:
46
- group = self.params["top_group"]
47
- self._model_group = group
48
- return self._model_group
49
-
50
- @property
51
- def model(self) -> type[BrowserGroupModel] | None:
52
- """Memoize the model for the browse list."""
53
- if not self._model:
54
- model = GROUP_MODEL_MAP.get(self.model_group)
55
- if model is None:
56
- group = self.kwargs["group"]
57
- detail = f"Cannot browse {group=}"
58
- LOG.debug(detail)
59
- raise NotFound(detail=detail)
60
- self._model = model
61
- return self._model
62
-
63
- @property
64
- def rel_prefix(self):
65
- """Memoize model rel prefix."""
66
- if self._rel_prefix is None:
67
- self._rel_prefix = self.get_rel_prefix(self.model)
68
- return self._rel_prefix
69
-
70
30
  def raise_redirect(self, reason, route_mask=None, settings_mask=None):
71
31
  """Redirect the client to a valid group url."""
72
- route = mapping_to_dict(self.DEFAULT_ROUTE)
32
+ route = deepcopy(dict(self.DEFAULT_ROUTE))
73
33
  if route_mask:
74
34
  route["params"].update(route_mask) # type: ignore
75
35
  settings = deepcopy(dict(self.params))
@@ -113,7 +73,7 @@ class BrowserValidateView(SearchFilterView):
113
73
  settings_mask = {"top_group": valid_top_group, "breadcrumbs": breadcrumbs}
114
74
  self.raise_redirect(reason, route, settings_mask)
115
75
 
116
- def _get_valid_browse_nav_groups(self, valid_top_groups):
76
+ def set_valid_browse_nav_groups(self, valid_top_groups):
117
77
  """Get valid nav groups for the current settings.
118
78
 
119
79
  Valid nav groups are the top group and below that are also
@@ -136,7 +96,7 @@ class BrowserValidateView(SearchFilterView):
136
96
  reason = f"Nav group {nav_group} unavailable, redirect to {ROOT_GROUP}"
137
97
  self.raise_redirect(reason)
138
98
 
139
- return tuple(valid_nav_groups)
99
+ self.valid_nav_groups = tuple(valid_nav_groups)
140
100
 
141
101
  def _validate_folder_settings(self):
142
102
  """Check that all the view variables for folder mode are set right."""
@@ -153,7 +113,7 @@ class BrowserValidateView(SearchFilterView):
153
113
 
154
114
  valid_top_groups = (FOLDER_GROUP,)
155
115
  self._validate_top_group(valid_top_groups)
156
- return valid_top_groups
116
+ self.valid_nav_groups = valid_top_groups
157
117
 
158
118
  def _validate_browser_group_settings(self):
159
119
  """Check that all the view variables for browser mode are set right."""
@@ -172,24 +132,29 @@ class BrowserValidateView(SearchFilterView):
172
132
 
173
133
  # Validate Browser nav_group
174
134
  # Redirect if nav group is wrong
175
- return self._get_valid_browse_nav_groups(valid_top_groups)
135
+ self.set_valid_browse_nav_groups(valid_top_groups)
176
136
 
177
- def _validate_story_arc_settings(self) -> tuple[str, ...]:
137
+ def _validate_story_arc_settings(self):
178
138
  """Validate story arc settings."""
179
139
  valid_top_groups = (STORY_ARC_GROUP,)
180
140
  self._validate_top_group(valid_top_groups)
181
- return valid_top_groups
182
-
183
- @property
184
- def valid_nav_groups(self) -> tuple[str, ...]:
185
- """Memoize valid nav groups."""
186
- if self._valid_nav_groups is None:
187
- group = self.kwargs["group"]
188
- if group == FOLDER_GROUP:
189
- vng = self._validate_folder_settings()
190
- elif group == STORY_ARC_GROUP:
191
- vng = self._validate_story_arc_settings()
192
- else:
193
- vng = self._validate_browser_group_settings()
194
- self._valid_nav_groups = vng
195
- return self._valid_nav_groups
141
+ self.valid_nav_groups = valid_top_groups
142
+
143
+ def validate_settings(self):
144
+ """Validate group and top group settings."""
145
+ group = self.kwargs["group"]
146
+ if group == FOLDER_GROUP:
147
+ self._validate_folder_settings()
148
+ elif group == STORY_ARC_GROUP:
149
+ self._validate_story_arc_settings()
150
+ else:
151
+ self._validate_browser_group_settings()
152
+
153
+ # Validate order
154
+ if self.order_key == "filename" and not self.admin_flags["folder_view"]:
155
+ pks = self.kwargs["pks"]
156
+ page = self.kwargs["page"]
157
+ route_changes = {"group": group, "pks": pks, "page": page}
158
+ reason = "order by filename not allowed by admin flag."
159
+ settings_mask = {"order_by": "sort_name"}
160
+ self.raise_redirect(reason, route_changes, settings_mask)
codex/views/download.py CHANGED
@@ -22,7 +22,7 @@ class DownloadView(AuthFilterAPIView):
22
22
  """Download a comic archive."""
23
23
  pk = kwargs.get("pk")
24
24
  try:
25
- group_acl_filter = self.get_group_acl_filter(Comic, self.request.user)
25
+ group_acl_filter = self.get_group_acl_filter(Comic)
26
26
  comic = (
27
27
  Comic.objects.filter(group_acl_filter)
28
28
  .only("path", "file_type")
codex/views/mixins.py CHANGED
@@ -12,7 +12,7 @@ class SharedAnnotationsMixin:
12
12
  """Cross view annotation methods."""
13
13
 
14
14
  @staticmethod
15
- def _get_order_groups(model_group, pks):
15
+ def _get_order_groups(pks, model_group):
16
16
  """Annotate sort_name."""
17
17
  if not pks or len(pks) > 1:
18
18
  order_groups = _SHOW_GROUPS
@@ -28,11 +28,12 @@ class SharedAnnotationsMixin:
28
28
  return order_groups
29
29
 
30
30
  @classmethod
31
- def get_sort_name_annotations(cls, model, model_group, pks, show):
31
+ def alias_sort_names(cls, qs, model, pks, model_group, show=None):
32
32
  """Annotate sort names for browser subclasses and reader."""
33
33
  sort_name_annotations = {}
34
- if model is Comic:
35
- order_groups = cls._get_order_groups(model_group, pks)
34
+ comic_sort_names = []
35
+ if model == Comic:
36
+ order_groups = cls._get_order_groups(pks, model_group)
36
37
  for order_group in order_groups:
37
38
  if show and not show.get(order_group):
38
39
  continue
@@ -41,19 +42,23 @@ class SharedAnnotationsMixin:
41
42
  name_field = "name" if group_name == "volume" else "sort_name"
42
43
  sort_name = F(f"{group_name}__{name_field}")
43
44
  sort_name_annotations[ann_name] = sort_name
44
- elif model is Volume:
45
+ comic_sort_names.append(ann_name)
46
+ elif model == Volume:
45
47
  sort_name_annotations["sort_name"] = F("name")
46
- return sort_name_annotations
48
+
49
+ if sort_name_annotations:
50
+ qs = qs.alias(**sort_name_annotations)
51
+ return qs, comic_sort_names
47
52
 
48
53
  @classmethod
49
- def annotate_group_names(cls, qs):
54
+ def annotate_group_names(cls, qs, model):
50
55
  """Annotate name fields by hoisting them up."""
51
56
  # Optimized to only lookup what is used on the frontend
52
57
  target = cls.TARGET # type: ignore
53
58
  if target not in frozenset({"browser", "opds1", "opds2", "reader"}):
54
59
  return qs
55
60
  group_names = {}
56
- if qs.model is Comic:
61
+ if model == Comic:
57
62
  if target != "reader":
58
63
  group_names["publisher_name"] = F("publisher__name")
59
64
  if target == "opds2":
@@ -64,25 +69,8 @@ class SharedAnnotationsMixin:
64
69
  "volume_name": F("volume__name"),
65
70
  }
66
71
  )
67
- elif qs.model is Volume:
72
+ elif model == Volume:
68
73
  group_names["series_name"] = F("series__name")
69
- elif qs.model is Imprint:
74
+ elif model == Imprint:
70
75
  group_names["publisher_name"] = F("publisher__name")
71
76
  return qs.annotate(**group_names)
72
-
73
-
74
- class BookmarkSearchMixin:
75
- """Create Bookmark Search kwargs."""
76
-
77
- @staticmethod
78
- def get_bookmark_search_kwargs(auth_filter, comic_filter=None):
79
- """Get the search kwargs for a user's authentication state."""
80
- # search kwargs are relative to the bookmark object.
81
- search_kwargs = {}
82
- search_kwargs.update(auth_filter)
83
-
84
- if comic_filter:
85
- for key, value in comic_filter.items():
86
- search_kwargs[f"comic__{key}"] = value
87
-
88
- return search_kwargs
codex/views/opds/const.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """OPDS Common consts."""
2
2
 
3
+ from collections.abc import Mapping
3
4
  from types import MappingProxyType
4
5
 
5
6
  from codex.models import (
@@ -62,7 +63,7 @@ class MimeType:
62
63
  HTML = "text/html"
63
64
  AUTH_BASIC = "http://opds-spec.org/auth/basic"
64
65
  COOKIE = "cookie"
65
- FILE_TYPE_MAP: MappingProxyType[str, str] = MappingProxyType(
66
+ FILE_TYPE_MAP: Mapping[str, str] = MappingProxyType(
66
67
  {
67
68
  "CBZ": "application/vnd.comicbook+zip",
68
69
  "CBR": "application/vnd.comicbook+rar",
@@ -70,7 +71,7 @@ class MimeType:
70
71
  "PDF": "application/pdf",
71
72
  }
72
73
  )
73
- SIMPLE_FILE_TYPE_MAP: MappingProxyType[str, str] = MappingProxyType(
74
+ SIMPLE_FILE_TYPE_MAP: Mapping[str, str] = MappingProxyType(
74
75
  {
75
76
  # PocketBooks needs app/zip
76
77
  "CBZ": "application/zip",
codex/views/opds/urls.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from django.urls import reverse
4
4
  from rest_framework.response import Response
5
5
 
6
- from codex.choices import DEFAULT_BROWSER_ROUTE
6
+ from codex.serializers.choices import DEFAULTS
7
7
  from codex.serializers.opds.urls import OPDSURLsSerializer
8
8
  from codex.views.auth import AuthGenericAPIView
9
9
  from codex.views.util import pop_name
@@ -19,7 +19,7 @@ class OPDSURLsView(AuthGenericAPIView):
19
19
  def get(self, *args, **kwargs):
20
20
  """Resolve the urls."""
21
21
  obj = {}
22
- route = DEFAULT_BROWSER_ROUTE
22
+ route = DEFAULTS["breadcrumbs"][0]
23
23
  route = pop_name(route)
24
24
  for version in _OPDS_VERSIONS:
25
25
  key = f"v{version}"
codex/views/opds/util.py CHANGED
@@ -5,11 +5,11 @@ from django.http.response import HttpResponseRedirect
5
5
  from django.urls import reverse
6
6
  from django.utils.http import urlencode
7
7
 
8
- from codex.choices import DEFAULT_BROWSER_ROUTE
9
8
  from codex.models import (
10
9
  Contributor,
11
10
  ContributorPerson,
12
11
  )
12
+ from codex.serializers.choices import DEFAULTS
13
13
  from codex.views.auth import GroupACLMixin
14
14
  from codex.views.opds.const import OPDS_M2M_MODELS
15
15
 
@@ -71,7 +71,8 @@ def full_redirect_view(url_name):
71
71
 
72
72
  def func(request):
73
73
  """Redirect to view, forwarding query strings and auth."""
74
- kwargs = dict(DEFAULT_BROWSER_ROUTE)
74
+ kwargs = DEFAULTS["breadcrumbs"][0]
75
+ kwargs.pop("name", None)
75
76
  url = reverse(url_name, kwargs=kwargs)
76
77
 
77
78
  # Forward the query string.
@@ -82,9 +82,7 @@ class OPDS1EntryLinksMixin:
82
82
  else:
83
83
  mime_type = MimeType.NAV
84
84
 
85
- thr_count = (
86
- 0 if self.fake else 1 if self.obj.group == "c" else self.obj.child_count
87
- )
85
+ thr_count = 0 if self.fake else self.obj.child_count
88
86
  rel = Rel.ALTERNATE if metadata else "subsection"
89
87
 
90
88
  return OPDS1Link(rel, href, mime_type, thr_count=thr_count)
@@ -2,14 +2,13 @@
2
2
 
3
3
  from dataclasses import dataclass
4
4
  from types import MappingProxyType
5
- from typing import Any
6
5
 
7
6
  from django.urls import reverse
8
7
 
9
8
  from codex.models import AdminFlag
10
9
  from codex.views.browser.browser import BrowserView
11
- from codex.views.opds.const import MimeType, Rel, UserAgentNames
12
- from codex.views.opds.util import get_user_agent_name, update_href_query_params
10
+ from codex.views.opds.const import MimeType, Rel
11
+ from codex.views.opds.util import update_href_query_params
13
12
  from codex.views.opds.v1.data import OPDS1Link
14
13
  from codex.views.opds.v1.entry.data import OPDS1EntryData, OPDS1EntryObject
15
14
  from codex.views.opds.v1.entry.entry import OPDS1Entry
@@ -82,61 +81,13 @@ DEFAULT_FACETS = {
82
81
  class OPDS1FacetsView(BrowserView):
83
82
  """OPDS 1 Facets methods."""
84
83
 
85
- def __init__(self, *args, **kwargs):
86
- """Initialize properties."""
87
- super().__init__(*args, **kwargs)
88
- self._user_agent_name: str | None = None
89
- self._mime_type_map: MappingProxyType[str, str] | None = None
90
- self._use_facets: bool | None = None
91
- self._obj: MappingProxyType[str, Any] | None = None
92
-
93
- @property
94
- def user_agent_name(self) -> str:
95
- """Memoize user agent name."""
96
- if self._user_agent_name is None:
97
- self._user_agent_name = get_user_agent_name(self.request)
98
- return self._user_agent_name
99
-
100
- @property
101
- def mime_type_map(self) -> MappingProxyType[str, str]:
102
- """Memoize mime type map."""
103
- if self._mime_type_map is None:
104
- self._mime_type_map = (
105
- MimeType.SIMPLE_FILE_TYPE_MAP
106
- if self.user_agent_name in UserAgentNames.SIMPLE_DOWNLOAD_MIME_TYPES
107
- else MimeType.FILE_TYPE_MAP
108
- )
109
- return self._mime_type_map
110
-
111
- @property
112
- def use_facets(self) -> bool:
113
- """Memoize use_facets."""
114
- if self._use_facets is None:
115
- self._use_facets = self.user_agent_name in UserAgentNames.FACET_SUPPORT
116
- return self._use_facets
117
-
118
- @property
119
- def obj(self) -> MappingProxyType[str, Any]:
120
- """Get the browser page and serialize it for this subclass."""
121
- if self._obj is None:
122
- group_qs, book_qs, num_pages, total_count, zero_pad, mtime = (
123
- self._get_group_and_books()
124
- )
125
- book_qs = book_qs.select_related("series", "volume", "language")
126
-
127
- title = self.get_browser_page_title()
128
- self._obj = MappingProxyType(
129
- {
130
- "title": title,
131
- "groups": group_qs,
132
- "books": book_qs,
133
- "zero_pad": zero_pad,
134
- "num_pages": num_pages,
135
- "total_count": total_count,
136
- "mtime": mtime,
137
- }
138
- )
139
- return self._obj
84
+ OPDS = 1
85
+ # Overwritten by get_object()
86
+ use_facets = False
87
+ skip_order_facets = False
88
+ acquisition_groups = frozenset()
89
+ mime_type_map = MimeType.FILE_TYPE_MAP
90
+ obj = MappingProxyType({})
140
91
 
141
92
  def _facet(self, kwargs, facet_group, facet_title, new_query_params):
142
93
  kwargs = pop_name(kwargs)
@@ -169,9 +120,9 @@ class OPDS1FacetsView(BrowserView):
169
120
  )
170
121
  qps = {**self.request.GET}
171
122
  qps.update(query_params)
172
- zero_pad: int = self.obj["zero_pad"] # type: ignore
123
+ zero_pad = self.obj["zero_pad"]
173
124
  data = OPDS1EntryData(
174
- self.opds_acquisition_groups, zero_pad, False, self.mime_type_map
125
+ self.acquisition_groups, zero_pad, False, self.mime_type_map
175
126
  )
176
127
  return OPDS1Entry(entry_obj, qps, data)
177
128
 
@@ -234,11 +185,7 @@ class OPDS1FacetsView(BrowserView):
234
185
  def facets(self, entries=False, root=True):
235
186
  """Return facets."""
236
187
  facets = []
237
- group = self.kwargs.get("group")
238
- add_order_facets = (
239
- group != "c" and self.user_agent_name not in UserAgentNames.CLIENT_REORDERS
240
- )
241
- if not add_order_facets:
188
+ if not self.skip_order_facets:
242
189
  facets += self._facet_group(FacetGroups.ORDER_BY, entries)
243
190
  facets += self._facet_group(FacetGroups.ORDER_REVERSE, entries)
244
191
  if root:
@@ -1,5 +1,6 @@
1
1
  """OPDS v1 feed."""
2
2
 
3
+ from types import MappingProxyType
3
4
  from typing import TYPE_CHECKING, Any
4
5
 
5
6
  from drf_spectacular.utils import extend_schema
@@ -16,7 +17,8 @@ from codex.serializers.opds.v1 import (
16
17
  from codex.settings.settings import FALSY
17
18
  from codex.views.const import MAX_OBJ_PER_PAGE
18
19
  from codex.views.opds.auth import OPDSTemplateView
19
- from codex.views.opds.const import BLANK_TITLE
20
+ from codex.views.opds.const import BLANK_TITLE, MimeType, UserAgentNames
21
+ from codex.views.opds.util import get_user_agent_name
20
22
  from codex.views.opds.v1.entry.data import OPDS1EntryData
21
23
  from codex.views.opds.v1.entry.entry import OPDS1Entry
22
24
  from codex.views.opds.v1.links import (
@@ -53,14 +55,14 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
53
55
  def opds_ns(self):
54
56
  """Dynamic opds namespace."""
55
57
  try:
56
- return OpdsNs.ACQUISITION if self.is_opds_acquisition else OpdsNs.CATALOG
58
+ return OpdsNs.ACQUISITION if self.is_aq_feed else OpdsNs.CATALOG
57
59
  except Exception:
58
60
  LOG.exception("Getting OPDS v1 namespace")
59
61
 
60
62
  @property
61
63
  def is_acquisition(self):
62
64
  """Is acquisition."""
63
- return self.is_opds_acquisition
65
+ return self.is_aq_feed
64
66
 
65
67
  @property
66
68
  def id_tag(self):
@@ -126,7 +128,7 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
126
128
  if objs := self.obj.get(key):
127
129
  zero_pad: int = self.obj["zero_pad"] # type: ignore
128
130
  data = OPDS1EntryData(
129
- self.opds_acquisition_groups, zero_pad, metadata, self.mime_type_map
131
+ self.acquisition_groups, zero_pad, metadata, self.mime_type_map
130
132
  )
131
133
  fallback = bool(self.admin_flags.get("folder_view"))
132
134
  import_pks = set()
@@ -164,8 +166,65 @@ class OPDS1FeedView(OPDS1LinksView, OPDSTemplateView):
164
166
  LOG.exception("Getting OPDS v1 entries")
165
167
  return entries
166
168
 
169
+ def get_object(self): # type: ignore
170
+ """Get the browser page and serialize it for this subclass."""
171
+ group_qs, book_qs, num_pages, total_count, zero_pad, mtime = (
172
+ super()._get_group_and_books()
173
+ )
174
+ book_qs = book_qs.select_related("series", "volume", "language")
175
+
176
+ title = self.get_browser_page_title()
177
+ self.obj = MappingProxyType(
178
+ {
179
+ "title": title,
180
+ "groups": group_qs,
181
+ "books": book_qs,
182
+ "zero_pad": zero_pad,
183
+ "num_pages": num_pages,
184
+ "total_count": total_count,
185
+ "mtime": mtime,
186
+ }
187
+ )
188
+
189
+ self.is_aq_feed = self.model_group in ("c", "f")
190
+
191
+ # Do not return a Mapping despite the type. Return self for the serializer.
192
+ return self
193
+
194
+ def _set_user_agent_variables(self):
195
+ """Set User Agent variables."""
196
+ user_agent_name = get_user_agent_name(self.request)
197
+
198
+ self.user_facets = user_agent_name in UserAgentNames.FACET_SUPPORT
199
+ self.skip_order_facets = user_agent_name in UserAgentNames.CLIENT_REORDERS
200
+ if user_agent_name in UserAgentNames.SIMPLE_DOWNLOAD_MIME_TYPES:
201
+ self.mime_type_map = MimeType.SIMPLE_FILE_TYPE_MAP
202
+
203
+ def set_opds_request_type(self):
204
+ """Set the opds request type variables."""
205
+ group = self.kwargs.get("group")
206
+ if group == "a":
207
+ self.acquisition_groups = frozenset({"a", "c"})
208
+ pks = self.kwargs["pks"]
209
+ self.is_opds_1_acquisition = group in self.acquisition_groups and pks
210
+ else:
211
+ self.acquisition_groups = frozenset({*self.valid_nav_groups[-2:]} | {"c"})
212
+ self.is_opds_1_acquisition = group in self.acquisition_groups
213
+ self.is_opds_metadata = (
214
+ self.request.GET.get("opdsMetadata", "").lower() not in FALSY
215
+ )
216
+
217
+ def init_request(self):
218
+ """Initialize request."""
219
+ super().init_request()
220
+ self.set_opds_request_type()
221
+ self._set_user_agent_variables()
222
+ self.skip_order_facets |= self.kwargs.get("group") == "c"
223
+
167
224
  @extend_schema(parameters=[input_serializer_class])
168
225
  def get(self, *_args, **_kwargs):
169
226
  """Get the feed."""
170
- serializer = self.get_serializer(self)
227
+ self.init_request()
228
+ obj = self.get_object()
229
+ serializer = self.get_serializer(obj)
171
230
  return Response(serializer.data, content_type=self.content_type)