codex 1.5.19__py3-none-any.whl → 1.6.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of codex might be problematic. Click here for more details.

Files changed (800) hide show
  1. codex/exceptions.py +14 -5
  2. codex/librarian/covers/create.py +2 -1
  3. codex/librarian/covers/path.py +1 -2
  4. codex/librarian/importer/const.py +34 -13
  5. codex/librarian/importer/create_comics.py +0 -1
  6. codex/librarian/importer/create_fks.py +35 -21
  7. codex/librarian/importer/failed_imports.py +2 -2
  8. codex/librarian/importer/moved.py +12 -10
  9. codex/librarian/importer/query_fks.py +124 -78
  10. codex/librarian/importer/update_comics.py +0 -1
  11. codex/librarian/janitor/cleanup.py +35 -14
  12. codex/migrations/0027_sort_name.py +149 -0
  13. codex/models/admin.py +1 -0
  14. codex/models/base.py +3 -0
  15. codex/models/comic.py +32 -35
  16. codex/models/const.py +18 -0
  17. codex/models/functions.py +12 -0
  18. codex/models/groups.py +40 -8
  19. codex/models/paths.py +5 -4
  20. codex/models/query.py +91 -0
  21. codex/search/backend.py +2 -2
  22. codex/search/backend_search.py +232 -0
  23. codex/search/engine.py +1 -9
  24. codex/search/query.py +44 -0
  25. codex/serializers/admin.py +3 -1
  26. codex/serializers/browser/filters.py +3 -8
  27. codex/serializers/{metadata.py → browser/metadata.py} +1 -1
  28. codex/serializers/browser/mixins.py +32 -0
  29. codex/serializers/browser/page.py +6 -18
  30. codex/serializers/browser/settings.py +1 -0
  31. codex/serializers/choices.py +22 -12
  32. codex/serializers/mixins.py +0 -22
  33. codex/serializers/opds/urls.py +10 -0
  34. codex/serializers/reader.py +7 -13
  35. codex/serializers/route.py +36 -0
  36. codex/settings/settings.py +57 -25
  37. codex/signals/django_signals.py +6 -0
  38. codex/static_root/assets/{VCheckbox-D7WF5wMC.c98c1beb207e.js → VCheckbox-Aq_ImMNC.da711f8b6a15.js} +1 -1
  39. codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.br +0 -0
  40. codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.gz +0 -0
  41. codex/static_root/assets/{VCheckbox-D7WF5wMC.js → VCheckbox-Aq_ImMNC.js} +1 -1
  42. codex/static_root/assets/VCheckbox-Aq_ImMNC.js.br +0 -0
  43. codex/static_root/assets/VCheckbox-Aq_ImMNC.js.gz +0 -0
  44. codex/static_root/assets/{VCheckboxBtn-CzTNt4cV.39d03f0eaf4b.js → VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js} +1 -1
  45. codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.br +0 -0
  46. codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.gz +0 -0
  47. codex/static_root/assets/{VCheckboxBtn-CzTNt4cV.js → VCheckboxBtn-D3izCBa7.js} +1 -1
  48. codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.br +0 -0
  49. codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.gz +0 -0
  50. codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js +1 -0
  51. codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.br +0 -0
  52. codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.gz +0 -0
  53. codex/static_root/assets/VCombobox-DLL1EsSN.js +1 -0
  54. codex/static_root/assets/VCombobox-DLL1EsSN.js.br +0 -0
  55. codex/static_root/assets/VCombobox-DLL1EsSN.js.gz +0 -0
  56. codex/static_root/assets/VDialog-C5Nxd_zP.css +1 -0
  57. codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
  58. codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
  59. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css +1 -0
  60. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
  61. codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
  62. codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js +1 -0
  63. codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.br +0 -0
  64. codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.gz +0 -0
  65. codex/static_root/assets/VDialog-yDKzaGEj.js +1 -0
  66. codex/static_root/assets/VDialog-yDKzaGEj.js.br +0 -0
  67. codex/static_root/assets/VDialog-yDKzaGEj.js.gz +0 -0
  68. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js +1 -0
  69. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.br +0 -0
  70. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.gz +0 -0
  71. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js +1 -0
  72. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.br +0 -0
  73. codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.gz +0 -0
  74. codex/static_root/assets/{VRadioGroup-DGplihzh.3f4a5d26a1cb.js → VRadioGroup-DEezv8bL.4a609e74e507.js} +1 -1
  75. codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.br +0 -0
  76. codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.gz +0 -0
  77. codex/static_root/assets/{VRadioGroup-DGplihzh.js → VRadioGroup-DEezv8bL.js} +1 -1
  78. codex/static_root/assets/VRadioGroup-DEezv8bL.js.br +0 -0
  79. codex/static_root/assets/VRadioGroup-DEezv8bL.js.gz +0 -0
  80. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +1 -0
  81. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  82. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  83. codex/static_root/assets/VSelect-ARDhJiQK.css +1 -0
  84. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  85. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  86. codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js +1 -0
  87. codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.br +0 -0
  88. codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.gz +0 -0
  89. codex/static_root/assets/VSelect-BKXcujPy.js +1 -0
  90. codex/static_root/assets/VSelect-BKXcujPy.js.br +0 -0
  91. codex/static_root/assets/VSelect-BKXcujPy.js.gz +0 -0
  92. codex/static_root/assets/{VSelectionControl-DvHEDoJy.30ca0aef8bd2.js → VSelectionControl-DJAPiluk.402a7ad84a8e.js} +1 -1
  93. codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.br +0 -0
  94. codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.gz +0 -0
  95. codex/static_root/assets/{VSelectionControl-DvHEDoJy.js → VSelectionControl-DJAPiluk.js} +1 -1
  96. codex/static_root/assets/VSelectionControl-DJAPiluk.js.br +0 -0
  97. codex/static_root/assets/VSelectionControl-DJAPiluk.js.gz +0 -0
  98. codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js +1 -0
  99. codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.br +0 -0
  100. codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.gz +0 -0
  101. codex/static_root/assets/VSlideGroup-C-kO93Va.js +1 -0
  102. codex/static_root/assets/VSlideGroup-C-kO93Va.js.br +0 -0
  103. codex/static_root/assets/VSlideGroup-C-kO93Va.js.gz +0 -0
  104. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css +1 -0
  105. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.br +0 -0
  106. codex/static_root/assets/VSlideGroup-Dk_1upHt.2c836070ec75.css.gz +0 -0
  107. codex/static_root/assets/VSlideGroup-Dk_1upHt.css +1 -0
  108. codex/static_root/assets/VSlideGroup-Dk_1upHt.css.br +0 -0
  109. codex/static_root/assets/VSlideGroup-Dk_1upHt.css.gz +0 -0
  110. codex/static_root/assets/{VTable-TEvK4Tmn.c68c176d93db.js → VTable-9vsIQObs.0a135029e924.js} +1 -1
  111. codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.br +0 -0
  112. codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.gz +0 -0
  113. codex/static_root/assets/{VTable-TEvK4Tmn.js → VTable-9vsIQObs.js} +1 -1
  114. codex/static_root/assets/VTable-9vsIQObs.js.br +0 -0
  115. codex/static_root/assets/VTable-9vsIQObs.js.gz +0 -0
  116. codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js +1 -0
  117. codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.br +0 -0
  118. codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.gz +0 -0
  119. codex/static_root/assets/VTextField-DI9HAmWh.js +1 -0
  120. codex/static_root/assets/VTextField-DI9HAmWh.js.br +0 -0
  121. codex/static_root/assets/VTextField-DI9HAmWh.js.gz +0 -0
  122. codex/static_root/assets/{VSlideGroup-CTW-HJl4.css → VTextField-DuIIp-VY.580110ad24dc.css} +1 -1
  123. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
  124. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
  125. codex/static_root/assets/{VSlideGroup-CTW-HJl4.8b75b66602c9.css → VTextField-DuIIp-VY.css} +1 -1
  126. codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
  127. codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
  128. codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js +1 -0
  129. codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.br +0 -0
  130. codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.gz +0 -0
  131. codex/static_root/assets/VWindowItem-DpnYkF3y.js +1 -0
  132. codex/static_root/assets/VWindowItem-DpnYkF3y.js.br +0 -0
  133. codex/static_root/assets/VWindowItem-DpnYkF3y.js.gz +0 -0
  134. codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js +1 -0
  135. codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.br +0 -0
  136. codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.gz +0 -0
  137. codex/static_root/assets/admin-2BTkfSQA.js +1 -0
  138. codex/static_root/assets/admin-2BTkfSQA.js.br +0 -0
  139. codex/static_root/assets/admin-2BTkfSQA.js.gz +0 -0
  140. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css +1 -0
  141. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
  142. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
  143. codex/static_root/assets/admin-BbrOU9Y9.css +1 -0
  144. codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
  145. codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
  146. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +1 -0
  147. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
  148. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
  149. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +1 -0
  150. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
  151. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
  152. codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js +30 -0
  153. codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.br +0 -0
  154. codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.gz +0 -0
  155. codex/static_root/assets/admin-drawer-panel-mtdde7vY.js +30 -0
  156. codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.br +0 -0
  157. codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.gz +0 -0
  158. codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js +1 -0
  159. codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.br +0 -0
  160. codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.gz +0 -0
  161. codex/static_root/assets/browser-BA-AwUn-.js +1 -0
  162. codex/static_root/assets/browser-BA-AwUn-.js.br +0 -0
  163. codex/static_root/assets/browser-BA-AwUn-.js.gz +0 -0
  164. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css +1 -0
  165. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.br +0 -0
  166. codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.gz +0 -0
  167. codex/static_root/assets/browser-CZeVqRNy.css +1 -0
  168. codex/static_root/assets/browser-CZeVqRNy.css.br +0 -0
  169. codex/static_root/assets/browser-CZeVqRNy.css.gz +0 -0
  170. codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js +1 -0
  171. codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.br +0 -0
  172. codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.gz +0 -0
  173. codex/static_root/assets/change-password-dialog-C8YONrId.js +1 -0
  174. codex/static_root/assets/change-password-dialog-C8YONrId.js.br +0 -0
  175. codex/static_root/assets/change-password-dialog-C8YONrId.js.gz +0 -0
  176. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +1 -0
  177. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
  178. codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
  179. codex/static_root/assets/change-password-dialog-HvIvmmIE.css +1 -0
  180. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
  181. codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
  182. codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js +1 -0
  183. codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.br +0 -0
  184. codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.gz +0 -0
  185. codex/static_root/assets/confirm-dialog-Clg-o2G-.js +1 -0
  186. codex/static_root/assets/confirm-dialog-Clg-o2G-.js.br +0 -0
  187. codex/static_root/assets/confirm-dialog-Clg-o2G-.js.gz +0 -0
  188. codex/static_root/assets/{datetime-column-C3t430gN.97e5c5364aef.js → datetime-column-DejrPZxl.9fd36bef6fdd.js} +1 -1
  189. codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.br +0 -0
  190. codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.gz +0 -0
  191. codex/static_root/assets/{datetime-column-C3t430gN.js → datetime-column-DejrPZxl.js} +1 -1
  192. codex/static_root/assets/datetime-column-DejrPZxl.js.br +0 -0
  193. codex/static_root/assets/datetime-column-DejrPZxl.js.gz +0 -0
  194. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +1 -0
  195. codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
  196. codex/static_root/assets/datetime-column-Dg_RxTVM.css +1 -0
  197. codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
  198. codex/static_root/assets/{filter-BQ_FuhIp.ee8b78c4aa55.js → filter-DHo57nTp.4d79a276d806.js} +1 -1
  199. codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.br +0 -0
  200. codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.gz +0 -0
  201. codex/static_root/assets/{filter-BQ_FuhIp.js → filter-DHo57nTp.js} +1 -1
  202. codex/static_root/assets/filter-DHo57nTp.js.br +0 -0
  203. codex/static_root/assets/filter-DHo57nTp.js.gz +0 -0
  204. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +1 -0
  205. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
  206. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
  207. codex/static_root/assets/flag-tab-BacZLlVi.css +1 -0
  208. codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
  209. codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
  210. codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js +1 -0
  211. codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.br +0 -0
  212. codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.gz +0 -0
  213. codex/static_root/assets/flag-tab-CagMLGSE.js +1 -0
  214. codex/static_root/assets/flag-tab-CagMLGSE.js.br +0 -0
  215. codex/static_root/assets/flag-tab-CagMLGSE.js.gz +0 -0
  216. codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js +1 -0
  217. codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.br +0 -0
  218. codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.gz +0 -0
  219. codex/static_root/assets/group-tab-BTAjhv32.js +1 -0
  220. codex/static_root/assets/group-tab-BTAjhv32.js.br +0 -0
  221. codex/static_root/assets/group-tab-BTAjhv32.js.gz +0 -0
  222. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css +1 -0
  223. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.br +0 -0
  224. codex/static_root/assets/group-tab-CBKuS3Iv.7e4746634782.css.gz +0 -0
  225. codex/static_root/assets/group-tab-CBKuS3Iv.css +1 -0
  226. codex/static_root/assets/group-tab-CBKuS3Iv.css.br +0 -0
  227. codex/static_root/assets/group-tab-CBKuS3Iv.css.gz +0 -0
  228. codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js +1 -0
  229. codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.br +0 -0
  230. codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.gz +0 -0
  231. codex/static_root/assets/http-error-DaHEsKbT.js +1 -0
  232. codex/static_root/assets/http-error-DaHEsKbT.js.br +0 -0
  233. codex/static_root/assets/http-error-DaHEsKbT.js.gz +0 -0
  234. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css +1 -0
  235. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.br +0 -0
  236. codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.gz +0 -0
  237. codex/static_root/assets/http-error-_wlDqEcp.css +1 -0
  238. codex/static_root/assets/http-error-_wlDqEcp.css.br +0 -0
  239. codex/static_root/assets/http-error-_wlDqEcp.css.gz +0 -0
  240. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +1 -0
  241. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +2 -0
  242. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
  243. codex/static_root/assets/library-tab-CXbkxNZt.css +1 -0
  244. codex/static_root/assets/library-tab-CXbkxNZt.css.br +2 -0
  245. codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
  246. codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js +1 -0
  247. codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.br +0 -0
  248. codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.gz +0 -0
  249. codex/static_root/assets/library-tab-Cud4639b.js +1 -0
  250. codex/static_root/assets/library-tab-Cud4639b.js.br +0 -0
  251. codex/static_root/assets/library-tab-Cud4639b.js.gz +0 -0
  252. codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js +32 -0
  253. codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.br +0 -0
  254. codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.gz +0 -0
  255. codex/static_root/assets/main-BnzL_Byv.js +32 -0
  256. codex/static_root/assets/main-BnzL_Byv.js.br +0 -0
  257. codex/static_root/assets/main-BnzL_Byv.js.gz +0 -0
  258. codex/static_root/assets/{main-ED57DJUb.b84231dc384d.css → main-sK7HC5h4.6f04ddfc5f1a.css} +1 -1
  259. codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
  260. codex/static_root/assets/{main-ED57DJUb.b84231dc384d.css.gz → main-sK7HC5h4.6f04ddfc5f1a.css.gz} +0 -0
  261. codex/static_root/assets/{main-ED57DJUb.css → main-sK7HC5h4.css} +1 -1
  262. codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
  263. codex/static_root/assets/{main-ED57DJUb.css.gz → main-sK7HC5h4.css.gz} +0 -0
  264. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css +1 -0
  265. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
  266. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
  267. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css +1 -0
  268. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
  269. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
  270. codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js +1 -0
  271. codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.br +0 -0
  272. codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.gz +0 -0
  273. codex/static_root/assets/pagination-toolbar-Covpd-k8.js +1 -0
  274. codex/static_root/assets/pagination-toolbar-Covpd-k8.js.br +0 -0
  275. codex/static_root/assets/pagination-toolbar-Covpd-k8.js.gz +0 -0
  276. codex/static_root/assets/{pdf-doc-x5fJF5N8.67b5456d222e.js → pdf-doc-CD2lnB32.1cef3bf498be.js} +1 -1
  277. codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.br +0 -0
  278. codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.gz +0 -0
  279. codex/static_root/assets/{pdf-doc-x5fJF5N8.js → pdf-doc-CD2lnB32.js} +1 -1
  280. codex/static_root/assets/pdf-doc-CD2lnB32.js.br +0 -0
  281. codex/static_root/assets/pdf-doc-CD2lnB32.js.gz +0 -0
  282. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css +1 -0
  283. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.br +0 -0
  284. codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.gz +0 -0
  285. codex/static_root/assets/reader-Dspy46e8.css +1 -0
  286. codex/static_root/assets/reader-Dspy46e8.css.br +0 -0
  287. codex/static_root/assets/reader-Dspy46e8.css.gz +0 -0
  288. codex/static_root/assets/reader-L85y7IXD.6de58f450421.js +2 -0
  289. codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.br +0 -0
  290. codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.gz +0 -0
  291. codex/static_root/assets/reader-L85y7IXD.js +2 -0
  292. codex/static_root/assets/reader-L85y7IXD.js.br +0 -0
  293. codex/static_root/assets/reader-L85y7IXD.js.gz +0 -0
  294. codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js +1 -0
  295. codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.br +0 -0
  296. codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.gz +0 -0
  297. codex/static_root/assets/relation-chips-BpuqEeqM.js +1 -0
  298. codex/static_root/assets/relation-chips-BpuqEeqM.js.br +0 -0
  299. codex/static_root/assets/relation-chips-BpuqEeqM.js.gz +0 -0
  300. codex/static_root/assets/relation-chips-D6T6FX7G.css +1 -0
  301. codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
  302. codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
  303. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css +1 -0
  304. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
  305. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
  306. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css +1 -0
  307. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.br +0 -0
  308. codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.gz +0 -0
  309. codex/static_root/assets/settings-drawer-BgUazs7O.css +1 -0
  310. codex/static_root/assets/settings-drawer-BgUazs7O.css.br +0 -0
  311. codex/static_root/assets/settings-drawer-BgUazs7O.css.gz +0 -0
  312. codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js +2 -0
  313. codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.br +0 -0
  314. codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.gz +0 -0
  315. codex/static_root/assets/settings-drawer-CLMcxfVd.js +2 -0
  316. codex/static_root/assets/settings-drawer-CLMcxfVd.js.br +0 -0
  317. codex/static_root/assets/settings-drawer-CLMcxfVd.js.gz +0 -0
  318. codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js +1 -0
  319. codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.br +0 -0
  320. codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.gz +0 -0
  321. codex/static_root/assets/stats-tab-CQa3-W05.js +1 -0
  322. codex/static_root/assets/stats-tab-CQa3-W05.js.br +0 -0
  323. codex/static_root/assets/stats-tab-CQa3-W05.js.gz +0 -0
  324. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +1 -0
  325. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +2 -0
  326. codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
  327. codex/static_root/assets/stats-tab-TsyG3wH_.css +1 -0
  328. codex/static_root/assets/stats-tab-TsyG3wH_.css.br +2 -0
  329. codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
  330. codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css +1 -0
  331. codex/static_root/assets/{task-tab-DoX9OA37.9bb355f36dbf.css.br → task-tab-1XiWn9xP.06cb8e1b794d.css.br} +1 -1
  332. codex/static_root/assets/task-tab-1XiWn9xP.06cb8e1b794d.css.gz +0 -0
  333. codex/static_root/assets/task-tab-1XiWn9xP.css +1 -0
  334. codex/static_root/assets/{task-tab-DoX9OA37.css.br → task-tab-1XiWn9xP.css.br} +1 -1
  335. codex/static_root/assets/task-tab-1XiWn9xP.css.gz +0 -0
  336. codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js +1 -0
  337. codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.br +0 -0
  338. codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.gz +0 -0
  339. codex/static_root/assets/task-tab-OPhBm-O2.js +1 -0
  340. codex/static_root/assets/task-tab-OPhBm-O2.js.br +0 -0
  341. codex/static_root/assets/task-tab-OPhBm-O2.js.gz +0 -0
  342. codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js +1 -0
  343. codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.br +0 -0
  344. codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.gz +0 -0
  345. codex/static_root/assets/unauthorized-ClQNhRb5.js +1 -0
  346. codex/static_root/assets/unauthorized-ClQNhRb5.js.br +0 -0
  347. codex/static_root/assets/unauthorized-ClQNhRb5.js.gz +0 -0
  348. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css +1 -0
  349. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.br +0 -0
  350. codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.gz +0 -0
  351. codex/static_root/assets/unauthorized-DWgytUow.css +1 -0
  352. codex/static_root/assets/unauthorized-DWgytUow.css.br +0 -0
  353. codex/static_root/assets/unauthorized-DWgytUow.css.gz +0 -0
  354. codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js +1 -0
  355. codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.br +0 -0
  356. codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.gz +0 -0
  357. codex/static_root/assets/user-tab-BKkxa2WL.js +1 -0
  358. codex/static_root/assets/user-tab-BKkxa2WL.js.br +0 -0
  359. codex/static_root/assets/user-tab-BKkxa2WL.js.gz +0 -0
  360. codex/static_root/img/folder.f28936a3701d.svg +1 -0
  361. codex/static_root/img/folder.f28936a3701d.svg.br +0 -0
  362. codex/static_root/img/folder.f28936a3701d.svg.gz +0 -0
  363. codex/static_root/img/folder.svg +1 -0
  364. codex/static_root/img/folder.svg.br +0 -0
  365. codex/static_root/img/folder.svg.gz +0 -0
  366. codex/static_root/img/imprint.7c2adebb76d6.svg +1 -0
  367. codex/static_root/img/imprint.7c2adebb76d6.svg.br +0 -0
  368. codex/static_root/img/imprint.7c2adebb76d6.svg.gz +0 -0
  369. codex/static_root/img/imprint.svg +1 -0
  370. codex/static_root/img/imprint.svg.br +0 -0
  371. codex/static_root/img/imprint.svg.gz +0 -0
  372. codex/static_root/img/logo-32.5688944495bf.webp +0 -0
  373. codex/static_root/img/logo-32.webp +0 -0
  374. codex/static_root/img/logo-maskable-180.c58d966aa64e.webp +0 -0
  375. codex/static_root/img/logo-maskable-180.webp +0 -0
  376. codex/static_root/img/logo-maskable.8f7de7f1c6a8.svg +1 -0
  377. codex/static_root/img/logo-maskable.8f7de7f1c6a8.svg.br +0 -0
  378. codex/static_root/img/logo-maskable.8f7de7f1c6a8.svg.gz +0 -0
  379. codex/static_root/img/logo-maskable.svg +1 -83
  380. codex/static_root/img/logo-maskable.svg.br +0 -0
  381. codex/static_root/img/logo-maskable.svg.gz +0 -0
  382. codex/static_root/img/logo.0f5344a028ca.svg +1 -0
  383. codex/static_root/img/logo.0f5344a028ca.svg.br +0 -0
  384. codex/static_root/img/logo.0f5344a028ca.svg.gz +0 -0
  385. codex/static_root/img/logo.svg +1 -80
  386. codex/static_root/img/logo.svg.br +0 -0
  387. codex/static_root/img/logo.svg.gz +0 -0
  388. codex/static_root/img/missing-cover-165.0743171f2148.webp +0 -0
  389. codex/static_root/img/missing-cover-165.webp +0 -0
  390. codex/static_root/img/missing-cover.ddb038cc2c56.svg +1 -0
  391. codex/static_root/img/missing-cover.ddb038cc2c56.svg.br +0 -0
  392. codex/static_root/img/missing-cover.ddb038cc2c56.svg.gz +0 -0
  393. codex/static_root/img/missing-cover.svg +1 -0
  394. codex/static_root/img/missing-cover.svg.br +0 -0
  395. codex/static_root/img/missing-cover.svg.gz +0 -0
  396. codex/static_root/img/publisher.82d378fcd601.svg +1 -0
  397. codex/static_root/img/publisher.82d378fcd601.svg.br +0 -0
  398. codex/static_root/img/publisher.82d378fcd601.svg.gz +0 -0
  399. codex/static_root/img/publisher.svg +1 -0
  400. codex/static_root/img/publisher.svg.br +0 -0
  401. codex/static_root/img/publisher.svg.gz +0 -0
  402. codex/static_root/img/series.ec8a889b8f95.svg +1 -0
  403. codex/static_root/img/series.ec8a889b8f95.svg.br +0 -0
  404. codex/static_root/img/series.ec8a889b8f95.svg.gz +0 -0
  405. codex/static_root/img/series.svg +1 -0
  406. codex/static_root/img/series.svg.br +0 -0
  407. codex/static_root/img/series.svg.gz +0 -0
  408. codex/static_root/img/story-arc.ca10014355ef.svg +1 -0
  409. codex/static_root/img/story-arc.ca10014355ef.svg.br +1 -0
  410. codex/static_root/img/story-arc.ca10014355ef.svg.gz +0 -0
  411. codex/static_root/img/story-arc.svg +1 -0
  412. codex/static_root/img/story-arc.svg.br +1 -0
  413. codex/static_root/img/story-arc.svg.gz +0 -0
  414. codex/static_root/img/volume.814e4c79461a.svg +1 -0
  415. codex/static_root/img/volume.814e4c79461a.svg.br +0 -0
  416. codex/static_root/img/volume.814e4c79461a.svg.gz +0 -0
  417. codex/static_root/img/volume.svg +1 -0
  418. codex/static_root/img/volume.svg.br +0 -0
  419. codex/static_root/img/volume.svg.gz +0 -0
  420. codex/static_root/js/{choices-admin.bb730e4f26ac.json → choices-admin.248384ece3f6.json} +1 -1
  421. codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
  422. codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
  423. codex/static_root/js/choices-admin.json +1 -1
  424. codex/static_root/js/choices-admin.json.br +0 -0
  425. codex/static_root/js/choices-admin.json.gz +0 -0
  426. codex/static_root/js/choices.b60817ba0a93.json +1 -0
  427. codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
  428. codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
  429. codex/static_root/js/choices.json +1 -1
  430. codex/static_root/js/choices.json.br +0 -0
  431. codex/static_root/js/choices.json.gz +0 -0
  432. codex/static_root/manifest.c5fa10a7dfdc.json +641 -0
  433. codex/static_root/manifest.c5fa10a7dfdc.json.br +0 -0
  434. codex/static_root/manifest.c5fa10a7dfdc.json.gz +0 -0
  435. codex/static_root/manifest.json +354 -320
  436. codex/static_root/manifest.json.br +0 -0
  437. codex/static_root/manifest.json.gz +0 -0
  438. codex/static_root/staticfiles.json +1 -1
  439. codex/templates/headers-script-globals.html +1 -4
  440. codex/urls/api/admin.py +5 -7
  441. codex/urls/api/browser.py +5 -5
  442. codex/urls/api/reader.py +3 -3
  443. codex/urls/api/v3.py +2 -0
  444. codex/urls/app.py +8 -3
  445. codex/urls/converters.py +39 -0
  446. codex/urls/opds/root.py +3 -4
  447. codex/urls/opds/v1.py +1 -1
  448. codex/urls/opds/v2.py +6 -1
  449. codex/urls/root.py +2 -1
  450. codex/views/admin/user.py +0 -5
  451. codex/views/auth.py +10 -12
  452. codex/views/bookmark.py +6 -5
  453. codex/views/browser/base.py +88 -26
  454. codex/views/browser/browser.py +245 -319
  455. codex/views/browser/browser_annotations.py +236 -187
  456. codex/views/browser/browser_breadcrumbs.py +175 -0
  457. codex/views/browser/browser_order_by.py +44 -89
  458. codex/views/browser/choices.py +39 -45
  459. codex/views/browser/filters/annotations.py +34 -0
  460. codex/views/browser/filters/group.py +8 -7
  461. codex/views/browser/filters/search.py +157 -24
  462. codex/views/browser/metadata.py +98 -104
  463. codex/views/const.py +17 -0
  464. codex/views/cover.py +22 -4
  465. codex/views/download.py +3 -12
  466. codex/views/error.py +2 -2
  467. codex/views/frontend.py +4 -1
  468. codex/views/mixins.py +76 -0
  469. codex/views/opds/urls.py +28 -0
  470. codex/views/opds/util.py +7 -7
  471. codex/views/opds/v1/entry/data.py +2 -2
  472. codex/views/opds/v1/entry/entry.py +5 -5
  473. codex/views/opds/v1/entry/links.py +69 -44
  474. codex/views/opds/v1/facets.py +4 -4
  475. codex/views/opds/v1/feed.py +32 -21
  476. codex/views/opds/v1/links.py +14 -14
  477. codex/views/opds/v1/opensearch_v1.py +4 -1
  478. codex/views/opds/v2/feed.py +52 -35
  479. codex/views/opds/v2/publications.py +17 -15
  480. codex/views/opds/v2/top_links.py +2 -2
  481. codex/views/reader/page.py +4 -0
  482. codex/views/reader/reader.py +168 -79
  483. codex/views/session.py +21 -12
  484. {codex-1.5.19.dist-info → codex-1.6.0a0.dist-info}/METADATA +22 -11
  485. {codex-1.5.19.dist-info → codex-1.6.0a0.dist-info}/RECORD +488 -429
  486. codex/db_functions.py +0 -25
  487. codex/img/missing-cover.webp +0 -0
  488. codex/static_root/assets/VCheckbox-D7WF5wMC.c98c1beb207e.js.br +0 -0
  489. codex/static_root/assets/VCheckbox-D7WF5wMC.c98c1beb207e.js.gz +0 -0
  490. codex/static_root/assets/VCheckbox-D7WF5wMC.js.br +0 -0
  491. codex/static_root/assets/VCheckbox-D7WF5wMC.js.gz +0 -0
  492. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.39d03f0eaf4b.js.br +0 -0
  493. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.39d03f0eaf4b.js.gz +0 -0
  494. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.js.br +0 -0
  495. codex/static_root/assets/VCheckboxBtn-CzTNt4cV.js.gz +0 -0
  496. codex/static_root/assets/VCombobox-BWrSBDP9.52f0c0067437.js +0 -1
  497. codex/static_root/assets/VCombobox-BWrSBDP9.52f0c0067437.js.br +0 -0
  498. codex/static_root/assets/VCombobox-BWrSBDP9.52f0c0067437.js.gz +0 -0
  499. codex/static_root/assets/VCombobox-BWrSBDP9.js +0 -1
  500. codex/static_root/assets/VCombobox-BWrSBDP9.js.br +0 -0
  501. codex/static_root/assets/VCombobox-BWrSBDP9.js.gz +0 -0
  502. codex/static_root/assets/VDataTableVirtual-BMondxto.8b48739e5470.js +0 -1
  503. codex/static_root/assets/VDataTableVirtual-BMondxto.8b48739e5470.js.br +0 -0
  504. codex/static_root/assets/VDataTableVirtual-BMondxto.8b48739e5470.js.gz +0 -0
  505. codex/static_root/assets/VDataTableVirtual-BMondxto.js +0 -1
  506. codex/static_root/assets/VDataTableVirtual-BMondxto.js.br +0 -0
  507. codex/static_root/assets/VDataTableVirtual-BMondxto.js.gz +0 -0
  508. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.a73a7dd5614a.css +0 -1
  509. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.a73a7dd5614a.css.br +0 -0
  510. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.a73a7dd5614a.css.gz +0 -0
  511. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.css +0 -1
  512. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.css.br +0 -0
  513. codex/static_root/assets/VDataTableVirtual-C5zDmmxd.css.gz +0 -0
  514. codex/static_root/assets/VExpansionPanels-CDS9qp1D.9fdf68e2f68f.js +0 -1
  515. codex/static_root/assets/VExpansionPanels-CDS9qp1D.9fdf68e2f68f.js.br +0 -0
  516. codex/static_root/assets/VExpansionPanels-CDS9qp1D.9fdf68e2f68f.js.gz +0 -0
  517. codex/static_root/assets/VExpansionPanels-CDS9qp1D.js +0 -1
  518. codex/static_root/assets/VExpansionPanels-CDS9qp1D.js.br +0 -0
  519. codex/static_root/assets/VExpansionPanels-CDS9qp1D.js.gz +0 -0
  520. codex/static_root/assets/VMain-8vjSGbNt.4a97ed735dc3.js +0 -1
  521. codex/static_root/assets/VMain-8vjSGbNt.4a97ed735dc3.js.br +0 -0
  522. codex/static_root/assets/VMain-8vjSGbNt.4a97ed735dc3.js.gz +0 -0
  523. codex/static_root/assets/VMain-8vjSGbNt.js +0 -1
  524. codex/static_root/assets/VMain-8vjSGbNt.js.br +0 -0
  525. codex/static_root/assets/VMain-8vjSGbNt.js.gz +0 -0
  526. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css +0 -1
  527. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css.br +0 -0
  528. codex/static_root/assets/VMain-Byt37V4c.a00e206f3cfa.css.gz +0 -0
  529. codex/static_root/assets/VMain-Byt37V4c.css +0 -1
  530. codex/static_root/assets/VMain-Byt37V4c.css.br +0 -0
  531. codex/static_root/assets/VMain-Byt37V4c.css.gz +0 -0
  532. codex/static_root/assets/VRadioGroup-DGplihzh.3f4a5d26a1cb.js.br +0 -0
  533. codex/static_root/assets/VRadioGroup-DGplihzh.3f4a5d26a1cb.js.gz +0 -0
  534. codex/static_root/assets/VRadioGroup-DGplihzh.js.br +0 -0
  535. codex/static_root/assets/VRadioGroup-DGplihzh.js.gz +0 -0
  536. codex/static_root/assets/VSelect-DfV5vhqb.d24dcc61a20f.js +0 -1
  537. codex/static_root/assets/VSelect-DfV5vhqb.d24dcc61a20f.js.br +0 -0
  538. codex/static_root/assets/VSelect-DfV5vhqb.d24dcc61a20f.js.gz +0 -0
  539. codex/static_root/assets/VSelect-DfV5vhqb.js +0 -1
  540. codex/static_root/assets/VSelect-DfV5vhqb.js.br +0 -0
  541. codex/static_root/assets/VSelect-DfV5vhqb.js.gz +0 -0
  542. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css +0 -1
  543. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.br +0 -0
  544. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.gz +0 -0
  545. codex/static_root/assets/VSelect-MGVSeLgr.css +0 -1
  546. codex/static_root/assets/VSelect-MGVSeLgr.css.br +0 -0
  547. codex/static_root/assets/VSelect-MGVSeLgr.css.gz +0 -0
  548. codex/static_root/assets/VSelectionControl-DvHEDoJy.30ca0aef8bd2.js.br +0 -0
  549. codex/static_root/assets/VSelectionControl-DvHEDoJy.30ca0aef8bd2.js.gz +0 -0
  550. codex/static_root/assets/VSelectionControl-DvHEDoJy.js.br +0 -0
  551. codex/static_root/assets/VSelectionControl-DvHEDoJy.js.gz +0 -0
  552. codex/static_root/assets/VSlideGroup-BdgkaRxE.ba78a4e0efc0.js +0 -1
  553. codex/static_root/assets/VSlideGroup-BdgkaRxE.ba78a4e0efc0.js.br +0 -0
  554. codex/static_root/assets/VSlideGroup-BdgkaRxE.ba78a4e0efc0.js.gz +0 -0
  555. codex/static_root/assets/VSlideGroup-BdgkaRxE.js +0 -1
  556. codex/static_root/assets/VSlideGroup-BdgkaRxE.js.br +0 -0
  557. codex/static_root/assets/VSlideGroup-BdgkaRxE.js.gz +0 -0
  558. codex/static_root/assets/VSlideGroup-CTW-HJl4.8b75b66602c9.css.br +0 -0
  559. codex/static_root/assets/VSlideGroup-CTW-HJl4.8b75b66602c9.css.gz +0 -0
  560. codex/static_root/assets/VSlideGroup-CTW-HJl4.css.br +0 -0
  561. codex/static_root/assets/VSlideGroup-CTW-HJl4.css.gz +0 -0
  562. codex/static_root/assets/VTable-TEvK4Tmn.c68c176d93db.js.br +0 -0
  563. codex/static_root/assets/VTable-TEvK4Tmn.c68c176d93db.js.gz +0 -0
  564. codex/static_root/assets/VTable-TEvK4Tmn.js.br +0 -0
  565. codex/static_root/assets/VTable-TEvK4Tmn.js.gz +0 -0
  566. codex/static_root/assets/VToolbar-D7BgLWdG.css +0 -1
  567. codex/static_root/assets/VToolbar-D7BgLWdG.css.br +0 -0
  568. codex/static_root/assets/VToolbar-D7BgLWdG.css.gz +0 -0
  569. codex/static_root/assets/VToolbar-D7BgLWdG.e4d8a79cc655.css +0 -1
  570. codex/static_root/assets/VToolbar-D7BgLWdG.e4d8a79cc655.css.br +0 -0
  571. codex/static_root/assets/VToolbar-D7BgLWdG.e4d8a79cc655.css.gz +0 -0
  572. codex/static_root/assets/VToolbar-DCXw9xBt.576c107a78d3.js +0 -2
  573. codex/static_root/assets/VToolbar-DCXw9xBt.576c107a78d3.js.br +0 -0
  574. codex/static_root/assets/VToolbar-DCXw9xBt.576c107a78d3.js.gz +0 -0
  575. codex/static_root/assets/VToolbar-DCXw9xBt.js +0 -2
  576. codex/static_root/assets/VToolbar-DCXw9xBt.js.br +0 -0
  577. codex/static_root/assets/VToolbar-DCXw9xBt.js.gz +0 -0
  578. codex/static_root/assets/VWindowItem-CCYriq5i.8a6d4a38dabd.js +0 -1
  579. codex/static_root/assets/VWindowItem-CCYriq5i.8a6d4a38dabd.js.br +0 -0
  580. codex/static_root/assets/VWindowItem-CCYriq5i.8a6d4a38dabd.js.gz +0 -0
  581. codex/static_root/assets/VWindowItem-CCYriq5i.js +0 -1
  582. codex/static_root/assets/VWindowItem-CCYriq5i.js.br +0 -0
  583. codex/static_root/assets/VWindowItem-CCYriq5i.js.gz +0 -0
  584. codex/static_root/assets/admin-A2T028Mn.be3fa80456de.js +0 -1
  585. codex/static_root/assets/admin-A2T028Mn.be3fa80456de.js.br +0 -0
  586. codex/static_root/assets/admin-A2T028Mn.be3fa80456de.js.gz +0 -0
  587. codex/static_root/assets/admin-A2T028Mn.js +0 -1
  588. codex/static_root/assets/admin-A2T028Mn.js.br +0 -0
  589. codex/static_root/assets/admin-A2T028Mn.js.gz +0 -0
  590. codex/static_root/assets/admin-C8Kk7_Ni.255594400b96.css +0 -1
  591. codex/static_root/assets/admin-C8Kk7_Ni.255594400b96.css.br +0 -0
  592. codex/static_root/assets/admin-C8Kk7_Ni.255594400b96.css.gz +0 -0
  593. codex/static_root/assets/admin-C8Kk7_Ni.css +0 -1
  594. codex/static_root/assets/admin-C8Kk7_Ni.css.br +0 -0
  595. codex/static_root/assets/admin-C8Kk7_Ni.css.gz +0 -0
  596. codex/static_root/assets/admin-drawer-panel-DffXGW1d.8f7f39ffb127.js +0 -30
  597. codex/static_root/assets/admin-drawer-panel-DffXGW1d.8f7f39ffb127.js.br +0 -0
  598. codex/static_root/assets/admin-drawer-panel-DffXGW1d.8f7f39ffb127.js.gz +0 -0
  599. codex/static_root/assets/admin-drawer-panel-DffXGW1d.js +0 -30
  600. codex/static_root/assets/admin-drawer-panel-DffXGW1d.js.br +0 -0
  601. codex/static_root/assets/admin-drawer-panel-DffXGW1d.js.gz +0 -0
  602. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.af4fa9980c09.css +0 -1
  603. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.af4fa9980c09.css.br +0 -0
  604. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.af4fa9980c09.css.gz +0 -0
  605. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.css +0 -1
  606. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.css.br +0 -0
  607. codex/static_root/assets/admin-drawer-panel-Dv_IY0x2.css.gz +0 -0
  608. codex/static_root/assets/browser-CY9BdW6B.c9c3e5dffcbf.js +0 -1
  609. codex/static_root/assets/browser-CY9BdW6B.c9c3e5dffcbf.js.br +0 -0
  610. codex/static_root/assets/browser-CY9BdW6B.c9c3e5dffcbf.js.gz +0 -0
  611. codex/static_root/assets/browser-CY9BdW6B.js +0 -1
  612. codex/static_root/assets/browser-CY9BdW6B.js.br +0 -0
  613. codex/static_root/assets/browser-CY9BdW6B.js.gz +0 -0
  614. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css +0 -1
  615. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css.br +0 -0
  616. codex/static_root/assets/browser-HtKThxA9.69be059a55f4.css.gz +0 -0
  617. codex/static_root/assets/browser-HtKThxA9.css +0 -1
  618. codex/static_root/assets/browser-HtKThxA9.css.br +0 -0
  619. codex/static_root/assets/browser-HtKThxA9.css.gz +0 -0
  620. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css +0 -1
  621. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css.br +0 -0
  622. codex/static_root/assets/change-password-dialog-BRmvkNir.2fead58a78c7.css.gz +0 -0
  623. codex/static_root/assets/change-password-dialog-BRmvkNir.css +0 -1
  624. codex/static_root/assets/change-password-dialog-BRmvkNir.css.br +0 -0
  625. codex/static_root/assets/change-password-dialog-BRmvkNir.css.gz +0 -0
  626. codex/static_root/assets/change-password-dialog-Dr1CYQ46.afdd502a985d.js +0 -1
  627. codex/static_root/assets/change-password-dialog-Dr1CYQ46.afdd502a985d.js.br +0 -0
  628. codex/static_root/assets/change-password-dialog-Dr1CYQ46.afdd502a985d.js.gz +0 -0
  629. codex/static_root/assets/change-password-dialog-Dr1CYQ46.js +0 -1
  630. codex/static_root/assets/change-password-dialog-Dr1CYQ46.js.br +0 -0
  631. codex/static_root/assets/change-password-dialog-Dr1CYQ46.js.gz +0 -0
  632. codex/static_root/assets/confirm-dialog-BoVvCI5y.8549e2429bdf.js +0 -1
  633. codex/static_root/assets/confirm-dialog-BoVvCI5y.8549e2429bdf.js.br +0 -0
  634. codex/static_root/assets/confirm-dialog-BoVvCI5y.8549e2429bdf.js.gz +0 -0
  635. codex/static_root/assets/confirm-dialog-BoVvCI5y.js +0 -1
  636. codex/static_root/assets/confirm-dialog-BoVvCI5y.js.br +0 -0
  637. codex/static_root/assets/confirm-dialog-BoVvCI5y.js.gz +0 -0
  638. codex/static_root/assets/confirm-footer-BPuy6r_H.3aa74b8e07c1.css +0 -1
  639. codex/static_root/assets/confirm-footer-BPuy6r_H.3aa74b8e07c1.css.br +0 -0
  640. codex/static_root/assets/confirm-footer-BPuy6r_H.3aa74b8e07c1.css.gz +0 -0
  641. codex/static_root/assets/confirm-footer-BPuy6r_H.css +0 -1
  642. codex/static_root/assets/confirm-footer-BPuy6r_H.css.br +0 -0
  643. codex/static_root/assets/confirm-footer-BPuy6r_H.css.gz +0 -0
  644. codex/static_root/assets/confirm-footer-CIu2tkFD.0aa16c0fc333.js +0 -1
  645. codex/static_root/assets/confirm-footer-CIu2tkFD.0aa16c0fc333.js.br +0 -0
  646. codex/static_root/assets/confirm-footer-CIu2tkFD.0aa16c0fc333.js.gz +0 -0
  647. codex/static_root/assets/confirm-footer-CIu2tkFD.js +0 -1
  648. codex/static_root/assets/confirm-footer-CIu2tkFD.js.br +0 -0
  649. codex/static_root/assets/confirm-footer-CIu2tkFD.js.gz +0 -0
  650. codex/static_root/assets/datetime-column-BWWfvyhn.a4a94ba57c73.css +0 -1
  651. codex/static_root/assets/datetime-column-BWWfvyhn.a4a94ba57c73.css.br +0 -0
  652. codex/static_root/assets/datetime-column-BWWfvyhn.css +0 -1
  653. codex/static_root/assets/datetime-column-BWWfvyhn.css.br +0 -0
  654. codex/static_root/assets/datetime-column-C3t430gN.97e5c5364aef.js.br +0 -0
  655. codex/static_root/assets/datetime-column-C3t430gN.97e5c5364aef.js.gz +0 -0
  656. codex/static_root/assets/datetime-column-C3t430gN.js.br +0 -0
  657. codex/static_root/assets/datetime-column-C3t430gN.js.gz +0 -0
  658. codex/static_root/assets/filter-BQ_FuhIp.ee8b78c4aa55.js.br +0 -0
  659. codex/static_root/assets/filter-BQ_FuhIp.ee8b78c4aa55.js.gz +0 -0
  660. codex/static_root/assets/filter-BQ_FuhIp.js.br +0 -0
  661. codex/static_root/assets/filter-BQ_FuhIp.js.gz +0 -0
  662. codex/static_root/assets/flag-tab-D380D1SI.f4bc34623651.js +0 -1
  663. codex/static_root/assets/flag-tab-D380D1SI.f4bc34623651.js.br +0 -0
  664. codex/static_root/assets/flag-tab-D380D1SI.f4bc34623651.js.gz +0 -0
  665. codex/static_root/assets/flag-tab-D380D1SI.js +0 -1
  666. codex/static_root/assets/flag-tab-D380D1SI.js.br +0 -0
  667. codex/static_root/assets/flag-tab-D380D1SI.js.gz +0 -0
  668. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css +0 -1
  669. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css.br +0 -0
  670. codex/static_root/assets/flag-tab-D_HjkAta.203bedf35ce7.css.gz +0 -0
  671. codex/static_root/assets/flag-tab-D_HjkAta.css +0 -1
  672. codex/static_root/assets/flag-tab-D_HjkAta.css.br +0 -0
  673. codex/static_root/assets/flag-tab-D_HjkAta.css.gz +0 -0
  674. codex/static_root/assets/group-tab-CDcOyQyw.9c85a4fb987a.js +0 -1
  675. codex/static_root/assets/group-tab-CDcOyQyw.9c85a4fb987a.js.br +0 -0
  676. codex/static_root/assets/group-tab-CDcOyQyw.9c85a4fb987a.js.gz +0 -0
  677. codex/static_root/assets/group-tab-CDcOyQyw.js +0 -1
  678. codex/static_root/assets/group-tab-CDcOyQyw.js.br +0 -0
  679. codex/static_root/assets/group-tab-CDcOyQyw.js.gz +0 -0
  680. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css +0 -1
  681. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css.br +0 -0
  682. codex/static_root/assets/group-tab-GFacGljN.98df9e04f2a7.css.gz +0 -0
  683. codex/static_root/assets/group-tab-GFacGljN.css +0 -1
  684. codex/static_root/assets/group-tab-GFacGljN.css.br +0 -0
  685. codex/static_root/assets/group-tab-GFacGljN.css.gz +0 -0
  686. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css +0 -1
  687. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css.br +0 -0
  688. codex/static_root/assets/http-error-C25WdjWC.1c7337ad6e1d.css.gz +0 -0
  689. codex/static_root/assets/http-error-C25WdjWC.css +0 -1
  690. codex/static_root/assets/http-error-C25WdjWC.css.br +0 -0
  691. codex/static_root/assets/http-error-C25WdjWC.css.gz +0 -0
  692. codex/static_root/assets/http-error-D4BCbtF2.d3d31021f2ca.js +0 -1
  693. codex/static_root/assets/http-error-D4BCbtF2.d3d31021f2ca.js.br +0 -0
  694. codex/static_root/assets/http-error-D4BCbtF2.d3d31021f2ca.js.gz +0 -0
  695. codex/static_root/assets/http-error-D4BCbtF2.js +0 -1
  696. codex/static_root/assets/http-error-D4BCbtF2.js.br +0 -0
  697. codex/static_root/assets/http-error-D4BCbtF2.js.gz +0 -0
  698. codex/static_root/assets/library-tab-BpWTK9vs.b2a752770db5.js +0 -1
  699. codex/static_root/assets/library-tab-BpWTK9vs.b2a752770db5.js.br +0 -0
  700. codex/static_root/assets/library-tab-BpWTK9vs.b2a752770db5.js.gz +0 -0
  701. codex/static_root/assets/library-tab-BpWTK9vs.js +0 -1
  702. codex/static_root/assets/library-tab-BpWTK9vs.js.br +0 -0
  703. codex/static_root/assets/library-tab-BpWTK9vs.js.gz +0 -0
  704. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css +0 -1
  705. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css.br +0 -3
  706. codex/static_root/assets/library-tab-DZKDtQ5v.c8123e4bc5a4.css.gz +0 -0
  707. codex/static_root/assets/library-tab-DZKDtQ5v.css +0 -1
  708. codex/static_root/assets/library-tab-DZKDtQ5v.css.br +0 -3
  709. codex/static_root/assets/library-tab-DZKDtQ5v.css.gz +0 -0
  710. codex/static_root/assets/main-DB0zDxIA.e71734100695.js +0 -32
  711. codex/static_root/assets/main-DB0zDxIA.e71734100695.js.br +0 -0
  712. codex/static_root/assets/main-DB0zDxIA.e71734100695.js.gz +0 -0
  713. codex/static_root/assets/main-DB0zDxIA.js +0 -32
  714. codex/static_root/assets/main-DB0zDxIA.js.br +0 -0
  715. codex/static_root/assets/main-DB0zDxIA.js.gz +0 -0
  716. codex/static_root/assets/main-ED57DJUb.b84231dc384d.css.br +0 -0
  717. codex/static_root/assets/main-ED57DJUb.css.br +0 -0
  718. codex/static_root/assets/metadata-dialog-BlvHIpPe.aca38025f67b.js +0 -1
  719. codex/static_root/assets/metadata-dialog-BlvHIpPe.aca38025f67b.js.br +0 -0
  720. codex/static_root/assets/metadata-dialog-BlvHIpPe.aca38025f67b.js.gz +0 -0
  721. codex/static_root/assets/metadata-dialog-BlvHIpPe.js +0 -1
  722. codex/static_root/assets/metadata-dialog-BlvHIpPe.js.br +0 -0
  723. codex/static_root/assets/metadata-dialog-BlvHIpPe.js.gz +0 -0
  724. codex/static_root/assets/metadata-dialog-CNrYFyyx.87cbe14f8752.css +0 -1
  725. codex/static_root/assets/metadata-dialog-CNrYFyyx.87cbe14f8752.css.br +0 -0
  726. codex/static_root/assets/metadata-dialog-CNrYFyyx.87cbe14f8752.css.gz +0 -0
  727. codex/static_root/assets/metadata-dialog-CNrYFyyx.css +0 -1
  728. codex/static_root/assets/metadata-dialog-CNrYFyyx.css.br +0 -0
  729. codex/static_root/assets/metadata-dialog-CNrYFyyx.css.gz +0 -0
  730. codex/static_root/assets/pdf-doc-x5fJF5N8.67b5456d222e.js.br +0 -0
  731. codex/static_root/assets/pdf-doc-x5fJF5N8.67b5456d222e.js.gz +0 -0
  732. codex/static_root/assets/pdf-doc-x5fJF5N8.js.br +0 -0
  733. codex/static_root/assets/pdf-doc-x5fJF5N8.js.gz +0 -0
  734. codex/static_root/assets/reader-BDo-P8Ww.46ce70253011.css +0 -1
  735. codex/static_root/assets/reader-BDo-P8Ww.46ce70253011.css.br +0 -0
  736. codex/static_root/assets/reader-BDo-P8Ww.46ce70253011.css.gz +0 -0
  737. codex/static_root/assets/reader-BDo-P8Ww.css +0 -1
  738. codex/static_root/assets/reader-BDo-P8Ww.css.br +0 -0
  739. codex/static_root/assets/reader-BDo-P8Ww.css.gz +0 -0
  740. codex/static_root/assets/reader-CXmsYhy4.3f98149c37d9.js +0 -2
  741. codex/static_root/assets/reader-CXmsYhy4.3f98149c37d9.js.br +0 -0
  742. codex/static_root/assets/reader-CXmsYhy4.3f98149c37d9.js.gz +0 -0
  743. codex/static_root/assets/reader-CXmsYhy4.js +0 -2
  744. codex/static_root/assets/reader-CXmsYhy4.js.br +0 -0
  745. codex/static_root/assets/reader-CXmsYhy4.js.gz +0 -0
  746. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css +0 -1
  747. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css.br +0 -0
  748. codex/static_root/assets/stats-tab-BTZLMBCU.7e0a9547c12e.css.gz +0 -0
  749. codex/static_root/assets/stats-tab-BTZLMBCU.css +0 -1
  750. codex/static_root/assets/stats-tab-BTZLMBCU.css.br +0 -0
  751. codex/static_root/assets/stats-tab-BTZLMBCU.css.gz +0 -0
  752. codex/static_root/assets/stats-tab-xK8fU37K.24f65a4c7479.js +0 -1
  753. codex/static_root/assets/stats-tab-xK8fU37K.24f65a4c7479.js.br +0 -0
  754. codex/static_root/assets/stats-tab-xK8fU37K.24f65a4c7479.js.gz +0 -0
  755. codex/static_root/assets/stats-tab-xK8fU37K.js +0 -1
  756. codex/static_root/assets/stats-tab-xK8fU37K.js.br +0 -0
  757. codex/static_root/assets/stats-tab-xK8fU37K.js.gz +0 -0
  758. codex/static_root/assets/task-tab-D-j9C5kb.f8defdafa5d1.js +0 -1
  759. codex/static_root/assets/task-tab-D-j9C5kb.f8defdafa5d1.js.br +0 -0
  760. codex/static_root/assets/task-tab-D-j9C5kb.f8defdafa5d1.js.gz +0 -0
  761. codex/static_root/assets/task-tab-D-j9C5kb.js +0 -1
  762. codex/static_root/assets/task-tab-D-j9C5kb.js.br +0 -0
  763. codex/static_root/assets/task-tab-D-j9C5kb.js.gz +0 -0
  764. codex/static_root/assets/task-tab-DoX9OA37.9bb355f36dbf.css +0 -1
  765. codex/static_root/assets/task-tab-DoX9OA37.9bb355f36dbf.css.gz +0 -0
  766. codex/static_root/assets/task-tab-DoX9OA37.css +0 -1
  767. codex/static_root/assets/task-tab-DoX9OA37.css.gz +0 -0
  768. codex/static_root/assets/user-tab-DMcrO04A.501813c958bd.js +0 -1
  769. codex/static_root/assets/user-tab-DMcrO04A.501813c958bd.js.br +0 -0
  770. codex/static_root/assets/user-tab-DMcrO04A.501813c958bd.js.gz +0 -0
  771. codex/static_root/assets/user-tab-DMcrO04A.js +0 -1
  772. codex/static_root/assets/user-tab-DMcrO04A.js.br +0 -0
  773. codex/static_root/assets/user-tab-DMcrO04A.js.gz +0 -0
  774. codex/static_root/img/logo-32.dbb98f6dfde4.webp +0 -0
  775. codex/static_root/img/logo-maskable-180.201cbde62d55.webp +0 -0
  776. codex/static_root/img/logo-maskable.d618c2d7943f.svg +0 -83
  777. codex/static_root/img/logo-maskable.d618c2d7943f.svg.br +0 -0
  778. codex/static_root/img/logo-maskable.d618c2d7943f.svg.gz +0 -0
  779. codex/static_root/img/logo.3fb129bfbd92.svg +0 -80
  780. codex/static_root/img/logo.3fb129bfbd92.svg.br +0 -0
  781. codex/static_root/img/logo.3fb129bfbd92.svg.gz +0 -0
  782. codex/static_root/img/missing-cover.8c8c1d4f6782.webp +0 -0
  783. codex/static_root/img/missing-cover.webp +0 -0
  784. codex/static_root/img/missing-page.71bb10f7c72a.svg +0 -13
  785. codex/static_root/img/missing-page.71bb10f7c72a.svg.br +0 -0
  786. codex/static_root/img/missing-page.71bb10f7c72a.svg.gz +0 -0
  787. codex/static_root/img/missing-page.svg +0 -13
  788. codex/static_root/img/missing-page.svg.br +0 -0
  789. codex/static_root/img/missing-page.svg.gz +0 -0
  790. codex/static_root/js/choices-admin.bb730e4f26ac.json.br +0 -0
  791. codex/static_root/js/choices-admin.bb730e4f26ac.json.gz +0 -0
  792. codex/static_root/js/choices.fedcbd1ec085.json +0 -1
  793. codex/static_root/js/choices.fedcbd1ec085.json.br +0 -0
  794. codex/static_root/js/choices.fedcbd1ec085.json.gz +0 -0
  795. codex/static_root/manifest.8e6c35516db7.json +0 -607
  796. codex/static_root/manifest.8e6c35516db7.json.br +0 -0
  797. codex/static_root/manifest.8e6c35516db7.json.gz +0 -0
  798. {codex-1.5.19.dist-info → codex-1.6.0a0.dist-info}/LICENSE +0 -0
  799. {codex-1.5.19.dist-info → codex-1.6.0a0.dist-info}/WHEEL +0 -0
  800. {codex-1.5.19.dist-info → codex-1.6.0a0.dist-info}/entry_points.txt +0 -0
@@ -1,91 +1,137 @@
1
1
  """Base view for metadata annotations."""
2
2
 
3
- from os.path import sep
3
+ from os import sep
4
+ from types import MappingProxyType
4
5
 
5
6
  from django.db.models import (
7
+ Avg,
6
8
  BooleanField,
7
9
  Case,
8
- CharField,
9
- Count,
10
10
  DateTimeField,
11
11
  F,
12
12
  FilteredRelation,
13
13
  Max,
14
14
  Min,
15
- OuterRef,
16
15
  Q,
17
- Subquery,
18
16
  Sum,
19
17
  Value,
20
18
  When,
21
19
  )
22
- from django.db.models.fields import PositiveSmallIntegerField
23
- from django.db.models.functions import Least, Lower, Reverse, Right, StrIndex, Substr
20
+ from django.db.models.fields import CharField, PositiveSmallIntegerField
21
+ from django.db.models.functions import Least, Reverse, Right, StrIndex
24
22
 
25
- from codex.models import Comic
26
- from codex.views.browser.browser_order_by import BrowserOrderByView
23
+ from codex.models import BrowserGroupModel, Comic, Folder, StoryArc, Volume
24
+ from codex.models.functions import JsonGroupArray
25
+ from codex.views.browser.browser_order_by import (
26
+ BrowserOrderByView,
27
+ )
28
+ from codex.views.mixins import SharedAnnotationsMixin
27
29
 
28
30
 
29
- class BrowserAnnotationsView(BrowserOrderByView):
31
+ class BrowserAnnotationsView(BrowserOrderByView, SharedAnnotationsMixin):
30
32
  """Base class for views that need special metadata annotations."""
31
33
 
32
- _ONE_INTEGERFIELD = Value(1, PositiveSmallIntegerField())
33
34
  _NONE_INTEGERFIELD = Value(None, PositiveSmallIntegerField())
34
35
  _NONE_DATETIMEFIELD = Value(None, DateTimeField())
35
- _ARTICLES = frozenset(
36
- ("a", "an", "the") # en # noqa RUF005
37
- + ("un", "unos", "unas", "el", "los", "la", "las") # es
38
- + ("un", "une", "le", "les", "la", "les", "l'") # fr
39
- + ("o", "a", "os") # pt
40
- # pt "as" conflicts with English
41
- + ("der", "dem", "des", "das") # de
42
- # de: "den & die conflict with English
43
- + ("il", "lo", "gli", "la", "le", "l'") # it
44
- # it: "i" conflicts with English
45
- + ("de", "het", "een") # nl
46
- + ("en", "ett") # sw
47
- + ("en", "ei", "et") # no
48
- + ("en", "et") # da
49
- + ("el", "la", "els", "les", "un", "una", "uns", "unes", "na") # ct
36
+ _ORDER_AGGREGATE_FUNCS = MappingProxyType(
37
+ # These are annotated to order_value becauase they're simple relations
38
+ {
39
+ "age_rating": Avg,
40
+ "community_rating": Avg,
41
+ "created_at": Min,
42
+ "critical_rating": Avg,
43
+ "date": Min,
44
+ "page_count": Sum,
45
+ "size": Sum,
46
+ "updated_at": Min,
47
+ }
48
+ )
49
+ _ANNOTATED_ORDER_FIELDS = frozenset(
50
+ # These are annotated with their own functions
51
+ {
52
+ "filename",
53
+ "sort_name",
54
+ "search_score",
55
+ "bookmark_updated_at",
56
+ "story_arc_number",
57
+ }
58
+ )
59
+ _ALTERNATE_GROUP_BY: MappingProxyType[type[BrowserGroupModel], str] = (
60
+ MappingProxyType({Comic: "id", Volume: "name"})
50
61
  )
51
62
 
52
- is_opds_1_acquisition = False
53
-
54
- def _annotate_search_score(self, queryset, search_scores, model):
55
- """Annotate the search score for ordering by search score.
63
+ def __init__(self, *args, **kwargs):
64
+ """Set params for the type checker."""
65
+ super().__init__(*args, **kwargs)
66
+ self.is_opds_1_acquisition = False
67
+ self.comic_sort_names = ()
68
+ self.bm_annotataion_data: dict[BrowserGroupModel, tuple[str, dict]] = {}
69
+
70
+ def get_group_by(self, model=None):
71
+ """Get the group by for the model."""
72
+ if model is None:
73
+ model = self.model
74
+ return self._ALTERNATE_GROUP_BY.get(model, "sort_name") # type: ignore
75
+
76
+ def _alias_sort_names(self, qs, model):
77
+ """Annotate sort_name."""
78
+ if self.order_key != "sort_name" and not (
79
+ model == StoryArc and self.order_key == "story_arc_number"
80
+ ):
81
+ return qs
82
+ pks = self.kwargs.get("pks")
83
+ model_group = self.model_group # type: ignore
84
+ show = MappingProxyType(self.params["show"]) # type: ignore
85
+ qs, self.comic_sort_names = self.alias_sort_names(
86
+ qs, model, pks, model_group, show
87
+ )
88
+ return qs
56
89
 
57
- Choose the maximum matching score for the group.
58
- """
59
- if self.order_key != "search_score":
60
- return queryset
61
- if search_scores:
62
- whens = []
63
- prefix = "" if model == Comic else self.rel_prefix
64
- for pk, score in search_scores.items():
65
- when = {prefix + "pk": pk, "then": score}
66
- whens.append(When(**when))
67
- search_score = Max(Case(*whens, default=0.0))
90
+ def _alias_filename(self, qs, model):
91
+ """Calculate filename from path in the db."""
92
+ if self.order_key != "filename":
93
+ return qs
94
+ if model == Folder:
95
+ filename = F("name")
68
96
  else:
69
- search_score = Value(0.0)
97
+ prefix = "" if model == Comic else self.rel_prefix
98
+ path_rel = prefix + "path"
99
+ filename = self.order_agg_func(
100
+ Right(
101
+ path_rel,
102
+ StrIndex(Reverse(F(path_rel)), Value(sep)) - 1, # type: ignore
103
+ output_field=CharField(),
104
+ )
105
+ )
106
+ return qs.alias(filename=filename)
70
107
 
71
- return queryset.annotate(search_score=search_score)
108
+ def _alias_story_arc_number(self, qs):
109
+ if self.order_key != "story_arc_number":
110
+ return qs
72
111
 
73
- def _annotate_cover_pk(self, qs, model):
74
- """Annotate the query set for the coverpath for the sort."""
75
- # Select comics for the children by an outer ref for annotation
76
- # Order the descendant comics by the sort argumentst
77
- if model == Comic:
78
- cover_pk = F("pk")
112
+ # Get story_arc__pk
113
+ group = self.kwargs["group"]
114
+ pks = self.kwargs["pks"]
115
+ if group == self.STORY_ARC_GROUP and pks:
116
+ story_arc_pks = pks
79
117
  else:
80
- # I wish I could figure out how to get a cover for each group
81
- # without a subquery.
82
- cover_qs = qs.filter(pk=OuterRef("pk"))
83
- cover_qs = self.add_order_by(cover_qs, model, True)
84
- cover_qs = cover_qs.values(self.rel_prefix + "pk")
85
- cover_qs = cover_qs[:1]
86
- cover_pk = Subquery(cover_qs)
118
+ story_arc_pks = self.params.get("filters", {}).get( # type: ignore
119
+ "story_arcs", ()
120
+ )
87
121
 
88
- return qs.annotate(cover_pk=cover_pk)
122
+ # If we have one annotate it.
123
+ if story_arc_pks:
124
+ san_rel = self.rel_prefix + "story_arc_numbers"
125
+ rel = f"{san_rel}"
126
+ condition = Q(**{f"{san_rel}__story_arc__in": story_arc_pks})
127
+ qs = qs.alias(
128
+ selected_story_arc_number=FilteredRelation(rel, condition=condition),
129
+ )
130
+ story_arc_number = self.order_agg_func("selected_story_arc_number__number")
131
+ else:
132
+ story_arc_number = self._NONE_INTEGERFIELD
133
+
134
+ return qs.alias(story_arc_number=story_arc_number)
89
135
 
90
136
  def _annotate_page_count(self, qs, model):
91
137
  """Hoist up total page_count of children."""
@@ -93,126 +139,91 @@ class BrowserAnnotationsView(BrowserOrderByView):
93
139
  if model == Comic:
94
140
  return qs
95
141
 
96
- page_count_sum = Sum(self.rel_prefix + "page_count", distinct=True)
97
- return qs.annotate(page_count=page_count_sum)
98
-
99
- def _annotate_child_count(self, qs, model):
100
- """Annotate Child Count."""
101
- if model == Comic:
102
- child_count_sum = self._ONE_INTEGERFIELD
142
+ rel = self.rel_prefix + "page_count"
143
+ page_count_sum = Sum(rel, distinct=True)
144
+ if self.TARGET == "browser":
145
+ qs = qs.alias(page_count=page_count_sum)
103
146
  else:
104
- child_count_sum = Count(self.rel_prefix + "pk", distinct=True)
105
- qs = qs.annotate(child_count=child_count_sum)
106
- if model != Comic:
107
- # XXX Extra filter for empty groups
108
- qs = qs.filter(child_count__gt=0)
147
+ qs = qs.annotate(page_count=page_count_sum)
109
148
  return qs
110
149
 
111
150
  def _annotate_bookmark_updated_at(self, qs, model):
112
- """Annotate bookmark_updated_at."""
113
- bm_rel = self.get_bm_rel(model)
114
- bm_filter = self._get_my_bookmark_filter(bm_rel)
115
-
116
151
  if self.is_opds_1_acquisition or self.order_key == "bookmark_updated_at":
152
+ bm_rel = self.get_bm_rel(model)
153
+ bm_filter = self._get_my_bookmark_filter(bm_rel)
154
+ self.bm_annotation_data[model] = bm_rel, bm_filter # type: ignore
155
+
117
156
  updated_at_rel = f"{bm_rel}__updated_at"
118
- bookmark_updated_at_aggregate = Min(
157
+ bookmark_updated_at_aggregate = self.order_agg_func(
119
158
  updated_at_rel,
120
159
  default=self._NONE_DATETIMEFIELD,
121
160
  filter=bm_filter,
122
161
  )
123
- qs = qs.annotate(bookmark_updated_at=bookmark_updated_at_aggregate)
124
-
125
- bm_annotation_data = bm_rel, bm_filter
126
- return qs, bm_annotation_data
127
-
128
- def _annotate_sort_name(self, queryset, model):
129
- """Sort groups by name ignoring articles."""
130
- # TODO move this into the database on import.
131
- if self.order_key != "sort_name":
132
- return queryset
133
-
134
- if self.kwargs.get("group") == self.FOLDER_GROUP and model == Comic:
135
- # File View Filename
136
- return queryset.annotate(
137
- sort_name=Right(
138
- "path",
139
- StrIndex(Reverse(F("path")), Value(sep)) - 1, # type: ignore
140
- output_field=CharField(),
141
- )
142
- )
143
-
144
- ##################################################
145
- # Otherwise Remove articles from the browse name #
146
- ##################################################
147
-
148
- # first_space_index
149
- first_field = model.ORDERING[0]
150
- queryset = queryset.annotate(
151
- first_space_index=StrIndex(first_field, Value(" "))
152
- )
153
-
154
- # lowercase_first_word
155
- lowercase_first_word = Lower(
156
- Substr(first_field, 1, length=(F("first_space_index") - 1)) # type: ignore
157
- )
158
- queryset = queryset.annotate(
159
- lowercase_first_word=Case(
160
- When(Q(first_space_index__gt=0), then=lowercase_first_word)
161
- ),
162
- default=Value(""),
163
- )
164
-
165
- # sort_name
166
- return queryset.annotate(
167
- sort_name=Case(
168
- When(
169
- lowercase_first_word__in=self._ARTICLES,
170
- then=Substr(
171
- first_field,
172
- F("first_space_index") + 1, # type: ignore
173
- ),
174
- ),
175
- default=first_field,
176
- )
177
- )
178
-
179
- def _annotate_story_arc_number(self, qs):
180
- if self.order_key != "story_arc_number":
181
- return qs
162
+ if self.TARGET == "opds1":
163
+ qs = qs.annotate(bookmark_updated_at=bookmark_updated_at_aggregate)
164
+ else:
165
+ qs = qs.alias(bookmark_updated_at=bookmark_updated_at_aggregate)
166
+ return qs
182
167
 
183
- # Get story_arc__pk
184
- group = self.kwargs["group"]
185
- pk = self.kwargs["pk"]
186
- if group == self.STORY_ARC_GROUP and pk:
187
- story_arc_pk = pk
188
- elif story_arc_pks := self.params.get("filters", {}).get( # type: ignore
189
- "story_arcs", []
190
- ):
191
- story_arc_pk = story_arc_pks[0]
168
+ def _annotate_order_value(self, qs, model):
169
+ """Annotate a main key for sorting and browser card display."""
170
+ # Determine order func
171
+ if model == Folder and self.order_key == "filename":
172
+ order_value = F("name")
173
+ elif model == Comic or self.order_key in self._ANNOTATED_ORDER_FIELDS:
174
+ # These are annotated in browser_annotaions
175
+ order_value = F(self.order_key)
192
176
  else:
193
- story_arc_pk = None
177
+ agg_func = self._ORDER_AGGREGATE_FUNCS[self.order_key]
178
+ agg_func = self.order_agg_func if agg_func == Min else agg_func
179
+ field = self.rel_prefix + self.order_key
180
+ order_value = agg_func(field)
194
181
 
195
- # If we have one annotate it.
196
- if story_arc_pk:
197
- san_rel = self.rel_prefix + "story_arc_numbers"
198
- rel = f"{san_rel}"
199
- condition = Q(**{f"{san_rel}__story_arc": story_arc_pk})
200
- qs = qs.annotate(
201
- selected_story_arc_number=FilteredRelation(rel, condition=condition),
202
- story_arc_number=F("selected_story_arc_number__number"),
203
- )
182
+ if self.TARGET == "browser":
183
+ qs = qs.annotate(order_value=order_value)
204
184
  else:
205
- qs = qs.annotate(story_arc_number=self._NONE_INTEGERFIELD)
185
+ qs = qs.alias(order_value=order_value)
206
186
  return qs
207
187
 
208
- def _annotate_order_value(self, qs, model):
209
- """Annotate a main key for sorting."""
210
- order_func = self.get_order_value(model)
211
- return qs.annotate(order_value=order_func)
188
+ def annotate_order_aggregates(self, qs, model):
189
+ """Annotate common aggregates between browser and metadata."""
190
+ # ids coming first or before cover_qs works.
191
+ qs = qs.annotate(ids=JsonGroupArray("id", distinct=True))
192
+ qs = self._alias_sort_names(qs, model)
193
+ qs = self._alias_filename(qs, model)
194
+ qs = self._alias_story_arc_number(qs)
195
+ qs = self._annotate_page_count(qs, model)
196
+ qs = self._annotate_bookmark_updated_at(qs, model)
197
+ if model != Comic:
198
+ # comic orders on indexed fields when it can
199
+ qs = self._annotate_order_value(qs, model)
200
+ return qs
212
201
 
213
- def _annotate_bookmarks(self, qs, model, bm_annotation_data):
202
+ def _annotate_cover_pk(self, qs, model):
203
+ """Annotate the query set for the coverpath for the sort."""
204
+ # Select comics for the children by an outer ref for annotation
205
+ # Order the descendant comics by the sort argumentst
206
+ if model == Comic:
207
+ qs = qs.annotate(cover_pk=F("pk"))
208
+ qs = qs.annotate(cover_mtime=F("updated_at"))
209
+ elif self.admin_flags["dynamic_group_covers"]:
210
+ # At this point it's only been filtered.
211
+ qs = qs.annotate(cover_pk=F(self.rel_prefix + "pk"))
212
+ qs = qs.annotate(cover_pks=JsonGroupArray("cover_pk", distinct=True))
213
+ return qs
214
+
215
+ def _annotate_group(self, qs, model):
216
+ """Annotate Group."""
217
+ value = "c" if model == Comic else self.model_group # type: ignore
218
+ return qs.annotate(group=Value(value, CharField(max_length=1)))
219
+
220
+ def _annotate_bookmarks(self, qs, model):
214
221
  """Hoist up bookmark annotations."""
215
- bm_rel, bm_filter = bm_annotation_data
222
+ if model in self.bm_annotation_data: # type: ignore
223
+ bm_rel, bm_filter = self.bm_annotation_data[model] # type: ignore
224
+ else:
225
+ bm_rel = self.get_bm_rel(model)
226
+ bm_filter = self._get_my_bookmark_filter(bm_rel)
216
227
 
217
228
  page_rel = f"{bm_rel}__page"
218
229
  finished_rel = f"{bm_rel}__finished"
@@ -253,46 +264,84 @@ class BrowserAnnotationsView(BrowserOrderByView):
253
264
  output_field=PositiveSmallIntegerField(),
254
265
  distinct=True,
255
266
  )
256
- qs = qs.annotate(finished_count=finished_count)
267
+ qs = qs.alias(finished_count=finished_count)
257
268
  finished_aggregate = Case(
258
- When(
259
- Q(finished_count=F("child_count")) | Q(page=F("page_count")),
260
- then=True,
261
- ),
262
- When(page=0, then=False),
269
+ When(finished_count=F("child_count"), then=True),
270
+ When(finished_count=0, then=False),
263
271
  default=None,
264
272
  output_field=BooleanField(),
265
273
  )
266
274
 
267
- qs = qs.annotate(page=bookmark_page)
268
- return qs.annotate(finished=finished_aggregate)
275
+ if self.is_opds_1_acquisition:
276
+ qs = qs.annotate(page=bookmark_page)
277
+ else:
278
+ qs = qs.alias(page=bookmark_page)
279
+
280
+ if self.TARGET in frozenset({"metadata", "browser"}):
281
+ qs.annotate(finished=finished_aggregate)
282
+ else:
283
+ # Only used for progress
284
+ qs.alias(finished=finished_aggregate)
285
+ return qs
269
286
 
270
- @staticmethod
271
- def _annotate_progress(queryset):
272
- """Compute progress for each member of a queryset."""
287
+ def _annotate_progress(self, qs):
288
+ """Compute progress for each member of a qs."""
289
+ if self.TARGET not in frozenset({"metadata", "browser"}):
290
+ return qs
273
291
  # Requires bookmark and annotation hoisted from bookmarks.
274
292
  # Requires page_count native to comic or aggregated
275
293
  # Least guard is for rare instances when bookmarks are set to
276
294
  # invalid high values
277
295
  then = Least(F("page") * 100.0 / F("page_count"), Value(100.0))
278
296
  progress = Case(When(page_count__gt=0, then=then), default=0.0)
279
- return queryset.annotate(progress=progress)
280
-
281
- @staticmethod
282
- def _annotate_mtime(queryset):
283
- return queryset.annotate(mtime=Max("updated_at"))
297
+ return qs.annotate(progress=progress)
284
298
 
285
- def annotate_common_aggregates(self, qs, model, search_scores):
286
- """Annotate common aggregates between browser and metadata."""
287
- qs = self._annotate_search_score(qs, search_scores, model)
288
- qs = self._annotate_child_count(qs, model)
289
- qs = self._annotate_page_count(qs, model)
290
- qs, bm_annotation_data = self._annotate_bookmark_updated_at(qs, model)
291
- qs = self._annotate_sort_name(qs, model)
292
- qs = self._annotate_story_arc_number(qs)
293
- # cover annotation depends on order_value, in metadata too.
294
- qs = self._annotate_order_value(qs, model)
299
+ def annotate_card_aggregates(self, qs, model):
300
+ """Annotate aggregates that appear the browser card."""
295
301
  qs = self._annotate_cover_pk(qs, model)
296
- qs = self._annotate_bookmarks(qs, model, bm_annotation_data)
297
- qs = self._annotate_mtime(qs)
302
+ if model == Comic:
303
+ # comic adds order_value for cards late
304
+ qs = self._annotate_order_value(qs, model)
305
+ qs = self._annotate_group(qs, model)
306
+ qs = self.annotate_group_names(qs, model)
307
+ qs = self._annotate_bookmarks(qs, model)
298
308
  return self._annotate_progress(qs)
309
+
310
+ def annotate_for_metadata(self, qs, _model):
311
+ """Annotations only for metadata."""
312
+ return qs.annotate(mtime=Max("updated_at"))
313
+
314
+ def _get_cover_pk_query(self, cover_pks):
315
+ """Get Cover Pk queryset for comic queryset."""
316
+ comic_qs = Comic.objects.filter(pk__in=cover_pks)
317
+ comic_qs = self.annotate_search_score(
318
+ comic_qs, Comic, self.cover_search_score_pairs
319
+ )
320
+ comic_qs = self.annotate_order_aggregates(comic_qs, Comic)
321
+ comic_qs = self.add_order_by(comic_qs, Comic) # type: ignore
322
+ comic_qs = comic_qs.group_by("id")
323
+ return comic_qs.only("pk", "updated_at")
324
+
325
+ def re_cover_multi_groups(self, group_qs):
326
+ """Python hack to re-cover groups collapsed with group_by."""
327
+ # This would be better in the main query but OuterRef can't access annotations.
328
+ # So cover_pks aggregates all covers from multi groups like ids does.
329
+ if self.is_model_comic or not self.admin_flags["dynamic_group_covers"]: # type: ignore
330
+ return group_qs
331
+ recovered_group_list = []
332
+ for group in group_qs:
333
+ cover_pks = group.cover_pks
334
+ if len(cover_pks) == 1:
335
+ cover_pk = cover_pks[0]
336
+ cover_updated_at = group.updated_at
337
+ else:
338
+ comic_qs = self._get_cover_pk_query(cover_pks)
339
+ # print(comic_qs.explain())
340
+ # print(comic_qs.query)
341
+ cover = comic_qs[0]
342
+ cover_pk = cover.pk
343
+ cover_updated_at = cover.updated_at
344
+ group.cover_pk = cover_pk
345
+ group.cover_mtime = cover_updated_at
346
+ recovered_group_list.append(group)
347
+ return recovered_group_list
@@ -0,0 +1,175 @@
1
+ """Browser breadcrumbs calculations."""
2
+
3
+ from types import MappingProxyType
4
+ from typing import TYPE_CHECKING
5
+
6
+ from codex.views.browser.browser_annotations import BrowserAnnotationsView
7
+ from codex.views.const import GROUP_NAME_MAP
8
+
9
+ if TYPE_CHECKING:
10
+ from codex.models.paths import Folder
11
+
12
+
13
+ class BrowserBreadcrumbsView(BrowserAnnotationsView):
14
+ """Browser breadcrumbs calculations."""
15
+
16
+ def _is_breadcrumbs_child_folder(self, is_child, group, up_group, up_pks) -> bool:
17
+ """Is this page a child of the folder breadcrumbs."""
18
+ if group != up_group or not self.group_instance: # type: ignore
19
+ return is_child
20
+ parent_folder: Folder | None = self.group_instance.parent_folder # type: ignore
21
+ parent_pk = parent_folder.pk if parent_folder else 0
22
+ return parent_pk in up_pks or (not up_pks and not parent_pk)
23
+
24
+ def _is_breadcrumbs_child_groups(self, is_child, group, up_group, up_pks):
25
+ """Is this page a child of the group breadcrumbs."""
26
+ show = self.params["show"]
27
+ test_parent = False
28
+ parent_group = None
29
+ for show_group, enabled in reversed(show.items()):
30
+ if not test_parent:
31
+ if show_group == group:
32
+ test_parent = True
33
+ continue
34
+ if not enabled:
35
+ continue
36
+ group_name = GROUP_NAME_MAP[show_group]
37
+ parent_group = getattr(self.group_instance, group_name, None) # type: ignore
38
+ is_child = bool(parent_group) and (parent_group.pk in up_pks)
39
+ break
40
+ if up_group == "r" and not parent_group:
41
+ is_child = True
42
+ return is_child
43
+
44
+ def _is_breadcrumbs_child(self, breadcrumbs) -> bool:
45
+ """Is the current route a child of the stored breadcrumbs."""
46
+ is_child = False
47
+ if not breadcrumbs:
48
+ return is_child
49
+ group = self.kwargs["group"]
50
+ pks = self.kwargs["pks"]
51
+ up_route = breadcrumbs[-1]
52
+ up_group = up_route["group"]
53
+ up_pks = up_route["pks"]
54
+ if group == self.STORY_ARC_GROUP:
55
+ is_child = group == up_group and pks
56
+ elif group == self.FOLDER_GROUP:
57
+ is_child = self._is_breadcrumbs_child_folder(
58
+ is_child, group, up_group, up_pks
59
+ )
60
+ else:
61
+ is_child = self._is_breadcrumbs_child_groups(
62
+ is_child, group, up_group, up_pks
63
+ )
64
+
65
+ return is_child
66
+
67
+ def _create_breadcrumbs_story_arc(self):
68
+ """Create the only story_arc parent."""
69
+ return [
70
+ {
71
+ "group": self.STORY_ARC_GROUP,
72
+ "pks": (),
73
+ "page": 1,
74
+ "name": "",
75
+ }
76
+ ]
77
+
78
+ def _create_breadcrumbs_folder(self):
79
+ """Create folder parents."""
80
+ breadcrumbs = []
81
+ folder: Folder | None = self.group_instance # type: ignore
82
+ while folder:
83
+ folder = folder.parent_folder # type: ignore
84
+ pks = (folder.pk,) if folder else ()
85
+ name = folder.name if folder else ""
86
+ parent_route = {
87
+ "group": self.FOLDER_GROUP,
88
+ "pks": pks,
89
+ "page": 1,
90
+ "name": name,
91
+ }
92
+ breadcrumbs = [parent_route, *breadcrumbs]
93
+ return breadcrumbs
94
+
95
+ def _create_breadcrumbs_group(self):
96
+ """Create browse group parents."""
97
+ breadcrumbs = [{"group": self.ROOT_GROUP, "pks": (), "page": 1, "name": ""}]
98
+ show = self.params["show"]
99
+ for show_group, enabled in show.items():
100
+ if not enabled or show_group not in self.valid_nav_groups: # type: ignore
101
+ continue
102
+ attr = GROUP_NAME_MAP[show_group] # + "_id"
103
+ parent_group = getattr(self.group_instance, attr, None) # type: ignore
104
+ if parent_group:
105
+ pks = (parent_group.pk,)
106
+ name = parent_group.name
107
+ parent_group_route = {
108
+ "group": show_group,
109
+ "pks": pks,
110
+ "page": 1,
111
+ "name": name,
112
+ }
113
+ breadcrumbs += [parent_group_route]
114
+ return breadcrumbs
115
+
116
+ def _create_breadcrumbs(self):
117
+ """Create breadcrumbs from scratch."""
118
+ pks = self.kwargs["pks"]
119
+ if not pks:
120
+ return []
121
+ group = self.kwargs["group"]
122
+ if group == self.STORY_ARC_GROUP:
123
+ breadcrumbs = self._create_breadcrumbs_story_arc()
124
+ elif group == self.FOLDER_GROUP:
125
+ breadcrumbs = self._create_breadcrumbs_folder()
126
+ else:
127
+ breadcrumbs = self._create_breadcrumbs_group()
128
+ return breadcrumbs
129
+
130
+ def _is_breadcrumbs_identical(self, breadcrumbs):
131
+ if not breadcrumbs:
132
+ return False
133
+
134
+ up_route = breadcrumbs[-1]
135
+ if not up_route:
136
+ return False
137
+
138
+ group = self.kwargs["group"]
139
+ if up_route["group"] != group:
140
+ return False
141
+
142
+ pks = self.kwargs["pks"]
143
+ if set(up_route["pks"]) != set(pks):
144
+ return False
145
+
146
+ return True
147
+
148
+ def get_parent_breadcrumbs(
149
+ self,
150
+ ) -> tuple[dict[str, str | tuple[int, ...] | int], ...]:
151
+ """Get the breadcrumbs."""
152
+ breadcrumbs: list[dict[str, str | tuple[int, ...] | int]] = list(
153
+ self.get_from_session("breadcrumbs")
154
+ )
155
+
156
+ if not self._is_breadcrumbs_identical(breadcrumbs):
157
+ if not self._is_breadcrumbs_child(breadcrumbs):
158
+ breadcrumbs = self._create_breadcrumbs()
159
+
160
+ # Add current route to crumbs
161
+ group = self.kwargs["group"]
162
+ pks = self.kwargs["pks"]
163
+ name = self.group_instance.name if self.group_instance else "" # type: ignore
164
+ page = self.kwargs["page"]
165
+ route = {"group": group, "pks": pks, "page": page, "name": name}
166
+ breadcrumbs += [route]
167
+
168
+ # Save params
169
+ params = dict(self.params)
170
+ params["breadcrumbs"] = breadcrumbs
171
+ self.params = MappingProxyType(params)
172
+
173
+ parent_breadcrumbs = breadcrumbs[:-1] if breadcrumbs else ()
174
+
175
+ return tuple(parent_breadcrumbs)