codex 1.5.12__py3-none-any.whl → 1.5.13a0__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 (414) hide show
  1. codex/librarian/importer/importerd.py +2 -1
  2. codex/librarian/importer/query_fks.py +1 -2
  3. codex/middleware.py +50 -1
  4. codex/serializers/browser/settings.py +1 -0
  5. codex/serializers/choices.py +11 -13
  6. codex/serializers/metadata.py +1 -1
  7. codex/settings/settings.py +16 -5
  8. codex/signals/django_signals.py +6 -0
  9. codex/static_root/assets/{VCheckbox-CC74mhfV.a196e804e50a.js → VCheckbox-BMRDCYNv.7f791d7f065f.js} +1 -1
  10. codex/static_root/assets/VCheckbox-BMRDCYNv.7f791d7f065f.js.br +0 -0
  11. codex/static_root/assets/VCheckbox-BMRDCYNv.7f791d7f065f.js.gz +0 -0
  12. codex/static_root/assets/{VCheckbox-CC74mhfV.js → VCheckbox-BMRDCYNv.js} +1 -1
  13. codex/static_root/assets/VCheckbox-BMRDCYNv.js.br +0 -0
  14. codex/static_root/assets/VCheckbox-BMRDCYNv.js.gz +0 -0
  15. codex/static_root/assets/{VCheckboxBtn-F27UKlAX.f74083aa5b9a.js → VCheckboxBtn-Dqp1u3GT.e80f145994b6.js} +1 -1
  16. codex/static_root/assets/VCheckboxBtn-Dqp1u3GT.e80f145994b6.js.br +0 -0
  17. codex/static_root/assets/VCheckboxBtn-Dqp1u3GT.e80f145994b6.js.gz +0 -0
  18. codex/static_root/assets/{VCheckboxBtn-F27UKlAX.js → VCheckboxBtn-Dqp1u3GT.js} +1 -1
  19. codex/static_root/assets/VCheckboxBtn-Dqp1u3GT.js.br +0 -0
  20. codex/static_root/assets/VCheckboxBtn-Dqp1u3GT.js.gz +0 -0
  21. codex/static_root/assets/VCombobox-DT52mZm3.309c77be4a55.js +1 -0
  22. codex/static_root/assets/VCombobox-DT52mZm3.309c77be4a55.js.br +0 -0
  23. codex/static_root/assets/VCombobox-DT52mZm3.309c77be4a55.js.gz +0 -0
  24. codex/static_root/assets/VCombobox-DT52mZm3.js +1 -0
  25. codex/static_root/assets/VCombobox-DT52mZm3.js.br +0 -0
  26. codex/static_root/assets/VCombobox-DT52mZm3.js.gz +0 -0
  27. codex/static_root/assets/{VDataTableVirtual-BgFOSn9_.969c17b4d50a.js → VDataTableVirtual-DhhCmPtf.0735a1ab4c2f.js} +1 -1
  28. codex/static_root/assets/VDataTableVirtual-DhhCmPtf.0735a1ab4c2f.js.br +0 -0
  29. codex/static_root/assets/VDataTableVirtual-DhhCmPtf.0735a1ab4c2f.js.gz +0 -0
  30. codex/static_root/assets/{VDataTableVirtual-BgFOSn9_.js → VDataTableVirtual-DhhCmPtf.js} +1 -1
  31. codex/static_root/assets/VDataTableVirtual-DhhCmPtf.js.br +0 -0
  32. codex/static_root/assets/VDataTableVirtual-DhhCmPtf.js.gz +0 -0
  33. codex/static_root/assets/{VExpansionPanel-B06inlZY.a77a3ad1cc7c.js → VExpansionPanel-C593uXqT.d3bdc6a21208.js} +1 -1
  34. codex/static_root/assets/VExpansionPanel-C593uXqT.d3bdc6a21208.js.br +0 -0
  35. codex/static_root/assets/VExpansionPanel-C593uXqT.d3bdc6a21208.js.gz +0 -0
  36. codex/static_root/assets/{VExpansionPanel-B06inlZY.js → VExpansionPanel-C593uXqT.js} +1 -1
  37. codex/static_root/assets/VExpansionPanel-C593uXqT.js.br +0 -0
  38. codex/static_root/assets/VExpansionPanel-C593uXqT.js.gz +0 -0
  39. codex/static_root/assets/{VMain-BR4DrMy3.977234e0db6a.js → VMain-DmQvRj_k.1e0480bed9a6.js} +1 -1
  40. codex/static_root/assets/VMain-DmQvRj_k.1e0480bed9a6.js.br +0 -0
  41. codex/static_root/assets/VMain-DmQvRj_k.1e0480bed9a6.js.gz +0 -0
  42. codex/static_root/assets/{VMain-BR4DrMy3.js → VMain-DmQvRj_k.js} +1 -1
  43. codex/static_root/assets/VMain-DmQvRj_k.js.br +0 -0
  44. codex/static_root/assets/VMain-DmQvRj_k.js.gz +0 -0
  45. codex/static_root/assets/{VRadioGroup-DE0KmC8d.75bdb9d16a32.js → VRadioGroup-Bi16yPEE.69086b77f9b1.js} +1 -1
  46. codex/static_root/assets/VRadioGroup-Bi16yPEE.69086b77f9b1.js.br +0 -0
  47. codex/static_root/assets/VRadioGroup-Bi16yPEE.69086b77f9b1.js.gz +0 -0
  48. codex/static_root/assets/{VRadioGroup-DE0KmC8d.js → VRadioGroup-Bi16yPEE.js} +1 -1
  49. codex/static_root/assets/VRadioGroup-Bi16yPEE.js.br +0 -0
  50. codex/static_root/assets/VRadioGroup-Bi16yPEE.js.gz +0 -0
  51. codex/static_root/assets/VSelect-BdaijxdJ.b969ab260593.js +1 -0
  52. codex/static_root/assets/VSelect-BdaijxdJ.b969ab260593.js.br +0 -0
  53. codex/static_root/assets/VSelect-BdaijxdJ.b969ab260593.js.gz +0 -0
  54. codex/static_root/assets/VSelect-BdaijxdJ.js +1 -0
  55. codex/static_root/assets/VSelect-BdaijxdJ.js.br +0 -0
  56. codex/static_root/assets/VSelect-BdaijxdJ.js.gz +0 -0
  57. codex/static_root/assets/{VSelectionControl-B1zPb_Yg.6112179d7a5d.js → VSelectionControl-CuM4okF6.0f246e35e86f.js} +1 -1
  58. codex/static_root/assets/VSelectionControl-CuM4okF6.0f246e35e86f.js.br +0 -0
  59. codex/static_root/assets/VSelectionControl-CuM4okF6.0f246e35e86f.js.gz +0 -0
  60. codex/static_root/assets/{VSelectionControl-B1zPb_Yg.js → VSelectionControl-CuM4okF6.js} +1 -1
  61. codex/static_root/assets/VSelectionControl-CuM4okF6.js.br +0 -0
  62. codex/static_root/assets/VSelectionControl-CuM4okF6.js.gz +0 -0
  63. codex/static_root/assets/VSlideGroup-ztBraz6g.02a0bc55b207.js +1 -0
  64. codex/static_root/assets/VSlideGroup-ztBraz6g.02a0bc55b207.js.br +0 -0
  65. codex/static_root/assets/VSlideGroup-ztBraz6g.02a0bc55b207.js.gz +0 -0
  66. codex/static_root/assets/VSlideGroup-ztBraz6g.js +1 -0
  67. codex/static_root/assets/VSlideGroup-ztBraz6g.js.br +0 -0
  68. codex/static_root/assets/VSlideGroup-ztBraz6g.js.gz +0 -0
  69. codex/static_root/assets/{VTable-Pq4fCfUx.e50f8ad539ed.js → VTable-CGuSv-Tv.eddaf5526d93.js} +1 -1
  70. codex/static_root/assets/VTable-CGuSv-Tv.eddaf5526d93.js.br +0 -0
  71. codex/static_root/assets/VTable-CGuSv-Tv.eddaf5526d93.js.gz +0 -0
  72. codex/static_root/assets/{VTable-Pq4fCfUx.js → VTable-CGuSv-Tv.js} +1 -1
  73. codex/static_root/assets/VTable-CGuSv-Tv.js.br +0 -0
  74. codex/static_root/assets/VTable-CGuSv-Tv.js.gz +0 -0
  75. codex/static_root/assets/{VToolbar-BFYiqJrS.js → VToolbar-CAUhD2BO.279f0b52347a.js} +2 -2
  76. codex/static_root/assets/VToolbar-CAUhD2BO.279f0b52347a.js.br +0 -0
  77. codex/static_root/assets/VToolbar-CAUhD2BO.279f0b52347a.js.gz +0 -0
  78. codex/static_root/assets/{VToolbar-BFYiqJrS.71a0cbf7eb5f.js → VToolbar-CAUhD2BO.js} +2 -2
  79. codex/static_root/assets/VToolbar-CAUhD2BO.js.br +0 -0
  80. codex/static_root/assets/VToolbar-CAUhD2BO.js.gz +0 -0
  81. codex/static_root/assets/{VToolbar-BfNszTMd.css → VToolbar-DqQLg1UA.614d6a4a8a09.css} +1 -1
  82. codex/static_root/assets/VToolbar-DqQLg1UA.614d6a4a8a09.css.br +0 -0
  83. codex/static_root/assets/{VToolbar-BfNszTMd.111f25cdd6d8.css.gz → VToolbar-DqQLg1UA.614d6a4a8a09.css.gz} +0 -0
  84. codex/static_root/assets/{VToolbar-BfNszTMd.111f25cdd6d8.css → VToolbar-DqQLg1UA.css} +1 -1
  85. codex/static_root/assets/VToolbar-DqQLg1UA.css.br +0 -0
  86. codex/static_root/assets/{VToolbar-BfNszTMd.css.gz → VToolbar-DqQLg1UA.css.gz} +0 -0
  87. codex/static_root/assets/{VWindowItem-g6ho7Hf4.b91f9a3277ff.js → VWindowItem-YvmNwCNb.20a7e3b303de.js} +1 -1
  88. codex/static_root/assets/VWindowItem-YvmNwCNb.20a7e3b303de.js.br +0 -0
  89. codex/static_root/assets/VWindowItem-YvmNwCNb.20a7e3b303de.js.gz +0 -0
  90. codex/static_root/assets/{VWindowItem-g6ho7Hf4.js → VWindowItem-YvmNwCNb.js} +1 -1
  91. codex/static_root/assets/VWindowItem-YvmNwCNb.js.br +0 -0
  92. codex/static_root/assets/VWindowItem-YvmNwCNb.js.gz +0 -0
  93. codex/static_root/assets/{admin-BVkY78tW.affa20285127.js → admin-Ch9WezG1.bdace86b7c25.js} +1 -1
  94. codex/static_root/assets/admin-Ch9WezG1.bdace86b7c25.js.br +0 -0
  95. codex/static_root/assets/admin-Ch9WezG1.bdace86b7c25.js.gz +0 -0
  96. codex/static_root/assets/{admin-BVkY78tW.js → admin-Ch9WezG1.js} +1 -1
  97. codex/static_root/assets/admin-Ch9WezG1.js.br +0 -0
  98. codex/static_root/assets/admin-Ch9WezG1.js.gz +0 -0
  99. codex/static_root/assets/{admin-drawer-panel-CEiEnUcn.9f94b93a422f.js → admin-drawer-panel-B4VvGE4s.2265b9de60ab.js} +4 -4
  100. codex/static_root/assets/admin-drawer-panel-B4VvGE4s.2265b9de60ab.js.br +0 -0
  101. codex/static_root/assets/admin-drawer-panel-B4VvGE4s.2265b9de60ab.js.gz +0 -0
  102. codex/static_root/assets/{admin-drawer-panel-CEiEnUcn.js → admin-drawer-panel-B4VvGE4s.js} +4 -4
  103. codex/static_root/assets/admin-drawer-panel-B4VvGE4s.js.br +0 -0
  104. codex/static_root/assets/admin-drawer-panel-B4VvGE4s.js.gz +0 -0
  105. codex/static_root/assets/{admin-drawer-panel-s72VfDNy.css → admin-drawer-panel-Bk7z8uTR.1531b747003f.css} +1 -1
  106. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.1531b747003f.css.br +0 -0
  107. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.1531b747003f.css.gz +0 -0
  108. codex/static_root/assets/{admin-drawer-panel-s72VfDNy.ef6cb7221a44.css → admin-drawer-panel-Bk7z8uTR.css} +1 -1
  109. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.css.br +0 -0
  110. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.css.gz +0 -0
  111. codex/static_root/assets/browser-BTel3sYS.8e1515bc039a.js +1 -0
  112. codex/static_root/assets/browser-BTel3sYS.8e1515bc039a.js.br +0 -0
  113. codex/static_root/assets/browser-BTel3sYS.8e1515bc039a.js.gz +0 -0
  114. codex/static_root/assets/browser-BTel3sYS.js +1 -0
  115. codex/static_root/assets/browser-BTel3sYS.js.br +0 -0
  116. codex/static_root/assets/browser-BTel3sYS.js.gz +0 -0
  117. codex/static_root/assets/{browser-2i0QA4La.587673ac534c.css → browser-CVvV08AM.7667051b18fa.css} +1 -1
  118. codex/static_root/assets/browser-CVvV08AM.7667051b18fa.css.br +0 -0
  119. codex/static_root/assets/browser-CVvV08AM.7667051b18fa.css.gz +0 -0
  120. codex/static_root/assets/{browser-2i0QA4La.css → browser-CVvV08AM.css} +1 -1
  121. codex/static_root/assets/browser-CVvV08AM.css.br +0 -0
  122. codex/static_root/assets/browser-CVvV08AM.css.gz +0 -0
  123. codex/static_root/assets/{change-password-dialog-Bh7raeqK.6d408dcd933e.js → change-password-dialog-Dq6GjT1T.01b56b4b3e87.js} +1 -1
  124. codex/static_root/assets/change-password-dialog-Dq6GjT1T.01b56b4b3e87.js.br +0 -0
  125. codex/static_root/assets/change-password-dialog-Dq6GjT1T.01b56b4b3e87.js.gz +0 -0
  126. codex/static_root/assets/{change-password-dialog-Bh7raeqK.js → change-password-dialog-Dq6GjT1T.js} +1 -1
  127. codex/static_root/assets/change-password-dialog-Dq6GjT1T.js.br +0 -0
  128. codex/static_root/assets/change-password-dialog-Dq6GjT1T.js.gz +0 -0
  129. codex/static_root/assets/{confirm-dialog-CvURjaTY.cadecd4cefa8.js → confirm-dialog-D-Fx-51m.54b7d1059106.js} +1 -1
  130. codex/static_root/assets/confirm-dialog-D-Fx-51m.54b7d1059106.js.br +0 -0
  131. codex/static_root/assets/confirm-dialog-D-Fx-51m.54b7d1059106.js.gz +0 -0
  132. codex/static_root/assets/{confirm-dialog-CvURjaTY.js → confirm-dialog-D-Fx-51m.js} +1 -1
  133. codex/static_root/assets/confirm-dialog-D-Fx-51m.js.br +0 -0
  134. codex/static_root/assets/confirm-dialog-D-Fx-51m.js.gz +0 -0
  135. codex/static_root/assets/{confirm-footer-C-VAfuKA.js → confirm-footer-Yn7T9Tns.75a17cd86ca9.js} +1 -1
  136. codex/static_root/assets/confirm-footer-Yn7T9Tns.75a17cd86ca9.js.br +0 -0
  137. codex/static_root/assets/confirm-footer-Yn7T9Tns.75a17cd86ca9.js.gz +0 -0
  138. codex/static_root/assets/{confirm-footer-C-VAfuKA.c93e5c6d41ca.js → confirm-footer-Yn7T9Tns.js} +1 -1
  139. codex/static_root/assets/confirm-footer-Yn7T9Tns.js.br +0 -0
  140. codex/static_root/assets/confirm-footer-Yn7T9Tns.js.gz +0 -0
  141. codex/static_root/assets/{datetime-column-CDcOr8sM.951343a94a28.js → datetime-column-DCeBthsr.22ed49a999cc.js} +1 -1
  142. codex/static_root/assets/datetime-column-DCeBthsr.22ed49a999cc.js.br +0 -0
  143. codex/static_root/assets/datetime-column-DCeBthsr.22ed49a999cc.js.gz +0 -0
  144. codex/static_root/assets/{datetime-column-CDcOr8sM.js → datetime-column-DCeBthsr.js} +1 -1
  145. codex/static_root/assets/datetime-column-DCeBthsr.js.br +0 -0
  146. codex/static_root/assets/datetime-column-DCeBthsr.js.gz +0 -0
  147. codex/static_root/assets/{filter-DT8MlNGw.c70bb7a7c265.js → filter-Dw0fjwZi.4d5f085435ab.js} +1 -1
  148. codex/static_root/assets/filter-Dw0fjwZi.4d5f085435ab.js.br +0 -0
  149. codex/static_root/assets/filter-Dw0fjwZi.4d5f085435ab.js.gz +0 -0
  150. codex/static_root/assets/{filter-DT8MlNGw.js → filter-Dw0fjwZi.js} +1 -1
  151. codex/static_root/assets/filter-Dw0fjwZi.js.br +0 -0
  152. codex/static_root/assets/filter-Dw0fjwZi.js.gz +0 -0
  153. codex/static_root/assets/{flag-tab-BVoz2gvN.591572581c12.js → flag-tab-D5RcLFbQ.1073708ea164.js} +1 -1
  154. codex/static_root/assets/flag-tab-D5RcLFbQ.1073708ea164.js.br +0 -0
  155. codex/static_root/assets/flag-tab-D5RcLFbQ.1073708ea164.js.gz +0 -0
  156. codex/static_root/assets/{flag-tab-BVoz2gvN.js → flag-tab-D5RcLFbQ.js} +1 -1
  157. codex/static_root/assets/flag-tab-D5RcLFbQ.js.br +0 -0
  158. codex/static_root/assets/flag-tab-D5RcLFbQ.js.gz +0 -0
  159. codex/static_root/assets/{group-tab-Bu1Hn4DR.8c9fb2cbc4d2.js → group-tab-BilDDNz5.cba5ca15d7cf.js} +1 -1
  160. codex/static_root/assets/group-tab-BilDDNz5.cba5ca15d7cf.js.br +0 -0
  161. codex/static_root/assets/group-tab-BilDDNz5.cba5ca15d7cf.js.gz +0 -0
  162. codex/static_root/assets/{group-tab-Bu1Hn4DR.js → group-tab-BilDDNz5.js} +1 -1
  163. codex/static_root/assets/group-tab-BilDDNz5.js.br +0 -0
  164. codex/static_root/assets/group-tab-BilDDNz5.js.gz +0 -0
  165. codex/static_root/assets/{http-error-BKXUvS3w.93b39ceed2f7.js → http-error-DHmC7qgT.bde1d32b55df.js} +1 -1
  166. codex/static_root/assets/http-error-DHmC7qgT.bde1d32b55df.js.br +0 -0
  167. codex/static_root/assets/http-error-DHmC7qgT.bde1d32b55df.js.gz +0 -0
  168. codex/static_root/assets/{http-error-BKXUvS3w.js → http-error-DHmC7qgT.js} +1 -1
  169. codex/static_root/assets/http-error-DHmC7qgT.js.br +0 -0
  170. codex/static_root/assets/http-error-DHmC7qgT.js.gz +0 -0
  171. codex/static_root/assets/{library-tab-AM2XWHOr.31f48701358e.js → library-tab-dbcbv0--.efa2d1d95b77.js} +1 -1
  172. codex/static_root/assets/library-tab-dbcbv0--.efa2d1d95b77.js.br +0 -0
  173. codex/static_root/assets/library-tab-dbcbv0--.efa2d1d95b77.js.gz +0 -0
  174. codex/static_root/assets/{library-tab-AM2XWHOr.js → library-tab-dbcbv0--.js} +1 -1
  175. codex/static_root/assets/library-tab-dbcbv0--.js.br +0 -0
  176. codex/static_root/assets/library-tab-dbcbv0--.js.gz +0 -0
  177. codex/static_root/assets/main-CCY5Ix0K.b067e1d16cf5.js +37 -0
  178. codex/static_root/assets/main-CCY5Ix0K.b067e1d16cf5.js.br +0 -0
  179. codex/static_root/assets/main-CCY5Ix0K.b067e1d16cf5.js.gz +0 -0
  180. codex/static_root/assets/main-CCY5Ix0K.js +37 -0
  181. codex/static_root/assets/main-CCY5Ix0K.js.br +0 -0
  182. codex/static_root/assets/main-CCY5Ix0K.js.gz +0 -0
  183. codex/static_root/assets/{metadata-dialog-BGJasBRy.0553851658b5.css → metadata-dialog-DIhQctD5.67830cc94cf8.css} +1 -1
  184. codex/static_root/assets/metadata-dialog-DIhQctD5.67830cc94cf8.css.br +0 -0
  185. codex/static_root/assets/metadata-dialog-DIhQctD5.67830cc94cf8.css.gz +0 -0
  186. codex/static_root/assets/{metadata-dialog-BGJasBRy.css → metadata-dialog-DIhQctD5.css} +1 -1
  187. codex/static_root/assets/metadata-dialog-DIhQctD5.css.br +0 -0
  188. codex/static_root/assets/metadata-dialog-DIhQctD5.css.gz +0 -0
  189. codex/static_root/assets/metadata-dialog-PkTw4uLp.ce5ee2377f27.js +1 -0
  190. codex/static_root/assets/metadata-dialog-PkTw4uLp.ce5ee2377f27.js.br +0 -0
  191. codex/static_root/assets/metadata-dialog-PkTw4uLp.ce5ee2377f27.js.gz +0 -0
  192. codex/static_root/assets/metadata-dialog-PkTw4uLp.js +1 -0
  193. codex/static_root/assets/metadata-dialog-PkTw4uLp.js.br +0 -0
  194. codex/static_root/assets/metadata-dialog-PkTw4uLp.js.gz +0 -0
  195. codex/static_root/assets/{pdf-doc-DSq1qfWx.50ab90d8b609.js → pdf-doc-CEOcuQZb.260e6671eb89.js} +1 -1
  196. codex/static_root/assets/pdf-doc-CEOcuQZb.260e6671eb89.js.br +0 -0
  197. codex/static_root/assets/pdf-doc-CEOcuQZb.260e6671eb89.js.gz +0 -0
  198. codex/static_root/assets/{pdf-doc-DSq1qfWx.js → pdf-doc-CEOcuQZb.js} +1 -1
  199. codex/static_root/assets/pdf-doc-CEOcuQZb.js.br +0 -0
  200. codex/static_root/assets/pdf-doc-CEOcuQZb.js.gz +0 -0
  201. codex/static_root/assets/{reader-Qan6W6n-.fc91835a9075.js → reader-DRsYPz-3.b8ee57f2aefc.js} +2 -2
  202. codex/static_root/assets/reader-DRsYPz-3.b8ee57f2aefc.js.br +0 -0
  203. codex/static_root/assets/reader-DRsYPz-3.b8ee57f2aefc.js.gz +0 -0
  204. codex/static_root/assets/{reader-Qan6W6n-.js → reader-DRsYPz-3.js} +2 -2
  205. codex/static_root/assets/reader-DRsYPz-3.js.br +0 -0
  206. codex/static_root/assets/reader-DRsYPz-3.js.gz +0 -0
  207. codex/static_root/assets/{stats-tab-Bv3OL9xU.949c9b8d7135.js → stats-tab-QTG23RuT.654f85e0b21d.js} +1 -1
  208. codex/static_root/assets/stats-tab-QTG23RuT.654f85e0b21d.js.br +0 -0
  209. codex/static_root/assets/stats-tab-QTG23RuT.654f85e0b21d.js.gz +0 -0
  210. codex/static_root/assets/{stats-tab-Bv3OL9xU.js → stats-tab-QTG23RuT.js} +1 -1
  211. codex/static_root/assets/stats-tab-QTG23RuT.js.br +0 -0
  212. codex/static_root/assets/stats-tab-QTG23RuT.js.gz +0 -0
  213. codex/static_root/assets/{task-tab-CgR8JqMw.54569b8df927.js → task-tab-Kff6fz1t.28380a665ed7.js} +1 -1
  214. codex/static_root/assets/task-tab-Kff6fz1t.28380a665ed7.js.br +0 -0
  215. codex/static_root/assets/task-tab-Kff6fz1t.28380a665ed7.js.gz +0 -0
  216. codex/static_root/assets/{task-tab-CgR8JqMw.js → task-tab-Kff6fz1t.js} +1 -1
  217. codex/static_root/assets/task-tab-Kff6fz1t.js.br +0 -0
  218. codex/static_root/assets/task-tab-Kff6fz1t.js.gz +0 -0
  219. codex/static_root/assets/{user-tab-Xzi_07Yi.d165773d55aa.js → user-tab-OAmZ6CQl.eed3361aefb7.js} +1 -1
  220. codex/static_root/assets/user-tab-OAmZ6CQl.eed3361aefb7.js.br +0 -0
  221. codex/static_root/assets/user-tab-OAmZ6CQl.eed3361aefb7.js.gz +0 -0
  222. codex/static_root/assets/{user-tab-Xzi_07Yi.js → user-tab-OAmZ6CQl.js} +1 -1
  223. codex/static_root/assets/user-tab-OAmZ6CQl.js.br +0 -0
  224. codex/static_root/assets/user-tab-OAmZ6CQl.js.gz +0 -0
  225. codex/static_root/js/choices.a4dcff295a04.json +1 -0
  226. codex/static_root/js/choices.a4dcff295a04.json.br +0 -0
  227. codex/static_root/js/choices.a4dcff295a04.json.gz +0 -0
  228. codex/static_root/js/choices.json +1 -1
  229. codex/static_root/js/choices.json.br +0 -0
  230. codex/static_root/js/choices.json.gz +0 -0
  231. codex/static_root/{manifest.0d2e5eed5ce7.json → manifest.13219ea92507.json} +217 -216
  232. codex/static_root/manifest.13219ea92507.json.br +0 -0
  233. codex/static_root/manifest.13219ea92507.json.gz +0 -0
  234. codex/static_root/manifest.json +217 -216
  235. codex/static_root/manifest.json.br +0 -0
  236. codex/static_root/manifest.json.gz +0 -0
  237. codex/static_root/staticfiles.json +1 -1
  238. codex/views/bookmark.py +12 -9
  239. codex/views/browser/base.py +21 -8
  240. codex/views/browser/browser.py +88 -68
  241. codex/views/browser/browser_annotations.py +19 -7
  242. codex/views/browser/choices.py +3 -5
  243. codex/views/browser/const.py +3 -0
  244. codex/views/browser/filters/search.py +114 -26
  245. codex/views/browser/metadata.py +4 -6
  246. codex/views/error.py +7 -3
  247. codex/views/opds/v1/feed.py +2 -1
  248. codex/views/opds/v2/feed.py +4 -6
  249. codex/views/opds/v2/publications.py +6 -3
  250. codex/views/session.py +1 -0
  251. {codex-1.5.12.dist-info → codex-1.5.13a0.dist-info}/METADATA +23 -12
  252. {codex-1.5.12.dist-info → codex-1.5.13a0.dist-info}/RECORD +255 -254
  253. codex/static_root/assets/VCheckbox-CC74mhfV.a196e804e50a.js.br +0 -0
  254. codex/static_root/assets/VCheckbox-CC74mhfV.a196e804e50a.js.gz +0 -0
  255. codex/static_root/assets/VCheckbox-CC74mhfV.js.br +0 -0
  256. codex/static_root/assets/VCheckbox-CC74mhfV.js.gz +0 -0
  257. codex/static_root/assets/VCheckboxBtn-F27UKlAX.f74083aa5b9a.js.br +0 -0
  258. codex/static_root/assets/VCheckboxBtn-F27UKlAX.f74083aa5b9a.js.gz +0 -0
  259. codex/static_root/assets/VCheckboxBtn-F27UKlAX.js.br +0 -0
  260. codex/static_root/assets/VCheckboxBtn-F27UKlAX.js.gz +0 -0
  261. codex/static_root/assets/VCombobox-Dw57vrly.b1184dfd61ec.js +0 -1
  262. codex/static_root/assets/VCombobox-Dw57vrly.b1184dfd61ec.js.br +0 -0
  263. codex/static_root/assets/VCombobox-Dw57vrly.b1184dfd61ec.js.gz +0 -0
  264. codex/static_root/assets/VCombobox-Dw57vrly.js +0 -1
  265. codex/static_root/assets/VCombobox-Dw57vrly.js.br +0 -0
  266. codex/static_root/assets/VCombobox-Dw57vrly.js.gz +0 -0
  267. codex/static_root/assets/VDataTableVirtual-BgFOSn9_.969c17b4d50a.js.br +0 -0
  268. codex/static_root/assets/VDataTableVirtual-BgFOSn9_.969c17b4d50a.js.gz +0 -0
  269. codex/static_root/assets/VDataTableVirtual-BgFOSn9_.js.br +0 -0
  270. codex/static_root/assets/VDataTableVirtual-BgFOSn9_.js.gz +0 -0
  271. codex/static_root/assets/VExpansionPanel-B06inlZY.a77a3ad1cc7c.js.br +0 -0
  272. codex/static_root/assets/VExpansionPanel-B06inlZY.a77a3ad1cc7c.js.gz +0 -0
  273. codex/static_root/assets/VExpansionPanel-B06inlZY.js.br +0 -0
  274. codex/static_root/assets/VExpansionPanel-B06inlZY.js.gz +0 -0
  275. codex/static_root/assets/VMain-BR4DrMy3.977234e0db6a.js.br +0 -0
  276. codex/static_root/assets/VMain-BR4DrMy3.977234e0db6a.js.gz +0 -0
  277. codex/static_root/assets/VMain-BR4DrMy3.js.br +0 -0
  278. codex/static_root/assets/VMain-BR4DrMy3.js.gz +0 -0
  279. codex/static_root/assets/VRadioGroup-DE0KmC8d.75bdb9d16a32.js.br +0 -0
  280. codex/static_root/assets/VRadioGroup-DE0KmC8d.75bdb9d16a32.js.gz +0 -0
  281. codex/static_root/assets/VRadioGroup-DE0KmC8d.js.br +0 -0
  282. codex/static_root/assets/VRadioGroup-DE0KmC8d.js.gz +0 -0
  283. codex/static_root/assets/VSelect-5wraBYHV.54a9a3184add.js +0 -1
  284. codex/static_root/assets/VSelect-5wraBYHV.54a9a3184add.js.br +0 -0
  285. codex/static_root/assets/VSelect-5wraBYHV.54a9a3184add.js.gz +0 -0
  286. codex/static_root/assets/VSelect-5wraBYHV.js +0 -1
  287. codex/static_root/assets/VSelect-5wraBYHV.js.br +0 -0
  288. codex/static_root/assets/VSelect-5wraBYHV.js.gz +0 -0
  289. codex/static_root/assets/VSelectionControl-B1zPb_Yg.6112179d7a5d.js.br +0 -0
  290. codex/static_root/assets/VSelectionControl-B1zPb_Yg.6112179d7a5d.js.gz +0 -0
  291. codex/static_root/assets/VSelectionControl-B1zPb_Yg.js.br +0 -0
  292. codex/static_root/assets/VSelectionControl-B1zPb_Yg.js.gz +0 -0
  293. codex/static_root/assets/VSlideGroup-F7Hpcms9.dce335daf8f2.js +0 -1
  294. codex/static_root/assets/VSlideGroup-F7Hpcms9.dce335daf8f2.js.br +0 -0
  295. codex/static_root/assets/VSlideGroup-F7Hpcms9.dce335daf8f2.js.gz +0 -0
  296. codex/static_root/assets/VSlideGroup-F7Hpcms9.js +0 -1
  297. codex/static_root/assets/VSlideGroup-F7Hpcms9.js.br +0 -0
  298. codex/static_root/assets/VSlideGroup-F7Hpcms9.js.gz +0 -0
  299. codex/static_root/assets/VTable-Pq4fCfUx.e50f8ad539ed.js.br +0 -0
  300. codex/static_root/assets/VTable-Pq4fCfUx.e50f8ad539ed.js.gz +0 -0
  301. codex/static_root/assets/VTable-Pq4fCfUx.js.br +0 -0
  302. codex/static_root/assets/VTable-Pq4fCfUx.js.gz +0 -0
  303. codex/static_root/assets/VToolbar-BFYiqJrS.71a0cbf7eb5f.js.br +0 -0
  304. codex/static_root/assets/VToolbar-BFYiqJrS.71a0cbf7eb5f.js.gz +0 -0
  305. codex/static_root/assets/VToolbar-BFYiqJrS.js.br +0 -0
  306. codex/static_root/assets/VToolbar-BFYiqJrS.js.gz +0 -0
  307. codex/static_root/assets/VToolbar-BfNszTMd.111f25cdd6d8.css.br +0 -0
  308. codex/static_root/assets/VToolbar-BfNszTMd.css.br +0 -0
  309. codex/static_root/assets/VWindowItem-g6ho7Hf4.b91f9a3277ff.js.br +0 -0
  310. codex/static_root/assets/VWindowItem-g6ho7Hf4.b91f9a3277ff.js.gz +0 -0
  311. codex/static_root/assets/VWindowItem-g6ho7Hf4.js.br +0 -0
  312. codex/static_root/assets/VWindowItem-g6ho7Hf4.js.gz +0 -0
  313. codex/static_root/assets/admin-BVkY78tW.affa20285127.js.br +0 -0
  314. codex/static_root/assets/admin-BVkY78tW.affa20285127.js.gz +0 -0
  315. codex/static_root/assets/admin-BVkY78tW.js.br +0 -0
  316. codex/static_root/assets/admin-BVkY78tW.js.gz +0 -0
  317. codex/static_root/assets/admin-drawer-panel-CEiEnUcn.9f94b93a422f.js.br +0 -0
  318. codex/static_root/assets/admin-drawer-panel-CEiEnUcn.9f94b93a422f.js.gz +0 -0
  319. codex/static_root/assets/admin-drawer-panel-CEiEnUcn.js.br +0 -0
  320. codex/static_root/assets/admin-drawer-panel-CEiEnUcn.js.gz +0 -0
  321. codex/static_root/assets/admin-drawer-panel-s72VfDNy.css.br +0 -0
  322. codex/static_root/assets/admin-drawer-panel-s72VfDNy.css.gz +0 -0
  323. codex/static_root/assets/admin-drawer-panel-s72VfDNy.ef6cb7221a44.css.br +0 -0
  324. codex/static_root/assets/admin-drawer-panel-s72VfDNy.ef6cb7221a44.css.gz +0 -0
  325. codex/static_root/assets/browser-2i0QA4La.587673ac534c.css.br +0 -0
  326. codex/static_root/assets/browser-2i0QA4La.587673ac534c.css.gz +0 -0
  327. codex/static_root/assets/browser-2i0QA4La.css.br +0 -0
  328. codex/static_root/assets/browser-2i0QA4La.css.gz +0 -0
  329. codex/static_root/assets/browser-BYquFWOA.3e5479490c40.js +0 -1
  330. codex/static_root/assets/browser-BYquFWOA.3e5479490c40.js.br +0 -0
  331. codex/static_root/assets/browser-BYquFWOA.3e5479490c40.js.gz +0 -0
  332. codex/static_root/assets/browser-BYquFWOA.js +0 -1
  333. codex/static_root/assets/browser-BYquFWOA.js.br +0 -0
  334. codex/static_root/assets/browser-BYquFWOA.js.gz +0 -0
  335. codex/static_root/assets/change-password-dialog-Bh7raeqK.6d408dcd933e.js.br +0 -0
  336. codex/static_root/assets/change-password-dialog-Bh7raeqK.6d408dcd933e.js.gz +0 -0
  337. codex/static_root/assets/change-password-dialog-Bh7raeqK.js.br +0 -0
  338. codex/static_root/assets/change-password-dialog-Bh7raeqK.js.gz +0 -0
  339. codex/static_root/assets/confirm-dialog-CvURjaTY.cadecd4cefa8.js.br +0 -0
  340. codex/static_root/assets/confirm-dialog-CvURjaTY.cadecd4cefa8.js.gz +0 -0
  341. codex/static_root/assets/confirm-dialog-CvURjaTY.js.br +0 -0
  342. codex/static_root/assets/confirm-dialog-CvURjaTY.js.gz +0 -0
  343. codex/static_root/assets/confirm-footer-C-VAfuKA.c93e5c6d41ca.js.br +0 -0
  344. codex/static_root/assets/confirm-footer-C-VAfuKA.c93e5c6d41ca.js.gz +0 -0
  345. codex/static_root/assets/confirm-footer-C-VAfuKA.js.br +0 -0
  346. codex/static_root/assets/confirm-footer-C-VAfuKA.js.gz +0 -0
  347. codex/static_root/assets/datetime-column-CDcOr8sM.951343a94a28.js.br +0 -0
  348. codex/static_root/assets/datetime-column-CDcOr8sM.951343a94a28.js.gz +0 -0
  349. codex/static_root/assets/datetime-column-CDcOr8sM.js.br +0 -0
  350. codex/static_root/assets/datetime-column-CDcOr8sM.js.gz +0 -0
  351. codex/static_root/assets/filter-DT8MlNGw.c70bb7a7c265.js.br +0 -0
  352. codex/static_root/assets/filter-DT8MlNGw.c70bb7a7c265.js.gz +0 -0
  353. codex/static_root/assets/filter-DT8MlNGw.js.br +0 -0
  354. codex/static_root/assets/filter-DT8MlNGw.js.gz +0 -0
  355. codex/static_root/assets/flag-tab-BVoz2gvN.591572581c12.js.br +0 -0
  356. codex/static_root/assets/flag-tab-BVoz2gvN.591572581c12.js.gz +0 -0
  357. codex/static_root/assets/flag-tab-BVoz2gvN.js.br +0 -0
  358. codex/static_root/assets/flag-tab-BVoz2gvN.js.gz +0 -0
  359. codex/static_root/assets/group-tab-Bu1Hn4DR.8c9fb2cbc4d2.js.br +0 -0
  360. codex/static_root/assets/group-tab-Bu1Hn4DR.8c9fb2cbc4d2.js.gz +0 -0
  361. codex/static_root/assets/group-tab-Bu1Hn4DR.js.br +0 -0
  362. codex/static_root/assets/group-tab-Bu1Hn4DR.js.gz +0 -0
  363. codex/static_root/assets/http-error-BKXUvS3w.93b39ceed2f7.js.br +0 -4
  364. codex/static_root/assets/http-error-BKXUvS3w.93b39ceed2f7.js.gz +0 -0
  365. codex/static_root/assets/http-error-BKXUvS3w.js.br +0 -4
  366. codex/static_root/assets/http-error-BKXUvS3w.js.gz +0 -0
  367. codex/static_root/assets/library-tab-AM2XWHOr.31f48701358e.js.br +0 -0
  368. codex/static_root/assets/library-tab-AM2XWHOr.31f48701358e.js.gz +0 -0
  369. codex/static_root/assets/library-tab-AM2XWHOr.js.br +0 -0
  370. codex/static_root/assets/library-tab-AM2XWHOr.js.gz +0 -0
  371. codex/static_root/assets/main-Dr7xEc75.ffae6222f1aa.js +0 -37
  372. codex/static_root/assets/main-Dr7xEc75.ffae6222f1aa.js.br +0 -0
  373. codex/static_root/assets/main-Dr7xEc75.ffae6222f1aa.js.gz +0 -0
  374. codex/static_root/assets/main-Dr7xEc75.js +0 -37
  375. codex/static_root/assets/main-Dr7xEc75.js.br +0 -0
  376. codex/static_root/assets/main-Dr7xEc75.js.gz +0 -0
  377. codex/static_root/assets/metadata-dialog-BGJasBRy.0553851658b5.css.br +0 -0
  378. codex/static_root/assets/metadata-dialog-BGJasBRy.0553851658b5.css.gz +0 -0
  379. codex/static_root/assets/metadata-dialog-BGJasBRy.css.br +0 -0
  380. codex/static_root/assets/metadata-dialog-BGJasBRy.css.gz +0 -0
  381. codex/static_root/assets/metadata-dialog-DFDQ4P3u.b959bb5a03a2.js +0 -1
  382. codex/static_root/assets/metadata-dialog-DFDQ4P3u.b959bb5a03a2.js.br +0 -0
  383. codex/static_root/assets/metadata-dialog-DFDQ4P3u.b959bb5a03a2.js.gz +0 -0
  384. codex/static_root/assets/metadata-dialog-DFDQ4P3u.js +0 -1
  385. codex/static_root/assets/metadata-dialog-DFDQ4P3u.js.br +0 -0
  386. codex/static_root/assets/metadata-dialog-DFDQ4P3u.js.gz +0 -0
  387. codex/static_root/assets/pdf-doc-DSq1qfWx.50ab90d8b609.js.br +0 -0
  388. codex/static_root/assets/pdf-doc-DSq1qfWx.50ab90d8b609.js.gz +0 -0
  389. codex/static_root/assets/pdf-doc-DSq1qfWx.js.br +0 -0
  390. codex/static_root/assets/pdf-doc-DSq1qfWx.js.gz +0 -0
  391. codex/static_root/assets/reader-Qan6W6n-.fc91835a9075.js.br +0 -0
  392. codex/static_root/assets/reader-Qan6W6n-.fc91835a9075.js.gz +0 -0
  393. codex/static_root/assets/reader-Qan6W6n-.js.br +0 -0
  394. codex/static_root/assets/reader-Qan6W6n-.js.gz +0 -0
  395. codex/static_root/assets/stats-tab-Bv3OL9xU.949c9b8d7135.js.br +0 -0
  396. codex/static_root/assets/stats-tab-Bv3OL9xU.949c9b8d7135.js.gz +0 -0
  397. codex/static_root/assets/stats-tab-Bv3OL9xU.js.br +0 -0
  398. codex/static_root/assets/stats-tab-Bv3OL9xU.js.gz +0 -0
  399. codex/static_root/assets/task-tab-CgR8JqMw.54569b8df927.js.br +0 -0
  400. codex/static_root/assets/task-tab-CgR8JqMw.54569b8df927.js.gz +0 -0
  401. codex/static_root/assets/task-tab-CgR8JqMw.js.br +0 -0
  402. codex/static_root/assets/task-tab-CgR8JqMw.js.gz +0 -0
  403. codex/static_root/assets/user-tab-Xzi_07Yi.d165773d55aa.js.br +0 -0
  404. codex/static_root/assets/user-tab-Xzi_07Yi.d165773d55aa.js.gz +0 -0
  405. codex/static_root/assets/user-tab-Xzi_07Yi.js.br +0 -0
  406. codex/static_root/assets/user-tab-Xzi_07Yi.js.gz +0 -0
  407. codex/static_root/js/choices.fedcbd1ec085.json +0 -1
  408. codex/static_root/js/choices.fedcbd1ec085.json.br +0 -0
  409. codex/static_root/js/choices.fedcbd1ec085.json.gz +0 -0
  410. codex/static_root/manifest.0d2e5eed5ce7.json.br +0 -0
  411. codex/static_root/manifest.0d2e5eed5ce7.json.gz +0 -0
  412. {codex-1.5.12.dist-info → codex-1.5.13a0.dist-info}/LICENSE +0 -0
  413. {codex-1.5.12.dist-info → codex-1.5.13a0.dist-info}/WHEEL +0 -0
  414. {codex-1.5.12.dist-info → codex-1.5.13a0.dist-info}/entry_points.txt +0 -0
Binary file
Binary file
@@ -1 +1 @@
1
- {"paths": {"rest_framework/docs/js/api.js": "rest_framework/docs/js/api.18a5ba8a1bd8.js", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/img/favicon.ico": "rest_framework/docs/img/favicon.5195b4d0f3eb.ico", "rest_framework/docs/img/grid.png": "rest_framework/docs/img/grid.a4b938cf382b.png", "rest_framework/docs/css/highlight.css": "rest_framework/docs/css/highlight.e0e4d973c6d7.css", "rest_framework/docs/css/base.css": "rest_framework/docs/css/base.e630f8f4990e.css", "rest_framework/docs/css/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.8851fb9336c9.js", "rest_framework/js/jquery-3.7.1.min.js": "rest_framework/js/jquery-3.7.1.min.2c872dbe60f4.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/bootstrap-theme.min.css.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.css", "rest_framework/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "img/logo-maskable.svg": "img/logo-maskable.d618c2d7943f.svg", "img/logo-maskable-180.webp": "img/logo-maskable-180.201cbde62d55.webp", "img/logo.svg": "img/logo.3fb129bfbd92.svg", "img/missing-cover.webp": "img/missing-cover.8c8c1d4f6782.webp", "img/missing-page.svg": "img/missing-page.71bb10f7c72a.svg", "img/logo-32.webp": "img/logo-32.dbb98f6dfde4.webp", "js/choices.json": "js/choices.fedcbd1ec085.json", "js/choices-admin.json": "js/choices-admin.bb730e4f26ac.json", "assets/VRadioGroup-DE0KmC8d.js": "assets/VRadioGroup-DE0KmC8d.75bdb9d16a32.js", "assets/VExpansionPanel-DsEXVcCv.css": "assets/VExpansionPanel-DsEXVcCv.20b475255e66.css", "assets/browser-BYquFWOA.js": "assets/browser-BYquFWOA.3e5479490c40.js", "assets/admin-drawer-panel-CEiEnUcn.js": "assets/admin-drawer-panel-CEiEnUcn.9f94b93a422f.js", "assets/datetime-column-BWWfvyhn.css": "assets/datetime-column-BWWfvyhn.a4a94ba57c73.css", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/pdf-doc-CMzaSV_K.css": "assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css", "assets/VToolbar-BFYiqJrS.js": "assets/VToolbar-BFYiqJrS.71a0cbf7eb5f.js", "assets/confirm-dialog-CvURjaTY.js": "assets/confirm-dialog-CvURjaTY.cadecd4cefa8.js", "assets/VTable-DflzroDq.css": "assets/VTable-DflzroDq.160504d82195.css", "assets/datetime-column-CDcOr8sM.js": "assets/datetime-column-CDcOr8sM.951343a94a28.js", "assets/VSlideGroup-C3lY91uv.css": "assets/VSlideGroup-C3lY91uv.d268067bd078.css", "assets/user-tab-Xzi_07Yi.js": "assets/user-tab-Xzi_07Yi.d165773d55aa.js", "assets/VMain-Byt37V4c.css": "assets/VMain-Byt37V4c.a00e206f3cfa.css", "assets/change-password-dialog-Bh7raeqK.js": "assets/change-password-dialog-Bh7raeqK.6d408dcd933e.js", "assets/VCheckbox-CC74mhfV.js": "assets/VCheckbox-CC74mhfV.a196e804e50a.js", "assets/confirm-footer-C-VAfuKA.js": "assets/confirm-footer-C-VAfuKA.c93e5c6d41ca.js", "assets/stats-tab-Bv3OL9xU.js": "assets/stats-tab-Bv3OL9xU.949c9b8d7135.js", "assets/group-tab-Bu1Hn4DR.js": "assets/group-tab-Bu1Hn4DR.8c9fb2cbc4d2.js", "assets/stats-tab-BTZLMBCU.css": "assets/stats-tab-BTZLMBCU.7e0a9547c12e.css", "assets/confirm-footer-VFepxcJs.css": "assets/confirm-footer-VFepxcJs.4150131304bd.css", "assets/change-password-dialog-BRmvkNir.css": "assets/change-password-dialog-BRmvkNir.2fead58a78c7.css", "assets/library-tab-DZKDtQ5v.css": "assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css", "assets/http-error-BKXUvS3w.js": "assets/http-error-BKXUvS3w.93b39ceed2f7.js", "assets/to-case-kUm8NPpW.js": "assets/to-case-kUm8NPpW.5b3369f511b8.js", "assets/task-tab-CgR8JqMw.js": "assets/task-tab-CgR8JqMw.54569b8df927.js", "assets/reader-Qan6W6n-.js": "assets/reader-Qan6W6n-.fc91835a9075.js", "assets/main-CacfwsKA.css": "assets/main-CacfwsKA.4f849ef1c5db.css", "assets/metadata-dialog-BGJasBRy.css": "assets/metadata-dialog-BGJasBRy.0553851658b5.css", "assets/admin-BVkY78tW.js": "assets/admin-BVkY78tW.affa20285127.js", "assets/VSelectionControl-BFq--28I.css": "assets/VSelectionControl-BFq--28I.9687c77461fc.css", "assets/VDataTableVirtual-BgFOSn9_.js": "assets/VDataTableVirtual-BgFOSn9_.969c17b4d50a.js", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "assets/VTable-Pq4fCfUx.js": "assets/VTable-Pq4fCfUx.e50f8ad539ed.js", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/metadata-dialog-DFDQ4P3u.js": "assets/metadata-dialog-DFDQ4P3u.b959bb5a03a2.js", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/browser-2i0QA4La.css": "assets/browser-2i0QA4La.587673ac534c.css", "assets/VCheckboxBtn-F27UKlAX.js": "assets/VCheckboxBtn-F27UKlAX.f74083aa5b9a.js", "assets/VWindowItem-g6ho7Hf4.js": "assets/VWindowItem-g6ho7Hf4.b91f9a3277ff.js", "assets/admin-drawer-panel-s72VfDNy.css": "assets/admin-drawer-panel-s72VfDNy.ef6cb7221a44.css", "assets/VSlideGroup-F7Hpcms9.js": "assets/VSlideGroup-F7Hpcms9.dce335daf8f2.js", "assets/VExpansionPanel-B06inlZY.js": "assets/VExpansionPanel-B06inlZY.a77a3ad1cc7c.js", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/reader-CG6zrLXl.css": "assets/reader-CG6zrLXl.ed017042cd53.css", "assets/task-tab-DoX9OA37.css": "assets/task-tab-DoX9OA37.9bb355f36dbf.css", "assets/VDataTableVirtual-Cbj7IGxa.css": "assets/VDataTableVirtual-Cbj7IGxa.24039b2ebc61.css", "assets/index-DUjeNg4x.js": "assets/index-DUjeNg4x.36d50c91b2c1.js", "assets/filter-DT8MlNGw.js": "assets/filter-DT8MlNGw.c70bb7a7c265.js", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/flag-tab-BVoz2gvN.js": "assets/flag-tab-BVoz2gvN.591572581c12.js", "assets/flag-tab-DUXtbpA2.css": "assets/flag-tab-DUXtbpA2.8d52c0d4f9e2.css", "assets/http-error-C25WdjWC.css": "assets/http-error-C25WdjWC.1c7337ad6e1d.css", "assets/VCombobox-Dw57vrly.js": "assets/VCombobox-Dw57vrly.b1184dfd61ec.js", "assets/VToolbar-BfNszTMd.css": "assets/VToolbar-BfNszTMd.111f25cdd6d8.css", "assets/pdf-doc-DSq1qfWx.js": "assets/pdf-doc-DSq1qfWx.50ab90d8b609.js", "assets/VMain-BR4DrMy3.js": "assets/VMain-BR4DrMy3.977234e0db6a.js", "assets/library-tab-AM2XWHOr.js": "assets/library-tab-AM2XWHOr.31f48701358e.js", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/VSelectionControl-B1zPb_Yg.js": "assets/VSelectionControl-B1zPb_Yg.6112179d7a5d.js", "assets/main-Dr7xEc75.js": "assets/main-Dr7xEc75.ffae6222f1aa.js", "assets/VSelect-BdGop3V2.css": "assets/VSelect-BdGop3V2.604c1785fbff.css", "assets/VCombobox-BksEr4D_.css": "assets/VCombobox-BksEr4D_.73429bb75f42.css", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/group-tab-3DXxSetf.css": "assets/group-tab-3DXxSetf.5fc2cfa31669.css", "assets/VSelect-5wraBYHV.js": "assets/VSelect-5wraBYHV.54a9a3184add.js", "assets/admin-Df82-Qcj.css": "assets/admin-Df82-Qcj.4c3f0406ee54.css", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.0d2e5eed5ce7.json"}, "version": "1.1", "hash": "8a8b8b143f62"}
1
+ {"paths": {"rest_framework/docs/js/api.js": "rest_framework/docs/js/api.18a5ba8a1bd8.js", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/img/favicon.ico": "rest_framework/docs/img/favicon.5195b4d0f3eb.ico", "rest_framework/docs/img/grid.png": "rest_framework/docs/img/grid.a4b938cf382b.png", "rest_framework/docs/css/highlight.css": "rest_framework/docs/css/highlight.e0e4d973c6d7.css", "rest_framework/docs/css/base.css": "rest_framework/docs/css/base.e630f8f4990e.css", "rest_framework/docs/css/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.8851fb9336c9.js", "rest_framework/js/jquery-3.7.1.min.js": "rest_framework/js/jquery-3.7.1.min.2c872dbe60f4.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/bootstrap-theme.min.css.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.css", "rest_framework/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "img/logo-maskable.svg": "img/logo-maskable.d618c2d7943f.svg", "img/logo-maskable-180.webp": "img/logo-maskable-180.201cbde62d55.webp", "img/logo.svg": "img/logo.3fb129bfbd92.svg", "img/missing-cover.webp": "img/missing-cover.8c8c1d4f6782.webp", "img/missing-page.svg": "img/missing-page.71bb10f7c72a.svg", "img/logo-32.webp": "img/logo-32.dbb98f6dfde4.webp", "js/choices.json": "js/choices.a4dcff295a04.json", "js/choices-admin.json": "js/choices-admin.bb730e4f26ac.json", "assets/reader-DRsYPz-3.js": "assets/reader-DRsYPz-3.b8ee57f2aefc.js", "assets/VToolbar-CAUhD2BO.js": "assets/VToolbar-CAUhD2BO.279f0b52347a.js", "assets/VExpansionPanel-DsEXVcCv.css": "assets/VExpansionPanel-DsEXVcCv.20b475255e66.css", "assets/VTable-CGuSv-Tv.js": "assets/VTable-CGuSv-Tv.eddaf5526d93.js", "assets/group-tab-BilDDNz5.js": "assets/group-tab-BilDDNz5.cba5ca15d7cf.js", "assets/VCheckbox-BMRDCYNv.js": "assets/VCheckbox-BMRDCYNv.7f791d7f065f.js", "assets/datetime-column-BWWfvyhn.css": "assets/datetime-column-BWWfvyhn.a4a94ba57c73.css", "assets/VSlideGroup-ztBraz6g.js": "assets/VSlideGroup-ztBraz6g.02a0bc55b207.js", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/pdf-doc-CMzaSV_K.css": "assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css", "assets/VTable-DflzroDq.css": "assets/VTable-DflzroDq.160504d82195.css", "assets/VExpansionPanel-C593uXqT.js": "assets/VExpansionPanel-C593uXqT.d3bdc6a21208.js", "assets/VSelectionControl-CuM4okF6.js": "assets/VSelectionControl-CuM4okF6.0f246e35e86f.js", "assets/VSlideGroup-C3lY91uv.css": "assets/VSlideGroup-C3lY91uv.d268067bd078.css", "assets/VMain-Byt37V4c.css": "assets/VMain-Byt37V4c.a00e206f3cfa.css", "assets/VRadioGroup-Bi16yPEE.js": "assets/VRadioGroup-Bi16yPEE.69086b77f9b1.js", "assets/VMain-DmQvRj_k.js": "assets/VMain-DmQvRj_k.1e0480bed9a6.js", "assets/VWindowItem-YvmNwCNb.js": "assets/VWindowItem-YvmNwCNb.20a7e3b303de.js", "assets/stats-tab-BTZLMBCU.css": "assets/stats-tab-BTZLMBCU.7e0a9547c12e.css", "assets/confirm-footer-VFepxcJs.css": "assets/confirm-footer-VFepxcJs.4150131304bd.css", "assets/change-password-dialog-BRmvkNir.css": "assets/change-password-dialog-BRmvkNir.2fead58a78c7.css", "assets/library-tab-DZKDtQ5v.css": "assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css", "assets/to-case-kUm8NPpW.js": "assets/to-case-kUm8NPpW.5b3369f511b8.js", "assets/task-tab-Kff6fz1t.js": "assets/task-tab-Kff6fz1t.28380a665ed7.js", "assets/admin-drawer-panel-B4VvGE4s.js": "assets/admin-drawer-panel-B4VvGE4s.2265b9de60ab.js", "assets/main-CacfwsKA.css": "assets/main-CacfwsKA.4f849ef1c5db.css", "assets/change-password-dialog-Dq6GjT1T.js": "assets/change-password-dialog-Dq6GjT1T.01b56b4b3e87.js", "assets/flag-tab-D5RcLFbQ.js": "assets/flag-tab-D5RcLFbQ.1073708ea164.js", "assets/VSelectionControl-BFq--28I.css": "assets/VSelectionControl-BFq--28I.9687c77461fc.css", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "assets/user-tab-OAmZ6CQl.js": "assets/user-tab-OAmZ6CQl.eed3361aefb7.js", "assets/pdf-doc-CEOcuQZb.js": "assets/pdf-doc-CEOcuQZb.260e6671eb89.js", "assets/library-tab-dbcbv0--.js": "assets/library-tab-dbcbv0--.efa2d1d95b77.js", "assets/VCheckboxBtn-Dqp1u3GT.js": "assets/VCheckboxBtn-Dqp1u3GT.e80f145994b6.js", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/datetime-column-DCeBthsr.js": "assets/datetime-column-DCeBthsr.22ed49a999cc.js", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/main-CCY5Ix0K.js": "assets/main-CCY5Ix0K.b067e1d16cf5.js", "assets/VToolbar-DqQLg1UA.css": "assets/VToolbar-DqQLg1UA.614d6a4a8a09.css", "assets/http-error-DHmC7qgT.js": "assets/http-error-DHmC7qgT.bde1d32b55df.js", "assets/filter-Dw0fjwZi.js": "assets/filter-Dw0fjwZi.4d5f085435ab.js", "assets/metadata-dialog-DIhQctD5.css": "assets/metadata-dialog-DIhQctD5.67830cc94cf8.css", "assets/stats-tab-QTG23RuT.js": "assets/stats-tab-QTG23RuT.654f85e0b21d.js", "assets/metadata-dialog-PkTw4uLp.js": "assets/metadata-dialog-PkTw4uLp.ce5ee2377f27.js", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/admin-drawer-panel-Bk7z8uTR.css": "assets/admin-drawer-panel-Bk7z8uTR.1531b747003f.css", "assets/reader-CG6zrLXl.css": "assets/reader-CG6zrLXl.ed017042cd53.css", "assets/task-tab-DoX9OA37.css": "assets/task-tab-DoX9OA37.9bb355f36dbf.css", "assets/VDataTableVirtual-Cbj7IGxa.css": "assets/VDataTableVirtual-Cbj7IGxa.24039b2ebc61.css", "assets/index-DUjeNg4x.js": "assets/index-DUjeNg4x.36d50c91b2c1.js", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/browser-BTel3sYS.js": "assets/browser-BTel3sYS.8e1515bc039a.js", "assets/flag-tab-DUXtbpA2.css": "assets/flag-tab-DUXtbpA2.8d52c0d4f9e2.css", "assets/http-error-C25WdjWC.css": "assets/http-error-C25WdjWC.1c7337ad6e1d.css", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/admin-Ch9WezG1.js": "assets/admin-Ch9WezG1.bdace86b7c25.js", "assets/VSelect-BdGop3V2.css": "assets/VSelect-BdGop3V2.604c1785fbff.css", "assets/VCombobox-BksEr4D_.css": "assets/VCombobox-BksEr4D_.73429bb75f42.css", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/confirm-dialog-D-Fx-51m.js": "assets/confirm-dialog-D-Fx-51m.54b7d1059106.js", "assets/group-tab-3DXxSetf.css": "assets/group-tab-3DXxSetf.5fc2cfa31669.css", "assets/VDataTableVirtual-DhhCmPtf.js": "assets/VDataTableVirtual-DhhCmPtf.0735a1ab4c2f.js", "assets/VCombobox-DT52mZm3.js": "assets/VCombobox-DT52mZm3.309c77be4a55.js", "assets/browser-CVvV08AM.css": "assets/browser-CVvV08AM.7667051b18fa.css", "assets/confirm-footer-Yn7T9Tns.js": "assets/confirm-footer-Yn7T9Tns.75a17cd86ca9.js", "assets/admin-Df82-Qcj.css": "assets/admin-Df82-Qcj.4c3f0406ee54.css", "assets/VSelect-BdaijxdJ.js": "assets/VSelect-BdaijxdJ.b969ab260593.js", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.13219ea92507.json"}, "version": "1.1", "hash": "d55016a34609"}
codex/views/bookmark.py CHANGED
@@ -92,18 +92,15 @@ class BookmarkBaseView(GenericAPIView, GroupACLMixin):
92
92
  .only(*self._BOOKMARK_ONLY_FIELDS)
93
93
  )
94
94
  update_bookmarks = []
95
- existing_comic_pks = set()
96
95
  for bm in existing_bookmarks:
97
96
  self._update_bookmarks_validate_page(bm, updates)
98
97
  self._update_bookmarks_validate_two_pages(bm, updates)
99
98
  for key, value in updates.items():
100
99
  setattr(bm, key, value)
101
100
  update_bookmarks.append(bm)
102
- existing_comic_pks.add(bm.comic.pk)
103
101
  if update_bookmarks:
104
102
  update_fields = list(self._BOOKMARK_UPDATE_FIELDS | updates.keys())
105
103
  Bookmark.objects.bulk_update(update_bookmarks, update_fields)
106
- return existing_comic_pks
107
104
 
108
105
  @staticmethod
109
106
  def _create_bookmarks_validate_two_pages(updates):
@@ -114,19 +111,25 @@ class BookmarkBaseView(GenericAPIView, GroupACLMixin):
114
111
  ):
115
112
  updates.pop("two_pages", None)
116
113
 
117
- def _create_bookmarks(
118
- self, existing_comic_pks, comic_filter, search_kwargs, updates
119
- ):
114
+ def _create_bookmarks(self, comic_filter, search_kwargs, updates):
120
115
  """Create new bookmarks for comics that don't exist yet."""
121
116
  self._create_bookmarks_validate_two_pages(updates)
122
117
  if not updates:
123
118
  return
124
119
  create_bookmarks = []
125
120
  group_acl_filter = self.get_group_acl_filter(Comic)
121
+ for key, value in search_kwargs.items():
122
+ exclude = {"bookmark__" + key: value}
123
+ break
124
+ else:
125
+ reason = (
126
+ f"No user or session_id found for creating bookmarks: {search_kwargs}"
127
+ )
128
+ raise ValueError(reason)
126
129
  create_bookmark_comics = (
127
130
  Comic.objects.filter(group_acl_filter)
128
131
  .filter(**comic_filter)
129
- .exclude(pk__in=existing_comic_pks)
132
+ .exclude(**exclude)
130
133
  .only(*self._COMIC_ONLY_FIELDS)
131
134
  )
132
135
  for comic in create_bookmark_comics:
@@ -151,8 +154,8 @@ class BookmarkBaseView(GenericAPIView, GroupACLMixin):
151
154
  def update_bookmarks(self, updates, comic_filter):
152
155
  """Update a user bookmark."""
153
156
  search_kwargs = self.get_bookmark_search_kwargs(comic_filter)
154
- existing_comic_pks = self._update_bookmarks(search_kwargs, updates)
155
- self._create_bookmarks(existing_comic_pks, comic_filter, search_kwargs, updates)
157
+ self._update_bookmarks(search_kwargs, updates)
158
+ self._create_bookmarks(comic_filter, search_kwargs, updates)
156
159
 
157
160
 
158
161
  class BookmarkView(BookmarkBaseView):
@@ -6,7 +6,6 @@ from types import MappingProxyType
6
6
  from urllib.parse import unquote_plus
7
7
 
8
8
  from django.contrib.auth.models import User
9
- from django.db.models import Q
10
9
  from djangorestframework_camel_case.settings import api_settings
11
10
  from djangorestframework_camel_case.util import underscoreize
12
11
 
@@ -64,19 +63,23 @@ class BrowserBaseView(
64
63
  self._is_admin = user and isinstance(user, User) and user.is_staff
65
64
  return self._is_admin
66
65
 
67
- def get_query_filters_without_group(self, model, search_scores: dict):
66
+ def get_query_filters_without_group(
67
+ self,
68
+ model,
69
+ ):
68
70
  """Return all the filters except the group filter."""
69
71
  object_filter = self.get_group_acl_filter(model)
70
- object_filter &= self.get_search_filter(model, search_scores)
71
72
  object_filter &= self.get_bookmark_filter(model)
72
73
  object_filter &= self.get_comic_field_filter(self.rel_prefix)
73
74
  return object_filter
74
75
 
75
- def get_query_filters(self, model, search_scores: dict | None, choices=False):
76
+ def get_query_filters(
77
+ self,
78
+ model,
79
+ choices=False,
80
+ ):
76
81
  """Return the main object filter and the one for aggregates."""
77
- if search_scores is None:
78
- return Q(False)
79
- object_filter = self.get_query_filters_without_group(model, search_scores)
82
+ object_filter = self.get_query_filters_without_group(model)
80
83
  object_filter &= self.get_group_filter(choices)
81
84
  return object_filter
82
85
 
@@ -89,10 +92,20 @@ class BrowserBaseView(
89
92
  parsed_val = json.loads(unquoted_val)
90
93
  if not parsed_val:
91
94
  continue
95
+ parsed_key = key
96
+ elif key in ("q", "query"):
97
+ # "query" is used by opds v2
98
+ if "q" in result:
99
+ continue
100
+ parsed_val = val.strip()
101
+ if not parsed_val:
102
+ continue
103
+ parsed_key = "q"
92
104
  else:
105
+ parsed_key = key
93
106
  parsed_val = val
94
107
 
95
- result[key] = parsed_val
108
+ result[parsed_key] = parsed_val
96
109
  return underscoreize(result, **api_settings.JSON_UNDERSCOREIZE)
97
110
 
98
111
  def parse_params(self):
@@ -29,11 +29,19 @@ from codex.models import (
29
29
  Volume,
30
30
  )
31
31
  from codex.serializers.browser.page import BrowserPageSerializer
32
- from codex.serializers.choices import CHOICES, DEFAULTS
32
+ from codex.serializers.choices import DEFAULTS
33
33
  from codex.views.auth import IsAuthenticatedOrEnabledNonUsers
34
34
  from codex.views.browser.browser_annotations import BrowserAnnotationsView
35
+ from codex.views.browser.const import MAX_OBJ_PER_PAGE
36
+ from codex.views.browser.filters.search import SearchScores
35
37
 
36
38
  LOG = get_logger(__name__)
39
+ _ADMIN_FLAG_VALUE_KEY_MAP = MappingProxyType(
40
+ {
41
+ AdminFlag.FlagChoices.FOLDER_VIEW.value: "folder_view",
42
+ AdminFlag.FlagChoices.IMPORT_METADATA.value: "import_metadata",
43
+ }
44
+ )
37
45
 
38
46
 
39
47
  class BrowserView(BrowserAnnotationsView):
@@ -42,7 +50,6 @@ class BrowserView(BrowserAnnotationsView):
42
50
  permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
43
51
  serializer_class = BrowserPageSerializer
44
52
 
45
- MAX_OBJ_PER_PAGE = 100
46
53
  _NAV_GROUPS = "rpisv"
47
54
 
48
55
  _GROUP_INSTANCE_SELECT_RELATED = MappingProxyType(
@@ -102,7 +109,12 @@ class BrowserView(BrowserAnnotationsView):
102
109
  value = "c" if model == Comic else self.model_group
103
110
  return queryset.annotate(group=Value(value, CharField(max_length=1)))
104
111
 
105
- def _add_annotations(self, queryset, model, search_scores: dict):
112
+ def _add_annotations(
113
+ self,
114
+ queryset,
115
+ model,
116
+ search_scores: SearchScores | None,
117
+ ):
106
118
  """Annotations for display and sorting."""
107
119
  queryset = self.annotate_common_aggregates(queryset, model, search_scores)
108
120
  queryset = self._annotate_group(queryset, model)
@@ -132,29 +144,35 @@ class BrowserView(BrowserAnnotationsView):
132
144
  self.model_group = self._get_model_group()
133
145
  self.model = self.GROUP_MODEL_MAP[self.model_group]
134
146
 
135
- def _get_group_queryset(self, search_scores: dict | None):
147
+ def _get_common_queryset(self, model):
148
+ """Create queryset common to group & books."""
149
+ object_filter = self.get_query_filters(model, False)
150
+ qs = model.objects.filter(object_filter)
151
+ binary = self.params.get("order_by") != "search_score"
152
+ search_scores = self.get_search_scores(binary=binary)
153
+ qs = self._add_annotations(qs, model, search_scores)
154
+ qs = self.apply_search_filter(qs, model, search_scores)
155
+ qs = self.add_order_by(qs, model)
156
+ if limit := self.params.get("limit"):
157
+ # limit only is set by some opds views
158
+ qs = qs[:limit]
159
+ return qs
160
+
161
+ def _get_group_queryset(self):
136
162
  """Create group queryset."""
137
163
  if not self.model:
138
- reason = "Model not set in browser."
164
+ reason = "Model not set for browser queryset."
139
165
  raise ValueError(reason)
140
- if self.model != Comic and search_scores is not None:
141
- object_filter = self.get_query_filters(self.model, search_scores, False)
142
- qs = self.model.objects.filter(object_filter)
143
- qs = self._add_annotations(qs, self.model, search_scores)
144
- qs = self.add_order_by(qs, self.model)
145
- else:
166
+ elif self.model == Comic: # noqa: RET506
146
167
  qs = self.model.objects.none()
168
+ else:
169
+ qs = self._get_common_queryset(self.model)
147
170
  return qs
148
171
 
149
- def _get_book_queryset(self, search_scores: dict | None):
172
+ def _get_book_queryset(self):
150
173
  """Create book queryset."""
151
- if self.model in (Comic, Folder) and search_scores is not None:
152
- object_filter = self.get_query_filters(Comic, search_scores, False)
153
- qs = Comic.objects.filter(object_filter)
154
- qs = self._add_annotations(qs, Comic, search_scores)
155
- qs = self.add_order_by(qs, Comic)
156
- if limit := self.params.get("limit"):
157
- qs = qs[:limit]
174
+ if self.model in (Comic, Folder):
175
+ qs = self._get_common_queryset(Comic)
158
176
  else:
159
177
  qs = Comic.objects.none()
160
178
  return qs
@@ -193,8 +211,13 @@ class BrowserView(BrowserAnnotationsView):
193
211
  )
194
212
  except self.group_class.DoesNotExist:
195
213
  group = self.kwargs.get("group")
196
- reason = f"{group}={pk} does not exist!"
197
- self._raise_redirect({"group": group, "pk": 0, "page": 1}, reason)
214
+ pk = self.kwargs.get("pk")
215
+ page = self.kwargs.get("page")
216
+ if group == "r" and pk == 0 and page == 1:
217
+ self.group_instance: BrowserGroupModel | None = None
218
+ else:
219
+ reason = f"{group}={pk} does not exist!"
220
+ self._raise_redirect({"group": group, "pk": 0, "page": 1}, reason)
198
221
  else:
199
222
  self.group_instance: BrowserGroupModel | None = None
200
223
 
@@ -276,11 +299,22 @@ class BrowserView(BrowserAnnotationsView):
276
299
  "group_count": group_count,
277
300
  }
278
301
 
279
- def _page_out_of_bounds(self, page, num_pages):
302
+ def _page_out_of_bounds(self, num_pages):
280
303
  """Redirect page out of bounds."""
281
304
  group = self.kwargs.get("group")
282
- pk = self.kwargs.get("pk", 1)
283
- new_page = num_pages if page > num_pages else 1
305
+ pk = self.kwargs.get("pk", 0)
306
+ page = self.kwargs.get("page", 1)
307
+ if group == "r" and pk == 0 and page == 1:
308
+ # Don't redirect if on the root page.
309
+ return
310
+
311
+ if num_pages:
312
+ new_page = num_pages if page > num_pages else 1
313
+ else:
314
+ # Redirect to root.
315
+ group = "r"
316
+ pk = 0
317
+ new_page = 1
284
318
  route_changes = {"group": group, "pk": pk, "page": new_page}
285
319
  reason = f"{page=} does not exist!"
286
320
  LOG.debug(f"{reason} redirect to page {new_page}.")
@@ -289,7 +323,7 @@ class BrowserView(BrowserAnnotationsView):
289
323
  def _paginate_section(self, model, qs, page):
290
324
  """Paginate a group or Comic section."""
291
325
  orphans = 5 if self.model_group != "f" else 0
292
- paginator = Paginator(qs, self.MAX_OBJ_PER_PAGE, orphans=orphans)
326
+ paginator = Paginator(qs, MAX_OBJ_PER_PAGE, orphans=orphans)
293
327
  try:
294
328
  qs = paginator.page(page).object_list
295
329
  except EmptyPage:
@@ -311,27 +345,25 @@ class BrowserView(BrowserAnnotationsView):
311
345
 
312
346
  def _paginate_books(self, book_qs, total_group_count, page_obj_count):
313
347
  """Paginate the book object list based on how many group/folders are showing."""
314
- if page_obj_count >= self.MAX_OBJ_PER_PAGE:
348
+ if page_obj_count >= MAX_OBJ_PER_PAGE:
315
349
  # No books for this page
316
350
  page_book_qs = Comic.objects.none()
317
351
  else:
318
- group_remainder = total_group_count % self.MAX_OBJ_PER_PAGE
352
+ group_remainder = total_group_count % MAX_OBJ_PER_PAGE
319
353
  if page_obj_count:
320
354
  # There are books after the groups on the same page
321
355
  # Manually add books without the paginator
322
- book_limit = self.MAX_OBJ_PER_PAGE - group_remainder
356
+ book_limit = MAX_OBJ_PER_PAGE - group_remainder
323
357
  page_book_qs = book_qs[:book_limit]
324
358
  else:
325
359
  # There are books after the groups on a new page
326
360
  if group_remainder:
327
- book_offset = self.MAX_OBJ_PER_PAGE - group_remainder
361
+ book_offset = MAX_OBJ_PER_PAGE - group_remainder
328
362
  page_book_qs = book_qs[book_offset:]
329
363
  else:
330
364
  page_book_qs = book_qs
331
365
 
332
- num_group_and_mixed_pages = ceil(
333
- total_group_count / self.MAX_OBJ_PER_PAGE
334
- )
366
+ num_group_and_mixed_pages = ceil(total_group_count / MAX_OBJ_PER_PAGE)
335
367
  book_only_page = self.kwargs.get("page", 1) - num_group_and_mixed_pages
336
368
  page_book_qs = self._paginate_section(
337
369
  Comic, page_book_qs, book_only_page
@@ -342,20 +374,18 @@ class BrowserView(BrowserAnnotationsView):
342
374
  """Paginate the queryset into a group and book object lists."""
343
375
  page = self.kwargs.get("page", 1)
344
376
  if page < 1:
345
- self._page_out_of_bounds(page, 1)
346
-
347
- page_obj_count = 0
377
+ self._page_out_of_bounds(1)
348
378
 
349
- total_group_count = group_qs.count()
350
- total_book_count = book_qs.count()
379
+ group_count = group_qs.count()
380
+ book_count = book_qs.count()
351
381
 
352
- num_pages = ceil((total_group_count + total_book_count) / self.MAX_OBJ_PER_PAGE)
382
+ num_pages = ceil((group_count + book_count) / MAX_OBJ_PER_PAGE)
353
383
  if page > num_pages:
354
- self._page_out_of_bounds(page, num_pages)
384
+ self._page_out_of_bounds(num_pages)
355
385
 
356
- page_group_qs = self._paginate_groups(group_qs, total_group_count)
357
- page_obj_count += page_group_qs.count()
358
- page_book_qs = self._paginate_books(book_qs, total_group_count, page_obj_count)
386
+ page_group_qs = self._paginate_groups(group_qs, group_count)
387
+ page_obj_count = page_group_qs.count()
388
+ page_book_qs = self._paginate_books(book_qs, group_count, page_obj_count)
359
389
  page_obj_count += page_book_qs.count()
360
390
 
361
391
  return page_group_qs, page_book_qs, num_pages, page_obj_count
@@ -366,11 +396,10 @@ class BrowserView(BrowserAnnotationsView):
366
396
  self.set_rel_prefix(self.model)
367
397
  self._set_group_instance() # Placed up here to invalidate earlier
368
398
  # Create the main query with the filters
369
- search_scores: dict | None = self.get_search_scores()
370
399
  group = self.kwargs.get("group")
371
400
 
372
- group_qs = self._get_group_queryset(search_scores)
373
- book_qs = self._get_book_queryset(search_scores)
401
+ group_qs = self._get_group_queryset()
402
+ book_qs = self._get_book_queryset()
374
403
 
375
404
  # Paginate
376
405
  group_qs, book_qs, num_pages, total_count = self._paginate(group_qs, book_qs)
@@ -389,19 +418,6 @@ class BrowserView(BrowserAnnotationsView):
389
418
  else:
390
419
  up_route = {}
391
420
 
392
- admin_flag_objs = AdminFlag.objects.only("on").filter(
393
- key__in=(
394
- AdminFlag.FlagChoices.FOLDER_VIEW.value,
395
- AdminFlag.FlagChoices.IMPORT_METADATA.value,
396
- )
397
- )
398
- admin_flags = {}
399
- for flag in admin_flag_objs:
400
- if flag.key == AdminFlag.FlagChoices.FOLDER_VIEW.value:
401
- admin_flags["folder_view"] = flag.on
402
- if flag.key == AdminFlag.FlagChoices.IMPORT_METADATA.value:
403
- admin_flags["import_metadata"] = flag.on
404
-
405
421
  libraries_exist = Library.objects.exists()
406
422
 
407
423
  # runs obj list query twice :/
@@ -426,7 +442,7 @@ class BrowserView(BrowserAnnotationsView):
426
442
  "issue_number_max": issue_number_max,
427
443
  "num_pages": num_pages,
428
444
  "total_count": total_count,
429
- "admin_flags": admin_flags,
445
+ "admin_flags": self.admin_flags,
430
446
  "libraries_exist": libraries_exist,
431
447
  "covers_timestamp": covers_timestamp,
432
448
  }
@@ -549,19 +565,23 @@ class BrowserView(BrowserAnnotationsView):
549
565
  page = self.kwargs.get("page", 1)
550
566
  self.params["route"] = {"group": group, "pk": pk, "page": page}
551
567
 
568
+ def _set_admin_flags(self):
569
+ """Set browser relevant admin flags."""
570
+ admin_pairs = AdminFlag.objects.filter(
571
+ key__in=_ADMIN_FLAG_VALUE_KEY_MAP.keys()
572
+ ).values_list("key", "on")
573
+ admin_flags = {}
574
+ for key, on in admin_pairs:
575
+ export_key = _ADMIN_FLAG_VALUE_KEY_MAP[key]
576
+ admin_flags[export_key] = on
577
+ self.admin_flags = MappingProxyType(admin_flags)
578
+
552
579
  def validate_settings(self):
553
580
  """Validate group and top group settings."""
554
581
  group = self.kwargs.get("group")
555
582
  self.set_order_key()
556
- enable_folder_view = False
557
- if group == self.FOLDER_GROUP or self.order_key == "path":
558
- key = AdminFlag.FlagChoices.FOLDER_VIEW.value
559
- try:
560
- enable_folder_view = AdminFlag.objects.only("on").get(key=key).on
561
- except Exception as err:
562
- title = CHOICES["admin"]["adminFlags"].get(key)
563
- LOG.warning(f"Getting {title} AdminFlag: {err}")
564
-
583
+ self._set_admin_flags()
584
+ enable_folder_view = self.admin_flags["folder_view"]
565
585
  if group == self.FOLDER_GROUP:
566
586
  self._validate_folder_settings(enable_folder_view)
567
587
  elif group == self.STORY_ARC_GROUP:
@@ -24,6 +24,11 @@ from django.db.models.functions import Least, Lower, Reverse, Right, StrIndex, S
24
24
 
25
25
  from codex.models import Comic
26
26
  from codex.views.browser.browser_order_by import BrowserOrderByView
27
+ from codex.views.browser.filters.search import SearchScores
28
+
29
+ SEARCH_SCORE_MAX = 100.0
30
+ SEARCH_SCORE_MIN = 0.001
31
+ SEARCH_SCORE_EMPTY = 0.0
27
32
 
28
33
 
29
34
  class BrowserAnnotationsView(BrowserOrderByView):
@@ -51,7 +56,9 @@ class BrowserAnnotationsView(BrowserOrderByView):
51
56
 
52
57
  is_opds_1_acquisition = False
53
58
 
54
- def _annotate_search_score(self, queryset, search_scores, model):
59
+ def _annotate_search_score(
60
+ self, queryset, search_scores: SearchScores | None, model
61
+ ):
55
62
  """Annotate the search score for ordering by search score.
56
63
 
57
64
  Choose the maximum matching score for the group.
@@ -60,10 +67,15 @@ class BrowserAnnotationsView(BrowserOrderByView):
60
67
  return queryset
61
68
  whens = []
62
69
  prefix = "" if model == Comic else self.rel_prefix
63
- for pk, score in search_scores.items():
64
- when = {prefix + "pk": pk, "then": score}
65
- whens.append(When(**when))
66
- annotate = {"search_score": Max(Case(*whens, default=0.0))}
70
+ if search_scores:
71
+ for pk, score in search_scores.scores:
72
+ when = {prefix + "pk": pk, "then": score}
73
+ whens.append(When(**when))
74
+ if search_scores.prev_pks:
75
+ whens.append(When(pk__in=search_scores.prev_pks, then=SEARCH_SCORE_MAX))
76
+ if search_scores.next_pks:
77
+ whens.append(When(pk__in=search_scores.next_pks, then=SEARCH_SCORE_MIN))
78
+ annotate = {"search_score": Max(Case(*whens, default=SEARCH_SCORE_EMPTY))}
67
79
  return queryset.annotate(**annotate)
68
80
 
69
81
  def _annotate_cover_pk(self, queryset, model):
@@ -270,7 +282,7 @@ class BrowserAnnotationsView(BrowserOrderByView):
270
282
  def _annotate_mtime(queryset):
271
283
  return queryset.annotate(mtime=Max("updated_at"))
272
284
 
273
- def annotate_common_aggregates(self, qs, model, search_scores):
285
+ def annotate_common_aggregates(self, qs, model, search_scores: SearchScores | None):
274
286
  """Annotate common aggregates between browser and metadata."""
275
287
  qs = self._annotate_search_score(qs, search_scores, model)
276
288
  qs = self._annotate_child_count(qs, model)
@@ -280,8 +292,8 @@ class BrowserAnnotationsView(BrowserOrderByView):
280
292
  qs = self._annotate_bookmark_updated_at(qs, bm_rel, bm_filter)
281
293
  qs = self._annotate_sort_name(qs, model)
282
294
  qs = self._annotate_story_arc_number(qs)
295
+ # cover depends annotations for order-by, in metadata too
283
296
  qs = self._annotate_order_value(qs, model)
284
- # cover depends on the above annotations for order-by
285
297
  qs = self._annotate_cover_pk(qs, model)
286
298
  qs = self._annotate_bookmarks(qs, model, bm_rel, bm_filter)
287
299
  qs = self._annotate_mtime(qs)
@@ -123,11 +123,9 @@ class BrowserChoicesViewBase(BrowserBaseView):
123
123
 
124
124
  def get_object(self):
125
125
  """Get the comic subquery use for the choices."""
126
- search_scores = self.get_search_scores()
127
- if search_scores is None:
128
- return self.model.objects.none() # type: ignore
129
- object_filter = self.get_query_filters(self.model, search_scores, True)
130
- return self.model.objects.filter(object_filter) # type: ignore
126
+ object_filter = self.get_query_filters(self.model, choices=True)
127
+ qs = self.model.objects.filter(object_filter) # type: ignore
128
+ return self.apply_binary_search_filter(qs)
131
129
 
132
130
  def _set_model(self):
133
131
  """Set the model to query."""
@@ -0,0 +1,3 @@
1
+ """Browser constants."""
2
+
3
+ MAX_OBJ_PER_PAGE = 100