codex 1.5.11__py3-none-any.whl → 1.5.13__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/applications/websocket.py +1 -7
  2. codex/librarian/importer/importerd.py +2 -1
  3. codex/librarian/importer/query_fks.py +1 -1
  4. codex/middleware.py +50 -1
  5. codex/serializers/choices.py +11 -14
  6. codex/serializers/metadata.py +1 -1
  7. codex/settings/settings.py +11 -5
  8. codex/static_root/assets/{VCheckbox-DILNxNH0.b3d636874a55.js → VCheckbox-Wdu19ZuZ.50f2bf3b5ce5.js} +1 -1
  9. codex/static_root/assets/VCheckbox-Wdu19ZuZ.50f2bf3b5ce5.js.br +0 -0
  10. codex/static_root/assets/VCheckbox-Wdu19ZuZ.50f2bf3b5ce5.js.gz +0 -0
  11. codex/static_root/assets/{VCheckbox-DILNxNH0.js → VCheckbox-Wdu19ZuZ.js} +1 -1
  12. codex/static_root/assets/VCheckbox-Wdu19ZuZ.js.br +0 -0
  13. codex/static_root/assets/VCheckbox-Wdu19ZuZ.js.gz +0 -0
  14. codex/static_root/assets/{VCheckboxBtn-BvTV50hB.743d5045dd85.js → VCheckboxBtn-C7NngFC8.bd12ea5cdbb2.js} +1 -1
  15. codex/static_root/assets/VCheckboxBtn-C7NngFC8.bd12ea5cdbb2.js.br +0 -0
  16. codex/static_root/assets/VCheckboxBtn-C7NngFC8.bd12ea5cdbb2.js.gz +0 -0
  17. codex/static_root/assets/{VCheckboxBtn-BvTV50hB.js → VCheckboxBtn-C7NngFC8.js} +1 -1
  18. codex/static_root/assets/VCheckboxBtn-C7NngFC8.js.br +0 -0
  19. codex/static_root/assets/VCheckboxBtn-C7NngFC8.js.gz +0 -0
  20. codex/static_root/assets/VCombobox-DxNlm_ji.11d317a5dbb0.js +1 -0
  21. codex/static_root/assets/VCombobox-DxNlm_ji.11d317a5dbb0.js.br +0 -0
  22. codex/static_root/assets/VCombobox-DxNlm_ji.11d317a5dbb0.js.gz +0 -0
  23. codex/static_root/assets/VCombobox-DxNlm_ji.js +1 -0
  24. codex/static_root/assets/VCombobox-DxNlm_ji.js.br +0 -0
  25. codex/static_root/assets/VCombobox-DxNlm_ji.js.gz +0 -0
  26. codex/static_root/assets/{VDataTableVirtual-CeSA4IqW.222e223c0c5b.js → VDataTableVirtual-C4UTS-jp.4b2926f1461f.js} +1 -1
  27. codex/static_root/assets/VDataTableVirtual-C4UTS-jp.4b2926f1461f.js.br +0 -0
  28. codex/static_root/assets/VDataTableVirtual-C4UTS-jp.4b2926f1461f.js.gz +0 -0
  29. codex/static_root/assets/{VDataTableVirtual-CeSA4IqW.js → VDataTableVirtual-C4UTS-jp.js} +1 -1
  30. codex/static_root/assets/VDataTableVirtual-C4UTS-jp.js.br +0 -0
  31. codex/static_root/assets/VDataTableVirtual-C4UTS-jp.js.gz +0 -0
  32. codex/static_root/assets/{VExpansionPanel-D2KdJaNN.382ab5af5c16.js → VExpansionPanel-1dRZTsNe.6213946f43dd.js} +1 -1
  33. codex/static_root/assets/VExpansionPanel-1dRZTsNe.6213946f43dd.js.br +0 -0
  34. codex/static_root/assets/VExpansionPanel-1dRZTsNe.6213946f43dd.js.gz +0 -0
  35. codex/static_root/assets/{VExpansionPanel-D2KdJaNN.js → VExpansionPanel-1dRZTsNe.js} +1 -1
  36. codex/static_root/assets/VExpansionPanel-1dRZTsNe.js.br +0 -0
  37. codex/static_root/assets/VExpansionPanel-1dRZTsNe.js.gz +0 -0
  38. codex/static_root/assets/{VMain-CLPkUtlY.093dce5ee73d.js → VMain-BVEH6Gtw.51ef468a0780.js} +1 -1
  39. codex/static_root/assets/VMain-BVEH6Gtw.51ef468a0780.js.br +0 -0
  40. codex/static_root/assets/VMain-BVEH6Gtw.51ef468a0780.js.gz +0 -0
  41. codex/static_root/assets/{VMain-CLPkUtlY.js → VMain-BVEH6Gtw.js} +1 -1
  42. codex/static_root/assets/VMain-BVEH6Gtw.js.br +0 -0
  43. codex/static_root/assets/VMain-BVEH6Gtw.js.gz +0 -0
  44. codex/static_root/assets/{VRadioGroup-CKhS3Tqq.d335c4ea0c80.js → VRadioGroup-D1WV2cpK.83ba2e5689fd.js} +1 -1
  45. codex/static_root/assets/VRadioGroup-D1WV2cpK.83ba2e5689fd.js.br +0 -0
  46. codex/static_root/assets/VRadioGroup-D1WV2cpK.83ba2e5689fd.js.gz +0 -0
  47. codex/static_root/assets/{VRadioGroup-CKhS3Tqq.js → VRadioGroup-D1WV2cpK.js} +1 -1
  48. codex/static_root/assets/VRadioGroup-D1WV2cpK.js.br +0 -0
  49. codex/static_root/assets/VRadioGroup-D1WV2cpK.js.gz +0 -0
  50. codex/static_root/assets/VSelect-BVyqMxyX.4a54cd987085.js +1 -0
  51. codex/static_root/assets/VSelect-BVyqMxyX.4a54cd987085.js.br +0 -0
  52. codex/static_root/assets/VSelect-BVyqMxyX.4a54cd987085.js.gz +0 -0
  53. codex/static_root/assets/VSelect-BVyqMxyX.js +1 -0
  54. codex/static_root/assets/VSelect-BVyqMxyX.js.br +0 -0
  55. codex/static_root/assets/VSelect-BVyqMxyX.js.gz +0 -0
  56. codex/static_root/assets/{VSelectionControl-BG0YEK9P.ac59951f8127.js → VSelectionControl-CC9aC7ZT.2db18d99af8b.js} +1 -1
  57. codex/static_root/assets/VSelectionControl-CC9aC7ZT.2db18d99af8b.js.br +0 -0
  58. codex/static_root/assets/VSelectionControl-CC9aC7ZT.2db18d99af8b.js.gz +0 -0
  59. codex/static_root/assets/{VSelectionControl-BG0YEK9P.js → VSelectionControl-CC9aC7ZT.js} +1 -1
  60. codex/static_root/assets/VSelectionControl-CC9aC7ZT.js.br +0 -0
  61. codex/static_root/assets/VSelectionControl-CC9aC7ZT.js.gz +0 -0
  62. codex/static_root/assets/VSlideGroup-D4n-jSgi.686cc428f5fe.js +1 -0
  63. codex/static_root/assets/VSlideGroup-D4n-jSgi.686cc428f5fe.js.br +0 -0
  64. codex/static_root/assets/VSlideGroup-D4n-jSgi.686cc428f5fe.js.gz +0 -0
  65. codex/static_root/assets/VSlideGroup-D4n-jSgi.js +1 -0
  66. codex/static_root/assets/VSlideGroup-D4n-jSgi.js.br +0 -0
  67. codex/static_root/assets/VSlideGroup-D4n-jSgi.js.gz +0 -0
  68. codex/static_root/assets/{VTable-Ca3_Vvv_.6fd02dbba881.js → VTable-DioYRJkC.c8638b2267ed.js} +1 -1
  69. codex/static_root/assets/VTable-DioYRJkC.c8638b2267ed.js.br +0 -0
  70. codex/static_root/assets/VTable-DioYRJkC.c8638b2267ed.js.gz +0 -0
  71. codex/static_root/assets/{VTable-Ca3_Vvv_.js → VTable-DioYRJkC.js} +1 -1
  72. codex/static_root/assets/VTable-DioYRJkC.js.br +0 -0
  73. codex/static_root/assets/VTable-DioYRJkC.js.gz +0 -0
  74. codex/static_root/assets/{VToolbar-CjnGoYm_.js → VToolbar-DLtG5Ngs.e32539656f6d.js} +2 -2
  75. codex/static_root/assets/VToolbar-DLtG5Ngs.e32539656f6d.js.br +0 -0
  76. codex/static_root/assets/VToolbar-DLtG5Ngs.e32539656f6d.js.gz +0 -0
  77. codex/static_root/assets/{VToolbar-CjnGoYm_.211df7fe7fc4.js → VToolbar-DLtG5Ngs.js} +2 -2
  78. codex/static_root/assets/VToolbar-DLtG5Ngs.js.br +0 -0
  79. codex/static_root/assets/VToolbar-DLtG5Ngs.js.gz +0 -0
  80. codex/static_root/assets/{VToolbar-BfNszTMd.111f25cdd6d8.css → VToolbar-DqQLg1UA.614d6a4a8a09.css} +1 -1
  81. codex/static_root/assets/VToolbar-DqQLg1UA.614d6a4a8a09.css.br +0 -0
  82. codex/static_root/assets/{VToolbar-BfNszTMd.111f25cdd6d8.css.gz → VToolbar-DqQLg1UA.614d6a4a8a09.css.gz} +0 -0
  83. codex/static_root/assets/{VToolbar-BfNszTMd.css → VToolbar-DqQLg1UA.css} +1 -1
  84. codex/static_root/assets/VToolbar-DqQLg1UA.css.br +0 -0
  85. codex/static_root/assets/{VToolbar-BfNszTMd.css.gz → VToolbar-DqQLg1UA.css.gz} +0 -0
  86. codex/static_root/assets/{VWindowItem-Ren0_px5.3e2b2ffbdefb.js → VWindowItem-D-OB7e0u.ef9bcb84a01e.js} +1 -1
  87. codex/static_root/assets/VWindowItem-D-OB7e0u.ef9bcb84a01e.js.br +0 -0
  88. codex/static_root/assets/VWindowItem-D-OB7e0u.ef9bcb84a01e.js.gz +0 -0
  89. codex/static_root/assets/{VWindowItem-Ren0_px5.js → VWindowItem-D-OB7e0u.js} +1 -1
  90. codex/static_root/assets/VWindowItem-D-OB7e0u.js.br +0 -0
  91. codex/static_root/assets/VWindowItem-D-OB7e0u.js.gz +0 -0
  92. codex/static_root/assets/{admin-B0qtMbYo.071dfdcac325.js → admin-BwXMQOyX.8c44117c2b63.js} +1 -1
  93. codex/static_root/assets/admin-BwXMQOyX.8c44117c2b63.js.br +0 -0
  94. codex/static_root/assets/admin-BwXMQOyX.8c44117c2b63.js.gz +0 -0
  95. codex/static_root/assets/{admin-B0qtMbYo.js → admin-BwXMQOyX.js} +1 -1
  96. codex/static_root/assets/admin-BwXMQOyX.js.br +0 -0
  97. codex/static_root/assets/admin-BwXMQOyX.js.gz +0 -0
  98. codex/static_root/assets/{admin-drawer-panel-s72VfDNy.css → admin-drawer-panel-Bk7z8uTR.1531b747003f.css} +1 -1
  99. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.1531b747003f.css.br +0 -0
  100. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.1531b747003f.css.gz +0 -0
  101. codex/static_root/assets/{admin-drawer-panel-s72VfDNy.ef6cb7221a44.css → admin-drawer-panel-Bk7z8uTR.css} +1 -1
  102. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.css.br +0 -0
  103. codex/static_root/assets/admin-drawer-panel-Bk7z8uTR.css.gz +0 -0
  104. codex/static_root/assets/{admin-drawer-panel-CYYEXqtK.db547c39eded.js → admin-drawer-panel-CGj6J0rX.cc680f2603da.js} +4 -4
  105. codex/static_root/assets/admin-drawer-panel-CGj6J0rX.cc680f2603da.js.br +0 -0
  106. codex/static_root/assets/admin-drawer-panel-CGj6J0rX.cc680f2603da.js.gz +0 -0
  107. codex/static_root/assets/{admin-drawer-panel-CYYEXqtK.js → admin-drawer-panel-CGj6J0rX.js} +4 -4
  108. codex/static_root/assets/admin-drawer-panel-CGj6J0rX.js.br +0 -0
  109. codex/static_root/assets/admin-drawer-panel-CGj6J0rX.js.gz +0 -0
  110. codex/static_root/assets/browser-BLCruguN.ca6300976f82.js +1 -0
  111. codex/static_root/assets/browser-BLCruguN.ca6300976f82.js.br +0 -0
  112. codex/static_root/assets/browser-BLCruguN.ca6300976f82.js.gz +0 -0
  113. codex/static_root/assets/browser-BLCruguN.js +1 -0
  114. codex/static_root/assets/browser-BLCruguN.js.br +0 -0
  115. codex/static_root/assets/browser-BLCruguN.js.gz +0 -0
  116. codex/static_root/assets/{browser-2i0QA4La.587673ac534c.css → browser-CTxEKlCx.211d729d9165.css} +1 -1
  117. codex/static_root/assets/browser-CTxEKlCx.211d729d9165.css.br +0 -0
  118. codex/static_root/assets/browser-CTxEKlCx.211d729d9165.css.gz +0 -0
  119. codex/static_root/assets/{browser-2i0QA4La.css → browser-CTxEKlCx.css} +1 -1
  120. codex/static_root/assets/browser-CTxEKlCx.css.br +0 -0
  121. codex/static_root/assets/browser-CTxEKlCx.css.gz +0 -0
  122. codex/static_root/assets/{change-password-dialog-DnkeLvMH.f9ac56354665.js → change-password-dialog-DDvVSrWH.915709700c5f.js} +1 -1
  123. codex/static_root/assets/change-password-dialog-DDvVSrWH.915709700c5f.js.br +0 -0
  124. codex/static_root/assets/change-password-dialog-DDvVSrWH.915709700c5f.js.gz +0 -0
  125. codex/static_root/assets/{change-password-dialog-DnkeLvMH.js → change-password-dialog-DDvVSrWH.js} +1 -1
  126. codex/static_root/assets/change-password-dialog-DDvVSrWH.js.br +0 -0
  127. codex/static_root/assets/change-password-dialog-DDvVSrWH.js.gz +0 -0
  128. codex/static_root/assets/{confirm-dialog-BfSatKsS.7e0e20b08c75.js → confirm-dialog-Bf3Sde_o.b437109b579e.js} +1 -1
  129. codex/static_root/assets/confirm-dialog-Bf3Sde_o.b437109b579e.js.br +0 -0
  130. codex/static_root/assets/confirm-dialog-Bf3Sde_o.b437109b579e.js.gz +0 -0
  131. codex/static_root/assets/{confirm-dialog-BfSatKsS.js → confirm-dialog-Bf3Sde_o.js} +1 -1
  132. codex/static_root/assets/confirm-dialog-Bf3Sde_o.js.br +0 -0
  133. codex/static_root/assets/confirm-dialog-Bf3Sde_o.js.gz +0 -0
  134. codex/static_root/assets/{confirm-footer-lfG418fS.js → confirm-footer-CLwOC3F0.d25d9085b300.js} +1 -1
  135. codex/static_root/assets/confirm-footer-CLwOC3F0.d25d9085b300.js.br +0 -0
  136. codex/static_root/assets/confirm-footer-CLwOC3F0.d25d9085b300.js.gz +0 -0
  137. codex/static_root/assets/{confirm-footer-lfG418fS.70e914e12343.js → confirm-footer-CLwOC3F0.js} +1 -1
  138. codex/static_root/assets/confirm-footer-CLwOC3F0.js.br +0 -0
  139. codex/static_root/assets/confirm-footer-CLwOC3F0.js.gz +0 -0
  140. codex/static_root/assets/{datetime-column-fbihmJHU.4bc0bcaee780.js → datetime-column-DGfoC2Ap.ef9499c6bca8.js} +1 -1
  141. codex/static_root/assets/datetime-column-DGfoC2Ap.ef9499c6bca8.js.br +0 -0
  142. codex/static_root/assets/datetime-column-DGfoC2Ap.ef9499c6bca8.js.gz +0 -0
  143. codex/static_root/assets/{datetime-column-fbihmJHU.js → datetime-column-DGfoC2Ap.js} +1 -1
  144. codex/static_root/assets/datetime-column-DGfoC2Ap.js.br +0 -0
  145. codex/static_root/assets/datetime-column-DGfoC2Ap.js.gz +0 -0
  146. codex/static_root/assets/{filter-omb1juiE.60e35137ed78.js → filter-DcOs3rVI.495426fbd29e.js} +1 -1
  147. codex/static_root/assets/filter-DcOs3rVI.495426fbd29e.js.br +0 -0
  148. codex/static_root/assets/filter-DcOs3rVI.495426fbd29e.js.gz +0 -0
  149. codex/static_root/assets/{filter-omb1juiE.js → filter-DcOs3rVI.js} +1 -1
  150. codex/static_root/assets/filter-DcOs3rVI.js.br +0 -0
  151. codex/static_root/assets/filter-DcOs3rVI.js.gz +0 -0
  152. codex/static_root/assets/{flag-tab-DMgtpnoL.026603d5e10f.js → flag-tab-D77e2Ors.068789b9d816.js} +1 -1
  153. codex/static_root/assets/flag-tab-D77e2Ors.068789b9d816.js.br +0 -0
  154. codex/static_root/assets/flag-tab-D77e2Ors.068789b9d816.js.gz +0 -0
  155. codex/static_root/assets/{flag-tab-DMgtpnoL.js → flag-tab-D77e2Ors.js} +1 -1
  156. codex/static_root/assets/flag-tab-D77e2Ors.js.br +0 -0
  157. codex/static_root/assets/flag-tab-D77e2Ors.js.gz +0 -0
  158. codex/static_root/assets/{group-tab-ChO_Opmg.310104b14401.js → group-tab-SxqTtCGs.c23d57a22257.js} +1 -1
  159. codex/static_root/assets/group-tab-SxqTtCGs.c23d57a22257.js.br +0 -0
  160. codex/static_root/assets/group-tab-SxqTtCGs.c23d57a22257.js.gz +0 -0
  161. codex/static_root/assets/{group-tab-ChO_Opmg.js → group-tab-SxqTtCGs.js} +1 -1
  162. codex/static_root/assets/group-tab-SxqTtCGs.js.br +0 -0
  163. codex/static_root/assets/group-tab-SxqTtCGs.js.gz +0 -0
  164. codex/static_root/assets/{http-error-C23KyICQ.abef3d8f559f.js → http-error-CKL2ByeE.c0333de973b1.js} +1 -1
  165. codex/static_root/assets/http-error-CKL2ByeE.c0333de973b1.js.br +0 -0
  166. codex/static_root/assets/http-error-CKL2ByeE.c0333de973b1.js.gz +0 -0
  167. codex/static_root/assets/{http-error-C23KyICQ.js → http-error-CKL2ByeE.js} +1 -1
  168. codex/static_root/assets/http-error-CKL2ByeE.js.br +0 -0
  169. codex/static_root/assets/http-error-CKL2ByeE.js.gz +0 -0
  170. codex/static_root/assets/{library-tab-DrLWFLoD.6c7ef6d70145.js → library-tab-jesPyoqi.453abce07ca3.js} +1 -1
  171. codex/static_root/assets/library-tab-jesPyoqi.453abce07ca3.js.br +0 -0
  172. codex/static_root/assets/library-tab-jesPyoqi.453abce07ca3.js.gz +0 -0
  173. codex/static_root/assets/{library-tab-DrLWFLoD.js → library-tab-jesPyoqi.js} +1 -1
  174. codex/static_root/assets/library-tab-jesPyoqi.js.br +0 -0
  175. codex/static_root/assets/library-tab-jesPyoqi.js.gz +0 -0
  176. codex/static_root/assets/main-BXWucpmC.f8485541954a.js +37 -0
  177. codex/static_root/assets/main-BXWucpmC.f8485541954a.js.br +0 -0
  178. codex/static_root/assets/main-BXWucpmC.f8485541954a.js.gz +0 -0
  179. codex/static_root/assets/main-BXWucpmC.js +37 -0
  180. codex/static_root/assets/main-BXWucpmC.js.br +0 -0
  181. codex/static_root/assets/main-BXWucpmC.js.gz +0 -0
  182. codex/static_root/assets/main-CGRrI5lh.4f849ef1c5db.css.br +0 -0
  183. codex/static_root/assets/main-CGRrI5lh.css.br +0 -0
  184. codex/static_root/assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf.br +0 -0
  185. codex/static_root/assets/materialdesignicons-webfont-B7mPwVP_.ttf.br +0 -0
  186. codex/static_root/assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot.br +0 -0
  187. codex/static_root/assets/materialdesignicons-webfont-CSr8KVlo.eot.br +0 -0
  188. codex/static_root/assets/{metadata-dialog-BtNY_zfF.js → metadata-dialog-Dkadd19B.0c95b145042a.js} +1 -1
  189. codex/static_root/assets/metadata-dialog-Dkadd19B.0c95b145042a.js.br +0 -0
  190. codex/static_root/assets/metadata-dialog-Dkadd19B.0c95b145042a.js.gz +0 -0
  191. codex/static_root/assets/{metadata-dialog-BtNY_zfF.22c1b81402ef.js → metadata-dialog-Dkadd19B.js} +1 -1
  192. codex/static_root/assets/metadata-dialog-Dkadd19B.js.br +0 -0
  193. codex/static_root/assets/metadata-dialog-Dkadd19B.js.gz +0 -0
  194. codex/static_root/assets/{metadata-dialog-BGJasBRy.0553851658b5.css → metadata-dialog-QTkQOBoB.2e6935edafee.css} +1 -1
  195. codex/static_root/assets/metadata-dialog-QTkQOBoB.2e6935edafee.css.br +0 -0
  196. codex/static_root/assets/metadata-dialog-QTkQOBoB.2e6935edafee.css.gz +0 -0
  197. codex/static_root/assets/{metadata-dialog-BGJasBRy.css → metadata-dialog-QTkQOBoB.css} +1 -1
  198. codex/static_root/assets/metadata-dialog-QTkQOBoB.css.br +0 -0
  199. codex/static_root/assets/metadata-dialog-QTkQOBoB.css.gz +0 -0
  200. codex/static_root/assets/{pdf-doc-CIsTX0uM.6ab3c037f453.js → pdf-doc-DwPaMGER.d981a9066397.js} +1 -1
  201. codex/static_root/assets/pdf-doc-DwPaMGER.d981a9066397.js.br +0 -0
  202. codex/static_root/assets/pdf-doc-DwPaMGER.d981a9066397.js.gz +0 -0
  203. codex/static_root/assets/{pdf-doc-CIsTX0uM.js → pdf-doc-DwPaMGER.js} +1 -1
  204. codex/static_root/assets/pdf-doc-DwPaMGER.js.br +0 -0
  205. codex/static_root/assets/pdf-doc-DwPaMGER.js.gz +0 -0
  206. codex/static_root/assets/{reader-CNqxQvt9.js → reader-7CNuDD9b.3593146c30b7.js} +2 -2
  207. codex/static_root/assets/reader-7CNuDD9b.3593146c30b7.js.br +0 -0
  208. codex/static_root/assets/reader-7CNuDD9b.3593146c30b7.js.gz +0 -0
  209. codex/static_root/assets/{reader-CNqxQvt9.8dc98a69b850.js → reader-7CNuDD9b.js} +2 -2
  210. codex/static_root/assets/reader-7CNuDD9b.js.br +0 -0
  211. codex/static_root/assets/reader-7CNuDD9b.js.gz +0 -0
  212. codex/static_root/assets/{stats-tab-CoaUKmOP.bec3968f1626.js → stats-tab-CyQ4G2G_.10e279711e77.js} +1 -1
  213. codex/static_root/assets/stats-tab-CyQ4G2G_.10e279711e77.js.br +0 -0
  214. codex/static_root/assets/stats-tab-CyQ4G2G_.10e279711e77.js.gz +0 -0
  215. codex/static_root/assets/{stats-tab-CoaUKmOP.js → stats-tab-CyQ4G2G_.js} +1 -1
  216. codex/static_root/assets/stats-tab-CyQ4G2G_.js.br +0 -0
  217. codex/static_root/assets/stats-tab-CyQ4G2G_.js.gz +0 -0
  218. codex/static_root/assets/{task-tab-Chc8Xt1f.cfc054dc04c9.js → task-tab-ncnnRysH.13fa947767f5.js} +1 -1
  219. codex/static_root/assets/task-tab-ncnnRysH.13fa947767f5.js.br +0 -0
  220. codex/static_root/assets/task-tab-ncnnRysH.13fa947767f5.js.gz +0 -0
  221. codex/static_root/assets/{task-tab-Chc8Xt1f.js → task-tab-ncnnRysH.js} +1 -1
  222. codex/static_root/assets/task-tab-ncnnRysH.js.br +0 -0
  223. codex/static_root/assets/task-tab-ncnnRysH.js.gz +0 -0
  224. codex/static_root/assets/{user-tab-DH1Xzfcb.99bb9c50aa13.js → user-tab-BWjYndVM.503bf20bdbc0.js} +1 -1
  225. codex/static_root/assets/user-tab-BWjYndVM.503bf20bdbc0.js.br +0 -0
  226. codex/static_root/assets/user-tab-BWjYndVM.503bf20bdbc0.js.gz +0 -0
  227. codex/static_root/assets/{user-tab-DH1Xzfcb.js → user-tab-BWjYndVM.js} +1 -1
  228. codex/static_root/assets/user-tab-BWjYndVM.js.br +0 -0
  229. codex/static_root/assets/user-tab-BWjYndVM.js.gz +0 -0
  230. codex/static_root/{manifest.3751733856af.json → manifest.a5c8e7a036f3.json} +213 -213
  231. codex/static_root/manifest.a5c8e7a036f3.json.br +0 -0
  232. codex/static_root/manifest.a5c8e7a036f3.json.gz +0 -0
  233. codex/static_root/manifest.json +213 -213
  234. codex/static_root/manifest.json.br +0 -0
  235. codex/static_root/manifest.json.gz +0 -0
  236. codex/static_root/rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf.br +0 -0
  237. codex/static_root/rest_framework/fonts/fontawesome-webfont.ttf.br +0 -0
  238. codex/static_root/rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf.br +0 -0
  239. codex/static_root/rest_framework/fonts/glyphicons-halflings-regular.ttf.br +0 -0
  240. codex/static_root/staticfiles.json +1 -1
  241. codex/views/bookmark.py +12 -9
  242. codex/views/browser/base.py +12 -1
  243. codex/views/browser/browser.py +82 -63
  244. codex/views/browser/const.py +3 -0
  245. codex/views/browser/filters/search.py +0 -4
  246. codex/views/error.py +6 -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-1.5.11.dist-info → codex-1.5.13.dist-info}/METADATA +22 -12
  251. {codex-1.5.11.dist-info → codex-1.5.13.dist-info}/RECORD +258 -257
  252. codex/static_root/assets/VCheckbox-DILNxNH0.b3d636874a55.js.br +0 -0
  253. codex/static_root/assets/VCheckbox-DILNxNH0.b3d636874a55.js.gz +0 -0
  254. codex/static_root/assets/VCheckbox-DILNxNH0.js.br +0 -0
  255. codex/static_root/assets/VCheckbox-DILNxNH0.js.gz +0 -0
  256. codex/static_root/assets/VCheckboxBtn-BvTV50hB.743d5045dd85.js.br +0 -0
  257. codex/static_root/assets/VCheckboxBtn-BvTV50hB.743d5045dd85.js.gz +0 -0
  258. codex/static_root/assets/VCheckboxBtn-BvTV50hB.js.br +0 -0
  259. codex/static_root/assets/VCheckboxBtn-BvTV50hB.js.gz +0 -0
  260. codex/static_root/assets/VCombobox-WMKXQHU8.bb480d30500d.js +0 -1
  261. codex/static_root/assets/VCombobox-WMKXQHU8.bb480d30500d.js.br +0 -0
  262. codex/static_root/assets/VCombobox-WMKXQHU8.bb480d30500d.js.gz +0 -0
  263. codex/static_root/assets/VCombobox-WMKXQHU8.js +0 -1
  264. codex/static_root/assets/VCombobox-WMKXQHU8.js.br +0 -0
  265. codex/static_root/assets/VCombobox-WMKXQHU8.js.gz +0 -0
  266. codex/static_root/assets/VDataTableVirtual-CeSA4IqW.222e223c0c5b.js.br +0 -0
  267. codex/static_root/assets/VDataTableVirtual-CeSA4IqW.222e223c0c5b.js.gz +0 -0
  268. codex/static_root/assets/VDataTableVirtual-CeSA4IqW.js.br +0 -0
  269. codex/static_root/assets/VDataTableVirtual-CeSA4IqW.js.gz +0 -0
  270. codex/static_root/assets/VExpansionPanel-D2KdJaNN.382ab5af5c16.js.br +0 -0
  271. codex/static_root/assets/VExpansionPanel-D2KdJaNN.382ab5af5c16.js.gz +0 -0
  272. codex/static_root/assets/VExpansionPanel-D2KdJaNN.js.br +0 -0
  273. codex/static_root/assets/VExpansionPanel-D2KdJaNN.js.gz +0 -0
  274. codex/static_root/assets/VMain-CLPkUtlY.093dce5ee73d.js.br +0 -0
  275. codex/static_root/assets/VMain-CLPkUtlY.093dce5ee73d.js.gz +0 -0
  276. codex/static_root/assets/VMain-CLPkUtlY.js.br +0 -0
  277. codex/static_root/assets/VMain-CLPkUtlY.js.gz +0 -0
  278. codex/static_root/assets/VRadioGroup-CKhS3Tqq.d335c4ea0c80.js.br +0 -0
  279. codex/static_root/assets/VRadioGroup-CKhS3Tqq.d335c4ea0c80.js.gz +0 -0
  280. codex/static_root/assets/VRadioGroup-CKhS3Tqq.js.br +0 -0
  281. codex/static_root/assets/VRadioGroup-CKhS3Tqq.js.gz +0 -0
  282. codex/static_root/assets/VSelect-CiEBG4Gg.58a09287143f.js +0 -1
  283. codex/static_root/assets/VSelect-CiEBG4Gg.58a09287143f.js.br +0 -0
  284. codex/static_root/assets/VSelect-CiEBG4Gg.58a09287143f.js.gz +0 -0
  285. codex/static_root/assets/VSelect-CiEBG4Gg.js +0 -1
  286. codex/static_root/assets/VSelect-CiEBG4Gg.js.br +0 -0
  287. codex/static_root/assets/VSelect-CiEBG4Gg.js.gz +0 -0
  288. codex/static_root/assets/VSelectionControl-BG0YEK9P.ac59951f8127.js.br +0 -0
  289. codex/static_root/assets/VSelectionControl-BG0YEK9P.ac59951f8127.js.gz +0 -0
  290. codex/static_root/assets/VSelectionControl-BG0YEK9P.js.br +0 -0
  291. codex/static_root/assets/VSelectionControl-BG0YEK9P.js.gz +0 -0
  292. codex/static_root/assets/VSlideGroup-Bm-rZo-p.816bb625906d.js +0 -1
  293. codex/static_root/assets/VSlideGroup-Bm-rZo-p.816bb625906d.js.br +0 -0
  294. codex/static_root/assets/VSlideGroup-Bm-rZo-p.816bb625906d.js.gz +0 -0
  295. codex/static_root/assets/VSlideGroup-Bm-rZo-p.js +0 -1
  296. codex/static_root/assets/VSlideGroup-Bm-rZo-p.js.br +0 -0
  297. codex/static_root/assets/VSlideGroup-Bm-rZo-p.js.gz +0 -0
  298. codex/static_root/assets/VTable-Ca3_Vvv_.6fd02dbba881.js.br +0 -0
  299. codex/static_root/assets/VTable-Ca3_Vvv_.6fd02dbba881.js.gz +0 -0
  300. codex/static_root/assets/VTable-Ca3_Vvv_.js.br +0 -0
  301. codex/static_root/assets/VTable-Ca3_Vvv_.js.gz +0 -0
  302. codex/static_root/assets/VToolbar-BfNszTMd.111f25cdd6d8.css.br +0 -0
  303. codex/static_root/assets/VToolbar-BfNszTMd.css.br +0 -0
  304. codex/static_root/assets/VToolbar-CjnGoYm_.211df7fe7fc4.js.br +0 -0
  305. codex/static_root/assets/VToolbar-CjnGoYm_.211df7fe7fc4.js.gz +0 -0
  306. codex/static_root/assets/VToolbar-CjnGoYm_.js.br +0 -0
  307. codex/static_root/assets/VToolbar-CjnGoYm_.js.gz +0 -0
  308. codex/static_root/assets/VWindowItem-Ren0_px5.3e2b2ffbdefb.js.br +0 -0
  309. codex/static_root/assets/VWindowItem-Ren0_px5.3e2b2ffbdefb.js.gz +0 -0
  310. codex/static_root/assets/VWindowItem-Ren0_px5.js.br +0 -0
  311. codex/static_root/assets/VWindowItem-Ren0_px5.js.gz +0 -0
  312. codex/static_root/assets/admin-B0qtMbYo.071dfdcac325.js.br +0 -0
  313. codex/static_root/assets/admin-B0qtMbYo.071dfdcac325.js.gz +0 -0
  314. codex/static_root/assets/admin-B0qtMbYo.js.br +0 -0
  315. codex/static_root/assets/admin-B0qtMbYo.js.gz +0 -0
  316. codex/static_root/assets/admin-drawer-panel-CYYEXqtK.db547c39eded.js.br +0 -0
  317. codex/static_root/assets/admin-drawer-panel-CYYEXqtK.db547c39eded.js.gz +0 -0
  318. codex/static_root/assets/admin-drawer-panel-CYYEXqtK.js.br +0 -0
  319. codex/static_root/assets/admin-drawer-panel-CYYEXqtK.js.gz +0 -0
  320. codex/static_root/assets/admin-drawer-panel-s72VfDNy.css.br +0 -0
  321. codex/static_root/assets/admin-drawer-panel-s72VfDNy.css.gz +0 -0
  322. codex/static_root/assets/admin-drawer-panel-s72VfDNy.ef6cb7221a44.css.br +0 -0
  323. codex/static_root/assets/admin-drawer-panel-s72VfDNy.ef6cb7221a44.css.gz +0 -0
  324. codex/static_root/assets/browser-2i0QA4La.587673ac534c.css.br +0 -0
  325. codex/static_root/assets/browser-2i0QA4La.587673ac534c.css.gz +0 -0
  326. codex/static_root/assets/browser-2i0QA4La.css.br +0 -0
  327. codex/static_root/assets/browser-2i0QA4La.css.gz +0 -0
  328. codex/static_root/assets/browser-B-4o2klP.469a3f62093a.js +0 -1
  329. codex/static_root/assets/browser-B-4o2klP.469a3f62093a.js.br +0 -0
  330. codex/static_root/assets/browser-B-4o2klP.469a3f62093a.js.gz +0 -0
  331. codex/static_root/assets/browser-B-4o2klP.js +0 -1
  332. codex/static_root/assets/browser-B-4o2klP.js.br +0 -0
  333. codex/static_root/assets/browser-B-4o2klP.js.gz +0 -0
  334. codex/static_root/assets/change-password-dialog-DnkeLvMH.f9ac56354665.js.br +0 -0
  335. codex/static_root/assets/change-password-dialog-DnkeLvMH.f9ac56354665.js.gz +0 -0
  336. codex/static_root/assets/change-password-dialog-DnkeLvMH.js.br +0 -0
  337. codex/static_root/assets/change-password-dialog-DnkeLvMH.js.gz +0 -0
  338. codex/static_root/assets/confirm-dialog-BfSatKsS.7e0e20b08c75.js.br +0 -0
  339. codex/static_root/assets/confirm-dialog-BfSatKsS.7e0e20b08c75.js.gz +0 -0
  340. codex/static_root/assets/confirm-dialog-BfSatKsS.js.br +0 -0
  341. codex/static_root/assets/confirm-dialog-BfSatKsS.js.gz +0 -0
  342. codex/static_root/assets/confirm-footer-lfG418fS.70e914e12343.js.br +0 -0
  343. codex/static_root/assets/confirm-footer-lfG418fS.70e914e12343.js.gz +0 -0
  344. codex/static_root/assets/confirm-footer-lfG418fS.js.br +0 -0
  345. codex/static_root/assets/confirm-footer-lfG418fS.js.gz +0 -0
  346. codex/static_root/assets/datetime-column-fbihmJHU.4bc0bcaee780.js.br +0 -0
  347. codex/static_root/assets/datetime-column-fbihmJHU.4bc0bcaee780.js.gz +0 -0
  348. codex/static_root/assets/datetime-column-fbihmJHU.js.br +0 -0
  349. codex/static_root/assets/datetime-column-fbihmJHU.js.gz +0 -0
  350. codex/static_root/assets/filter-omb1juiE.60e35137ed78.js.br +0 -0
  351. codex/static_root/assets/filter-omb1juiE.60e35137ed78.js.gz +0 -0
  352. codex/static_root/assets/filter-omb1juiE.js.br +0 -0
  353. codex/static_root/assets/filter-omb1juiE.js.gz +0 -0
  354. codex/static_root/assets/flag-tab-DMgtpnoL.026603d5e10f.js.br +0 -0
  355. codex/static_root/assets/flag-tab-DMgtpnoL.026603d5e10f.js.gz +0 -0
  356. codex/static_root/assets/flag-tab-DMgtpnoL.js.br +0 -0
  357. codex/static_root/assets/flag-tab-DMgtpnoL.js.gz +0 -0
  358. codex/static_root/assets/group-tab-ChO_Opmg.310104b14401.js.br +0 -0
  359. codex/static_root/assets/group-tab-ChO_Opmg.310104b14401.js.gz +0 -0
  360. codex/static_root/assets/group-tab-ChO_Opmg.js.br +0 -0
  361. codex/static_root/assets/group-tab-ChO_Opmg.js.gz +0 -0
  362. codex/static_root/assets/http-error-C23KyICQ.abef3d8f559f.js.br +0 -0
  363. codex/static_root/assets/http-error-C23KyICQ.abef3d8f559f.js.gz +0 -0
  364. codex/static_root/assets/http-error-C23KyICQ.js.br +0 -0
  365. codex/static_root/assets/http-error-C23KyICQ.js.gz +0 -0
  366. codex/static_root/assets/library-tab-DrLWFLoD.6c7ef6d70145.js.br +0 -0
  367. codex/static_root/assets/library-tab-DrLWFLoD.6c7ef6d70145.js.gz +0 -0
  368. codex/static_root/assets/library-tab-DrLWFLoD.js.br +0 -0
  369. codex/static_root/assets/library-tab-DrLWFLoD.js.gz +0 -0
  370. codex/static_root/assets/main-BgbJsukE.7cf51b816bdc.js +0 -37
  371. codex/static_root/assets/main-BgbJsukE.7cf51b816bdc.js.br +0 -0
  372. codex/static_root/assets/main-BgbJsukE.7cf51b816bdc.js.gz +0 -0
  373. codex/static_root/assets/main-BgbJsukE.js +0 -37
  374. codex/static_root/assets/main-BgbJsukE.js.br +0 -0
  375. codex/static_root/assets/main-BgbJsukE.js.gz +0 -0
  376. codex/static_root/assets/main-CacfwsKA.4f849ef1c5db.css.br +0 -0
  377. codex/static_root/assets/main-CacfwsKA.css.br +0 -0
  378. codex/static_root/assets/metadata-dialog-BGJasBRy.0553851658b5.css.br +0 -0
  379. codex/static_root/assets/metadata-dialog-BGJasBRy.0553851658b5.css.gz +0 -0
  380. codex/static_root/assets/metadata-dialog-BGJasBRy.css.br +0 -0
  381. codex/static_root/assets/metadata-dialog-BGJasBRy.css.gz +0 -0
  382. codex/static_root/assets/metadata-dialog-BtNY_zfF.22c1b81402ef.js.br +0 -0
  383. codex/static_root/assets/metadata-dialog-BtNY_zfF.22c1b81402ef.js.gz +0 -0
  384. codex/static_root/assets/metadata-dialog-BtNY_zfF.js.br +0 -0
  385. codex/static_root/assets/metadata-dialog-BtNY_zfF.js.gz +0 -0
  386. codex/static_root/assets/pdf-doc-CIsTX0uM.6ab3c037f453.js.br +0 -0
  387. codex/static_root/assets/pdf-doc-CIsTX0uM.6ab3c037f453.js.gz +0 -0
  388. codex/static_root/assets/pdf-doc-CIsTX0uM.js.br +0 -0
  389. codex/static_root/assets/pdf-doc-CIsTX0uM.js.gz +0 -0
  390. codex/static_root/assets/reader-CNqxQvt9.8dc98a69b850.js.br +0 -0
  391. codex/static_root/assets/reader-CNqxQvt9.8dc98a69b850.js.gz +0 -0
  392. codex/static_root/assets/reader-CNqxQvt9.js.br +0 -0
  393. codex/static_root/assets/reader-CNqxQvt9.js.gz +0 -0
  394. codex/static_root/assets/stats-tab-CoaUKmOP.bec3968f1626.js.br +0 -0
  395. codex/static_root/assets/stats-tab-CoaUKmOP.bec3968f1626.js.gz +0 -0
  396. codex/static_root/assets/stats-tab-CoaUKmOP.js.br +0 -0
  397. codex/static_root/assets/stats-tab-CoaUKmOP.js.gz +0 -0
  398. codex/static_root/assets/task-tab-Chc8Xt1f.cfc054dc04c9.js.br +0 -0
  399. codex/static_root/assets/task-tab-Chc8Xt1f.cfc054dc04c9.js.gz +0 -0
  400. codex/static_root/assets/task-tab-Chc8Xt1f.js.br +0 -0
  401. codex/static_root/assets/task-tab-Chc8Xt1f.js.gz +0 -0
  402. codex/static_root/assets/user-tab-DH1Xzfcb.99bb9c50aa13.js.br +0 -0
  403. codex/static_root/assets/user-tab-DH1Xzfcb.99bb9c50aa13.js.gz +0 -0
  404. codex/static_root/assets/user-tab-DH1Xzfcb.js.br +0 -0
  405. codex/static_root/assets/user-tab-DH1Xzfcb.js.gz +0 -0
  406. codex/static_root/manifest.3751733856af.json.br +0 -0
  407. codex/static_root/manifest.3751733856af.json.gz +0 -0
  408. /codex/static_root/assets/{main-CacfwsKA.4f849ef1c5db.css → main-CGRrI5lh.4f849ef1c5db.css} +0 -0
  409. /codex/static_root/assets/{main-CacfwsKA.4f849ef1c5db.css.gz → main-CGRrI5lh.4f849ef1c5db.css.gz} +0 -0
  410. /codex/static_root/assets/{main-CacfwsKA.css → main-CGRrI5lh.css} +0 -0
  411. /codex/static_root/assets/{main-CacfwsKA.css.gz → main-CGRrI5lh.css.gz} +0 -0
  412. {codex-1.5.11.dist-info → codex-1.5.13.dist-info}/LICENSE +0 -0
  413. {codex-1.5.11.dist-info → codex-1.5.13.dist-info}/WHEEL +0 -0
  414. {codex-1.5.11.dist-info → codex-1.5.13.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/VCheckbox-DILNxNH0.js": "assets/VCheckbox-DILNxNH0.b3d636874a55.js", "assets/VExpansionPanel-DsEXVcCv.css": "assets/VExpansionPanel-DsEXVcCv.20b475255e66.css", "assets/flag-tab-DMgtpnoL.js": "assets/flag-tab-DMgtpnoL.026603d5e10f.js", "assets/VSelectionControl-BG0YEK9P.js": "assets/VSelectionControl-BG0YEK9P.ac59951f8127.js", "assets/change-password-dialog-DnkeLvMH.js": "assets/change-password-dialog-DnkeLvMH.f9ac56354665.js", "assets/VWindowItem-Ren0_px5.js": "assets/VWindowItem-Ren0_px5.3e2b2ffbdefb.js", "assets/admin-drawer-panel-CYYEXqtK.js": "assets/admin-drawer-panel-CYYEXqtK.db547c39eded.js", "assets/stats-tab-CoaUKmOP.js": "assets/stats-tab-CoaUKmOP.bec3968f1626.js", "assets/datetime-column-BWWfvyhn.css": "assets/datetime-column-BWWfvyhn.a4a94ba57c73.css", "assets/library-tab-DrLWFLoD.js": "assets/library-tab-DrLWFLoD.6c7ef6d70145.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/VMain-CLPkUtlY.js": "assets/VMain-CLPkUtlY.093dce5ee73d.js", "assets/VTable-DflzroDq.css": "assets/VTable-DflzroDq.160504d82195.css", "assets/main-BgbJsukE.js": "assets/main-BgbJsukE.7cf51b816bdc.js", "assets/VSlideGroup-C3lY91uv.css": "assets/VSlideGroup-C3lY91uv.d268067bd078.css", "assets/VMain-Byt37V4c.css": "assets/VMain-Byt37V4c.a00e206f3cfa.css", "assets/VRadioGroup-CKhS3Tqq.js": "assets/VRadioGroup-CKhS3Tqq.d335c4ea0c80.js", "assets/VCheckboxBtn-BvTV50hB.js": "assets/VCheckboxBtn-BvTV50hB.743d5045dd85.js", "assets/stats-tab-BTZLMBCU.css": "assets/stats-tab-BTZLMBCU.7e0a9547c12e.css", "assets/confirm-footer-VFepxcJs.css": "assets/confirm-footer-VFepxcJs.4150131304bd.css", "assets/confirm-dialog-BfSatKsS.js": "assets/confirm-dialog-BfSatKsS.7e0e20b08c75.js", "assets/pdf-doc-CIsTX0uM.js": "assets/pdf-doc-CIsTX0uM.6ab3c037f453.js", "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/filter-omb1juiE.js": "assets/filter-omb1juiE.60e35137ed78.js", "assets/admin-B0qtMbYo.js": "assets/admin-B0qtMbYo.071dfdcac325.js", "assets/main-CacfwsKA.css": "assets/main-CacfwsKA.4f849ef1c5db.css", "assets/metadata-dialog-BGJasBRy.css": "assets/metadata-dialog-BGJasBRy.0553851658b5.css", "assets/metadata-dialog-BtNY_zfF.js": "assets/metadata-dialog-BtNY_zfF.22c1b81402ef.js", "assets/reader-CNqxQvt9.js": "assets/reader-CNqxQvt9.8dc98a69b850.js", "assets/VDataTableVirtual-CeSA4IqW.js": "assets/VDataTableVirtual-CeSA4IqW.222e223c0c5b.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/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/VTable-Ca3_Vvv_.js": "assets/VTable-Ca3_Vvv_.6fd02dbba881.js", "assets/VCombobox-WMKXQHU8.js": "assets/VCombobox-WMKXQHU8.bb480d30500d.js", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/browser-2i0QA4La.css": "assets/browser-2i0QA4La.587673ac534c.css", "assets/group-tab-ChO_Opmg.js": "assets/group-tab-ChO_Opmg.310104b14401.js", "assets/admin-drawer-panel-s72VfDNy.css": "assets/admin-drawer-panel-s72VfDNy.ef6cb7221a44.css", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/reader-CG6zrLXl.css": "assets/reader-CG6zrLXl.ed017042cd53.css", "assets/datetime-column-fbihmJHU.js": "assets/datetime-column-fbihmJHU.4bc0bcaee780.js", "assets/confirm-footer-lfG418fS.js": "assets/confirm-footer-lfG418fS.70e914e12343.js", "assets/VExpansionPanel-D2KdJaNN.js": "assets/VExpansionPanel-D2KdJaNN.382ab5af5c16.js", "assets/task-tab-DoX9OA37.css": "assets/task-tab-DoX9OA37.9bb355f36dbf.css", "assets/task-tab-Chc8Xt1f.js": "assets/task-tab-Chc8Xt1f.cfc054dc04c9.js", "assets/VDataTableVirtual-Cbj7IGxa.css": "assets/VDataTableVirtual-Cbj7IGxa.24039b2ebc61.css", "assets/browser-B-4o2klP.js": "assets/browser-B-4o2klP.469a3f62093a.js", "assets/index-DUjeNg4x.js": "assets/index-DUjeNg4x.36d50c91b2c1.js", "assets/user-tab-DH1Xzfcb.js": "assets/user-tab-DH1Xzfcb.99bb9c50aa13.js", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/flag-tab-DUXtbpA2.css": "assets/flag-tab-DUXtbpA2.8d52c0d4f9e2.css", "assets/http-error-C25WdjWC.css": "assets/http-error-C25WdjWC.1c7337ad6e1d.css", "assets/VToolbar-BfNszTMd.css": "assets/VToolbar-BfNszTMd.111f25cdd6d8.css", "assets/http-error-C23KyICQ.js": "assets/http-error-C23KyICQ.abef3d8f559f.js", "assets/VSelect-CiEBG4Gg.js": "assets/VSelect-CiEBG4Gg.58a09287143f.js", "assets/VToolbar-CjnGoYm_.js": "assets/VToolbar-CjnGoYm_.211df7fe7fc4.js", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/VSelect-BdGop3V2.css": "assets/VSelect-BdGop3V2.604c1785fbff.css", "assets/VCombobox-BksEr4D_.css": "assets/VCombobox-BksEr4D_.73429bb75f42.css", "assets/VSlideGroup-Bm-rZo-p.js": "assets/VSlideGroup-Bm-rZo-p.816bb625906d.js", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/group-tab-3DXxSetf.css": "assets/group-tab-3DXxSetf.5fc2cfa31669.css", "assets/admin-Df82-Qcj.css": "assets/admin-Df82-Qcj.4c3f0406ee54.css", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.3751733856af.json"}, "version": "1.1", "hash": "772816ffb3e6"}
1
+ {"paths": {"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/docs/js/highlight.pack.js": "rest_framework/docs/js/highlight.pack.479b5f21dcba.js", "rest_framework/docs/js/api.js": "rest_framework/docs/js/api.18a5ba8a1bd8.js", "rest_framework/docs/js/jquery.json-view.min.js": "rest_framework/docs/js/jquery.json-view.min.b7c2d6981377.js", "rest_framework/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/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/css/bootstrap-theme.min.css.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/font-awesome-4.0.3.css": "rest_framework/css/font-awesome-4.0.3.c1e1ea213abf.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.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/img/grid.png": "rest_framework/img/grid.a4b938cf382b.png", "rest_framework/img/glyphicons-halflings.png": "rest_framework/img/glyphicons-halflings.90233c9067e9.png", "rest_framework/img/glyphicons-halflings-white.png": "rest_framework/img/glyphicons-halflings-white.9bbc6e960299.png", "rest_framework/fonts/fontawesome-webfont.svg": "rest_framework/fonts/fontawesome-webfont.83e37a11f9d7.svg", "rest_framework/fonts/glyphicons-halflings-regular.woff": "rest_framework/fonts/glyphicons-halflings-regular.fa2772327f55.woff", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "rest_framework/fonts/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "rest_framework/fonts/glyphicons-halflings-regular.ttf": "rest_framework/fonts/glyphicons-halflings-regular.e18bbf611f2a.ttf", "rest_framework/fonts/fontawesome-webfont.ttf": "rest_framework/fonts/fontawesome-webfont.dcb26c7239d8.ttf", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "img/logo-32.webp": "img/logo-32.dbb98f6dfde4.webp", "img/missing-page.svg": "img/missing-page.71bb10f7c72a.svg", "img/missing-cover.webp": "img/missing-cover.8c8c1d4f6782.webp", "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", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "js/choices-admin.json": "js/choices-admin.bb730e4f26ac.json", "js/choices.json": "js/choices.fedcbd1ec085.json", "assets/VMain-BVEH6Gtw.js": "assets/VMain-BVEH6Gtw.51ef468a0780.js", "assets/VDataTableVirtual-C4UTS-jp.js": "assets/VDataTableVirtual-C4UTS-jp.4b2926f1461f.js", "assets/VSelect-BdGop3V2.css": "assets/VSelect-BdGop3V2.604c1785fbff.css", "assets/browser-CTxEKlCx.css": "assets/browser-CTxEKlCx.211d729d9165.css", "assets/VExpansionPanel-DsEXVcCv.css": "assets/VExpansionPanel-DsEXVcCv.20b475255e66.css", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/admin-drawer-panel-CGj6J0rX.js": "assets/admin-drawer-panel-CGj6J0rX.cc680f2603da.js", "assets/admin-Df82-Qcj.css": "assets/admin-Df82-Qcj.4c3f0406ee54.css", "assets/main-BXWucpmC.js": "assets/main-BXWucpmC.f8485541954a.js", "assets/VCheckboxBtn-C7NngFC8.js": "assets/VCheckboxBtn-C7NngFC8.bd12ea5cdbb2.js", "assets/change-password-dialog-BRmvkNir.css": "assets/change-password-dialog-BRmvkNir.2fead58a78c7.css", "assets/task-tab-ncnnRysH.js": "assets/task-tab-ncnnRysH.13fa947767f5.js", "assets/VTable-DioYRJkC.js": "assets/VTable-DioYRJkC.c8638b2267ed.js", "assets/flag-tab-DUXtbpA2.css": "assets/flag-tab-DUXtbpA2.8d52c0d4f9e2.css", "assets/VSelectionControl-BFq--28I.css": "assets/VSelectionControl-BFq--28I.9687c77461fc.css", "assets/flag-tab-D77e2Ors.js": "assets/flag-tab-D77e2Ors.068789b9d816.js", "assets/http-error-C25WdjWC.css": "assets/http-error-C25WdjWC.1c7337ad6e1d.css", "assets/VCheckbox-Wdu19ZuZ.js": "assets/VCheckbox-Wdu19ZuZ.50f2bf3b5ce5.js", "assets/VRadioGroup-D1WV2cpK.js": "assets/VRadioGroup-D1WV2cpK.83ba2e5689fd.js", "assets/stats-tab-CyQ4G2G_.js": "assets/stats-tab-CyQ4G2G_.10e279711e77.js", "assets/datetime-column-DGfoC2Ap.js": "assets/datetime-column-DGfoC2Ap.ef9499c6bca8.js", "assets/pdf-doc-CMzaSV_K.css": "assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/confirm-footer-CLwOC3F0.js": "assets/confirm-footer-CLwOC3F0.d25d9085b300.js", "assets/stats-tab-BTZLMBCU.css": "assets/stats-tab-BTZLMBCU.7e0a9547c12e.css", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/task-tab-DoX9OA37.css": "assets/task-tab-DoX9OA37.9bb355f36dbf.css", "assets/to-case-kUm8NPpW.js": "assets/to-case-kUm8NPpW.5b3369f511b8.js", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/VSlideGroup-D4n-jSgi.js": "assets/VSlideGroup-D4n-jSgi.686cc428f5fe.js", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/VSelect-BVyqMxyX.js": "assets/VSelect-BVyqMxyX.4a54cd987085.js", "assets/metadata-dialog-QTkQOBoB.css": "assets/metadata-dialog-QTkQOBoB.2e6935edafee.css", "assets/http-error-CKL2ByeE.js": "assets/http-error-CKL2ByeE.c0333de973b1.js", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/change-password-dialog-DDvVSrWH.js": "assets/change-password-dialog-DDvVSrWH.915709700c5f.js", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "assets/group-tab-3DXxSetf.css": "assets/group-tab-3DXxSetf.5fc2cfa31669.css", "assets/VCombobox-DxNlm_ji.js": "assets/VCombobox-DxNlm_ji.11d317a5dbb0.js", "assets/admin-drawer-panel-Bk7z8uTR.css": "assets/admin-drawer-panel-Bk7z8uTR.1531b747003f.css", "assets/reader-7CNuDD9b.js": "assets/reader-7CNuDD9b.3593146c30b7.js", "assets/VToolbar-DqQLg1UA.css": "assets/VToolbar-DqQLg1UA.614d6a4a8a09.css", "assets/VMain-Byt37V4c.css": "assets/VMain-Byt37V4c.a00e206f3cfa.css", "assets/main-CGRrI5lh.css": "assets/main-CGRrI5lh.4f849ef1c5db.css", "assets/VDataTableVirtual-Cbj7IGxa.css": "assets/VDataTableVirtual-Cbj7IGxa.24039b2ebc61.css", "assets/browser-BLCruguN.js": "assets/browser-BLCruguN.ca6300976f82.js", "assets/confirm-dialog-Bf3Sde_o.js": "assets/confirm-dialog-Bf3Sde_o.b437109b579e.js", "assets/VSlideGroup-C3lY91uv.css": "assets/VSlideGroup-C3lY91uv.d268067bd078.css", "assets/index-DUjeNg4x.js": "assets/index-DUjeNg4x.36d50c91b2c1.js", "assets/VWindowItem-D-OB7e0u.js": "assets/VWindowItem-D-OB7e0u.ef9bcb84a01e.js", "assets/group-tab-SxqTtCGs.js": "assets/group-tab-SxqTtCGs.c23d57a22257.js", "assets/VExpansionPanel-1dRZTsNe.js": "assets/VExpansionPanel-1dRZTsNe.6213946f43dd.js", "assets/VTable-DflzroDq.css": "assets/VTable-DflzroDq.160504d82195.css", "assets/library-tab-jesPyoqi.js": "assets/library-tab-jesPyoqi.453abce07ca3.js", "assets/VSelectionControl-CC9aC7ZT.js": "assets/VSelectionControl-CC9aC7ZT.2db18d99af8b.js", "assets/reader-CG6zrLXl.css": "assets/reader-CG6zrLXl.ed017042cd53.css", "assets/admin-BwXMQOyX.js": "assets/admin-BwXMQOyX.8c44117c2b63.js", "assets/VToolbar-DLtG5Ngs.js": "assets/VToolbar-DLtG5Ngs.e32539656f6d.js", "assets/datetime-column-BWWfvyhn.css": "assets/datetime-column-BWWfvyhn.a4a94ba57c73.css", "assets/metadata-dialog-Dkadd19B.js": "assets/metadata-dialog-Dkadd19B.0c95b145042a.js", "assets/library-tab-DZKDtQ5v.css": "assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css", "assets/VCombobox-BksEr4D_.css": "assets/VCombobox-BksEr4D_.73429bb75f42.css", "assets/confirm-footer-VFepxcJs.css": "assets/confirm-footer-VFepxcJs.4150131304bd.css", "assets/pdf-doc-DwPaMGER.js": "assets/pdf-doc-DwPaMGER.d981a9066397.js", "assets/user-tab-BWjYndVM.js": "assets/user-tab-BWjYndVM.503bf20bdbc0.js", "assets/filter-DcOs3rVI.js": "assets/filter-DcOs3rVI.495426fbd29e.js", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.a5c8e7a036f3.json"}, "version": "1.1", "hash": "54ff58bbb924"}
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):
@@ -89,10 +89,21 @@ class BrowserBaseView(
89
89
  parsed_val = json.loads(unquoted_val)
90
90
  if not parsed_val:
91
91
  continue
92
+ parsed_key = key
93
+ elif key in ("q", "query"):
94
+ # parse and strip query param
95
+ # "query" is used by opds v2
96
+ if "q" in result:
97
+ continue
98
+ parsed_val = val.strip()
99
+ if not parsed_val:
100
+ continue
101
+ parsed_key = "q"
92
102
  else:
103
+ parsed_key = key
93
104
  parsed_val = val
94
105
 
95
- result[key] = parsed_val
106
+ result[parsed_key] = parsed_val
96
107
  return underscoreize(result, **api_settings.JSON_UNDERSCOREIZE)
97
108
 
98
109
  def parse_params(self):
@@ -29,11 +29,18 @@ 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
35
36
 
36
37
  LOG = get_logger(__name__)
38
+ _ADMIN_FLAG_VALUE_KEY_MAP = MappingProxyType(
39
+ {
40
+ AdminFlag.FlagChoices.FOLDER_VIEW.value: "folder_view",
41
+ AdminFlag.FlagChoices.IMPORT_METADATA.value: "import_metadata",
42
+ }
43
+ )
37
44
 
38
45
 
39
46
  class BrowserView(BrowserAnnotationsView):
@@ -42,7 +49,6 @@ class BrowserView(BrowserAnnotationsView):
42
49
  permission_classes: ClassVar[list] = [IsAuthenticatedOrEnabledNonUsers] # type: ignore
43
50
  serializer_class = BrowserPageSerializer
44
51
 
45
- MAX_OBJ_PER_PAGE = 100
46
52
  _NAV_GROUPS = "rpisv"
47
53
 
48
54
  _GROUP_INSTANCE_SELECT_RELATED = MappingProxyType(
@@ -132,29 +138,32 @@ class BrowserView(BrowserAnnotationsView):
132
138
  self.model_group = self._get_model_group()
133
139
  self.model = self.GROUP_MODEL_MAP[self.model_group]
134
140
 
141
+ def _get_common_queryset(self, model, search_scores):
142
+ """Create queryset common to group & books."""
143
+ object_filter = self.get_query_filters(model, search_scores, False)
144
+ qs = model.objects.filter(object_filter)
145
+ qs = self._add_annotations(qs, model, search_scores)
146
+ qs = self.add_order_by(qs, model)
147
+ if limit := self.params.get("limit"):
148
+ # limit only is set by some opds views
149
+ qs = qs[:limit]
150
+ return qs
151
+
135
152
  def _get_group_queryset(self, search_scores: dict | None):
136
153
  """Create group queryset."""
137
154
  if not self.model:
138
- reason = "Model not set in browser."
155
+ reason = "Model not set for browser queryset."
139
156
  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:
157
+ if self.model == Comic or search_scores is None:
146
158
  qs = self.model.objects.none()
159
+ else:
160
+ qs = self._get_common_queryset(self.model, search_scores)
147
161
  return qs
148
162
 
149
163
  def _get_book_queryset(self, search_scores: dict | None):
150
164
  """Create book queryset."""
151
165
  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]
166
+ qs = self._get_common_queryset(Comic, search_scores)
158
167
  else:
159
168
  qs = Comic.objects.none()
160
169
  return qs
@@ -193,8 +202,12 @@ class BrowserView(BrowserAnnotationsView):
193
202
  )
194
203
  except self.group_class.DoesNotExist:
195
204
  group = self.kwargs.get("group")
196
- reason = f"{group}={pk} does not exist!"
197
- self._raise_redirect({"group": group, "pk": 0, "page": 1}, reason)
205
+ page = self.kwargs.get("page")
206
+ if group == "r" and pk == 0 and page == 1:
207
+ self.group_instance: BrowserGroupModel | None = None
208
+ else:
209
+ reason = f"{group}={pk} does not exist!"
210
+ self._raise_redirect({"group": group, "pk": 0, "page": 1}, reason)
198
211
  else:
199
212
  self.group_instance: BrowserGroupModel | None = None
200
213
 
@@ -276,11 +289,22 @@ class BrowserView(BrowserAnnotationsView):
276
289
  "group_count": group_count,
277
290
  }
278
291
 
279
- def _page_out_of_bounds(self, page, num_pages):
292
+ def _page_out_of_bounds(self, num_pages):
280
293
  """Redirect page out of bounds."""
281
294
  group = self.kwargs.get("group")
282
- pk = self.kwargs.get("pk", 1)
283
- new_page = num_pages if page > num_pages else 1
295
+ pk = self.kwargs.get("pk", 0)
296
+ page = self.kwargs.get("page", 1)
297
+ if group == "r" and pk == 0 and page == 1:
298
+ # Don't redirect if on the root page.
299
+ return
300
+
301
+ if num_pages:
302
+ new_page = num_pages if page > num_pages else 1
303
+ else:
304
+ # Redirect to root.
305
+ group = "r"
306
+ pk = 0
307
+ new_page = 1
284
308
  route_changes = {"group": group, "pk": pk, "page": new_page}
285
309
  reason = f"{page=} does not exist!"
286
310
  LOG.debug(f"{reason} redirect to page {new_page}.")
@@ -288,8 +312,8 @@ class BrowserView(BrowserAnnotationsView):
288
312
 
289
313
  def _paginate_section(self, model, qs, page):
290
314
  """Paginate a group or Comic section."""
291
- orphans = 5 if self.model_group != "f" else 0
292
- paginator = Paginator(qs, self.MAX_OBJ_PER_PAGE, orphans=orphans)
315
+ orphans = 0 if self.model_group == "f" or self.params.get("q") else 5
316
+ paginator = Paginator(qs, MAX_OBJ_PER_PAGE, orphans=orphans)
293
317
  try:
294
318
  qs = paginator.page(page).object_list
295
319
  except EmptyPage:
@@ -311,46 +335,49 @@ class BrowserView(BrowserAnnotationsView):
311
335
 
312
336
  def _paginate_books(self, book_qs, total_group_count, page_obj_count):
313
337
  """Paginate the book object list based on how many group/folders are showing."""
314
- if page_obj_count >= self.MAX_OBJ_PER_PAGE:
338
+ if page_obj_count >= MAX_OBJ_PER_PAGE:
315
339
  # No books for this page
316
340
  page_book_qs = Comic.objects.none()
317
341
  else:
318
- # There are books after the groups, paginate them
319
- max_books_on_mixed_page = self.MAX_OBJ_PER_PAGE - (
320
- total_group_count % self.MAX_OBJ_PER_PAGE
321
- )
342
+ group_remainder = total_group_count % MAX_OBJ_PER_PAGE
322
343
  if page_obj_count:
323
344
  # There are books after the groups on the same page
324
- # Manually add books to the end without the paginator
325
- page_book_qs = book_qs[:max_books_on_mixed_page]
345
+ # Manually add books without the paginator
346
+ book_limit = MAX_OBJ_PER_PAGE - group_remainder
347
+ page_book_qs = book_qs[:book_limit]
326
348
  else:
327
349
  # There are books after the groups on a new page
328
- page_book_qs = book_qs[max_books_on_mixed_page:]
329
- num_group_and_mixed_pages = ceil(
330
- total_group_count / self.MAX_OBJ_PER_PAGE
350
+ if group_remainder:
351
+ book_offset = MAX_OBJ_PER_PAGE - group_remainder
352
+ page_book_qs = book_qs[book_offset:]
353
+ else:
354
+ page_book_qs = book_qs
355
+
356
+ num_group_and_mixed_pages = ceil(total_group_count / MAX_OBJ_PER_PAGE)
357
+ book_only_page = self.kwargs.get("page", 1) - num_group_and_mixed_pages
358
+ page_book_qs = self._paginate_section(
359
+ Comic, page_book_qs, book_only_page
331
360
  )
332
- page = self.kwargs.get("page", 1) - num_group_and_mixed_pages
333
- page_book_qs = self._paginate_section(Comic, page_book_qs, page)
334
361
  return page_book_qs
335
362
 
336
363
  def _paginate(self, group_qs, book_qs):
337
364
  """Paginate the queryset into a group and book object lists."""
338
365
  page = self.kwargs.get("page", 1)
339
366
  if page < 1:
340
- self._page_out_of_bounds(page, 1)
367
+ self._page_out_of_bounds(1)
341
368
 
342
369
  page_obj_count = 0
343
370
 
344
- total_group_count = group_qs.count()
345
- total_book_count = book_qs.count()
371
+ group_count = group_qs.count()
372
+ book_count = book_qs.count()
346
373
 
347
- num_pages = ceil((total_group_count + total_book_count) / self.MAX_OBJ_PER_PAGE)
374
+ num_pages = ceil((group_count + book_count) / MAX_OBJ_PER_PAGE)
348
375
  if page > num_pages:
349
- self._page_out_of_bounds(page, num_pages)
376
+ self._page_out_of_bounds(num_pages)
350
377
 
351
- page_group_qs = self._paginate_groups(group_qs, total_group_count)
378
+ page_group_qs = self._paginate_groups(group_qs, group_count)
352
379
  page_obj_count += page_group_qs.count()
353
- page_book_qs = self._paginate_books(book_qs, total_group_count, page_obj_count)
380
+ page_book_qs = self._paginate_books(book_qs, group_count, page_obj_count)
354
381
  page_obj_count += page_book_qs.count()
355
382
 
356
383
  return page_group_qs, page_book_qs, num_pages, page_obj_count
@@ -384,19 +411,6 @@ class BrowserView(BrowserAnnotationsView):
384
411
  else:
385
412
  up_route = {}
386
413
 
387
- admin_flag_objs = AdminFlag.objects.only("on").filter(
388
- key__in=(
389
- AdminFlag.FlagChoices.FOLDER_VIEW.value,
390
- AdminFlag.FlagChoices.IMPORT_METADATA.value,
391
- )
392
- )
393
- admin_flags = {}
394
- for flag in admin_flag_objs:
395
- if flag.key == AdminFlag.FlagChoices.FOLDER_VIEW.value:
396
- admin_flags["folder_view"] = flag.on
397
- if flag.key == AdminFlag.FlagChoices.IMPORT_METADATA.value:
398
- admin_flags["import_metadata"] = flag.on
399
-
400
414
  libraries_exist = Library.objects.exists()
401
415
 
402
416
  # runs obj list query twice :/
@@ -421,7 +435,7 @@ class BrowserView(BrowserAnnotationsView):
421
435
  "issue_number_max": issue_number_max,
422
436
  "num_pages": num_pages,
423
437
  "total_count": total_count,
424
- "admin_flags": admin_flags,
438
+ "admin_flags": self.admin_flags,
425
439
  "libraries_exist": libraries_exist,
426
440
  "covers_timestamp": covers_timestamp,
427
441
  }
@@ -544,18 +558,23 @@ class BrowserView(BrowserAnnotationsView):
544
558
  page = self.kwargs.get("page", 1)
545
559
  self.params["route"] = {"group": group, "pk": pk, "page": page}
546
560
 
561
+ def _set_admin_flags(self):
562
+ """Set browser relevant admin flags."""
563
+ admin_pairs = AdminFlag.objects.filter(
564
+ key__in=_ADMIN_FLAG_VALUE_KEY_MAP.keys()
565
+ ).values_list("key", "on")
566
+ admin_flags = {}
567
+ for key, on in admin_pairs:
568
+ export_key = _ADMIN_FLAG_VALUE_KEY_MAP[key]
569
+ admin_flags[export_key] = on
570
+ self.admin_flags = MappingProxyType(admin_flags)
571
+
547
572
  def validate_settings(self):
548
573
  """Validate group and top group settings."""
549
574
  group = self.kwargs.get("group")
550
575
  self.set_order_key()
551
- enable_folder_view = False
552
- if group == self.FOLDER_GROUP or self.order_key == "path":
553
- key = AdminFlag.FlagChoices.FOLDER_VIEW.value
554
- try:
555
- enable_folder_view = AdminFlag.objects.only("on").get(key=key).on
556
- except Exception as err:
557
- title = CHOICES["admin"]["adminFlags"].get(key)
558
- LOG.warning(f"Getting {title} AdminFlag: {err}")
576
+ self._set_admin_flags()
577
+ enable_folder_view = self.admin_flags["folder_view"]
559
578
 
560
579
  if group == self.FOLDER_GROUP:
561
580
  self._validate_folder_settings(enable_folder_view)
@@ -0,0 +1,3 @@
1
+ """Browser constants."""
2
+
3
+ MAX_OBJ_PER_PAGE = 100
@@ -16,10 +16,6 @@ class SearchFilterMixin:
16
16
  """Perform the search and return the scores as a dict."""
17
17
  search_scores = {}
18
18
  text = self.params.get("q", "") # type: ignore
19
- if not text:
20
- # for opds 2
21
- text = self.params.get("query", "") # type: ignore
22
- text = text.strip()
23
19
  if not text:
24
20
  return search_scores
25
21
 
codex/views/error.py CHANGED
@@ -7,15 +7,18 @@ from rest_framework.views import exception_handler
7
7
 
8
8
  from codex.exceptions import SeeOtherRedirectError
9
9
 
10
+ _OPDS_PREFIX = "opds/v"
11
+
10
12
 
11
13
  def codex_exception_handler(exc, context):
12
14
  """Assume OPDS clients want redirects instead of errors."""
13
15
  response = None
14
16
  request = context.get("request")
15
- opds_start = reverse("opds:start")
16
- if request.path.startswith(opds_start):
17
+ if _OPDS_PREFIX in request.path:
18
+ name = "opds:v2:feed" if _OPDS_PREFIX + "2" in request.path else "opds:v1:feed"
19
+ opds_start = reverse(name)
17
20
  if isinstance(exc, SeeOtherRedirectError):
18
- response = exc.get_response("opds:v1:browser")
21
+ response = exc.get_response(opds_start)
19
22
  elif hasattr(exc, "status_code") and exc.status_code in (
20
23
  status.HTTP_400_BAD_REQUEST,
21
24
  status.HTTP_404_NOT_FOUND,
@@ -12,6 +12,7 @@ from codex.serializers.opds.v1 import (
12
12
  OPDS1TemplateSerializer,
13
13
  )
14
14
  from codex.views.browser.browser import BrowserView
15
+ from codex.views.browser.const import MAX_OBJ_PER_PAGE
15
16
  from codex.views.const import FALSY
16
17
  from codex.views.opds.const import BLANK_TITLE, MimeType
17
18
  from codex.views.opds.v1.entry.data import OPDS1EntryData
@@ -107,7 +108,7 @@ class OPDS1FeedView(CodexXMLTemplateView, LinksMixin):
107
108
  """Return opensearch:itemsPerPage."""
108
109
  try:
109
110
  if self.params.get("q"):
110
- return self.MAX_OBJ_PER_PAGE
111
+ return MAX_OBJ_PER_PAGE
111
112
  except Exception:
112
113
  LOG.exception("Getting OPDS v1 items per page")
113
114
 
@@ -11,6 +11,7 @@ from codex.logger.logging import get_logger
11
11
  from codex.models import AdminFlag
12
12
  from codex.serializers.opds.v2 import OPDS2FeedSerializer
13
13
  from codex.views.browser.browser import BrowserView
14
+ from codex.views.browser.const import MAX_OBJ_PER_PAGE
14
15
  from codex.views.const import FALSY
15
16
  from codex.views.opds.const import BLANK_TITLE
16
17
  from codex.views.opds.v2.const import (
@@ -175,14 +176,11 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
175
176
  """Get the browser page and serialize it for this subclass."""
176
177
  group = self.kwargs.get("group")
177
178
  if group in ("f", "a"):
178
- self.acquisition_groups = frozenset()
179
- else:
180
- self.acquisition_groups = frozenset(self.valid_nav_groups[-2:])
181
- if group == "a":
182
179
  pk = self.kwargs["pk"]
183
180
  self.is_opds_2_acquisition = bool(pk)
184
181
  else:
185
- self.is_opds_2_acquisition = group in self.acquisition_groups
182
+ acquisition_groups = frozenset(self.valid_nav_groups[-2:])
183
+ self.is_opds_2_acquisition = group in acquisition_groups
186
184
  self.is_opds_metadata = (
187
185
  self.request.query_params.get("opdsMetadata", "").lower() not in FALSY
188
186
  )
@@ -202,7 +200,7 @@ class OPDS2FeedView(PublicationMixin, TopLinksMixin):
202
200
  "metadata": {
203
201
  "title": title,
204
202
  "number_of_items": number_of_items,
205
- "items_per_page": self.MAX_OBJ_PER_PAGE,
203
+ "items_per_page": MAX_OBJ_PER_PAGE,
206
204
  "current_page": self.kwargs.get("page"),
207
205
  },
208
206
  "links": self.get_links(browser_page["up_route"]),
@@ -144,8 +144,10 @@ class PublicationMixin(LinksMixin):
144
144
 
145
145
  download_mime_type = MimeType.FILE_TYPE_MAP.get(obj.file_type, MimeType.OCTET)
146
146
  self_kwargs = {"group": "c", "pk": obj.pk, "page": 1}
147
- self_href_data = HrefData(self_kwargs, absolute_query_params=True)
148
- self_link_data = LinkData(Rel.SELF, self_href_data, mime_type=MimeType.OPDS_PUB)
147
+
148
+ # This would be for comic streaming which is not supported by OPDS 2 yet?
149
+ # self_href_data = HrefData(self_kwargs, absolute_query_params=True)
150
+ # self_link_data = LinkData(Rel.SELF, self_href_data, mime_type=MimeType.OPDS_PUB)
149
151
 
150
152
  alt_kwargs = self_kwargs
151
153
  alt_href_data = HrefData(
@@ -168,7 +170,8 @@ class PublicationMixin(LinksMixin):
168
170
  mime_type=download_mime_type,
169
171
  )
170
172
  links = [
171
- self.link(self_link_data),
173
+ # Comic streaming link (unsupported)
174
+ # self.link(self_link_data),
172
175
  self.link(alt_link_data),
173
176
  self.link(acq_link_data),
174
177
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: codex
3
- Version: 1.5.11
3
+ Version: 1.5.13
4
4
  Summary: A comic archive web server.
5
5
  Home-page: https://github.com/ajslater/codex
6
6
  License: GPL-3.0-only
@@ -267,15 +267,29 @@ remove users to groups.
267
267
  Codex can make groups for libraries that exclude groups of users or exclude
268
268
  everyone and include only certain groups of users.
269
269
 
270
- ### PDFs
270
+ ### PDF Metadata
271
271
 
272
- Codex only reads PDF metadata from the filename. If you decide to include PDFs
273
- in your comic library, I recommend taking time to rename your files so Codex can
274
- find some metadata. Codex recognizes several file naming schemes. This one has
275
- good results:
272
+ Codex reads PDF metadata from the filename, PDF metadata fields and also many
273
+ formats of common complex comic metadata if they are embedded in the PDF
274
+ `keywords` field.
275
+
276
+ If you decide to include PDFs in your comic library, I recommend taking time to
277
+ rename your files so Codex can find some metadata. Codex recognizes several file
278
+ naming schemes. This one has good results:
276
279
 
277
280
  `{series} v{volume} #{issue} {title} ({year}) {ignored}.pdf`
278
281
 
282
+ Complex comic metadata, such as ComicInfo.xml, can be also be embedded in the
283
+ keywords field by using the [comicbox](https://github.com/ajslater/comicbox)
284
+ command line tool. Codex will read this data because it relies on comicbox
285
+ internally. Not many people use comicbox or embedded metadata in PDFs in this
286
+ fashion, so you likely won't find it unless you've added it yourself.
287
+
288
+ ### 🗝️ API with Key Access
289
+
290
+ Codex has a limited number of API endpoints available with API Key Access. The
291
+ API Key is available on the admin/stats tab.
292
+
279
293
  ## <a name="configuration">🎛️ Configuration</a>
280
294
 
281
295
  ### Config Dir
@@ -384,7 +398,7 @@ If you must run Codex in an admin restricted memory environment you might want
384
398
  to temporarily give Codex a lot of memory to run a very large import job and
385
399
  then restrict it for normal operation.
386
400
 
387
- ## <a name="usage">📖 Usage</a>
401
+ ## <a name="use">📖 Use</a>
388
402
 
389
403
  ### 👤 Sessions & Accounts
390
404
 
@@ -395,11 +409,6 @@ To preserve these settings across browsers and after sessions expire, you may
395
409
  register an account with a username and password. You will have to contact your
396
410
  administrator to reset your password if you forget it.
397
411
 
398
- ### 🗝️ API with Key Access
399
-
400
- Codex has a limited number of API endpoints available with API Key Access. The
401
- API Key is available on the admin/stats tab.
402
-
403
412
  ### ᯤ OPDS
404
413
 
405
414
  Codex supports OPDS syndication and OPDS streaming. You may find the OPDS url in
@@ -438,6 +447,7 @@ authentication. In that case the OPDS url will look like:
438
447
  - [OPDS-PSE 1.2](https://github.com/anansi-project/opds-pse/blob/master/v1.2.md)
439
448
  - [OPDS Authentication 1.0](https://drafts.opds.io/authentication-for-opds-1.0.html)
440
449
  - [OpenSearch 1.1](https://github.com/dewitt/opensearch)
450
+ - [OPDS 2.0 (draft)](https://drafts.opds.io/opds-2.0.html)
441
451
 
442
452
  ## <a name="troubleshooting">🩺 Troubleshooting</a>
443
453