codex 1.6.2__py3-none-any.whl → 1.6.4__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 (513) hide show
  1. codex/exceptions.py +42 -16
  2. codex/librarian/importer/importer.py +1 -1
  3. codex/librarian/janitor/cleanup.py +2 -4
  4. codex/librarian/janitor/update.py +10 -9
  5. codex/librarian/search/merge.py +37 -40
  6. codex/librarian/search/remove.py +30 -26
  7. codex/librarian/search/update.py +47 -38
  8. codex/librarian/stats.py +177 -0
  9. codex/librarian/watchdog/events.py +2 -2
  10. codex/serializers/admin/__init__.py +1 -0
  11. codex/serializers/admin/flags.py +15 -0
  12. codex/serializers/admin/groups.py +38 -0
  13. codex/serializers/admin/libraries.py +90 -0
  14. codex/serializers/admin/stats.py +126 -0
  15. codex/serializers/admin/tasks.py +16 -0
  16. codex/serializers/admin/users.py +40 -0
  17. codex/serializers/fields.py +30 -0
  18. codex/serializers/reader.py +8 -3
  19. codex/settings/settings.py +3 -3
  20. codex/static_root/assets/VCheckbox-DtXDauZz.54986ad416a1.js +1 -0
  21. codex/static_root/assets/VCheckbox-DtXDauZz.54986ad416a1.js.br +0 -0
  22. codex/static_root/assets/VCheckbox-DtXDauZz.54986ad416a1.js.gz +0 -0
  23. codex/static_root/assets/VCheckbox-DtXDauZz.js +1 -0
  24. codex/static_root/assets/VCheckbox-DtXDauZz.js.br +0 -0
  25. codex/static_root/assets/VCheckbox-DtXDauZz.js.gz +0 -0
  26. codex/static_root/assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js +1 -0
  27. codex/static_root/assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js.br +0 -0
  28. codex/static_root/assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js.gz +0 -0
  29. codex/static_root/assets/VCheckboxBtn-B-iT1MnG.js +1 -0
  30. codex/static_root/assets/VCheckboxBtn-B-iT1MnG.js.br +0 -0
  31. codex/static_root/assets/VCheckboxBtn-B-iT1MnG.js.gz +0 -0
  32. codex/static_root/assets/{VCombobox-CGyBgdqr.2f85598cc1a4.js → VCombobox-DZmum3X-.330883d59742.js} +1 -1
  33. codex/static_root/assets/VCombobox-DZmum3X-.330883d59742.js.br +0 -0
  34. codex/static_root/assets/VCombobox-DZmum3X-.330883d59742.js.gz +0 -0
  35. codex/static_root/assets/{VCombobox-CGyBgdqr.js → VCombobox-DZmum3X-.js} +1 -1
  36. codex/static_root/assets/VCombobox-DZmum3X-.js.br +0 -0
  37. codex/static_root/assets/VCombobox-DZmum3X-.js.gz +0 -0
  38. codex/static_root/assets/VDialog-DUdNpXLj.6088d518d3af.js +1 -0
  39. codex/static_root/assets/VDialog-DUdNpXLj.6088d518d3af.js.br +0 -0
  40. codex/static_root/assets/VDialog-DUdNpXLj.6088d518d3af.js.gz +0 -0
  41. codex/static_root/assets/VDialog-DUdNpXLj.js +1 -0
  42. codex/static_root/assets/VDialog-DUdNpXLj.js.br +0 -0
  43. codex/static_root/assets/VDialog-DUdNpXLj.js.gz +0 -0
  44. codex/static_root/assets/VExpansionPanels-CurqcyFg.860017abf25d.js +1 -0
  45. codex/static_root/assets/VExpansionPanels-CurqcyFg.860017abf25d.js.br +0 -0
  46. codex/static_root/assets/VExpansionPanels-CurqcyFg.860017abf25d.js.gz +0 -0
  47. codex/static_root/assets/VExpansionPanels-CurqcyFg.js +1 -0
  48. codex/static_root/assets/VExpansionPanels-CurqcyFg.js.br +0 -0
  49. codex/static_root/assets/VExpansionPanels-CurqcyFg.js.gz +0 -0
  50. codex/static_root/assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js +1 -0
  51. codex/static_root/assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js.br +0 -0
  52. codex/static_root/assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js.gz +0 -0
  53. codex/static_root/assets/VRadioGroup-B7n5ReZ-.js +1 -0
  54. codex/static_root/assets/VRadioGroup-B7n5ReZ-.js.br +0 -0
  55. codex/static_root/assets/VRadioGroup-B7n5ReZ-.js.gz +0 -0
  56. codex/static_root/assets/VSelect-FYWbH55D.83e91b93fdd1.js +1 -0
  57. codex/static_root/assets/VSelect-FYWbH55D.83e91b93fdd1.js.br +0 -0
  58. codex/static_root/assets/VSelect-FYWbH55D.83e91b93fdd1.js.gz +0 -0
  59. codex/static_root/assets/VSelect-FYWbH55D.js +1 -0
  60. codex/static_root/assets/VSelect-FYWbH55D.js.br +0 -0
  61. codex/static_root/assets/VSelect-FYWbH55D.js.gz +0 -0
  62. codex/static_root/assets/{VSelectionControl-DXxRmssH.js → VSelectionControl-CUlT3cc_.63f60240eb49.js} +1 -1
  63. codex/static_root/assets/VSelectionControl-CUlT3cc_.63f60240eb49.js.br +0 -0
  64. codex/static_root/assets/VSelectionControl-CUlT3cc_.63f60240eb49.js.gz +0 -0
  65. codex/static_root/assets/{VSelectionControl-DXxRmssH.252890f79cc6.js → VSelectionControl-CUlT3cc_.js} +1 -1
  66. codex/static_root/assets/VSelectionControl-CUlT3cc_.js.br +0 -0
  67. codex/static_root/assets/VSelectionControl-CUlT3cc_.js.gz +0 -0
  68. codex/static_root/assets/{VSlideGroup-DYJ3EYZI.a2542c5f90c4.js → VSlideGroup-CRJazQeR.cb89b2222518.js} +1 -1
  69. codex/static_root/assets/VSlideGroup-CRJazQeR.cb89b2222518.js.br +0 -0
  70. codex/static_root/assets/VSlideGroup-CRJazQeR.cb89b2222518.js.gz +0 -0
  71. codex/static_root/assets/{VSlideGroup-DYJ3EYZI.js → VSlideGroup-CRJazQeR.js} +1 -1
  72. codex/static_root/assets/VSlideGroup-CRJazQeR.js.br +0 -0
  73. codex/static_root/assets/VSlideGroup-CRJazQeR.js.gz +0 -0
  74. codex/static_root/assets/{VTable-BveGtxei.7f76f4ef812b.js → VTable-DtyBEZ5i.6848f15ea03f.js} +1 -1
  75. codex/static_root/assets/VTable-DtyBEZ5i.6848f15ea03f.js.br +0 -0
  76. codex/static_root/assets/VTable-DtyBEZ5i.6848f15ea03f.js.gz +0 -0
  77. codex/static_root/assets/{VTable-BveGtxei.js → VTable-DtyBEZ5i.js} +1 -1
  78. codex/static_root/assets/VTable-DtyBEZ5i.js.br +0 -0
  79. codex/static_root/assets/VTable-DtyBEZ5i.js.gz +0 -0
  80. codex/static_root/assets/VTextField-DEiVah7O.fbf3130f2dea.js +1 -0
  81. codex/static_root/assets/VTextField-DEiVah7O.fbf3130f2dea.js.br +0 -0
  82. codex/static_root/assets/VTextField-DEiVah7O.fbf3130f2dea.js.gz +0 -0
  83. codex/static_root/assets/VTextField-DEiVah7O.js +1 -0
  84. codex/static_root/assets/VTextField-DEiVah7O.js.br +0 -0
  85. codex/static_root/assets/VTextField-DEiVah7O.js.gz +0 -0
  86. codex/static_root/assets/{VWindowItem-FBuIO_rT.8610c7e83905.js → VWindowItem-BVIaHO-S.419e4ecf523d.js} +1 -1
  87. codex/static_root/assets/VWindowItem-BVIaHO-S.419e4ecf523d.js.br +0 -0
  88. codex/static_root/assets/VWindowItem-BVIaHO-S.419e4ecf523d.js.gz +0 -0
  89. codex/static_root/assets/{VWindowItem-FBuIO_rT.js → VWindowItem-BVIaHO-S.js} +1 -1
  90. codex/static_root/assets/VWindowItem-BVIaHO-S.js.br +0 -0
  91. codex/static_root/assets/VWindowItem-BVIaHO-S.js.gz +0 -0
  92. codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-D4sw3N4T.b590f88a62cf.css} +1 -1
  93. codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.br +0 -0
  94. codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.gz +0 -0
  95. codex/static_root/assets/{admin-B8Bp2Uih.css → admin-D4sw3N4T.css} +1 -1
  96. codex/static_root/assets/admin-D4sw3N4T.css.br +0 -0
  97. codex/static_root/assets/admin-D4sw3N4T.css.gz +0 -0
  98. codex/static_root/assets/admin-S53JleJt.f629d90f73a1.js +1 -0
  99. codex/static_root/assets/admin-S53JleJt.f629d90f73a1.js.br +0 -0
  100. codex/static_root/assets/admin-S53JleJt.f629d90f73a1.js.gz +0 -0
  101. codex/static_root/assets/admin-S53JleJt.js +1 -0
  102. codex/static_root/assets/admin-S53JleJt.js.br +0 -0
  103. codex/static_root/assets/admin-S53JleJt.js.gz +0 -0
  104. codex/static_root/assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js +56 -0
  105. codex/static_root/assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js.br +0 -0
  106. codex/static_root/assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js.gz +0 -0
  107. codex/static_root/assets/admin-drawer-panel-CnAnGx7w.js +56 -0
  108. codex/static_root/assets/admin-drawer-panel-CnAnGx7w.js.br +0 -0
  109. codex/static_root/assets/admin-drawer-panel-CnAnGx7w.js.gz +0 -0
  110. codex/static_root/assets/browser-BHHgqDP7.7ada4400d63f.js +1 -0
  111. codex/static_root/assets/browser-BHHgqDP7.7ada4400d63f.js.br +0 -0
  112. codex/static_root/assets/browser-BHHgqDP7.7ada4400d63f.js.gz +0 -0
  113. codex/static_root/assets/browser-BHHgqDP7.js +1 -0
  114. codex/static_root/assets/browser-BHHgqDP7.js.br +0 -0
  115. codex/static_root/assets/browser-BHHgqDP7.js.gz +0 -0
  116. codex/static_root/assets/{browser-DP9eEJ0M.css → browser-Cy0OgQJC.css} +1 -1
  117. codex/static_root/assets/browser-Cy0OgQJC.css.br +0 -0
  118. codex/static_root/assets/browser-Cy0OgQJC.css.gz +0 -0
  119. codex/static_root/assets/{browser-DP9eEJ0M.276e8636f8fc.css → browser-Cy0OgQJC.fabbd1aaffcf.css} +1 -1
  120. codex/static_root/assets/browser-Cy0OgQJC.fabbd1aaffcf.css.br +0 -0
  121. codex/static_root/assets/browser-Cy0OgQJC.fabbd1aaffcf.css.gz +0 -0
  122. codex/static_root/assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js +1 -0
  123. codex/static_root/assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js.br +0 -0
  124. codex/static_root/assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js.gz +0 -0
  125. codex/static_root/assets/change-password-dialog-Du1bwEt1.js +1 -0
  126. codex/static_root/assets/change-password-dialog-Du1bwEt1.js.br +0 -0
  127. codex/static_root/assets/change-password-dialog-Du1bwEt1.js.gz +0 -0
  128. codex/static_root/assets/confirm-dialog-eKN71B8a.af6187dc12bd.js +1 -0
  129. codex/static_root/assets/confirm-dialog-eKN71B8a.af6187dc12bd.js.br +0 -0
  130. codex/static_root/assets/confirm-dialog-eKN71B8a.af6187dc12bd.js.gz +0 -0
  131. codex/static_root/assets/confirm-dialog-eKN71B8a.js +1 -0
  132. codex/static_root/assets/confirm-dialog-eKN71B8a.js.br +0 -0
  133. codex/static_root/assets/confirm-dialog-eKN71B8a.js.gz +0 -0
  134. codex/static_root/assets/{datetime-column-uaXWFgF_.29c6d50b483e.js → datetime-column-CWp600fz.e8a969126bf5.js} +1 -1
  135. codex/static_root/assets/datetime-column-CWp600fz.e8a969126bf5.js.br +0 -0
  136. codex/static_root/assets/datetime-column-CWp600fz.e8a969126bf5.js.gz +0 -0
  137. codex/static_root/assets/{datetime-column-uaXWFgF_.js → datetime-column-CWp600fz.js} +1 -1
  138. codex/static_root/assets/datetime-column-CWp600fz.js.br +0 -0
  139. codex/static_root/assets/datetime-column-CWp600fz.js.gz +0 -0
  140. codex/static_root/assets/{filter-DS84uTvk.4fbebd4832bc.js → filter-_XbKRiIR.2c7ac68a2d03.js} +1 -1
  141. codex/static_root/assets/filter-_XbKRiIR.2c7ac68a2d03.js.br +0 -0
  142. codex/static_root/assets/filter-_XbKRiIR.2c7ac68a2d03.js.gz +0 -0
  143. codex/static_root/assets/{filter-DS84uTvk.js → filter-_XbKRiIR.js} +1 -1
  144. codex/static_root/assets/filter-_XbKRiIR.js.br +0 -0
  145. codex/static_root/assets/filter-_XbKRiIR.js.gz +0 -0
  146. codex/static_root/assets/flag-tab-DBqpLKi7.css +1 -0
  147. codex/static_root/assets/flag-tab-DBqpLKi7.css.br +0 -0
  148. codex/static_root/assets/flag-tab-DBqpLKi7.css.gz +0 -0
  149. codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css +1 -0
  150. codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.br +0 -0
  151. codex/static_root/assets/flag-tab-DBqpLKi7.dcfef39f541e.css.gz +0 -0
  152. codex/static_root/assets/flag-tab-DR_nY0Jg.7323e386c6ed.js +1 -0
  153. codex/static_root/assets/flag-tab-DR_nY0Jg.7323e386c6ed.js.br +0 -0
  154. codex/static_root/assets/flag-tab-DR_nY0Jg.7323e386c6ed.js.gz +0 -0
  155. codex/static_root/assets/flag-tab-DR_nY0Jg.js +1 -0
  156. codex/static_root/assets/flag-tab-DR_nY0Jg.js.br +0 -0
  157. codex/static_root/assets/flag-tab-DR_nY0Jg.js.gz +0 -0
  158. codex/static_root/assets/group-tab-JKsMHOmK.d2c2f3fceec1.js +1 -0
  159. codex/static_root/assets/group-tab-JKsMHOmK.d2c2f3fceec1.js.br +0 -0
  160. codex/static_root/assets/group-tab-JKsMHOmK.d2c2f3fceec1.js.gz +0 -0
  161. codex/static_root/assets/group-tab-JKsMHOmK.js +1 -0
  162. codex/static_root/assets/group-tab-JKsMHOmK.js.br +0 -0
  163. codex/static_root/assets/group-tab-JKsMHOmK.js.gz +0 -0
  164. codex/static_root/assets/http-error-Bw1wUv3U.c8313a2fb129.js +1 -0
  165. codex/static_root/assets/http-error-Bw1wUv3U.c8313a2fb129.js.br +0 -0
  166. codex/static_root/assets/http-error-Bw1wUv3U.c8313a2fb129.js.gz +0 -0
  167. codex/static_root/assets/http-error-Bw1wUv3U.js +1 -0
  168. codex/static_root/assets/http-error-Bw1wUv3U.js.br +0 -0
  169. codex/static_root/assets/http-error-Bw1wUv3U.js.gz +0 -0
  170. codex/static_root/assets/{library-tab-BN0SfPcH.913e236f625e.css → library-tab-BygwYPnO.94e92aa60bf2.css} +1 -1
  171. codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.br +0 -0
  172. codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.gz +0 -0
  173. codex/static_root/assets/{library-tab-BN0SfPcH.css → library-tab-BygwYPnO.css} +1 -1
  174. codex/static_root/assets/library-tab-BygwYPnO.css.br +0 -0
  175. codex/static_root/assets/library-tab-BygwYPnO.css.gz +0 -0
  176. codex/static_root/assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js +1 -0
  177. codex/static_root/assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js.br +0 -0
  178. codex/static_root/assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js.gz +0 -0
  179. codex/static_root/assets/library-tab-D9GTugZ6.js +1 -0
  180. codex/static_root/assets/library-tab-D9GTugZ6.js.br +0 -0
  181. codex/static_root/assets/library-tab-D9GTugZ6.js.gz +0 -0
  182. codex/static_root/assets/main-BJiV8IG3.9d708627091f.js +6 -0
  183. codex/static_root/assets/main-BJiV8IG3.9d708627091f.js.br +0 -0
  184. codex/static_root/assets/main-BJiV8IG3.9d708627091f.js.gz +0 -0
  185. codex/static_root/assets/main-BJiV8IG3.js +6 -0
  186. codex/static_root/assets/main-BJiV8IG3.js.br +0 -0
  187. codex/static_root/assets/main-BJiV8IG3.js.gz +0 -0
  188. codex/static_root/assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js +1 -0
  189. codex/static_root/assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js.br +0 -0
  190. codex/static_root/assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js.gz +0 -0
  191. codex/static_root/assets/pagination-toolbar-i2VoXaGH.js +1 -0
  192. codex/static_root/assets/pagination-toolbar-i2VoXaGH.js.br +0 -0
  193. codex/static_root/assets/pagination-toolbar-i2VoXaGH.js.gz +0 -0
  194. codex/static_root/assets/{pdf-doc-C-4EM9-d.ce9cf66bd4c3.js → pdf-doc-BLxZLD38.4cf9622c535f.js} +1 -1
  195. codex/static_root/assets/pdf-doc-BLxZLD38.4cf9622c535f.js.br +0 -0
  196. codex/static_root/assets/pdf-doc-BLxZLD38.4cf9622c535f.js.gz +0 -0
  197. codex/static_root/assets/{pdf-doc-C-4EM9-d.js → pdf-doc-BLxZLD38.js} +1 -1
  198. codex/static_root/assets/pdf-doc-BLxZLD38.js.br +0 -0
  199. codex/static_root/assets/pdf-doc-BLxZLD38.js.gz +0 -0
  200. codex/static_root/assets/reader-D2O_yHTW.70d616ddc2cc.js +2 -0
  201. codex/static_root/assets/reader-D2O_yHTW.70d616ddc2cc.js.br +0 -0
  202. codex/static_root/assets/reader-D2O_yHTW.70d616ddc2cc.js.gz +0 -0
  203. codex/static_root/assets/reader-D2O_yHTW.js +2 -0
  204. codex/static_root/assets/reader-D2O_yHTW.js.br +0 -0
  205. codex/static_root/assets/reader-D2O_yHTW.js.gz +0 -0
  206. codex/static_root/assets/{reader-DOQuhDYO.css → reader-YbzUiD0T.css} +1 -1
  207. codex/static_root/assets/reader-YbzUiD0T.css.br +0 -0
  208. codex/static_root/assets/reader-YbzUiD0T.css.gz +0 -0
  209. codex/static_root/assets/{reader-DOQuhDYO.df559ad29ab3.css → reader-YbzUiD0T.efb15835b39d.css} +1 -1
  210. codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.br +0 -0
  211. codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.gz +0 -0
  212. codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-BGitp3yx.b3e7d178a615.css} +1 -1
  213. codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.br +0 -0
  214. codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.gz +0 -0
  215. codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-BGitp3yx.css} +1 -1
  216. codex/static_root/assets/relation-chips-BGitp3yx.css.br +0 -0
  217. codex/static_root/assets/relation-chips-BGitp3yx.css.gz +0 -0
  218. codex/static_root/assets/relation-chips-CmeSIsRq.f169f7c3f48d.js +1 -0
  219. codex/static_root/assets/relation-chips-CmeSIsRq.f169f7c3f48d.js.br +0 -0
  220. codex/static_root/assets/relation-chips-CmeSIsRq.f169f7c3f48d.js.gz +0 -0
  221. codex/static_root/assets/relation-chips-CmeSIsRq.js +1 -0
  222. codex/static_root/assets/relation-chips-CmeSIsRq.js.br +0 -0
  223. codex/static_root/assets/relation-chips-CmeSIsRq.js.gz +0 -0
  224. codex/static_root/assets/settings-drawer-XH4sNDNT.98ee4b24901c.js +2 -0
  225. codex/static_root/assets/settings-drawer-XH4sNDNT.98ee4b24901c.js.br +0 -0
  226. codex/static_root/assets/settings-drawer-XH4sNDNT.98ee4b24901c.js.gz +0 -0
  227. codex/static_root/assets/settings-drawer-XH4sNDNT.js +2 -0
  228. codex/static_root/assets/settings-drawer-XH4sNDNT.js.br +0 -0
  229. codex/static_root/assets/settings-drawer-XH4sNDNT.js.gz +0 -0
  230. codex/static_root/assets/stats-tab-BGxkeKnY.44cf18ca895d.js +1 -0
  231. codex/static_root/assets/stats-tab-BGxkeKnY.44cf18ca895d.js.br +0 -0
  232. codex/static_root/assets/stats-tab-BGxkeKnY.44cf18ca895d.js.gz +0 -0
  233. codex/static_root/assets/stats-tab-BGxkeKnY.js +1 -0
  234. codex/static_root/assets/stats-tab-BGxkeKnY.js.br +0 -0
  235. codex/static_root/assets/stats-tab-BGxkeKnY.js.gz +0 -0
  236. codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css +1 -0
  237. codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css.br +0 -0
  238. codex/static_root/assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css.gz +0 -0
  239. codex/static_root/assets/stats-tab-CI0u-X7Y.css +1 -0
  240. codex/static_root/assets/stats-tab-CI0u-X7Y.css.br +0 -0
  241. codex/static_root/assets/stats-tab-CI0u-X7Y.css.gz +0 -0
  242. codex/static_root/assets/task-tab-B74Kah_b.58627993d3c6.js +1 -0
  243. codex/static_root/assets/task-tab-B74Kah_b.58627993d3c6.js.br +0 -0
  244. codex/static_root/assets/task-tab-B74Kah_b.58627993d3c6.js.gz +0 -0
  245. codex/static_root/assets/task-tab-B74Kah_b.js +1 -0
  246. codex/static_root/assets/task-tab-B74Kah_b.js.br +0 -0
  247. codex/static_root/assets/task-tab-B74Kah_b.js.gz +0 -0
  248. codex/static_root/assets/unauthorized-BF45der6.d34f8022124b.js +1 -0
  249. codex/static_root/assets/unauthorized-BF45der6.d34f8022124b.js.br +0 -0
  250. codex/static_root/assets/unauthorized-BF45der6.d34f8022124b.js.gz +0 -0
  251. codex/static_root/assets/unauthorized-BF45der6.js +1 -0
  252. codex/static_root/assets/unauthorized-BF45der6.js.br +0 -0
  253. codex/static_root/assets/unauthorized-BF45der6.js.gz +0 -0
  254. codex/static_root/assets/user-tab-BP2TTPg5.e63a5661a1a5.js +1 -0
  255. codex/static_root/assets/user-tab-BP2TTPg5.e63a5661a1a5.js.br +0 -0
  256. codex/static_root/assets/user-tab-BP2TTPg5.e63a5661a1a5.js.gz +0 -0
  257. codex/static_root/assets/user-tab-BP2TTPg5.js +1 -0
  258. codex/static_root/assets/user-tab-BP2TTPg5.js.br +0 -0
  259. codex/static_root/assets/user-tab-BP2TTPg5.js.gz +0 -0
  260. codex/static_root/{manifest.80e911824648.json → manifest.7033f9a22987.json} +230 -242
  261. codex/static_root/manifest.7033f9a22987.json.br +0 -0
  262. codex/static_root/manifest.7033f9a22987.json.gz +0 -0
  263. codex/static_root/manifest.json +230 -242
  264. codex/static_root/manifest.json.br +0 -0
  265. codex/static_root/manifest.json.gz +0 -0
  266. codex/static_root/staticfiles.json +1 -1
  267. codex/urls/root.py +12 -2
  268. codex/views/admin/api_key.py +1 -1
  269. codex/views/admin/flag.py +1 -1
  270. codex/views/admin/group.py +1 -1
  271. codex/views/admin/library.py +1 -1
  272. codex/views/admin/stats.py +41 -155
  273. codex/views/admin/tasks.py +1 -1
  274. codex/views/admin/user.py +1 -1
  275. codex/views/browser/browser.py +4 -1
  276. codex/views/browser/filters/field.py +15 -6
  277. codex/views/browser/paginate.py +61 -11
  278. codex/views/browser/validate.py +7 -5
  279. codex/views/opds/binary.py +1 -3
  280. codex/views/opds/const.py +10 -0
  281. codex/views/opds/util.py +10 -0
  282. codex/views/opds/v1/entry/entry.py +14 -18
  283. codex/views/opds/v1/feed.py +10 -24
  284. codex/views/opds/v1/links.py +1 -0
  285. codex/views/reader/arcs.py +65 -53
  286. codex/views/reader/books.py +38 -38
  287. codex/views/reader/init.py +7 -5
  288. codex/views/reader/reader.py +0 -2
  289. {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/METADATA +18 -7
  290. {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/RECORD +293 -289
  291. codex/serializers/admin.py +0 -268
  292. codex/settings/logging.py +0 -20
  293. codex/static_root/assets/VCheckbox-DUxuAZcY.46ab94043464.js +0 -1
  294. codex/static_root/assets/VCheckbox-DUxuAZcY.46ab94043464.js.br +0 -0
  295. codex/static_root/assets/VCheckbox-DUxuAZcY.46ab94043464.js.gz +0 -0
  296. codex/static_root/assets/VCheckbox-DUxuAZcY.js +0 -1
  297. codex/static_root/assets/VCheckbox-DUxuAZcY.js.br +0 -0
  298. codex/static_root/assets/VCheckbox-DUxuAZcY.js.gz +0 -0
  299. codex/static_root/assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js +0 -1
  300. codex/static_root/assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js.br +0 -0
  301. codex/static_root/assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js.gz +0 -0
  302. codex/static_root/assets/VCheckboxBtn-BTFFFGx3.js +0 -1
  303. codex/static_root/assets/VCheckboxBtn-BTFFFGx3.js.br +0 -0
  304. codex/static_root/assets/VCheckboxBtn-BTFFFGx3.js.gz +0 -0
  305. codex/static_root/assets/VCombobox-CGyBgdqr.2f85598cc1a4.js.br +0 -0
  306. codex/static_root/assets/VCombobox-CGyBgdqr.2f85598cc1a4.js.gz +0 -0
  307. codex/static_root/assets/VCombobox-CGyBgdqr.js.br +0 -0
  308. codex/static_root/assets/VCombobox-CGyBgdqr.js.gz +0 -0
  309. codex/static_root/assets/VDialog-DTWAoZBU.b8c4c276e9b2.js +0 -1
  310. codex/static_root/assets/VDialog-DTWAoZBU.b8c4c276e9b2.js.br +0 -0
  311. codex/static_root/assets/VDialog-DTWAoZBU.b8c4c276e9b2.js.gz +0 -0
  312. codex/static_root/assets/VDialog-DTWAoZBU.js +0 -1
  313. codex/static_root/assets/VDialog-DTWAoZBU.js.br +0 -0
  314. codex/static_root/assets/VDialog-DTWAoZBU.js.gz +0 -0
  315. codex/static_root/assets/VExpansionPanels-D4psrz4h.b55b901da65f.js +0 -1
  316. codex/static_root/assets/VExpansionPanels-D4psrz4h.b55b901da65f.js.br +0 -0
  317. codex/static_root/assets/VExpansionPanels-D4psrz4h.b55b901da65f.js.gz +0 -0
  318. codex/static_root/assets/VExpansionPanels-D4psrz4h.js +0 -1
  319. codex/static_root/assets/VExpansionPanels-D4psrz4h.js.br +0 -0
  320. codex/static_root/assets/VExpansionPanels-D4psrz4h.js.gz +0 -0
  321. codex/static_root/assets/VRadioGroup-BY572TP-.6d47a94795de.js +0 -1
  322. codex/static_root/assets/VRadioGroup-BY572TP-.6d47a94795de.js.br +0 -2
  323. codex/static_root/assets/VRadioGroup-BY572TP-.6d47a94795de.js.gz +0 -0
  324. codex/static_root/assets/VRadioGroup-BY572TP-.js +0 -1
  325. codex/static_root/assets/VRadioGroup-BY572TP-.js.br +0 -2
  326. codex/static_root/assets/VRadioGroup-BY572TP-.js.gz +0 -0
  327. codex/static_root/assets/VSelect-Bnhj5kBc.fdcd87c9e253.js +0 -1
  328. codex/static_root/assets/VSelect-Bnhj5kBc.fdcd87c9e253.js.br +0 -0
  329. codex/static_root/assets/VSelect-Bnhj5kBc.fdcd87c9e253.js.gz +0 -0
  330. codex/static_root/assets/VSelect-Bnhj5kBc.js +0 -1
  331. codex/static_root/assets/VSelect-Bnhj5kBc.js.br +0 -0
  332. codex/static_root/assets/VSelect-Bnhj5kBc.js.gz +0 -0
  333. codex/static_root/assets/VSelectionControl-DXxRmssH.252890f79cc6.js.br +0 -0
  334. codex/static_root/assets/VSelectionControl-DXxRmssH.252890f79cc6.js.gz +0 -0
  335. codex/static_root/assets/VSelectionControl-DXxRmssH.js.br +0 -0
  336. codex/static_root/assets/VSelectionControl-DXxRmssH.js.gz +0 -0
  337. codex/static_root/assets/VSlideGroup-DYJ3EYZI.a2542c5f90c4.js.br +0 -0
  338. codex/static_root/assets/VSlideGroup-DYJ3EYZI.a2542c5f90c4.js.gz +0 -0
  339. codex/static_root/assets/VSlideGroup-DYJ3EYZI.js.br +0 -0
  340. codex/static_root/assets/VSlideGroup-DYJ3EYZI.js.gz +0 -0
  341. codex/static_root/assets/VTable-BveGtxei.7f76f4ef812b.js.br +0 -0
  342. codex/static_root/assets/VTable-BveGtxei.7f76f4ef812b.js.gz +0 -0
  343. codex/static_root/assets/VTable-BveGtxei.js.br +0 -0
  344. codex/static_root/assets/VTable-BveGtxei.js.gz +0 -0
  345. codex/static_root/assets/VTextField-BayL7C1D.6e53c61b3760.js +0 -1
  346. codex/static_root/assets/VTextField-BayL7C1D.6e53c61b3760.js.br +0 -0
  347. codex/static_root/assets/VTextField-BayL7C1D.6e53c61b3760.js.gz +0 -0
  348. codex/static_root/assets/VTextField-BayL7C1D.js +0 -1
  349. codex/static_root/assets/VTextField-BayL7C1D.js.br +0 -0
  350. codex/static_root/assets/VTextField-BayL7C1D.js.gz +0 -0
  351. codex/static_root/assets/VWindowItem-FBuIO_rT.8610c7e83905.js.br +0 -0
  352. codex/static_root/assets/VWindowItem-FBuIO_rT.8610c7e83905.js.gz +0 -0
  353. codex/static_root/assets/VWindowItem-FBuIO_rT.js.br +0 -0
  354. codex/static_root/assets/VWindowItem-FBuIO_rT.js.gz +0 -0
  355. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
  356. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
  357. codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
  358. codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
  359. codex/static_root/assets/admin-BH6uiYBb.d51c720fa05c.js +0 -1
  360. codex/static_root/assets/admin-BH6uiYBb.d51c720fa05c.js.br +0 -0
  361. codex/static_root/assets/admin-BH6uiYBb.d51c720fa05c.js.gz +0 -0
  362. codex/static_root/assets/admin-BH6uiYBb.js +0 -1
  363. codex/static_root/assets/admin-BH6uiYBb.js.br +0 -0
  364. codex/static_root/assets/admin-BH6uiYBb.js.gz +0 -0
  365. codex/static_root/assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js +0 -30
  366. codex/static_root/assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js.br +0 -0
  367. codex/static_root/assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js.gz +0 -0
  368. codex/static_root/assets/admin-drawer-panel-DsgLbfnD.js +0 -30
  369. codex/static_root/assets/admin-drawer-panel-DsgLbfnD.js.br +0 -0
  370. codex/static_root/assets/admin-drawer-panel-DsgLbfnD.js.gz +0 -0
  371. codex/static_root/assets/browser-BVciW8XB.ab1d75272aa7.js +0 -1
  372. codex/static_root/assets/browser-BVciW8XB.ab1d75272aa7.js.br +0 -0
  373. codex/static_root/assets/browser-BVciW8XB.ab1d75272aa7.js.gz +0 -0
  374. codex/static_root/assets/browser-BVciW8XB.js +0 -1
  375. codex/static_root/assets/browser-BVciW8XB.js.br +0 -0
  376. codex/static_root/assets/browser-BVciW8XB.js.gz +0 -0
  377. codex/static_root/assets/browser-DP9eEJ0M.276e8636f8fc.css.br +0 -0
  378. codex/static_root/assets/browser-DP9eEJ0M.276e8636f8fc.css.gz +0 -0
  379. codex/static_root/assets/browser-DP9eEJ0M.css.br +0 -0
  380. codex/static_root/assets/browser-DP9eEJ0M.css.gz +0 -0
  381. codex/static_root/assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js +0 -1
  382. codex/static_root/assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js.br +0 -0
  383. codex/static_root/assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js.gz +0 -0
  384. codex/static_root/assets/change-password-dialog-B-Sd5U5p.js +0 -1
  385. codex/static_root/assets/change-password-dialog-B-Sd5U5p.js.br +0 -0
  386. codex/static_root/assets/change-password-dialog-B-Sd5U5p.js.gz +0 -0
  387. codex/static_root/assets/confirm-dialog-CYEBgtj5.a5f142656c12.js +0 -1
  388. codex/static_root/assets/confirm-dialog-CYEBgtj5.a5f142656c12.js.br +0 -0
  389. codex/static_root/assets/confirm-dialog-CYEBgtj5.a5f142656c12.js.gz +0 -0
  390. codex/static_root/assets/confirm-dialog-CYEBgtj5.js +0 -1
  391. codex/static_root/assets/confirm-dialog-CYEBgtj5.js.br +0 -0
  392. codex/static_root/assets/confirm-dialog-CYEBgtj5.js.gz +0 -0
  393. codex/static_root/assets/datetime-column-uaXWFgF_.29c6d50b483e.js.br +0 -0
  394. codex/static_root/assets/datetime-column-uaXWFgF_.29c6d50b483e.js.gz +0 -0
  395. codex/static_root/assets/datetime-column-uaXWFgF_.js.br +0 -0
  396. codex/static_root/assets/datetime-column-uaXWFgF_.js.gz +0 -0
  397. codex/static_root/assets/filter-DS84uTvk.4fbebd4832bc.js.br +0 -0
  398. codex/static_root/assets/filter-DS84uTvk.4fbebd4832bc.js.gz +0 -0
  399. codex/static_root/assets/filter-DS84uTvk.js.br +0 -0
  400. codex/static_root/assets/filter-DS84uTvk.js.gz +0 -0
  401. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
  402. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
  403. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
  404. codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
  405. codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
  406. codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
  407. codex/static_root/assets/flag-tab-iJTJldQ1.213201f8e03b.js +0 -1
  408. codex/static_root/assets/flag-tab-iJTJldQ1.213201f8e03b.js.br +0 -0
  409. codex/static_root/assets/flag-tab-iJTJldQ1.213201f8e03b.js.gz +0 -0
  410. codex/static_root/assets/flag-tab-iJTJldQ1.js +0 -1
  411. codex/static_root/assets/flag-tab-iJTJldQ1.js.br +0 -0
  412. codex/static_root/assets/flag-tab-iJTJldQ1.js.gz +0 -0
  413. codex/static_root/assets/group-tab-DdmGE4gB.48ef83a4c7cd.js +0 -1
  414. codex/static_root/assets/group-tab-DdmGE4gB.48ef83a4c7cd.js.br +0 -0
  415. codex/static_root/assets/group-tab-DdmGE4gB.48ef83a4c7cd.js.gz +0 -0
  416. codex/static_root/assets/group-tab-DdmGE4gB.js +0 -1
  417. codex/static_root/assets/group-tab-DdmGE4gB.js.br +0 -0
  418. codex/static_root/assets/group-tab-DdmGE4gB.js.gz +0 -0
  419. codex/static_root/assets/http-error-HtOe8jPa.141ad665f81d.js +0 -1
  420. codex/static_root/assets/http-error-HtOe8jPa.141ad665f81d.js.br +0 -0
  421. codex/static_root/assets/http-error-HtOe8jPa.141ad665f81d.js.gz +0 -0
  422. codex/static_root/assets/http-error-HtOe8jPa.js +0 -1
  423. codex/static_root/assets/http-error-HtOe8jPa.js.br +0 -0
  424. codex/static_root/assets/http-error-HtOe8jPa.js.gz +0 -0
  425. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
  426. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
  427. codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
  428. codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
  429. codex/static_root/assets/library-tab-DLAJV6j2.dae6292bbf28.js +0 -1
  430. codex/static_root/assets/library-tab-DLAJV6j2.dae6292bbf28.js.br +0 -0
  431. codex/static_root/assets/library-tab-DLAJV6j2.dae6292bbf28.js.gz +0 -0
  432. codex/static_root/assets/library-tab-DLAJV6j2.js +0 -1
  433. codex/static_root/assets/library-tab-DLAJV6j2.js.br +0 -0
  434. codex/static_root/assets/library-tab-DLAJV6j2.js.gz +0 -0
  435. codex/static_root/assets/main-BjGE3kSi.22c1b19a9e19.js +0 -32
  436. codex/static_root/assets/main-BjGE3kSi.22c1b19a9e19.js.br +0 -0
  437. codex/static_root/assets/main-BjGE3kSi.22c1b19a9e19.js.gz +0 -0
  438. codex/static_root/assets/main-BjGE3kSi.js +0 -32
  439. codex/static_root/assets/main-BjGE3kSi.js.br +0 -0
  440. codex/static_root/assets/main-BjGE3kSi.js.gz +0 -0
  441. codex/static_root/assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js +0 -1
  442. codex/static_root/assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js.br +0 -0
  443. codex/static_root/assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js.gz +0 -0
  444. codex/static_root/assets/pagination-toolbar-C8CSryCU.js +0 -1
  445. codex/static_root/assets/pagination-toolbar-C8CSryCU.js.br +0 -0
  446. codex/static_root/assets/pagination-toolbar-C8CSryCU.js.gz +0 -0
  447. codex/static_root/assets/pdf-doc-C-4EM9-d.ce9cf66bd4c3.js.br +0 -0
  448. codex/static_root/assets/pdf-doc-C-4EM9-d.ce9cf66bd4c3.js.gz +0 -0
  449. codex/static_root/assets/pdf-doc-C-4EM9-d.js.br +0 -0
  450. codex/static_root/assets/pdf-doc-C-4EM9-d.js.gz +0 -0
  451. codex/static_root/assets/reader-C5OTEQ7N.fae52c1fee51.js +0 -2
  452. codex/static_root/assets/reader-C5OTEQ7N.fae52c1fee51.js.br +0 -0
  453. codex/static_root/assets/reader-C5OTEQ7N.fae52c1fee51.js.gz +0 -0
  454. codex/static_root/assets/reader-C5OTEQ7N.js +0 -2
  455. codex/static_root/assets/reader-C5OTEQ7N.js.br +0 -0
  456. codex/static_root/assets/reader-C5OTEQ7N.js.gz +0 -0
  457. codex/static_root/assets/reader-DOQuhDYO.css.br +0 -0
  458. codex/static_root/assets/reader-DOQuhDYO.css.gz +0 -0
  459. codex/static_root/assets/reader-DOQuhDYO.df559ad29ab3.css.br +0 -0
  460. codex/static_root/assets/reader-DOQuhDYO.df559ad29ab3.css.gz +0 -0
  461. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
  462. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
  463. codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
  464. codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
  465. codex/static_root/assets/relation-chips-C4LQSHRc.3c85433df91c.js +0 -1
  466. codex/static_root/assets/relation-chips-C4LQSHRc.3c85433df91c.js.br +0 -0
  467. codex/static_root/assets/relation-chips-C4LQSHRc.3c85433df91c.js.gz +0 -0
  468. codex/static_root/assets/relation-chips-C4LQSHRc.js +0 -1
  469. codex/static_root/assets/relation-chips-C4LQSHRc.js.br +0 -0
  470. codex/static_root/assets/relation-chips-C4LQSHRc.js.gz +0 -0
  471. codex/static_root/assets/settings-drawer-aTAAoX2A.2f21008ade35.js +0 -2
  472. codex/static_root/assets/settings-drawer-aTAAoX2A.2f21008ade35.js.br +0 -0
  473. codex/static_root/assets/settings-drawer-aTAAoX2A.2f21008ade35.js.gz +0 -0
  474. codex/static_root/assets/settings-drawer-aTAAoX2A.js +0 -2
  475. codex/static_root/assets/settings-drawer-aTAAoX2A.js.br +0 -0
  476. codex/static_root/assets/settings-drawer-aTAAoX2A.js.gz +0 -0
  477. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
  478. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
  479. codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
  480. codex/static_root/assets/stats-tab-B4yKuY0c.css +0 -1
  481. codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
  482. codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
  483. codex/static_root/assets/stats-tab-DL4J3pvZ.b9056e70a70f.js +0 -1
  484. codex/static_root/assets/stats-tab-DL4J3pvZ.b9056e70a70f.js.br +0 -0
  485. codex/static_root/assets/stats-tab-DL4J3pvZ.b9056e70a70f.js.gz +0 -0
  486. codex/static_root/assets/stats-tab-DL4J3pvZ.js +0 -1
  487. codex/static_root/assets/stats-tab-DL4J3pvZ.js.br +0 -0
  488. codex/static_root/assets/stats-tab-DL4J3pvZ.js.gz +0 -0
  489. codex/static_root/assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js +0 -1
  490. codex/static_root/assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js.br +0 -0
  491. codex/static_root/assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js.gz +0 -0
  492. codex/static_root/assets/task-tab-0t7ZJlwL.js +0 -1
  493. codex/static_root/assets/task-tab-0t7ZJlwL.js.br +0 -0
  494. codex/static_root/assets/task-tab-0t7ZJlwL.js.gz +0 -0
  495. codex/static_root/assets/to-case-BCB751sX.4afb5ae1b77d.js +0 -1
  496. codex/static_root/assets/to-case-BCB751sX.js +0 -1
  497. codex/static_root/assets/unauthorized-Ovy_BO5R.598527e8e4fe.js +0 -1
  498. codex/static_root/assets/unauthorized-Ovy_BO5R.598527e8e4fe.js.br +0 -0
  499. codex/static_root/assets/unauthorized-Ovy_BO5R.598527e8e4fe.js.gz +0 -0
  500. codex/static_root/assets/unauthorized-Ovy_BO5R.js +0 -1
  501. codex/static_root/assets/unauthorized-Ovy_BO5R.js.br +0 -0
  502. codex/static_root/assets/unauthorized-Ovy_BO5R.js.gz +0 -0
  503. codex/static_root/assets/user-tab-Cdspo8ie.8e886a1a28e8.js +0 -1
  504. codex/static_root/assets/user-tab-Cdspo8ie.8e886a1a28e8.js.br +0 -0
  505. codex/static_root/assets/user-tab-Cdspo8ie.8e886a1a28e8.js.gz +0 -0
  506. codex/static_root/assets/user-tab-Cdspo8ie.js +0 -1
  507. codex/static_root/assets/user-tab-Cdspo8ie.js.br +0 -0
  508. codex/static_root/assets/user-tab-Cdspo8ie.js.gz +0 -0
  509. codex/static_root/manifest.80e911824648.json.br +0 -0
  510. codex/static_root/manifest.80e911824648.json.gz +0 -0
  511. {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/LICENSE +0 -0
  512. {codex-1.6.2.dist-info → codex-1.6.4.dist-info}/WHEEL +0 -0
  513. {codex-1.6.2.dist-info → codex-1.6.4.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/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "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/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/jquery-3.7.1.min.js": "rest_framework/js/jquery-3.7.1.min.2c872dbe60f4.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.8851fb9336c9.js", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "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/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "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.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "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/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.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.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "img/publisher.svg": "img/publisher.82d378fcd601.svg", "img/logo-32.webp": "img/logo-32.5688944495bf.webp", "img/logo.svg": "img/logo.0f5344a028ca.svg", "img/story-arc.svg": "img/story-arc.ca10014355ef.svg", "img/missing-cover-165.webp": "img/missing-cover-165.0743171f2148.webp", "img/series.svg": "img/series.ec8a889b8f95.svg", "img/logo-maskable-180.webp": "img/logo-maskable-180.c58d966aa64e.webp", "img/missing-cover.svg": "img/missing-cover.ddb038cc2c56.svg", "img/folder.svg": "img/folder.f28936a3701d.svg", "img/volume.svg": "img/volume.814e4c79461a.svg", "img/logo-maskable.svg": "img/logo-maskable.8f7de7f1c6a8.svg", "img/imprint.svg": "img/imprint.7c2adebb76d6.svg", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "js/choices.json": "js/choices.2f6b0359e1ef.json", "js/choices-admin.json": "js/choices-admin.3521e2a863df.json", "assets/relation-chips-Bsc-gUxE.css": "assets/relation-chips-Bsc-gUxE.98e65384c3c9.css", "assets/group-tab-CBKuS3Iv.css": "assets/group-tab-CBKuS3Iv.7e4746634782.css", "assets/http-error-DKulXuTl.css": "assets/http-error-DKulXuTl.95e42988d5d5.css", "assets/VSelect-ARDhJiQK.css": "assets/VSelect-ARDhJiQK.372c098fb475.css", "assets/flag-tab-BNwbLfrN.css": "assets/flag-tab-BNwbLfrN.b755a99fff7e.css", "assets/admin-BH6uiYBb.js": "assets/admin-BH6uiYBb.d51c720fa05c.js", "assets/http-error-HtOe8jPa.js": "assets/http-error-HtOe8jPa.141ad665f81d.js", "assets/browser-DP9eEJ0M.css": "assets/browser-DP9eEJ0M.276e8636f8fc.css", "assets/group-tab-DdmGE4gB.js": "assets/group-tab-DdmGE4gB.48ef83a4c7cd.js", "assets/VSelectionControl-BFq--28I.css": "assets/VSelectionControl-BFq--28I.9687c77461fc.css", "assets/filter-DS84uTvk.js": "assets/filter-DS84uTvk.4fbebd4832bc.js", "assets/pdf-doc-CMzaSV_K.css": "assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css", "assets/VWindowItem-FBuIO_rT.js": "assets/VWindowItem-FBuIO_rT.8610c7e83905.js", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/VSlideGroup-Dk_1upHt.css": "assets/VSlideGroup-Dk_1upHt.2c836070ec75.css", "assets/admin-drawer-panel-mne3FIj6.css": "assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css", "assets/VTable-DflzroDq.css": "assets/VTable-DflzroDq.160504d82195.css", "assets/settings-drawer-nZ9HvKSZ.css": "assets/settings-drawer-nZ9HvKSZ.01744e131806.css", "assets/VDialog-lQ13FnRR.css": "assets/VDialog-lQ13FnRR.1c801ad756b2.css", "assets/library-tab-DLAJV6j2.js": "assets/library-tab-DLAJV6j2.dae6292bbf28.js", "assets/flag-tab-iJTJldQ1.js": "assets/flag-tab-iJTJldQ1.213201f8e03b.js", "assets/main-Ztp855PW.css": "assets/main-Ztp855PW.d46d718fac45.css", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/pagination-toolbar-C8CSryCU.js": "assets/pagination-toolbar-C8CSryCU.9b61f95fd710.js", "assets/VSelect-Bnhj5kBc.js": "assets/VSelect-Bnhj5kBc.fdcd87c9e253.js", "assets/to-case-BCB751sX.js": "assets/to-case-BCB751sX.4afb5ae1b77d.js", "assets/VCheckboxBtn-BTFFFGx3.js": "assets/VCheckboxBtn-BTFFFGx3.0cbde4eb9a65.js", "assets/change-password-dialog-0u6IJA87.css": "assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css", "assets/VTable-BveGtxei.js": "assets/VTable-BveGtxei.7f76f4ef812b.js", "assets/copy-to-clipboard-DoUvDJVx.js": "assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js", "assets/VDialog-DTWAoZBU.js": "assets/VDialog-DTWAoZBU.b8c4c276e9b2.js", "assets/browser-BVciW8XB.js": "assets/browser-BVciW8XB.ab1d75272aa7.js", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/main-BjGE3kSi.js": "assets/main-BjGE3kSi.22c1b19a9e19.js", "assets/datetime-column-uaXWFgF_.js": "assets/datetime-column-uaXWFgF_.29c6d50b483e.js", "assets/VCheckbox-DUxuAZcY.js": "assets/VCheckbox-DUxuAZcY.46ab94043464.js", "assets/pagination-toolbar-BA9ifTed.css": "assets/pagination-toolbar-BA9ifTed.b4865334ee08.css", "assets/user-tab-Cdspo8ie.js": "assets/user-tab-Cdspo8ie.8e886a1a28e8.js", "assets/relation-chips-C4LQSHRc.js": "assets/relation-chips-C4LQSHRc.3c85433df91c.js", "assets/reader-C5OTEQ7N.js": "assets/reader-C5OTEQ7N.fae52c1fee51.js", "assets/library-tab-BN0SfPcH.css": "assets/library-tab-BN0SfPcH.913e236f625e.css", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/admin-drawer-panel-DsgLbfnD.js": "assets/admin-drawer-panel-DsgLbfnD.7c409b430d79.js", "assets/task-tab-0t7ZJlwL.js": "assets/task-tab-0t7ZJlwL.1ff1cc14a6aa.js", "assets/VRadioGroup-BY572TP-.js": "assets/VRadioGroup-BY572TP-.6d47a94795de.js", "assets/pdf-doc-C-4EM9-d.js": "assets/pdf-doc-C-4EM9-d.ce9cf66bd4c3.js", "assets/VCombobox-BksEr4D_.css": "assets/VCombobox-BksEr4D_.73429bb75f42.css", "assets/change-password-dialog-B-Sd5U5p.js": "assets/change-password-dialog-B-Sd5U5p.adf0a0f02866.js", "assets/VSlideGroup-DYJ3EYZI.js": "assets/VSlideGroup-DYJ3EYZI.a2542c5f90c4.js", "assets/unauthorized-Ovy_BO5R.js": "assets/unauthorized-Ovy_BO5R.598527e8e4fe.js", "assets/admin-B8Bp2Uih.css": "assets/admin-B8Bp2Uih.82c98714bcf1.css", "assets/VSelectionControl-DXxRmssH.js": "assets/VSelectionControl-DXxRmssH.252890f79cc6.js", "assets/settings-drawer-aTAAoX2A.js": "assets/settings-drawer-aTAAoX2A.2f21008ade35.js", "assets/task-tab-1XiWn9xP.css": "assets/task-tab-1XiWn9xP.06cb8e1b794d.css", "assets/VTextField-BayL7C1D.js": "assets/VTextField-BayL7C1D.6e53c61b3760.js", "assets/stats-tab-B4yKuY0c.css": "assets/stats-tab-B4yKuY0c.a5728396494b.css", "assets/datetime-column-DeCthByU.css": "assets/datetime-column-DeCthByU.1f3bf499e063.css", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/reader-DOQuhDYO.css": "assets/reader-DOQuhDYO.df559ad29ab3.css", "assets/unauthorized-DWDO1s6-.css": "assets/unauthorized-DWDO1s6-.d34daac27dcf.css", "assets/VExpansionPanels-DsEXVcCv.css": "assets/VExpansionPanels-DsEXVcCv.20b475255e66.css", "assets/VExpansionPanels-D4psrz4h.js": "assets/VExpansionPanels-D4psrz4h.b55b901da65f.js", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/stats-tab-DL4J3pvZ.js": "assets/stats-tab-DL4J3pvZ.b9056e70a70f.js", "assets/VTextField-BssVoDhB.css": "assets/VTextField-BssVoDhB.4cc388b4aa3c.css", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "assets/VCombobox-CGyBgdqr.js": "assets/VCombobox-CGyBgdqr.2f85598cc1a4.js", "assets/confirm-dialog-CYEBgtj5.js": "assets/confirm-dialog-CYEBgtj5.a5f142656c12.js", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.80e911824648.json"}, "version": "1.1", "hash": "5a4e0c1aba28"}
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/jquery.json-view.min.css": "rest_framework/docs/css/jquery.json-view.min.a2e6beeb6710.css", "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/js/ajax-form.js": "rest_framework/js/ajax-form.4e1cdcb7acab.js", "rest_framework/js/csrf.js": "rest_framework/js/csrf.455080a7b2ce.js", "rest_framework/js/prettify-min.js": "rest_framework/js/prettify-min.709bfcc456c6.js", "rest_framework/js/jquery-3.7.1.min.js": "rest_framework/js/jquery-3.7.1.min.2c872dbe60f4.js", "rest_framework/js/default.js": "rest_framework/js/default.5b08897dbdc3.js", "rest_framework/js/bootstrap.min.js": "rest_framework/js/bootstrap.min.2f34b630ffe3.js", "rest_framework/js/coreapi-0.1.1.js": "rest_framework/js/coreapi-0.1.1.8851fb9336c9.js", "rest_framework/js/load-ajax-form.js": "rest_framework/js/load-ajax-form.8cdb3a9f3466.js", "rest_framework/fonts/fontawesome-webfont.woff": "rest_framework/fonts/fontawesome-webfont.3293616ec0c6.woff", "rest_framework/fonts/glyphicons-halflings-regular.eot": "rest_framework/fonts/glyphicons-halflings-regular.f4769f9bdb74.eot", "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/glyphicons-halflings-regular.woff2": "rest_framework/fonts/glyphicons-halflings-regular.448c34a56d69.woff2", "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.svg": "rest_framework/fonts/glyphicons-halflings-regular.08eda92397ae.svg", "rest_framework/fonts/fontawesome-webfont.eot": "rest_framework/fonts/fontawesome-webfont.8b27bc96115c.eot", "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/css/bootstrap.min.css.map": "rest_framework/css/bootstrap.min.css.cafbda9c0e9e.map", "rest_framework/css/default.css": "rest_framework/css/default.789dfb5732d7.css", "rest_framework/css/bootstrap.min.css": "rest_framework/css/bootstrap.min.f17d4516b026.css", "rest_framework/css/prettify.css": "rest_framework/css/prettify.a987f72342ee.css", "rest_framework/css/bootstrap-theme.min.css": "rest_framework/css/bootstrap-theme.min.1d4b05b397c3.css", "rest_framework/css/bootstrap-tweaks.css": "rest_framework/css/bootstrap-tweaks.ee4ee6acf9eb.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.map": "rest_framework/css/bootstrap-theme.min.css.51806092cc05.map", "img/publisher.svg": "img/publisher.82d378fcd601.svg", "img/logo-32.webp": "img/logo-32.5688944495bf.webp", "img/logo.svg": "img/logo.0f5344a028ca.svg", "img/story-arc.svg": "img/story-arc.ca10014355ef.svg", "img/missing-cover-165.webp": "img/missing-cover-165.0743171f2148.webp", "img/series.svg": "img/series.ec8a889b8f95.svg", "img/logo-maskable-180.webp": "img/logo-maskable-180.c58d966aa64e.webp", "img/missing-cover.svg": "img/missing-cover.ddb038cc2c56.svg", "img/folder.svg": "img/folder.f28936a3701d.svg", "img/volume.svg": "img/volume.814e4c79461a.svg", "img/logo-maskable.svg": "img/logo-maskable.8f7de7f1c6a8.svg", "img/imprint.svg": "img/imprint.7c2adebb76d6.svg", "pwa/offline.html": "pwa/offline.7bfaf9f94bf9.html", "js/choices.json": "js/choices.2f6b0359e1ef.json", "js/choices-admin.json": "js/choices-admin.3521e2a863df.json", "assets/group-tab-CBKuS3Iv.css": "assets/group-tab-CBKuS3Iv.7e4746634782.css", "assets/http-error-DKulXuTl.css": "assets/http-error-DKulXuTl.95e42988d5d5.css", "assets/VSelect-ARDhJiQK.css": "assets/VSelect-ARDhJiQK.372c098fb475.css", "assets/VSelectionControl-BFq--28I.css": "assets/VSelectionControl-BFq--28I.9687c77461fc.css", "assets/VTextField-DEiVah7O.js": "assets/VTextField-DEiVah7O.fbf3130f2dea.js", "assets/VSlideGroup-CRJazQeR.js": "assets/VSlideGroup-CRJazQeR.cb89b2222518.js", "assets/change-password-dialog-Du1bwEt1.js": "assets/change-password-dialog-Du1bwEt1.a4c0205c9bc4.js", "assets/pdf-doc-CMzaSV_K.css": "assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css", "assets/VExpansionPanels-CurqcyFg.js": "assets/VExpansionPanels-CurqcyFg.860017abf25d.js", "assets/VRadioGroup-B7n5ReZ-.js": "assets/VRadioGroup-B7n5ReZ-.8acbc71ac411.js", "assets/materialdesignicons-webfont-PXm3-2wK.woff": "assets/materialdesignicons-webfont-PXm3-2wK.026b7ac9c43c.woff", "assets/VSlideGroup-Dk_1upHt.css": "assets/VSlideGroup-Dk_1upHt.2c836070ec75.css", "assets/admin-drawer-panel-mne3FIj6.css": "assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css", "assets/VTable-DflzroDq.css": "assets/VTable-DflzroDq.160504d82195.css", "assets/settings-drawer-nZ9HvKSZ.css": "assets/settings-drawer-nZ9HvKSZ.01744e131806.css", "assets/VDialog-lQ13FnRR.css": "assets/VDialog-lQ13FnRR.1c801ad756b2.css", "assets/VTable-DtyBEZ5i.js": "assets/VTable-DtyBEZ5i.6848f15ea03f.js", "assets/relation-chips-BGitp3yx.css": "assets/relation-chips-BGitp3yx.b3e7d178a615.css", "assets/main-Ztp855PW.css": "assets/main-Ztp855PW.d46d718fac45.css", "assets/flag-tab-DR_nY0Jg.js": "assets/flag-tab-DR_nY0Jg.7323e386c6ed.js", "assets/settings-drawer-XH4sNDNT.js": "assets/settings-drawer-XH4sNDNT.98ee4b24901c.js", "assets/VSelect-FYWbH55D.js": "assets/VSelect-FYWbH55D.83e91b93fdd1.js", "assets/pagination-toolbar-i2VoXaGH.js": "assets/pagination-toolbar-i2VoXaGH.c3dbb703af2e.js", "assets/VRadioGroup-i2qP9Wu9.css": "assets/VRadioGroup-i2qP9Wu9.8e3741381961.css", "assets/pdf-doc-BLxZLD38.js": "assets/pdf-doc-BLxZLD38.4cf9622c535f.js", "assets/VSelectionControl-CUlT3cc_.js": "assets/VSelectionControl-CUlT3cc_.63f60240eb49.js", "assets/admin-D4sw3N4T.css": "assets/admin-D4sw3N4T.b590f88a62cf.css", "assets/group-tab-JKsMHOmK.js": "assets/group-tab-JKsMHOmK.d2c2f3fceec1.js", "assets/relation-chips-CmeSIsRq.js": "assets/relation-chips-CmeSIsRq.f169f7c3f48d.js", "assets/reader-YbzUiD0T.css": "assets/reader-YbzUiD0T.efb15835b39d.css", "assets/change-password-dialog-0u6IJA87.css": "assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css", "assets/copy-to-clipboard-DoUvDJVx.js": "assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js", "assets/stats-tab-BGxkeKnY.js": "assets/stats-tab-BGxkeKnY.44cf18ca895d.js", "assets/library-tab-D9GTugZ6.js": "assets/library-tab-D9GTugZ6.8e3e6ebd38b7.js", "assets/task-tab-B74Kah_b.js": "assets/task-tab-B74Kah_b.58627993d3c6.js", "assets/VWindowItem-ByIod57Y.css": "assets/VWindowItem-ByIod57Y.f7fb19752bf3.css", "assets/VCheckbox-DtXDauZz.js": "assets/VCheckbox-DtXDauZz.54986ad416a1.js", "assets/pagination-toolbar-BA9ifTed.css": "assets/pagination-toolbar-BA9ifTed.b4865334ee08.css", "assets/library-tab-BygwYPnO.css": "assets/library-tab-BygwYPnO.94e92aa60bf2.css", "assets/confirm-dialog-eKN71B8a.js": "assets/confirm-dialog-eKN71B8a.af6187dc12bd.js", "assets/user-tab-BP2TTPg5.js": "assets/user-tab-BP2TTPg5.e63a5661a1a5.js", "assets/browser-Cy0OgQJC.css": "assets/browser-Cy0OgQJC.fabbd1aaffcf.css", "assets/confirm-dialog-BmhedLCx.css": "assets/confirm-dialog-BmhedLCx.7c2103d319d4.css", "assets/admin-S53JleJt.js": "assets/admin-S53JleJt.f629d90f73a1.js", "assets/materialdesignicons-webfont-B7mPwVP_.ttf": "assets/materialdesignicons-webfont-B7mPwVP_.6e435534bd35.ttf", "assets/VWindowItem-BVIaHO-S.js": "assets/VWindowItem-BVIaHO-S.419e4ecf523d.js", "assets/VCombobox-DZmum3X-.js": "assets/VCombobox-DZmum3X-.330883d59742.js", "assets/VCombobox-BksEr4D_.css": "assets/VCombobox-BksEr4D_.73429bb75f42.css", "assets/browser-BHHgqDP7.js": "assets/browser-BHHgqDP7.7ada4400d63f.js", "assets/main-BJiV8IG3.js": "assets/main-BJiV8IG3.9d708627091f.js", "assets/flag-tab-DBqpLKi7.css": "assets/flag-tab-DBqpLKi7.dcfef39f541e.css", "assets/task-tab-1XiWn9xP.css": "assets/task-tab-1XiWn9xP.06cb8e1b794d.css", "assets/VCheckboxBtn-B-iT1MnG.js": "assets/VCheckboxBtn-B-iT1MnG.8505f6eed0fe.js", "assets/filter-_XbKRiIR.js": "assets/filter-_XbKRiIR.2c7ac68a2d03.js", "assets/http-error-Bw1wUv3U.js": "assets/http-error-Bw1wUv3U.c8313a2fb129.js", "assets/reader-D2O_yHTW.js": "assets/reader-D2O_yHTW.70d616ddc2cc.js", "assets/datetime-column-DeCthByU.css": "assets/datetime-column-DeCthByU.1f3bf499e063.css", "assets/materialdesignicons-webfont-CSr8KVlo.eot": "assets/materialdesignicons-webfont-CSr8KVlo.8ced95a05e3c.eot", "assets/VDialog-DUdNpXLj.js": "assets/VDialog-DUdNpXLj.6088d518d3af.js", "assets/datetime-column-CWp600fz.js": "assets/datetime-column-CWp600fz.e8a969126bf5.js", "assets/unauthorized-DWDO1s6-.css": "assets/unauthorized-DWDO1s6-.d34daac27dcf.css", "assets/stats-tab-CI0u-X7Y.css": "assets/stats-tab-CI0u-X7Y.2d1b6a0d4139.css", "assets/admin-drawer-panel-CnAnGx7w.js": "assets/admin-drawer-panel-CnAnGx7w.1e4eb021cb21.js", "assets/unauthorized-BF45der6.js": "assets/unauthorized-BF45der6.d34f8022124b.js", "assets/VExpansionPanels-DsEXVcCv.css": "assets/VExpansionPanels-DsEXVcCv.20b475255e66.css", "assets/VCheckbox-CMKdyl0R.css": "assets/VCheckbox-CMKdyl0R.016ed8d222c4.css", "assets/VTextField-BssVoDhB.css": "assets/VTextField-BssVoDhB.4cc388b4aa3c.css", "assets/materialdesignicons-webfont-Dp5v-WZN.woff2": "assets/materialdesignicons-webfont-Dp5v-WZN.1d7bcee1b302.woff2", "robots.txt": "robots.b6216d61c03e.txt", "manifest.json": "manifest.7033f9a22987.json"}, "version": "1.1", "hash": "792a07608140"}
codex/urls/root.py CHANGED
@@ -7,7 +7,7 @@ from django.contrib.staticfiles.storage import staticfiles_storage
7
7
  from django.urls import include, path, register_converter, set_script_prefix
8
8
  from django.views.generic.base import RedirectView
9
9
 
10
- from codex.settings.settings import ROOT_PATH
10
+ from codex.settings.settings import DEBUG, ROOT_PATH
11
11
  from codex.urls.converters import GroupConverter, IntListConverter
12
12
 
13
13
  register_converter(GroupConverter, "group")
@@ -17,7 +17,17 @@ if ROOT_PATH:
17
17
  # Django 5 bug https://code.djangoproject.com/ticket/35169#comment:11
18
18
  set_script_prefix(ROOT_PATH)
19
19
 
20
- urlpatterns = [
20
+
21
+ urlpatterns = []
22
+ if DEBUG:
23
+ # Pyright doesn't follow logic so will try to find these types.
24
+ from schema_graph.views import Schema # type: ignore
25
+
26
+ urlpatterns += [
27
+ path("schema/", Schema.as_view()), # type: ignore
28
+ ]
29
+
30
+ urlpatterns += [
21
31
  path(
22
32
  "favicon.ico",
23
33
  RedirectView.as_view(url=staticfiles_storage.url("img/logo-32.webp")),
@@ -4,7 +4,7 @@ from drf_spectacular.utils import extend_schema
4
4
  from rest_framework.response import Response
5
5
 
6
6
  from codex.models import Timestamp
7
- from codex.serializers.admin import APIKeySerializer
7
+ from codex.serializers.admin.stats import APIKeySerializer
8
8
  from codex.views.admin.auth import AdminGenericAPIView
9
9
 
10
10
 
codex/views/admin/flag.py CHANGED
@@ -5,7 +5,7 @@ from codex.librarian.notifier.tasks import LIBRARY_CHANGED_TASK
5
5
  from codex.logger.logging import get_logger
6
6
  from codex.models import AdminFlag
7
7
  from codex.registration import patch_registration_setting
8
- from codex.serializers.admin import AdminFlagSerializer
8
+ from codex.serializers.admin.flags import AdminFlagSerializer
9
9
  from codex.views.admin.auth import AdminModelViewSet
10
10
 
11
11
  LOG = get_logger(__name__)
@@ -6,7 +6,7 @@ from django.core.cache import cache
6
6
  from codex.librarian.mp_queue import LIBRARIAN_QUEUE
7
7
  from codex.librarian.notifier.tasks import LIBRARY_CHANGED_TASK
8
8
  from codex.logger.logging import get_logger
9
- from codex.serializers.admin import GroupSerializer
9
+ from codex.serializers.admin.groups import GroupSerializer
10
10
  from codex.views.admin.auth import AdminModelViewSet
11
11
 
12
12
  LOG = get_logger(__name__)
@@ -18,7 +18,7 @@ from codex.librarian.watchdog.tasks import (
18
18
  )
19
19
  from codex.logger.logging import get_logger
20
20
  from codex.models import FailedImport, Folder, Library
21
- from codex.serializers.admin import (
21
+ from codex.serializers.admin.libraries import (
22
22
  AdminFolderListSerializer,
23
23
  AdminFolderSerializer,
24
24
  FailedImportSerializer,
@@ -1,184 +1,70 @@
1
- """Admin Flag View."""
1
+ """Admin Stats View."""
2
2
 
3
- from pathlib import Path
4
- from platform import machine, python_version, release, system
5
- from types import MappingProxyType
6
3
  from typing import ClassVar
7
4
 
8
- from caseconverter import snakecase
9
- from django.contrib.sessions.models import Session
10
- from django.db.models import Count
11
5
  from drf_spectacular.utils import extend_schema
12
6
  from rest_framework.response import Response
7
+ from rest_framework.serializers import empty
13
8
 
9
+ from codex.librarian.stats import CodexStats
14
10
  from codex.logger.logging import get_logger
15
- from codex.models import (
16
- Comic,
17
- Library,
18
- Timestamp,
19
- )
11
+ from codex.models.admin import Timestamp
20
12
  from codex.permissions import HasAPIKeyOrIsAdminUser
21
- from codex.serializers.admin import AdminStatsRequestSerializer, AdminStatsSerializer
22
- from codex.version import VERSION
13
+ from codex.serializers.admin.stats import (
14
+ AdminStatsRequestSerializer,
15
+ StatsSerializer,
16
+ )
23
17
  from codex.views.admin.auth import AdminGenericAPIView
24
- from codex.views.const import CONFIG_MODELS, METADATA_MODELS, STATS_GROUP_MODELS
25
18
 
26
19
  LOG = get_logger(__name__)
27
- _KEY_MODELS_MAP = MappingProxyType(
28
- {
29
- "config": CONFIG_MODELS,
30
- "groups": STATS_GROUP_MODELS,
31
- "metadata": METADATA_MODELS,
32
- }
33
- )
34
- _DOCKERENV_PATH = Path("/.dockerenv")
35
- _CGROUP_PATH = Path("/proc/self/cgroup")
36
20
 
37
21
 
38
22
  class AdminStatsView(AdminGenericAPIView):
39
23
  """Admin Flag Viewset."""
40
24
 
41
25
  permission_classes: ClassVar[list] = [HasAPIKeyOrIsAdminUser] # type: ignore
42
- serializer_class = AdminStatsSerializer
26
+ serializer_class = StatsSerializer
43
27
  input_serializer_class = AdminStatsRequestSerializer
44
28
 
45
- @classmethod
46
- def _is_docker(cls):
47
- """Test if we're in a docker container."""
48
- try:
49
- return _DOCKERENV_PATH.is_file() or "docker" in _CGROUP_PATH.read_text()
50
- except Exception:
51
- return False
52
-
53
- def _get_request_counts(self, key):
54
- """Get lowercase names of requested fields."""
55
- request_count_list = self.request.GET.get(key, "")
56
- request_counts = set()
57
- for name in request_count_list.split(","):
58
- if name:
59
- request_counts.add(name.lower())
60
- return request_counts
61
-
62
- def _get_models(self, key):
63
- """Get models from request params."""
64
- request_model_list = self.request.GET.get(key)
65
- all_models = _KEY_MODELS_MAP[key]
66
- if request_model_list:
67
- models = []
68
- for model_name in request_model_list.split(","):
69
- for model in all_models:
70
- if model.__name__.lower() == model_name.lower():
71
- models.append(model)
72
- else:
73
- models = all_models
74
- return models
75
-
76
- def _get_model_counts(self, key):
77
- """Get database counts of each model group."""
78
- models = self._get_models(key)
79
- obj = {}
80
- for model in models:
81
- vnp = model._meta.verbose_name_plural
82
- if vnp:
83
- title = vnp.title()
84
- else:
85
- LOG.warning(f"No verbose plural name for {model.__name__}")
86
- title = model.__name__
87
-
88
- vnp_name = snakecase(title)
89
- vnp_name += "_count"
90
- qs = model.objects
91
- if model == Library:
92
- qs = qs.filter(covers_only=False)
93
- obj[vnp_name] = qs.count()
94
- return obj
95
-
96
- @staticmethod
97
- def _get_anon_sessions():
98
- """Return the number of anonymous sessions."""
99
- sessions = Session.objects.all()
100
- anon_sessions = 0
101
- for encoded_session in sessions:
102
- session = encoded_session.get_decoded()
103
- if not session.get("_auth_user_id"):
104
- anon_sessions += 1
105
-
106
- return anon_sessions
107
-
108
- def _get_platform(self, obj):
109
- """Add dict of platform information to object."""
110
- platform = {
111
- "docker": self._is_docker(),
112
- "machine": machine(),
113
- "system": system(),
114
- "system_release": release(),
115
- "python": python_version(),
116
- "codex": VERSION,
117
- }
118
- obj["platform"] = platform
119
-
120
- def _get_config(self, obj):
121
- """Add dict of config informaation to object."""
122
- config = self._get_model_counts("config")
123
- request_counts = self._get_request_counts("config")
124
- if not request_counts or "sessionanon" in request_counts:
125
- config["sessions_anon_count"] = self._get_anon_sessions()
126
- if not request_counts or "apikey" in request_counts:
127
- config["api_key"] = Timestamp.objects.get(
128
- key=Timestamp.TimestampChoices.API_KEY.value
129
- ).version
130
- obj["config"] = config
131
-
132
- def _get_file_types(self):
133
- """Query for file types."""
134
- file_types = {}
135
- qs = (
136
- Comic.objects.values("file_type")
137
- .annotate(count=Count("file_type"))
138
- .order_by()
139
- )
140
- for query_group in qs:
141
- value = query_group["file_type"]
142
- name = value.lower() if value else "unknown"
143
- field = f"{name}_count"
144
- file_types[field] = query_group["count"]
145
- return file_types
146
-
147
- def _get_groups(self, obj):
148
- """Add dict of groups information to object."""
149
- if not self.params or "groups" in self.params:
150
- groups = self._get_model_counts("groups")
151
- obj["groups"] = groups
152
-
153
- if not self.params or "fileTypes" in self.params:
154
- file_types = self._get_file_types()
155
- obj["file_types"] = file_types
156
-
157
- def _get_metadata(self, obj):
158
- """Add dict of metadata counts to object."""
159
- metadata = self._get_model_counts("metadata")
160
- obj["metadata"] = metadata
29
+ def _parse_params(self):
30
+ """Parse and input params."""
31
+ data = self.request.GET
32
+
33
+ input_serializer = self.input_serializer_class(data=data)
34
+ input_serializer.is_valid(raise_exception=True)
35
+ params = {}
36
+ if input_serializer.validated_data and not isinstance(
37
+ input_serializer.validated_data, empty
38
+ ):
39
+ for key, value in input_serializer.validated_data.items():
40
+ if value:
41
+ params[key] = value
42
+ return params
43
+
44
+ def _add_api_key(self, obj):
45
+ """Add the api key to the config object if specified."""
46
+ request_counts = self.params.get("config", {})
47
+ if request_counts and ("apikey" not in request_counts):
48
+ return
49
+ api_key = Timestamp.objects.get(
50
+ key=Timestamp.TimestampChoices.API_KEY.value
51
+ ).version
52
+ if "config" not in obj:
53
+ obj["config"] = {}
54
+ obj["config"]["api_key"] = api_key
161
55
 
162
56
  def get_object(self):
163
- """Construct the stats object."""
164
- obj = {}
165
- if not self.params or "platform" in self.params:
166
- self._get_platform(obj)
167
- if not self.params or "config" in self.params:
168
- self._get_config(obj)
169
- if not self.params or "groups" in self.params:
170
- self._get_groups(obj)
171
- if not self.params or "metadata" in self.params:
172
- self._get_metadata(obj)
57
+ """Get the stats object with an api key."""
58
+ getter = CodexStats(self.params)
59
+ obj = getter.get()
60
+ self._add_api_key(obj)
173
61
  return obj
174
62
 
175
63
  @extend_schema(parameters=[input_serializer_class])
176
64
  def get(self, *_args, **_kwargs):
177
65
  """Get the stats object and serialize it."""
178
- input_serializer = self.input_serializer_class(data=self.request.GET)
179
- input_serializer.is_valid()
180
- self.params = frozenset(input_serializer.validated_data.get("params", {})) # type: ignore
181
-
66
+ self.params = self._parse_params()
182
67
  obj = self.get_object()
68
+
183
69
  serializer = self.get_serializer(obj)
184
70
  return Response(serializer.data)
@@ -44,7 +44,7 @@ from codex.librarian.watchdog.tasks import (
44
44
  )
45
45
  from codex.logger.logging import get_logger
46
46
  from codex.models import LibrarianStatus
47
- from codex.serializers.admin import AdminLibrarianTaskSerializer
47
+ from codex.serializers.admin.tasks import AdminLibrarianTaskSerializer
48
48
  from codex.serializers.mixins import OKSerializer
49
49
  from codex.serializers.models.admin import LibrarianStatusSerializer
50
50
  from codex.views.admin.auth import AdminAPIView, AdminReadOnlyModelViewSet
codex/views/admin/user.py CHANGED
@@ -10,7 +10,7 @@ from rest_framework.status import HTTP_202_ACCEPTED, HTTP_400_BAD_REQUEST
10
10
  from codex.librarian.mp_queue import LIBRARIAN_QUEUE
11
11
  from codex.librarian.notifier.tasks import LIBRARY_CHANGED_TASK
12
12
  from codex.logger.logging import get_logger
13
- from codex.serializers.admin import UserChangePasswordSerializer, UserSerializer
13
+ from codex.serializers.admin.users import UserChangePasswordSerializer, UserSerializer
14
14
  from codex.views.admin.auth import AdminGenericAPIView, AdminModelViewSet
15
15
 
16
16
  LOG = get_logger(__name__)
@@ -81,7 +81,10 @@ class BrowserView(BrowserTitleView):
81
81
  self.group_query = self.group_class.objects.none()
82
82
  else:
83
83
  reason = f"{group}__in={pks} does not exist!"
84
- self.raise_redirect(reason, route_mask={"group": group})
84
+ settings_mask = {"breadcrumbs": []}
85
+ self.raise_redirect(
86
+ reason, route_mask={"group": group}, settings_mask=settings_mask
87
+ )
85
88
  elif self.group_class:
86
89
  self.group_query = self.group_class.objects.none()
87
90
  else:
@@ -18,9 +18,9 @@ _FILTER_REL_MAP = MappingProxyType(
18
18
  class ComicFieldFilterView(GroupFilterView):
19
19
  """Comic field filters."""
20
20
 
21
- def _filter_by_comic_field(self, field, rel_prefix):
21
+ @staticmethod
22
+ def _filter_by_comic_field(field, rel_prefix, filter_list):
22
23
  """Filter by a comic any2many attribute."""
23
- filter_list = self.params["filters"].get(field) # type: ignore
24
24
  filter_query = Q()
25
25
  if not filter_list:
26
26
  return filter_query
@@ -36,10 +36,19 @@ class ComicFieldFilterView(GroupFilterView):
36
36
  filter_query |= Q(**{f"{rel}__in": filter_list})
37
37
  return filter_query
38
38
 
39
+ @classmethod
40
+ def get_all_comic_field_filters(cls, rel_prefix, filters):
41
+ """Get all comicfiled filters for rel_prefix."""
42
+ comic_field_filter = Q()
43
+ for field in cls.FILTER_ATTRIBUTES:
44
+ filter_list = filters.get(field, [])
45
+ comic_field_filter &= cls._filter_by_comic_field(
46
+ field, rel_prefix, filter_list
47
+ )
48
+ return comic_field_filter
49
+
39
50
  def get_comic_field_filter(self, model):
40
51
  """Filter the comics based on the form filters."""
41
- comic_field_filter = Q()
42
52
  rel_prefix = self.get_rel_prefix(model)
43
- for attribute in self.FILTER_ATTRIBUTES:
44
- comic_field_filter &= self._filter_by_comic_field(attribute, rel_prefix)
45
- return comic_field_filter
53
+ filters = self.params["filters"] # type: ignore
54
+ return self.get_all_comic_field_filters(rel_prefix, filters)
@@ -13,6 +13,7 @@ from codex.views.browser.annotations import BrowserAnnotationsView
13
13
  from codex.views.const import (
14
14
  MAX_OBJ_PER_PAGE,
15
15
  )
16
+ from codex.views.util import Route
16
17
 
17
18
  LOG = get_logger(__name__)
18
19
 
@@ -20,30 +21,79 @@ LOG = get_logger(__name__)
20
21
  class BrowserPaginateView(BrowserAnnotationsView):
21
22
  """Browser pagination."""
22
23
 
24
+ def _get_back_one_page_route(self, num_pages):
25
+ """Get max page if oob or 1."""
26
+ LOG.debug("Redirect back one page.")
27
+ group = self.kwargs.get("group")
28
+ pks = self.kwargs.get("pks")
29
+ page = self.kwargs.get("page", 1)
30
+ new_page = num_pages if num_pages and page > num_pages else 1
31
+ pks = pks if pks else "0"
32
+ return {"group": group, "pks": pks, "page": new_page}
33
+
34
+ def _get_up_breadcrumbs(self):
35
+ """Walk up the breadcrumbs to get the next level up."""
36
+ breadcrumbs = self.params.get("breadcrumbs", [])
37
+ new_breadcrumbs = []
38
+ level = False
39
+ group = self.kwargs.get("group")
40
+ pks = self.kwargs.get("pks")
41
+ page = self.kwargs.get("page", 1)
42
+ current_route = Route(group=group, pks=pks, page=page)
43
+ for crumb in reversed(breadcrumbs):
44
+ if not level:
45
+ crumb_route = Route(**crumb)
46
+ level = current_route & crumb_route
47
+ continue
48
+ new_breadcrumbs = [crumb, *new_breadcrumbs]
49
+
50
+ if not new_breadcrumbs:
51
+ if group not in ("f", "a"):
52
+ group = "r"
53
+ top_route = {"group": group, "pks": (), "page": 1}
54
+ new_breadcrumbs = [top_route]
55
+ return new_breadcrumbs
56
+
57
+ def _get_up_page_redirect(self):
58
+ """Walk up the breadcrumbs."""
59
+ try:
60
+ up_breadcrumbs = self._get_up_breadcrumbs()
61
+ route_mask = up_breadcrumbs[-1]
62
+ settings_mask = {"breadcrumbs": up_breadcrumbs}
63
+ LOG.debug("Redirect up a level.")
64
+ except IndexError:
65
+ group = self.kwargs.get("group")
66
+ pks = "0"
67
+ new_page = 1
68
+ route_mask = {"group": group, "pks": pks, "page": new_page}
69
+ settings_mask = {"breadcrumbs": [route_mask]}
70
+ LOG.debug("Redirect to all at current group.")
71
+ return route_mask, settings_mask
72
+
23
73
  def _check_page_in_bounds(self, total_count):
24
74
  """Redirect page out of bounds."""
25
75
  pks = self.kwargs.get("pks")
26
76
  page = self.kwargs.get("page", 1)
27
77
  num_pages = ceil(total_count / MAX_OBJ_PER_PAGE)
28
- if (not pks and page == 1) or (page >= 1 and page <= num_pages):
78
+ if page == 1 or (page >= 1 and page <= num_pages):
29
79
  # Don't redirect if on the root page for the group.
30
80
  # Or page within valid range.
31
81
  return num_pages
32
82
 
33
- # Adjust route mask for redirect
34
83
  group = self.kwargs.get("group")
84
+ reason = f"{group=} {pks=} {page=} does not exist."
85
+
86
+ # Adjust route mask for redirect
35
87
  if num_pages and page > 1:
36
- # Try page 1 first.
37
- new_page = num_pages if num_pages and page > num_pages else 1
38
- pks = pks if pks else "0"
88
+ route_mask = self._get_back_one_page_route(num_pages)
89
+ settings_mask = None
39
90
  else:
40
- # Redirect to root for the group.
41
- pks = "0"
42
- new_page = 1
91
+ # This now only occurs when page < 1
92
+ route_mask, settings_mask = self._get_up_page_redirect()
43
93
 
44
- reason = f"{page=} does not exist!"
45
- route_changes = {"group": group, "pks": pks, "page": new_page}
46
- return self.raise_redirect(reason, route_mask=route_changes)
94
+ return self.raise_redirect(
95
+ reason, route_mask=route_mask, settings_mask=settings_mask
96
+ )
47
97
 
48
98
  def _paginate_section(self, model, qs, page):
49
99
  """Paginate a group or Comic section."""
@@ -15,7 +15,6 @@ from codex.views.const import (
15
15
  )
16
16
 
17
17
  LOG = get_logger(__name__)
18
- _NO_TOP_GROUP_VALIDATE = frozenset({"metadata"})
19
18
 
20
19
 
21
20
  class BrowserValidateView(BrowserBaseView):
@@ -56,8 +55,6 @@ class BrowserValidateView(BrowserBaseView):
56
55
  return valid_top_groups
57
56
 
58
57
  def _validate_top_group(self, valid_top_groups):
59
- # if self.TARGET in _NO_TOP_GROUP_VALIDATE:
60
- # return
61
58
  nav_group = self.kwargs.get("group")
62
59
  top_group = self.params.get("top_group")
63
60
  if top_group not in valid_top_groups:
@@ -72,7 +69,8 @@ class BrowserValidateView(BrowserBaseView):
72
69
  pks = self.kwargs.get("pks", ())
73
70
  page = self.kwargs["page"]
74
71
  route = {"group": nav_group, "pks": pks, "page": page}
75
- settings_mask = {"top_group": valid_top_group}
72
+ breadcrumbs = []
73
+ settings_mask = {"top_group": valid_top_group, "breadcrumbs": breadcrumbs}
76
74
  self.raise_redirect(reason, route, settings_mask)
77
75
 
78
76
  def set_valid_browse_nav_groups(self, valid_top_groups):
@@ -109,7 +107,11 @@ class BrowserValidateView(BrowserBaseView):
109
107
  if not self.admin_flags["folder_view"]:
110
108
  reason = "folder view disabled"
111
109
  valid_top_groups = self._get_valid_browse_top_groups()
112
- settings_mask = {"top_group": valid_top_groups[0]}
110
+ breadcrumbs = []
111
+ settings_mask = {
112
+ "top_group": valid_top_groups[0],
113
+ "breadcrumbs": breadcrumbs,
114
+ }
113
115
  self.raise_redirect(reason, settings_mask=settings_mask)
114
116
 
115
117
  valid_top_groups = (FOLDER_GROUP,)
@@ -6,11 +6,9 @@ from codex.views.opds.auth import OPDSAuthMixin
6
6
  from codex.views.reader.page import ReaderPageView
7
7
 
8
8
 
9
- class OPDSCoverView(CoverView):
9
+ class OPDSCoverView(OPDSAuthMixin, CoverView):
10
10
  """Cover View with Basic Auth."""
11
11
 
12
- # Panels sometimes doesn't send auth with cover requests.
13
-
14
12
 
15
13
  class OPDSDownloadView(OPDSAuthMixin, DownloadView):
16
14
  """Download View with Basic Auth."""
codex/views/opds/const.py CHANGED
@@ -84,3 +84,13 @@ class MimeType:
84
84
 
85
85
 
86
86
  OPDS_M2M_MODELS = (Character, Genre, Location, SeriesGroup, StoryArc, Tag, Team)
87
+
88
+
89
+ class UserAgentNames:
90
+ """Control whether to hack in facets with nav links."""
91
+
92
+ CLIENT_REORDERS = frozenset({"Chunky"})
93
+ FACET_SUPPORT = frozenset({"yar"}) # kybooks
94
+ SIMPLE_DOWNLOAD_MIME_TYPES = frozenset({"PocketBook Reader"})
95
+ # Other known valid names:
96
+ # "Chunky"
codex/views/opds/util.py CHANGED
@@ -93,3 +93,13 @@ def full_redirect_view(url_name):
93
93
  return response
94
94
 
95
95
  return func
96
+
97
+
98
+ def get_user_agent_name(request):
99
+ """Get the first part of the user agent."""
100
+ user_agent_name = ""
101
+ if (user_agent := request.headers.get("User-Agent")) and (
102
+ user_agent_parts := user_agent.split("/", 1)
103
+ ):
104
+ user_agent_name = user_agent_parts[0]
105
+ return user_agent_name