codex 1.7.3a2__py3-none-any.whl → 1.7.5__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 (632) hide show
  1. codex/asgi.py +2 -1
  2. codex/choices/__init__.py +1 -0
  3. codex/{choices.py → choices/admin.py} +47 -146
  4. codex/choices/browser.py +85 -0
  5. codex/{choices_to_json.py → choices/choices_to_json.py} +25 -19
  6. codex/choices/notifications.py +16 -0
  7. codex/choices/reader.py +33 -0
  8. codex/db.py +2 -2
  9. codex/exceptions.py +7 -8
  10. codex/librarian/bookmark/bookmarkd.py +8 -4
  11. codex/librarian/bookmark/update.py +84 -49
  12. codex/librarian/covers/create.py +10 -8
  13. codex/librarian/covers/path.py +6 -5
  14. codex/librarian/covers/purge.py +4 -4
  15. codex/librarian/importer/aggregate.py +2 -2
  16. codex/librarian/importer/cache.py +1 -4
  17. codex/librarian/importer/create_comics.py +2 -2
  18. codex/librarian/importer/create_covers.py +1 -1
  19. codex/librarian/importer/create_fks.py +4 -3
  20. codex/librarian/importer/deleted.py +3 -3
  21. codex/librarian/importer/extract.py +56 -37
  22. codex/librarian/importer/failed_imports.py +1 -1
  23. codex/librarian/importer/importer.py +8 -3
  24. codex/librarian/importer/importerd.py +3 -3
  25. codex/librarian/importer/link_comics.py +2 -1
  26. codex/librarian/importer/moved.py +2 -7
  27. codex/librarian/importer/query_fks.py +1 -4
  28. codex/librarian/importer/tasks.py +1 -1
  29. codex/librarian/janitor/integrity.py +27 -20
  30. codex/librarian/janitor/janitor.py +1 -1
  31. codex/librarian/janitor/latest_version.py +1 -1
  32. codex/librarian/janitor/update.py +5 -2
  33. codex/librarian/janitor/vacuum.py +1 -1
  34. codex/librarian/librariand.py +3 -2
  35. codex/librarian/notifier/notifierd.py +2 -1
  36. codex/librarian/notifier/tasks.py +17 -3
  37. codex/librarian/search/optimize.py +1 -1
  38. codex/librarian/search/remove.py +1 -1
  39. codex/librarian/search/update.py +4 -4
  40. codex/librarian/telemeter/stats.py +7 -6
  41. codex/librarian/telemeter/telemeter.py +0 -1
  42. codex/librarian/watchdog/db_snapshot.py +3 -3
  43. codex/librarian/watchdog/dir_snapshot_diff.py +3 -4
  44. codex/librarian/watchdog/emitter.py +4 -4
  45. codex/librarian/watchdog/event_batcherd.py +2 -1
  46. codex/librarian/watchdog/events.py +1 -2
  47. codex/librarian/watchdog/observers.py +15 -10
  48. codex/logger/logging.py +1 -1
  49. codex/memory.py +3 -2
  50. codex/migrations/0001_init.py +1 -2
  51. codex/migrations/0014_pdf_issue_suffix_remove_cover_image_sort_name.py +1 -2
  52. codex/migrations/0018_rename_userbookmark_bookmark.py +1 -2
  53. codex/migrations/0028_telemeter.py +1 -5
  54. codex/models/base.py +3 -1
  55. codex/models/bookmark.py +4 -7
  56. codex/models/comic.py +2 -2
  57. codex/models/functions.py +12 -5
  58. codex/models/groups.py +1 -1
  59. codex/models/library.py +1 -1
  60. codex/models/paths.py +4 -4
  61. codex/models/query.py +8 -4
  62. codex/models/util.py +1 -1
  63. codex/permissions.py +1 -1
  64. codex/run.py +1 -1
  65. codex/serializers/admin/libraries.py +1 -3
  66. codex/serializers/admin/stats.py +4 -4
  67. codex/serializers/admin/tasks.py +12 -2
  68. codex/serializers/auth.py +4 -2
  69. codex/serializers/browser/choices.py +87 -9
  70. codex/serializers/browser/filters.py +9 -37
  71. codex/serializers/browser/metadata.py +2 -1
  72. codex/serializers/browser/mixins.py +7 -3
  73. codex/serializers/browser/mtime.py +3 -5
  74. codex/serializers/browser/page.py +5 -4
  75. codex/serializers/browser/settings.py +8 -6
  76. codex/serializers/fields/__init__.py +37 -0
  77. codex/serializers/fields/auth.py +45 -0
  78. codex/serializers/fields/browser.py +85 -0
  79. codex/serializers/fields/group.py +13 -0
  80. codex/serializers/fields/reader.py +22 -0
  81. codex/serializers/fields/sanitized.py +13 -0
  82. codex/serializers/fields/session.py +16 -0
  83. codex/serializers/fields/stats.py +36 -0
  84. codex/serializers/fields/vuetify.py +62 -0
  85. codex/serializers/models/base.py +1 -1
  86. codex/serializers/models/comic.py +2 -2
  87. codex/serializers/models/groups.py +0 -2
  88. codex/serializers/models/named.py +1 -1
  89. codex/serializers/models/pycountry.py +1 -1
  90. codex/serializers/opds/authentication.py +4 -2
  91. codex/serializers/opds/urls.py +2 -1
  92. codex/serializers/opds/v1.py +4 -3
  93. codex/serializers/opds/v2/feed.py +2 -1
  94. codex/serializers/opds/v2/links.py +23 -21
  95. codex/serializers/opds/v2/metadata.py +4 -8
  96. codex/serializers/opds/v2/progression.py +5 -3
  97. codex/serializers/opds/v2/publication.py +16 -18
  98. codex/serializers/opds/v2/unused.py +15 -8
  99. codex/serializers/reader.py +16 -14
  100. codex/serializers/redirect.py +2 -1
  101. codex/serializers/route.py +6 -4
  102. codex/serializers/settings.py +5 -3
  103. codex/serializers/versions.py +2 -1
  104. codex/settings/settings.py +4 -3
  105. codex/signals/django_signals.py +1 -1
  106. codex/startup.py +2 -2
  107. codex/static_root/assets/{VCheckbox-FaT6MGfu.f6732060f734.js → VCheckbox-CWBDr4kF.6da5bf3a4d90.js} +1 -1
  108. codex/static_root/assets/VCheckbox-CWBDr4kF.6da5bf3a4d90.js.br +0 -0
  109. codex/static_root/assets/VCheckbox-CWBDr4kF.6da5bf3a4d90.js.gz +0 -0
  110. codex/static_root/assets/{VCheckbox-FaT6MGfu.js → VCheckbox-CWBDr4kF.js} +1 -1
  111. codex/static_root/assets/VCheckbox-CWBDr4kF.js.br +0 -0
  112. codex/static_root/assets/VCheckbox-CWBDr4kF.js.gz +0 -0
  113. codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.847452d574cf.js → VCheckboxBtn-COaoh3uF.fc880a386827.js} +1 -1
  114. codex/static_root/assets/VCheckboxBtn-COaoh3uF.fc880a386827.js.br +0 -0
  115. codex/static_root/assets/VCheckboxBtn-COaoh3uF.fc880a386827.js.gz +0 -0
  116. codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.js → VCheckboxBtn-COaoh3uF.js} +1 -1
  117. codex/static_root/assets/VCheckboxBtn-COaoh3uF.js.br +0 -0
  118. codex/static_root/assets/VCheckboxBtn-COaoh3uF.js.gz +0 -0
  119. codex/static_root/assets/{VCombobox-C8QLNlSl.6f431e19a453.js → VCombobox-DkOruNH0.f30f163ee632.js} +1 -1
  120. codex/static_root/assets/VCombobox-DkOruNH0.f30f163ee632.js.br +0 -0
  121. codex/static_root/assets/VCombobox-DkOruNH0.f30f163ee632.js.gz +0 -0
  122. codex/static_root/assets/{VCombobox-C8QLNlSl.js → VCombobox-DkOruNH0.js} +1 -1
  123. codex/static_root/assets/VCombobox-DkOruNH0.js.br +0 -0
  124. codex/static_root/assets/VCombobox-DkOruNH0.js.gz +0 -0
  125. codex/static_root/assets/{VDialog-RVLeW7je.c0c7eef71eec.js → VDialog-Dr7SuC2S.b6ec7df390f4.js} +1 -1
  126. codex/static_root/assets/VDialog-Dr7SuC2S.b6ec7df390f4.js.br +0 -0
  127. codex/static_root/assets/VDialog-Dr7SuC2S.b6ec7df390f4.js.gz +0 -0
  128. codex/static_root/assets/{VDialog-RVLeW7je.js → VDialog-Dr7SuC2S.js} +1 -1
  129. codex/static_root/assets/VDialog-Dr7SuC2S.js.br +0 -0
  130. codex/static_root/assets/VDialog-Dr7SuC2S.js.gz +0 -0
  131. codex/static_root/assets/{VDivider-D1Ot4vR2.9dbee744fb3c.js → VDivider-By4u2EsM.fd5ee9cce906.js} +1 -1
  132. codex/static_root/assets/VDivider-By4u2EsM.fd5ee9cce906.js.br +0 -0
  133. codex/static_root/assets/VDivider-By4u2EsM.fd5ee9cce906.js.gz +0 -0
  134. codex/static_root/assets/{VDivider-D1Ot4vR2.js → VDivider-By4u2EsM.js} +1 -1
  135. codex/static_root/assets/VDivider-By4u2EsM.js.br +0 -0
  136. codex/static_root/assets/VDivider-By4u2EsM.js.gz +0 -0
  137. codex/static_root/assets/{VExpansionPanels-DQilGHZk.43f06d0a45e0.js → VExpansionPanels-BhO9oj0a.e99bea455c78.js} +1 -1
  138. codex/static_root/assets/VExpansionPanels-BhO9oj0a.e99bea455c78.js.br +0 -0
  139. codex/static_root/assets/VExpansionPanels-BhO9oj0a.e99bea455c78.js.gz +0 -0
  140. codex/static_root/assets/{VExpansionPanels-DQilGHZk.js → VExpansionPanels-BhO9oj0a.js} +1 -1
  141. codex/static_root/assets/VExpansionPanels-BhO9oj0a.js.br +0 -0
  142. codex/static_root/assets/VExpansionPanels-BhO9oj0a.js.gz +0 -0
  143. codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js +1 -0
  144. codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js.br +0 -0
  145. codex/static_root/assets/VForm-XhA6T0Lc.70c9c6fcb9e3.js.gz +0 -0
  146. codex/static_root/assets/VForm-XhA6T0Lc.js +1 -0
  147. codex/static_root/assets/VForm-XhA6T0Lc.js.br +0 -0
  148. codex/static_root/assets/VForm-XhA6T0Lc.js.gz +0 -0
  149. codex/static_root/assets/{VRadioGroup-DoayJpcS.fe5e0e74ffee.js → VRadioGroup-C1WCG0rO.6ebc88e4f144.js} +1 -1
  150. codex/static_root/assets/VRadioGroup-C1WCG0rO.6ebc88e4f144.js.br +3 -0
  151. codex/static_root/assets/VRadioGroup-C1WCG0rO.6ebc88e4f144.js.gz +0 -0
  152. codex/static_root/assets/{VRadioGroup-DoayJpcS.js → VRadioGroup-C1WCG0rO.js} +1 -1
  153. codex/static_root/assets/VRadioGroup-C1WCG0rO.js.br +3 -0
  154. codex/static_root/assets/VRadioGroup-C1WCG0rO.js.gz +0 -0
  155. codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js +1 -0
  156. codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js.br +0 -0
  157. codex/static_root/assets/VSelect-Bbt1vrBg.ec45ee26818a.js.gz +0 -0
  158. codex/static_root/assets/VSelect-Bbt1vrBg.js +1 -0
  159. codex/static_root/assets/VSelect-Bbt1vrBg.js.br +0 -0
  160. codex/static_root/assets/VSelect-Bbt1vrBg.js.gz +0 -0
  161. codex/static_root/assets/{VSelectionControl-TaKCeZgb.ad6c96efe57c.js → VSelectionControl-BkZZAsWo.ba6eca944e2e.js} +1 -1
  162. codex/static_root/assets/VSelectionControl-BkZZAsWo.ba6eca944e2e.js.br +0 -0
  163. codex/static_root/assets/VSelectionControl-BkZZAsWo.ba6eca944e2e.js.gz +0 -0
  164. codex/static_root/assets/{VSelectionControl-TaKCeZgb.js → VSelectionControl-BkZZAsWo.js} +1 -1
  165. codex/static_root/assets/VSelectionControl-BkZZAsWo.js.br +0 -0
  166. codex/static_root/assets/VSelectionControl-BkZZAsWo.js.gz +0 -0
  167. codex/static_root/assets/{VTable-BfcOiEpa.77ef3973bb54.js → VTable-D7P2eIc2.5e94bf2a515b.js} +1 -1
  168. codex/static_root/assets/VTable-D7P2eIc2.5e94bf2a515b.js.br +0 -0
  169. codex/static_root/assets/VTable-D7P2eIc2.5e94bf2a515b.js.gz +0 -0
  170. codex/static_root/assets/{VTable-BfcOiEpa.js → VTable-D7P2eIc2.js} +1 -1
  171. codex/static_root/assets/VTable-D7P2eIc2.js.br +0 -0
  172. codex/static_root/assets/VTable-D7P2eIc2.js.gz +0 -0
  173. codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js +1 -0
  174. codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js.br +0 -0
  175. codex/static_root/assets/VWindowItem-Dm2szrjK.e77444188aa3.js.gz +0 -0
  176. codex/static_root/assets/VWindowItem-Dm2szrjK.js +1 -0
  177. codex/static_root/assets/VWindowItem-Dm2szrjK.js.br +0 -0
  178. codex/static_root/assets/VWindowItem-Dm2szrjK.js.gz +0 -0
  179. codex/static_root/assets/{admin-80eqCqtz.d6deb9edb8cb.js → admin-B4z2nA5P.da0c92500311.js} +1 -1
  180. codex/static_root/assets/admin-B4z2nA5P.da0c92500311.js.br +0 -0
  181. codex/static_root/assets/admin-B4z2nA5P.da0c92500311.js.gz +0 -0
  182. codex/static_root/assets/{admin-80eqCqtz.js → admin-B4z2nA5P.js} +1 -1
  183. codex/static_root/assets/admin-B4z2nA5P.js.br +0 -0
  184. codex/static_root/assets/admin-B4z2nA5P.js.gz +0 -0
  185. codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js +1 -0
  186. codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js.br +0 -0
  187. codex/static_root/assets/admin-BhW3PNO0.e444048e548d.js.gz +0 -0
  188. codex/static_root/assets/admin-BhW3PNO0.js +1 -0
  189. codex/static_root/assets/admin-BhW3PNO0.js.br +0 -0
  190. codex/static_root/assets/admin-BhW3PNO0.js.gz +0 -0
  191. codex/static_root/assets/{admin-menu-_aAGknKO.0c08c9bd2e7d.js → admin-menu-CL7S-xqR.92b6d84c4b70.js} +1 -1
  192. codex/static_root/assets/admin-menu-CL7S-xqR.92b6d84c4b70.js.br +0 -0
  193. codex/static_root/assets/admin-menu-CL7S-xqR.92b6d84c4b70.js.gz +0 -0
  194. codex/static_root/assets/{admin-menu-_aAGknKO.js → admin-menu-CL7S-xqR.js} +1 -1
  195. codex/static_root/assets/admin-menu-CL7S-xqR.js.br +0 -0
  196. codex/static_root/assets/admin-menu-CL7S-xqR.js.gz +0 -0
  197. codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js → admin-settings-button-progress-BgBgdELw.52d052e38bc1.js} +1 -1
  198. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.52d052e38bc1.js.br +0 -0
  199. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.52d052e38bc1.js.gz +0 -0
  200. codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.js → admin-settings-button-progress-BgBgdELw.js} +1 -1
  201. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.js.br +0 -0
  202. codex/static_root/assets/admin-settings-button-progress-BgBgdELw.js.gz +0 -0
  203. codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js +1 -0
  204. codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js.br +0 -0
  205. codex/static_root/assets/browser-Bi5ov5k8.a4af87c2a667.js.gz +0 -0
  206. codex/static_root/assets/browser-Bi5ov5k8.js +1 -0
  207. codex/static_root/assets/browser-Bi5ov5k8.js.br +0 -0
  208. codex/static_root/assets/browser-Bi5ov5k8.js.gz +0 -0
  209. codex/static_root/assets/{browser-chTZ1CM4.521193ab0710.css → browser-DNzQvgkY.aea8b7d7ca40.css} +1 -1
  210. codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.br +0 -0
  211. codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.gz +0 -0
  212. codex/static_root/assets/{browser-chTZ1CM4.css → browser-DNzQvgkY.css} +1 -1
  213. codex/static_root/assets/browser-DNzQvgkY.css.br +0 -0
  214. codex/static_root/assets/browser-DNzQvgkY.css.gz +0 -0
  215. codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js → change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js} +1 -1
  216. codex/static_root/assets/change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js.br +0 -0
  217. codex/static_root/assets/change-password-dialog-BKar2Bhb.3dbc2feb3c5a.js.gz +0 -0
  218. codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.js → change-password-dialog-BKar2Bhb.js} +1 -1
  219. codex/static_root/assets/change-password-dialog-BKar2Bhb.js.br +0 -0
  220. codex/static_root/assets/change-password-dialog-BKar2Bhb.js.gz +0 -0
  221. codex/static_root/assets/{confirm-dialog-DqCey9Iq.e315d7f8f752.js → confirm-dialog-BoBL4OoS.abef9614fea9.js} +1 -1
  222. codex/static_root/assets/confirm-dialog-BoBL4OoS.abef9614fea9.js.br +0 -0
  223. codex/static_root/assets/confirm-dialog-BoBL4OoS.abef9614fea9.js.gz +0 -0
  224. codex/static_root/assets/{confirm-dialog-DqCey9Iq.js → confirm-dialog-BoBL4OoS.js} +1 -1
  225. codex/static_root/assets/confirm-dialog-BoBL4OoS.js.br +0 -0
  226. codex/static_root/assets/confirm-dialog-BoBL4OoS.js.gz +0 -0
  227. codex/static_root/assets/{datetime-column-CBA8bYeO.17a31f189d66.js → datetime-column-BsiYRUKO.cadd764c06f4.js} +1 -1
  228. codex/static_root/assets/datetime-column-BsiYRUKO.cadd764c06f4.js.br +0 -0
  229. codex/static_root/assets/datetime-column-BsiYRUKO.cadd764c06f4.js.gz +0 -0
  230. codex/static_root/assets/{datetime-column-CBA8bYeO.js → datetime-column-BsiYRUKO.js} +1 -1
  231. codex/static_root/assets/datetime-column-BsiYRUKO.js.br +0 -0
  232. codex/static_root/assets/datetime-column-BsiYRUKO.js.gz +0 -0
  233. codex/static_root/assets/{filter-mnO3lLPo.659442401b16.js → filter-HnY0knto.07f807227dcc.js} +1 -1
  234. codex/static_root/assets/filter-HnY0knto.07f807227dcc.js.br +0 -0
  235. codex/static_root/assets/filter-HnY0knto.07f807227dcc.js.gz +0 -0
  236. codex/static_root/assets/{filter-mnO3lLPo.js → filter-HnY0knto.js} +1 -1
  237. codex/static_root/assets/filter-HnY0knto.js.br +0 -0
  238. codex/static_root/assets/filter-HnY0knto.js.gz +0 -0
  239. codex/static_root/assets/{flag-tab-Bc677pNb.1b8a2f7c0dc3.js → flag-tab-CcS5pve2.a1df5d92e222.js} +1 -1
  240. codex/static_root/assets/flag-tab-CcS5pve2.a1df5d92e222.js.br +0 -0
  241. codex/static_root/assets/flag-tab-CcS5pve2.a1df5d92e222.js.gz +0 -0
  242. codex/static_root/assets/{flag-tab-Bc677pNb.js → flag-tab-CcS5pve2.js} +1 -1
  243. codex/static_root/assets/flag-tab-CcS5pve2.js.br +0 -0
  244. codex/static_root/assets/flag-tab-CcS5pve2.js.gz +0 -0
  245. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css +1 -0
  246. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.br +0 -0
  247. codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.gz +0 -0
  248. codex/static_root/assets/flag-tab-D2MYXCHk.css +1 -0
  249. codex/static_root/assets/flag-tab-D2MYXCHk.css.br +0 -0
  250. codex/static_root/assets/flag-tab-D2MYXCHk.css.gz +0 -0
  251. codex/static_root/assets/{forwardRefs-DPRKg5wq.6f09e9d22fa6.js → forwardRefs-i80xmxX3.db393aeef392.js} +1 -1
  252. codex/static_root/assets/forwardRefs-i80xmxX3.db393aeef392.js.br +0 -0
  253. codex/static_root/assets/forwardRefs-i80xmxX3.db393aeef392.js.gz +0 -0
  254. codex/static_root/assets/{forwardRefs-DPRKg5wq.js → forwardRefs-i80xmxX3.js} +1 -1
  255. codex/static_root/assets/forwardRefs-i80xmxX3.js.br +0 -0
  256. codex/static_root/assets/forwardRefs-i80xmxX3.js.gz +0 -0
  257. codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js +1 -0
  258. codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js.br +0 -0
  259. codex/static_root/assets/group-tab-SY0gxmHW.f72e944c0ff1.js.gz +0 -0
  260. codex/static_root/assets/group-tab-SY0gxmHW.js +1 -0
  261. codex/static_root/assets/group-tab-SY0gxmHW.js.br +0 -0
  262. codex/static_root/assets/group-tab-SY0gxmHW.js.gz +0 -0
  263. codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js +1 -0
  264. codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js.br +0 -0
  265. codex/static_root/assets/http-error-EE8gtq5A.07e291d9d955.js.gz +0 -0
  266. codex/static_root/assets/http-error-EE8gtq5A.js +1 -0
  267. codex/static_root/assets/http-error-EE8gtq5A.js.br +0 -0
  268. codex/static_root/assets/http-error-EE8gtq5A.js.gz +0 -0
  269. codex/static_root/assets/{index-9nBCksDQ.4561d2608773.js → index-gVdawuuE.a940fadbac00.js} +1 -1
  270. codex/static_root/assets/index-gVdawuuE.a940fadbac00.js.br +0 -0
  271. codex/static_root/assets/index-gVdawuuE.a940fadbac00.js.gz +0 -0
  272. codex/static_root/assets/{index-9nBCksDQ.js → index-gVdawuuE.js} +1 -1
  273. codex/static_root/assets/index-gVdawuuE.js.br +0 -0
  274. codex/static_root/assets/index-gVdawuuE.js.gz +0 -0
  275. codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js +1 -0
  276. codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js.br +0 -0
  277. codex/static_root/assets/library-tab-BkDIAvyS.4b4c606b77d6.js.gz +0 -0
  278. codex/static_root/assets/library-tab-BkDIAvyS.js +1 -0
  279. codex/static_root/assets/library-tab-BkDIAvyS.js.br +0 -0
  280. codex/static_root/assets/library-tab-BkDIAvyS.js.gz +0 -0
  281. codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js +35 -0
  282. codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js.br +0 -0
  283. codex/static_root/assets/main-B5uflU6E.77f3fcd6873f.js.gz +0 -0
  284. codex/static_root/assets/main-B5uflU6E.js +35 -0
  285. codex/static_root/assets/main-B5uflU6E.js.br +0 -0
  286. codex/static_root/assets/main-B5uflU6E.js.gz +0 -0
  287. codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js +1 -0
  288. codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js.br +0 -0
  289. codex/static_root/assets/pager-full-pdf-wuQbceLs.111d9b9d4133.js.gz +0 -0
  290. codex/static_root/assets/pager-full-pdf-wuQbceLs.js +1 -0
  291. codex/static_root/assets/pager-full-pdf-wuQbceLs.js.br +0 -0
  292. codex/static_root/assets/pager-full-pdf-wuQbceLs.js.gz +0 -0
  293. codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js → pagination-toolbar-D3JQmDiD.8513a4db85f0.js} +1 -1
  294. codex/static_root/assets/pagination-toolbar-D3JQmDiD.8513a4db85f0.js.br +0 -0
  295. codex/static_root/assets/pagination-toolbar-D3JQmDiD.8513a4db85f0.js.gz +0 -0
  296. codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.js → pagination-toolbar-D3JQmDiD.js} +1 -1
  297. codex/static_root/assets/pagination-toolbar-D3JQmDiD.js.br +0 -0
  298. codex/static_root/assets/pagination-toolbar-D3JQmDiD.js.gz +0 -0
  299. codex/static_root/assets/{pdf-doc-6ZE_HmLT.10d1e16abc78.js → pdf-doc-DRBtl9_2.225a6d89e842.js} +1 -1
  300. codex/static_root/assets/pdf-doc-DRBtl9_2.225a6d89e842.js.br +0 -0
  301. codex/static_root/assets/pdf-doc-DRBtl9_2.225a6d89e842.js.gz +0 -0
  302. codex/static_root/assets/{pdf-doc-6ZE_HmLT.js → pdf-doc-DRBtl9_2.js} +1 -1
  303. codex/static_root/assets/pdf-doc-DRBtl9_2.js.br +0 -0
  304. codex/static_root/assets/pdf-doc-DRBtl9_2.js.gz +0 -0
  305. codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js +2 -0
  306. codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js.br +0 -0
  307. codex/static_root/assets/reader-DycvjN42.3e2b4f43235e.js.gz +0 -0
  308. codex/static_root/assets/reader-DycvjN42.js +2 -0
  309. codex/static_root/assets/reader-DycvjN42.js.br +0 -0
  310. codex/static_root/assets/reader-DycvjN42.js.gz +0 -0
  311. codex/static_root/assets/{relation-chips-C96hi5jm.js → relation-chips-CeHIEjvZ.04d302f4fa89.js} +1 -1
  312. codex/static_root/assets/relation-chips-CeHIEjvZ.04d302f4fa89.js.br +0 -0
  313. codex/static_root/assets/relation-chips-CeHIEjvZ.04d302f4fa89.js.gz +0 -0
  314. codex/static_root/assets/{relation-chips-C96hi5jm.9f07c9eb533a.js → relation-chips-CeHIEjvZ.js} +1 -1
  315. codex/static_root/assets/relation-chips-CeHIEjvZ.js.br +0 -0
  316. codex/static_root/assets/relation-chips-CeHIEjvZ.js.gz +0 -0
  317. codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js +2 -0
  318. codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js.br +0 -0
  319. codex/static_root/assets/settings-drawer-DY8o-jF3.48dfaafaa00e.js.gz +0 -0
  320. codex/static_root/assets/settings-drawer-DY8o-jF3.js +2 -0
  321. codex/static_root/assets/settings-drawer-DY8o-jF3.js.br +0 -0
  322. codex/static_root/assets/settings-drawer-DY8o-jF3.js.gz +0 -0
  323. codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js +1 -0
  324. codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js.br +0 -0
  325. codex/static_root/assets/ssrBoot-DRKt_yWP.3b5868abdd61.js.gz +0 -0
  326. codex/static_root/assets/ssrBoot-DRKt_yWP.js +1 -0
  327. codex/static_root/assets/ssrBoot-DRKt_yWP.js.br +0 -0
  328. codex/static_root/assets/ssrBoot-DRKt_yWP.js.gz +0 -0
  329. codex/static_root/assets/{stats-tab-CkT2ZGUQ.b704fa46dc88.js → stats-tab-C1XYGBKj.812896e1d09c.js} +1 -1
  330. codex/static_root/assets/stats-tab-C1XYGBKj.812896e1d09c.js.br +0 -0
  331. codex/static_root/assets/stats-tab-C1XYGBKj.812896e1d09c.js.gz +0 -0
  332. codex/static_root/assets/{stats-tab-CkT2ZGUQ.js → stats-tab-C1XYGBKj.js} +1 -1
  333. codex/static_root/assets/stats-tab-C1XYGBKj.js.br +0 -0
  334. codex/static_root/assets/stats-tab-C1XYGBKj.js.gz +0 -0
  335. codex/static_root/assets/task-tab-CW4DQ3KN.81812f9208e5.css +1 -0
  336. codex/static_root/assets/{task-tab-DfdUY9Rc.478e74041f80.css.br → task-tab-CW4DQ3KN.81812f9208e5.css.br} +1 -1
  337. codex/static_root/assets/task-tab-CW4DQ3KN.81812f9208e5.css.gz +0 -0
  338. codex/static_root/assets/task-tab-CW4DQ3KN.css +1 -0
  339. codex/static_root/assets/{task-tab-DfdUY9Rc.css.br → task-tab-CW4DQ3KN.css.br} +1 -1
  340. codex/static_root/assets/task-tab-CW4DQ3KN.css.gz +0 -0
  341. codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js +1 -0
  342. codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js.br +0 -0
  343. codex/static_root/assets/task-tab-lFRZ0NMd.6b90f40a1a9f.js.gz +0 -0
  344. codex/static_root/assets/task-tab-lFRZ0NMd.js +1 -0
  345. codex/static_root/assets/task-tab-lFRZ0NMd.js.br +0 -0
  346. codex/static_root/assets/task-tab-lFRZ0NMd.js.gz +0 -0
  347. codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js +1 -0
  348. codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js.br +0 -0
  349. codex/static_root/assets/unauthorized-Dld9cV8d.4e675fd9991c.js.gz +0 -0
  350. codex/static_root/assets/unauthorized-Dld9cV8d.js +1 -0
  351. codex/static_root/assets/unauthorized-Dld9cV8d.js.br +0 -0
  352. codex/static_root/assets/unauthorized-Dld9cV8d.js.gz +0 -0
  353. codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js +1 -0
  354. codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js.br +0 -0
  355. codex/static_root/assets/user-tab-CN9nBOMS.f88d8b1c8afe.js.gz +0 -0
  356. codex/static_root/assets/user-tab-CN9nBOMS.js +1 -0
  357. codex/static_root/assets/user-tab-CN9nBOMS.js.br +0 -0
  358. codex/static_root/assets/user-tab-CN9nBOMS.js.gz +0 -0
  359. codex/static_root/{manifest.225989a17f49.json → manifest.66d7229aa07a.json} +297 -277
  360. codex/static_root/manifest.66d7229aa07a.json.br +0 -0
  361. codex/static_root/manifest.66d7229aa07a.json.gz +0 -0
  362. codex/static_root/manifest.json +297 -277
  363. codex/static_root/manifest.json.br +0 -0
  364. codex/static_root/manifest.json.gz +0 -0
  365. codex/static_root/staticfiles.json +1 -1
  366. codex/status_controller.py +10 -13
  367. codex/urls/root.py +4 -3
  368. codex/views/admin/api_key.py +1 -4
  369. codex/views/admin/flag.py +2 -2
  370. codex/views/admin/group.py +2 -2
  371. codex/views/admin/library.py +4 -4
  372. codex/views/admin/stats.py +10 -10
  373. codex/views/admin/tasks.py +32 -13
  374. codex/views/admin/user.py +36 -10
  375. codex/views/auth.py +3 -8
  376. codex/views/bookmark.py +3 -3
  377. codex/views/browser/annotate/card.py +2 -2
  378. codex/views/browser/annotate/order.py +6 -7
  379. codex/views/browser/bookmark.py +3 -1
  380. codex/views/browser/breadcrumbs.py +6 -5
  381. codex/views/browser/browser.py +2 -2
  382. codex/views/browser/choices.py +7 -8
  383. codex/views/browser/cover.py +6 -3
  384. codex/views/browser/download.py +2 -1
  385. codex/views/browser/filters/bookmark.py +1 -1
  386. codex/views/browser/filters/field.py +1 -1
  387. codex/views/browser/filters/filter.py +6 -8
  388. codex/views/browser/filters/group.py +8 -8
  389. codex/views/browser/filters/search/field/expression.py +1 -2
  390. codex/views/browser/filters/search/field/optimize.py +1 -1
  391. codex/views/browser/filters/search/field/parse.py +2 -2
  392. codex/views/browser/filters/search/parse.py +4 -19
  393. codex/views/browser/group_mtime.py +1 -1
  394. codex/views/browser/metadata/copy_intersections.py +1 -1
  395. codex/views/browser/metadata/metadata.py +1 -1
  396. codex/views/browser/mtime.py +1 -1
  397. codex/views/browser/order_by.py +3 -5
  398. codex/views/browser/params.py +1 -1
  399. codex/views/browser/settings.py +1 -1
  400. codex/views/browser/validate.py +9 -6
  401. codex/views/const.py +0 -3
  402. codex/views/frontend.py +1 -1
  403. codex/views/mixins.py +3 -2
  404. codex/views/opds/authentication_v1.py +1 -1
  405. codex/views/opds/const.py +4 -4
  406. codex/views/opds/urls.py +1 -1
  407. codex/views/opds/util.py +4 -4
  408. codex/views/opds/v1/entry/entry.py +4 -2
  409. codex/views/opds/v1/entry/links.py +4 -5
  410. codex/views/opds/v1/facets.py +8 -4
  411. codex/views/opds/v1/feed.py +5 -5
  412. codex/views/opds/v1/links.py +11 -6
  413. codex/views/opds/v2/const.py +5 -2
  414. codex/views/opds/v2/feed.py +3 -3
  415. codex/views/opds/v2/links.py +18 -11
  416. codex/views/opds/v2/progression.py +8 -9
  417. codex/views/opds/v2/publications.py +10 -20
  418. codex/views/public.py +2 -2
  419. codex/views/reader/books.py +3 -2
  420. codex/views/reader/page.py +3 -3
  421. codex/views/reader/params.py +2 -2
  422. codex/views/reader/reader.py +6 -6
  423. codex/views/reader/settings.py +1 -1
  424. codex/views/session.py +10 -3
  425. codex/views/settings.py +2 -2
  426. codex/views/timezone.py +2 -2
  427. codex/views/util.py +10 -9
  428. codex/views/version.py +1 -1
  429. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/METADATA +3 -1
  430. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/RECORD +433 -415
  431. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/WHEEL +1 -1
  432. codex/serializers/fields.py +0 -183
  433. codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.br +0 -0
  434. codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.gz +0 -0
  435. codex/static_root/assets/VCheckbox-FaT6MGfu.js.br +0 -0
  436. codex/static_root/assets/VCheckbox-FaT6MGfu.js.gz +0 -0
  437. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.br +0 -0
  438. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.gz +0 -0
  439. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.br +0 -0
  440. codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.gz +0 -0
  441. codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.br +0 -0
  442. codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.gz +0 -0
  443. codex/static_root/assets/VCombobox-C8QLNlSl.js.br +0 -0
  444. codex/static_root/assets/VCombobox-C8QLNlSl.js.gz +0 -0
  445. codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.br +0 -0
  446. codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.gz +0 -0
  447. codex/static_root/assets/VDialog-RVLeW7je.js.br +0 -0
  448. codex/static_root/assets/VDialog-RVLeW7je.js.gz +0 -0
  449. codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.br +0 -0
  450. codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.gz +0 -0
  451. codex/static_root/assets/VDivider-D1Ot4vR2.js.br +0 -0
  452. codex/static_root/assets/VDivider-D1Ot4vR2.js.gz +0 -0
  453. codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.br +0 -0
  454. codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.gz +0 -0
  455. codex/static_root/assets/VExpansionPanels-DQilGHZk.js.br +0 -0
  456. codex/static_root/assets/VExpansionPanels-DQilGHZk.js.gz +0 -0
  457. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js +0 -1
  458. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.br +0 -0
  459. codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.gz +0 -0
  460. codex/static_root/assets/VForm-DNmY-qFJ.js +0 -1
  461. codex/static_root/assets/VForm-DNmY-qFJ.js.br +0 -0
  462. codex/static_root/assets/VForm-DNmY-qFJ.js.gz +0 -0
  463. codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.br +0 -0
  464. codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.gz +0 -0
  465. codex/static_root/assets/VRadioGroup-DoayJpcS.js.br +0 -0
  466. codex/static_root/assets/VRadioGroup-DoayJpcS.js.gz +0 -0
  467. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js +0 -1
  468. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.br +0 -0
  469. codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.gz +0 -0
  470. codex/static_root/assets/VSelect-BSLmHX_P.js +0 -1
  471. codex/static_root/assets/VSelect-BSLmHX_P.js.br +0 -0
  472. codex/static_root/assets/VSelect-BSLmHX_P.js.gz +0 -0
  473. codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.br +0 -0
  474. codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.gz +0 -0
  475. codex/static_root/assets/VSelectionControl-TaKCeZgb.js.br +0 -0
  476. codex/static_root/assets/VSelectionControl-TaKCeZgb.js.gz +0 -0
  477. codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.br +0 -0
  478. codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.gz +0 -0
  479. codex/static_root/assets/VTable-BfcOiEpa.js.br +0 -0
  480. codex/static_root/assets/VTable-BfcOiEpa.js.gz +0 -0
  481. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js +0 -1
  482. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.br +0 -0
  483. codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.gz +0 -0
  484. codex/static_root/assets/VWindowItem-D8lWcbQY.js +0 -1
  485. codex/static_root/assets/VWindowItem-D8lWcbQY.js.br +0 -0
  486. codex/static_root/assets/VWindowItem-D8lWcbQY.js.gz +0 -0
  487. codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.br +0 -0
  488. codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.gz +0 -0
  489. codex/static_root/assets/admin-80eqCqtz.js.br +0 -0
  490. codex/static_root/assets/admin-80eqCqtz.js.gz +0 -0
  491. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js +0 -1
  492. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.br +0 -0
  493. codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.gz +0 -0
  494. codex/static_root/assets/admin-CBLHOrM9.js +0 -1
  495. codex/static_root/assets/admin-CBLHOrM9.js.br +0 -0
  496. codex/static_root/assets/admin-CBLHOrM9.js.gz +0 -0
  497. codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.br +0 -0
  498. codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.gz +0 -0
  499. codex/static_root/assets/admin-menu-_aAGknKO.js.br +0 -0
  500. codex/static_root/assets/admin-menu-_aAGknKO.js.gz +0 -0
  501. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.br +0 -0
  502. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.gz +0 -0
  503. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.br +0 -0
  504. codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.gz +0 -0
  505. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js +0 -1
  506. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.br +0 -0
  507. codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.gz +0 -0
  508. codex/static_root/assets/browser-DAprXYuP.js +0 -1
  509. codex/static_root/assets/browser-DAprXYuP.js.br +0 -0
  510. codex/static_root/assets/browser-DAprXYuP.js.gz +0 -0
  511. codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.br +0 -0
  512. codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.gz +0 -0
  513. codex/static_root/assets/browser-chTZ1CM4.css.br +0 -0
  514. codex/static_root/assets/browser-chTZ1CM4.css.gz +0 -0
  515. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.br +0 -0
  516. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.gz +0 -0
  517. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.br +0 -0
  518. codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.gz +0 -0
  519. codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.br +0 -0
  520. codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.gz +0 -0
  521. codex/static_root/assets/confirm-dialog-DqCey9Iq.js.br +0 -0
  522. codex/static_root/assets/confirm-dialog-DqCey9Iq.js.gz +0 -0
  523. codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.br +0 -0
  524. codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.gz +0 -0
  525. codex/static_root/assets/datetime-column-CBA8bYeO.js.br +0 -0
  526. codex/static_root/assets/datetime-column-CBA8bYeO.js.gz +0 -0
  527. codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.br +0 -0
  528. codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.gz +0 -0
  529. codex/static_root/assets/filter-mnO3lLPo.js.br +0 -0
  530. codex/static_root/assets/filter-mnO3lLPo.js.gz +0 -0
  531. codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.br +0 -0
  532. codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.gz +0 -0
  533. codex/static_root/assets/flag-tab-Bc677pNb.js.br +0 -0
  534. codex/static_root/assets/flag-tab-Bc677pNb.js.gz +0 -0
  535. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css +0 -1
  536. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.br +0 -0
  537. codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.gz +0 -0
  538. codex/static_root/assets/flag-tab-glFvEgEl.css +0 -1
  539. codex/static_root/assets/flag-tab-glFvEgEl.css.br +0 -0
  540. codex/static_root/assets/flag-tab-glFvEgEl.css.gz +0 -0
  541. codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.br +0 -0
  542. codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.gz +0 -0
  543. codex/static_root/assets/forwardRefs-DPRKg5wq.js.br +0 -0
  544. codex/static_root/assets/forwardRefs-DPRKg5wq.js.gz +0 -0
  545. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js +0 -1
  546. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.br +0 -0
  547. codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.gz +0 -0
  548. codex/static_root/assets/group-tab-BVlBrMwo.js +0 -1
  549. codex/static_root/assets/group-tab-BVlBrMwo.js.br +0 -0
  550. codex/static_root/assets/group-tab-BVlBrMwo.js.gz +0 -0
  551. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js +0 -1
  552. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.br +0 -0
  553. codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.gz +0 -0
  554. codex/static_root/assets/http-error-C9-_aEBF.js +0 -1
  555. codex/static_root/assets/http-error-C9-_aEBF.js.br +0 -0
  556. codex/static_root/assets/http-error-C9-_aEBF.js.gz +0 -0
  557. codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.br +0 -0
  558. codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.gz +0 -0
  559. codex/static_root/assets/index-9nBCksDQ.js.br +0 -0
  560. codex/static_root/assets/index-9nBCksDQ.js.gz +0 -0
  561. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js +0 -1
  562. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.br +0 -0
  563. codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.gz +0 -0
  564. codex/static_root/assets/library-tab-BMe3dKzy.js +0 -1
  565. codex/static_root/assets/library-tab-BMe3dKzy.js.br +0 -0
  566. codex/static_root/assets/library-tab-BMe3dKzy.js.gz +0 -0
  567. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js +0 -35
  568. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.br +0 -0
  569. codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.gz +0 -0
  570. codex/static_root/assets/main-BbNUiWEb.js +0 -35
  571. codex/static_root/assets/main-BbNUiWEb.js.br +0 -0
  572. codex/static_root/assets/main-BbNUiWEb.js.gz +0 -0
  573. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js +0 -1
  574. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.br +0 -0
  575. codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.gz +0 -0
  576. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js +0 -1
  577. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.br +0 -0
  578. codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.gz +0 -0
  579. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.br +0 -0
  580. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.gz +0 -0
  581. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.br +0 -0
  582. codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.gz +0 -0
  583. codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.br +0 -0
  584. codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.gz +0 -0
  585. codex/static_root/assets/pdf-doc-6ZE_HmLT.js.br +0 -0
  586. codex/static_root/assets/pdf-doc-6ZE_HmLT.js.gz +0 -0
  587. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js +0 -2
  588. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.br +0 -0
  589. codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.gz +0 -0
  590. codex/static_root/assets/reader-DdPKDuh5.js +0 -2
  591. codex/static_root/assets/reader-DdPKDuh5.js.br +0 -0
  592. codex/static_root/assets/reader-DdPKDuh5.js.gz +0 -0
  593. codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.br +0 -0
  594. codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.gz +0 -0
  595. codex/static_root/assets/relation-chips-C96hi5jm.js.br +0 -0
  596. codex/static_root/assets/relation-chips-C96hi5jm.js.gz +0 -0
  597. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js +0 -2
  598. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.br +0 -0
  599. codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.gz +0 -0
  600. codex/static_root/assets/settings-drawer-CqbT1rid.js +0 -2
  601. codex/static_root/assets/settings-drawer-CqbT1rid.js.br +0 -0
  602. codex/static_root/assets/settings-drawer-CqbT1rid.js.gz +0 -0
  603. codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.br +0 -0
  604. codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.gz +0 -0
  605. codex/static_root/assets/stats-tab-CkT2ZGUQ.js.br +0 -0
  606. codex/static_root/assets/stats-tab-CkT2ZGUQ.js.gz +0 -0
  607. codex/static_root/assets/task-tab-DfdUY9Rc.478e74041f80.css +0 -1
  608. codex/static_root/assets/task-tab-DfdUY9Rc.478e74041f80.css.gz +0 -0
  609. codex/static_root/assets/task-tab-DfdUY9Rc.css +0 -1
  610. codex/static_root/assets/task-tab-DfdUY9Rc.css.gz +0 -0
  611. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js +0 -1
  612. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.br +0 -0
  613. codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.gz +0 -0
  614. codex/static_root/assets/task-tab-a-DcU3Tj.js +0 -1
  615. codex/static_root/assets/task-tab-a-DcU3Tj.js.br +0 -0
  616. codex/static_root/assets/task-tab-a-DcU3Tj.js.gz +0 -0
  617. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js +0 -1
  618. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.br +0 -0
  619. codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.gz +0 -0
  620. codex/static_root/assets/unauthorized-BOl5YpL3.js +0 -1
  621. codex/static_root/assets/unauthorized-BOl5YpL3.js.br +0 -0
  622. codex/static_root/assets/unauthorized-BOl5YpL3.js.gz +0 -0
  623. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js +0 -1
  624. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.br +0 -0
  625. codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.gz +0 -0
  626. codex/static_root/assets/user-tab-BBDZvD8G.js +0 -1
  627. codex/static_root/assets/user-tab-BBDZvD8G.js.br +0 -0
  628. codex/static_root/assets/user-tab-BBDZvD8G.js.gz +0 -0
  629. codex/static_root/manifest.225989a17f49.json.br +0 -0
  630. codex/static_root/manifest.225989a17f49.json.gz +0 -0
  631. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/LICENSE +0 -0
  632. {codex-1.7.3a2.dist-info → codex-1.7.5.dist-info}/entry_points.txt +0 -0
@@ -1,16 +1,12 @@
1
1
  """Generated by Django 5.0.6 on 2024-07-02 21:01."""
2
2
 
3
- from typing import ClassVar
4
-
5
3
  from django.db import migrations, models
6
4
 
7
5
 
8
6
  class Migration(migrations.Migration):
9
7
  """Migrate db."""
10
8
 
11
- dependencies: ClassVar[list] = [ # type: ignore
12
- ("codex", "0027_import_order_and_covers"),
13
- ]
9
+ dependencies = [("codex", "0027_import_order_and_covers")]
14
10
 
15
11
  operations = [
16
12
  migrations.AlterField(
codex/models/base.py CHANGED
@@ -27,9 +27,11 @@ class BaseModel(Model):
27
27
  updated_at = DateTimeField(auto_now=True)
28
28
  objects = GroupByManager()
29
29
 
30
- class Meta(ModelBase): # type: ignore
30
+ class Meta(ModelBase): # type: ignore[reportIncompatibleVariableOverride]
31
31
  """Without this a real table is created and joined to."""
32
32
 
33
+ # Model.Meta is not inheritable.
34
+
33
35
  abstract = True
34
36
  get_latest_by = "updated_at"
35
37
 
codex/models/bookmark.py CHANGED
@@ -23,18 +23,15 @@ def cascade_if_user_null(
23
23
  sub_objs,
24
24
  using, # noqa: ARG001
25
25
  ):
26
- """Cascade only if the user field is null.
26
+ """
27
+ Cascade only if the user field is null.
27
28
 
28
29
  Do this to keep deleting ephemeral session data from Bookmark table.
29
30
  Adapted from:
30
31
  https://github.com/django/django/blob/master/django/db/models/deletion.py#L23
31
32
  """
32
- null_user_sub_objs = []
33
- for sub_obj in sub_objs:
34
- # only cascade the ones with null user fields.
35
- if sub_obj.user is None:
36
- null_user_sub_objs.append(sub_obj)
37
-
33
+ # only cascade the ones with null user fields.
34
+ null_user_sub_objs = [sub_obj for sub_obj in sub_objs if sub_obj.user is None]
38
35
  if null_user_sub_objs:
39
36
  collector.collect(
40
37
  null_user_sub_objs,
codex/models/comic.py CHANGED
@@ -263,7 +263,7 @@ class Comic(WatchedPathBrowserGroup):
263
263
 
264
264
  @classmethod
265
265
  def get_title(
266
- cls, obj, volume=True, zero_pad=None, name=True, filename_fallback=False
266
+ cls, obj, volume: bool, name: bool, filename_fallback: bool, zero_pad=None
267
267
  ):
268
268
  """Create the comic title for display."""
269
269
  names = []
@@ -293,7 +293,7 @@ class Comic(WatchedPathBrowserGroup):
293
293
 
294
294
  def __str__(self):
295
295
  """Most common text representation for logging."""
296
- return self.get_title(self, filename_fallback=True)
296
+ return self.get_title(self, volume=True, name=True, filename_fallback=True)
297
297
 
298
298
  def issue(self) -> str:
299
299
  """Combine issue parts for search."""
codex/models/functions.py CHANGED
@@ -14,7 +14,10 @@ class JsonGroupArray(Aggregate):
14
14
  allow_distinct = True
15
15
  function = "JSON_GROUP_ARRAY"
16
16
  name = "JsonGroupArray"
17
- output_field = JSONField() # type: ignore
17
+
18
+ def __init__(self, *args, **kwargs):
19
+ """output_field is set in the constructor."""
20
+ super().__init__(*args, output_field=JSONField(), **kwargs)
18
21
 
19
22
 
20
23
  class GroupConcat(Aggregate):
@@ -25,7 +28,10 @@ class GroupConcat(Aggregate):
25
28
  allow_distinct = True
26
29
  function = "GROUP_CONCAT"
27
30
  name = "GroupConcat"
28
- output_field = CharField() # type: ignore
31
+
32
+ def __init__(self, *args, **kwargs):
33
+ """output_field is set in the constructor."""
34
+ super().__init__(*args, output_field=CharField(), **kwargs)
29
35
 
30
36
 
31
37
  @OneToOneField.register_lookup
@@ -36,8 +42,6 @@ class FTS5Match(Lookup):
36
42
 
37
43
  def as_sql(self, compiler, connection):
38
44
  """Generate MATCH sql."""
39
- # lhs, lhs_params = self.process_lhs(compiler, connection)
40
- # fts_table = lhs.split(".")[0]
41
45
  rhs, rhs_params = self.process_rhs(compiler, connection)
42
46
  # MATCH works on the table itself not the one_to_one rel.
43
47
  # Force the table name without substitutions by the optimizer
@@ -67,4 +71,7 @@ class ComicFTSRank(Func):
67
71
 
68
72
  function = "rank"
69
73
  template = '("codex_comicfts"."rank" * -1)'
70
- output_field = FloatField() # type: ignore
74
+
75
+ def __init__(self, *args, **kwargs):
76
+ """output_field is set in the constructor."""
77
+ super().__init__(*args, output_field=FloatField(), **kwargs)
codex/models/groups.py CHANGED
@@ -136,7 +136,7 @@ class WatchedPathBrowserGroup(BrowserGroupModel, WatchedPath):
136
136
  super().presave()
137
137
  WatchedPath.presave(self)
138
138
 
139
- class Meta(WatchedPath.Meta): # type: ignore
139
+ class Meta(BrowserGroupModel.Meta, WatchedPath.Meta):
140
140
  """Use Mixin Meta."""
141
141
 
142
142
  abstract = True
codex/models/library.py CHANGED
@@ -23,7 +23,7 @@ __all__ = ("Library", "validate_dir_exists")
23
23
  def validate_dir_exists(path):
24
24
  """Validate that a library exists."""
25
25
  if not Path(path).is_dir():
26
- raise ValidationError(_(f"{path} is not a directory"), params={"path": path})
26
+ raise ValidationError(_("{path} is not a directory"), params={"path": path})
27
27
 
28
28
 
29
29
  class Library(BaseModel):
codex/models/paths.py CHANGED
@@ -36,11 +36,11 @@ class WatchedPath(BaseModel):
36
36
  st[0] = st_record.st_mode
37
37
  st[1] = st_record.st_ino
38
38
  # st[2] = st_record.st_dev is ignored by diff
39
- # st[3] = st_record.st_nlink
40
- # st[4] = st_record.st_uid
41
- # st[5] = st_record.st_gid
39
+ # st[3] = st_record.st_nlink ignored
40
+ # st[4] = st_record.st_uid ignored
41
+ # st[5] = st_record.st_gid ignored
42
42
  st[6] = st_record.st_size
43
- # st[7] = st_record.st_atime
43
+ # st[7] = st_record.st_atime ignored
44
44
  st[8] = st_record.st_mtime
45
45
  self.stat = st
46
46
 
codex/models/query.py CHANGED
@@ -45,7 +45,7 @@ class GroupByQuery(Query):
45
45
  self.force_group_by_table = ""
46
46
  self.force_group_by_fields = ()
47
47
 
48
- def get_compiler(self, using=None, connection=None, elide_empty=True):
48
+ def get_compiler(self, using=None, connection=None, elide_empty=True): # noqa: FBT002
49
49
  """Use the custom compiler instead of SQLCompiler."""
50
50
  if self.compiler == "SQLCompiler":
51
51
  if using is None and connection is None:
@@ -59,7 +59,11 @@ class GroupByQuery(Query):
59
59
  )
60
60
  else:
61
61
  # Normal super() operation.
62
- compiler = super().get_compiler(using, connection, elide_empty) # type: ignore
62
+ compiler = super().get_compiler(
63
+ using=using,
64
+ connection=connection,
65
+ elide_empty=elide_empty, # type: ignore[reportCallIssue]
66
+ )
63
67
 
64
68
  return compiler
65
69
 
@@ -82,13 +86,13 @@ class GroupByQuerySet(QuerySet):
82
86
 
83
87
  def group_by(self, *fields, model=None):
84
88
  """Force group_by operator."""
85
- obj = self._chain() # type: ignore
89
+ obj = self._chain() # type: ignore[reportAttributeAccessIssue]
86
90
  obj.query.set_force_group_by(fields, model=model)
87
91
  return obj
88
92
 
89
93
  def demote_joins(self, tables):
90
94
  """Force INNER JOINS."""
91
- obj = self._chain() # type: ignore
95
+ obj = self._chain() # type: ignore[reportAttributeAccessIssue]
92
96
  obj.query.demote_joins(tables)
93
97
  return obj
94
98
 
codex/models/util.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Utilities for models."""
2
2
 
3
3
  _ARTICLES = frozenset(
4
- ("a", "an", "the") # en # noqa RUF005
4
+ ("a", "an", "the") # en # noqa: RUF005
5
5
  + ("un", "unos", "unas", "el", "los", "la", "las") # es
6
6
  + ("un", "une", "le", "les", "la", "les", "l'") # fr
7
7
  + ("o", "a", "os") # pt
codex/permissions.py CHANGED
@@ -8,7 +8,7 @@ from codex.models import Timestamp
8
8
  class HasAPIKeyOrIsAdminUser(BasePermission):
9
9
  """Does the request have the current api key."""
10
10
 
11
- def has_permission(self, request, view): # type: ignore
11
+ def has_permission(self, request, view):
12
12
  """Test the request api key against the database."""
13
13
  data = request.GET if request.method == "GET" else request.POST
14
14
  api_key = data.get("apiKey")
codex/run.py CHANGED
@@ -63,7 +63,7 @@ def run():
63
63
  serve(
64
64
  application,
65
65
  HYPERCORN_CONFIG,
66
- shutdown_trigger=SHUTDOWN_EVENT.wait, # type: ignore
66
+ shutdown_trigger=SHUTDOWN_EVENT.wait,
67
67
  )
68
68
  )
69
69
  librarian.stop()
@@ -85,6 +85,4 @@ class AdminFolderSerializer(Serializer):
85
85
 
86
86
  def validate_show_hidden(self, show_hidden):
87
87
  """Snakecase the showHidden field."""
88
- return (
89
- show_hidden == "true" or self.initial_data.get("showHidden") == "true" # type: ignore
90
- )
88
+ return show_hidden == "true" or self.initial_data.get("showHidden") == "true"
@@ -19,8 +19,8 @@ FILE_TYPES_CHOICES = ("CBZ", "CBR", "CBT", "PDF", "UNKNOWN")
19
19
  class StatsSystemSerializer(Serializer):
20
20
  """Platform System Information."""
21
21
 
22
- name = CharField(required=False, read_only=True)
23
- release = CharField(required=False, read_only=True)
22
+ name = CharField(read_only=True, required=False)
23
+ release = CharField(read_only=True, required=False)
24
24
 
25
25
 
26
26
  class StatsPlatformSerializer(Serializer):
@@ -42,7 +42,7 @@ class StatsConfigSerializer(Serializer):
42
42
  user_registered_count = IntegerField(required=False, read_only=True)
43
43
  auth_group_count = IntegerField(required=False, read_only=True)
44
44
  # Only for api
45
- api_key = CharField(required=False, read_only=True)
45
+ api_key = CharField(read_only=True, required=False)
46
46
 
47
47
 
48
48
  class StatsSessionsSerializer(Serializer):
@@ -123,4 +123,4 @@ class AdminStatsRequestSerializer(Serializer):
123
123
  class APIKeySerializer(Serializer):
124
124
  """API Key."""
125
125
 
126
- api_key = CharField(source="name")
126
+ api_key = CharField(source="name", read_only=True)
@@ -6,11 +6,21 @@ from rest_framework.serializers import (
6
6
  Serializer,
7
7
  )
8
8
 
9
- from codex.choices import ADMIN_TASKS
9
+ from codex.choices.admin import ADMIN_TASK_GROUPS
10
+
11
+ _ADMIN_TASK_CHOICES = tuple(
12
+ sorted(
13
+ [
14
+ item["value"]
15
+ for group in ADMIN_TASK_GROUPS["tasks"]
16
+ for item in group["tasks"]
17
+ ]
18
+ )
19
+ )
10
20
 
11
21
 
12
22
  class AdminLibrarianTaskSerializer(Serializer):
13
23
  """Get tasks from front end."""
14
24
 
15
- task = ChoiceField(choices=ADMIN_TASKS)
25
+ task = ChoiceField(choices=_ADMIN_TASK_CHOICES)
16
26
  library_id = IntegerField(required=False)
codex/serializers/auth.py CHANGED
@@ -8,6 +8,8 @@ from rest_framework.serializers import (
8
8
  )
9
9
 
10
10
  from codex.models import AdminFlag
11
+ from codex.serializers.fields.auth import TimezoneField
12
+ from codex.serializers.fields.sanitized import SanitizedCharField
11
13
  from codex.serializers.models.base import BaseModelSerializer
12
14
 
13
15
 
@@ -49,7 +51,7 @@ class UserSerializer(BaseModelSerializer):
49
51
  class TimezoneSerializer(Serializer):
50
52
  """Serialize Timezone submission from front end."""
51
53
 
52
- timezone = CharField(min_length=2)
54
+ timezone = TimezoneField(write_only=True)
53
55
 
54
56
 
55
57
  class UserCreateSerializer(BaseModelSerializer, TimezoneSerializer):
@@ -67,7 +69,7 @@ class UserLoginSerializer(UserCreateSerializer):
67
69
  """Serialize user login input."""
68
70
 
69
71
  # specify this so it doesn't trigger the username unique constraint.
70
- username = CharField(min_length=2)
72
+ username = SanitizedCharField(min_length=2)
71
73
 
72
74
  class Meta(UserCreateSerializer.Meta):
73
75
  """Explicit meta inheritance required."""
@@ -2,28 +2,106 @@
2
2
 
3
3
  from types import MappingProxyType
4
4
 
5
- from rest_framework.fields import ListField, SerializerMethodField
6
- from rest_framework.serializers import CharField, Serializer
5
+ from rest_framework.fields import (
6
+ BooleanField,
7
+ CharField,
8
+ ListField,
9
+ SerializerMethodField,
10
+ )
11
+ from rest_framework.serializers import Serializer
7
12
 
8
- from codex.serializers.browser.filters import BrowserSettingsFilterSerializer
9
13
  from codex.serializers.fields import (
14
+ VuetifyBooleanField,
10
15
  VuetifyCharField,
16
+ VuetifyDecadeField,
17
+ VuetifyFloatField,
11
18
  VuetifyIntegerField,
12
19
  )
20
+ from codex.serializers.fields.browser import BookmarkFilterField
13
21
  from codex.serializers.models.pycountry import CountrySerializer, LanguageSerializer
14
22
 
15
23
 
16
- class BrowserChoicesCharPkSerializer(Serializer):
24
+ class BrowserFilterChoicesSerializer(Serializer):
25
+ """All dynamic filters."""
26
+
27
+ age_rating = BooleanField(read_only=True)
28
+ community_rating = BooleanField(read_only=True)
29
+ characters = BooleanField(read_only=True)
30
+ country = BooleanField(read_only=True)
31
+ critical_rating = BooleanField(read_only=True)
32
+ contributors = BooleanField(read_only=True)
33
+ decade = BooleanField(read_only=True)
34
+ genres = BooleanField(read_only=True)
35
+ file_type = BooleanField(read_only=True)
36
+ identifier_type = BooleanField(read_only=True)
37
+ monochrome = BooleanField(read_only=True)
38
+ language = BooleanField(read_only=True)
39
+ locations = BooleanField(read_only=True)
40
+ original_format = BooleanField(read_only=True)
41
+ reading_direction = BooleanField(read_only=True)
42
+ series_groups = BooleanField(read_only=True)
43
+ stories = BooleanField(read_only=True)
44
+ story_arcs = BooleanField(read_only=True)
45
+ tagger = BooleanField(read_only=True)
46
+ tags = BooleanField(read_only=True)
47
+ teams = BooleanField(read_only=True)
48
+ year = BooleanField(read_only=True)
49
+
50
+
51
+ class BrowserSettingsFilterSerializer(Serializer):
52
+ """Filter values for settings."""
53
+
54
+ bookmark = BookmarkFilterField(required=False, read_only=True)
55
+ # Dynamic filters
56
+ age_rating = ListField(child=VuetifyCharField(), required=False, read_only=True)
57
+ characters = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
58
+ community_rating = ListField(
59
+ child=VuetifyFloatField(), required=False, read_only=True
60
+ )
61
+ country = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
62
+ contributors = ListField(
63
+ child=VuetifyIntegerField(), required=False, read_only=True
64
+ )
65
+ critical_rating = ListField(
66
+ child=VuetifyFloatField(), required=False, read_only=True
67
+ )
68
+ decade = ListField(child=VuetifyDecadeField(), required=False, read_only=True)
69
+ file_type = ListField(child=VuetifyCharField(), required=False, read_only=True)
70
+ genres = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
71
+ identifier_type = ListField(
72
+ child=VuetifyCharField(), required=False, read_only=True
73
+ )
74
+ language = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
75
+ locations = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
76
+ monochrome = ListField(child=VuetifyBooleanField(), required=False, read_only=True)
77
+ original_format = ListField(
78
+ child=VuetifyCharField(), required=False, read_only=True
79
+ )
80
+ reading_direction = ListField(
81
+ child=VuetifyCharField(), required=False, read_only=True
82
+ )
83
+ series_groups = ListField(
84
+ child=VuetifyIntegerField(), required=False, read_only=True
85
+ )
86
+ stories = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
87
+ story_arcs = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
88
+ tagger = ListField(child=VuetifyCharField(), required=False, read_only=True)
89
+ tags = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
90
+ teams = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
91
+ year = ListField(child=VuetifyIntegerField(), required=False, read_only=True)
92
+
93
+
94
+ class BrowserChoicesIntegerPkSerializer(Serializer):
17
95
  """Named Model Serializer."""
18
96
 
19
- pk = VuetifyCharField(read_only=True)
97
+ pk = VuetifyIntegerField(read_only=True)
20
98
  name = CharField(read_only=True)
21
99
 
22
100
 
23
- class BrowserChoicesIntegerPkSerializer(BrowserChoicesCharPkSerializer):
101
+ class BrowserChoicesCharPkSerializer(BrowserChoicesIntegerPkSerializer):
24
102
  """Named Model Serailizer with pk = char hack for languages & countries."""
25
103
 
26
- pk = VuetifyIntegerField(read_only=True)
104
+ pk = VuetifyCharField(read_only=True)
27
105
 
28
106
 
29
107
  _CHOICES_NAME_SERIALIZER_MAP = MappingProxyType(
@@ -50,9 +128,9 @@ class BrowserChoicesFilterSerializer(Serializer):
50
128
  if serializer_class:
51
129
  value = serializer_class(choices, many=True).data
52
130
  elif not serializer_class and field_name in _LIST_FIELDS:
53
- field: ListField = (
131
+ field: ListField = ( # type: ignore[reportAssignmentType]
54
132
  BrowserSettingsFilterSerializer().get_fields().get(field_name)
55
- ) # type: ignore
133
+ )
56
134
  value = field.to_representation(choices)
57
135
  else:
58
136
  value = BrowserChoicesIntegerPkSerializer(choices, many=True).data
@@ -1,68 +1,40 @@
1
1
  """Browser Settings Filter Serializers."""
2
2
 
3
- from rest_framework.serializers import BooleanField, ListField, Serializer
3
+ from rest_framework.serializers import ListField, Serializer
4
4
 
5
5
  from codex.serializers.fields import (
6
- BookmarkFilterField,
7
6
  VuetifyBooleanField,
8
- VuetifyCharField,
9
7
  VuetifyDecadeField,
10
8
  VuetifyFloatField,
11
9
  VuetifyIntegerField,
12
10
  )
11
+ from codex.serializers.fields.browser import BookmarkFilterField
13
12
 
14
13
 
15
- class BrowserFilterChoicesSerializer(Serializer):
16
- """All dynamic filters."""
17
-
18
- age_rating = BooleanField(read_only=True)
19
- community_rating = BooleanField(read_only=True)
20
- characters = BooleanField(read_only=True)
21
- country = BooleanField(read_only=True)
22
- critical_rating = BooleanField(read_only=True)
23
- contributors = BooleanField(read_only=True)
24
- decade = BooleanField(read_only=True)
25
- genres = BooleanField(read_only=True)
26
- file_type = BooleanField(read_only=True)
27
- identifier_type = BooleanField(read_only=True)
28
- monochrome = BooleanField(read_only=True)
29
- language = BooleanField(read_only=True)
30
- locations = BooleanField(read_only=True)
31
- original_format = BooleanField(read_only=True)
32
- reading_direction = BooleanField(read_only=True)
33
- series_groups = BooleanField(read_only=True)
34
- stories = BooleanField(read_only=True)
35
- story_arcs = BooleanField(read_only=True)
36
- tagger = BooleanField(read_only=True)
37
- tags = BooleanField(read_only=True)
38
- teams = BooleanField(read_only=True)
39
- year = BooleanField(read_only=True)
40
-
41
-
42
- class BrowserSettingsFilterSerializer(Serializer):
14
+ class BrowserSettingsFilterInputSerializer(Serializer):
43
15
  """Filter values for settings."""
44
16
 
45
17
  bookmark = BookmarkFilterField(required=False)
46
18
  # Dynamic filters
47
- age_rating = ListField(child=VuetifyCharField(), required=False)
19
+ age_rating = ListField(child=VuetifyIntegerField(), required=False)
48
20
  characters = ListField(child=VuetifyIntegerField(), required=False)
49
21
  community_rating = ListField(child=VuetifyFloatField(), required=False)
50
22
  country = ListField(child=VuetifyIntegerField(), required=False)
51
23
  contributors = ListField(child=VuetifyIntegerField(), required=False)
52
24
  critical_rating = ListField(child=VuetifyFloatField(), required=False)
53
25
  decade = ListField(child=VuetifyDecadeField(), required=False)
54
- file_type = ListField(child=VuetifyCharField(), required=False)
26
+ file_type = ListField(child=VuetifyIntegerField(), required=False)
55
27
  genres = ListField(child=VuetifyIntegerField(), required=False)
56
- identifier_type = ListField(child=VuetifyCharField(), required=False)
28
+ identifier_type = ListField(child=VuetifyIntegerField(), required=False)
57
29
  language = ListField(child=VuetifyIntegerField(), required=False)
58
30
  locations = ListField(child=VuetifyIntegerField(), required=False)
59
31
  monochrome = ListField(child=VuetifyBooleanField(), required=False)
60
- original_format = ListField(child=VuetifyCharField(), required=False)
61
- reading_direction = ListField(child=VuetifyCharField(), required=False)
32
+ original_format = ListField(child=VuetifyIntegerField(), required=False)
33
+ reading_direction = ListField(child=VuetifyIntegerField(), required=False)
62
34
  series_groups = ListField(child=VuetifyIntegerField(), required=False)
63
35
  stories = ListField(child=VuetifyIntegerField(), required=False)
64
36
  story_arcs = ListField(child=VuetifyIntegerField(), required=False)
65
- tagger = ListField(child=VuetifyCharField(), required=False)
37
+ tagger = ListField(child=VuetifyIntegerField(), required=False)
66
38
  tags = ListField(child=VuetifyIntegerField(), required=False)
67
39
  teams = ListField(child=VuetifyIntegerField(), required=False)
68
40
  year = ListField(child=VuetifyIntegerField(), required=False)
@@ -1,6 +1,7 @@
1
1
  """Codex Serializers for the metadata box."""
2
2
 
3
- from rest_framework.serializers import CharField, IntegerField, ListField, Serializer
3
+ from rest_framework.fields import CharField
4
+ from rest_framework.serializers import IntegerField, ListField, Serializer
4
5
 
5
6
  from codex.serializers.browser.mixins import BrowserAggregateSerializerMixin
6
7
  from codex.serializers.models.comic import ComicSerializer
@@ -5,7 +5,6 @@ from itertools import chain
5
5
 
6
6
  from rest_framework.serializers import (
7
7
  BooleanField,
8
- CharField,
9
8
  DecimalField,
10
9
  IntegerField,
11
10
  ListField,
@@ -14,6 +13,7 @@ from rest_framework.serializers import (
14
13
  )
15
14
 
16
15
  from codex.logger.logging import get_logger
16
+ from codex.serializers.fields.browser import TopGroupField
17
17
  from codex.util import max_none
18
18
  from codex.views.const import EPOCH_START
19
19
 
@@ -23,7 +23,7 @@ LOG = get_logger(__name__)
23
23
  class BrowserAggregateSerializerMixin(Serializer):
24
24
  """Mixin for browser, opds & metadata serializers."""
25
25
 
26
- group = CharField(read_only=True, max_length=1)
26
+ group = TopGroupField(read_only=True)
27
27
  ids = ListField(child=IntegerField(), read_only=True)
28
28
 
29
29
  # Aggregate Annotations
@@ -64,5 +64,9 @@ class BrowserAggregateSerializerMixin(Serializer):
64
64
  )
65
65
  mtime = self._get_max_updated_at(EPOCH_START, updated_ats)
66
66
  if obj.bmua_is_max:
67
- mtime: datetime = max_none(mtime, obj.bookmark_updated_at, EPOCH_START) # type: ignore
67
+ mtime: datetime | None = max_none(
68
+ mtime, obj.bookmark_updated_at, EPOCH_START
69
+ )
70
+ if mtime is None:
71
+ return 0
68
72
  return int(mtime.timestamp() * 1000)
@@ -1,17 +1,15 @@
1
1
  """Group Mtimes."""
2
2
 
3
- from rest_framework.serializers import CharField, Serializer
3
+ from rest_framework.serializers import Serializer
4
4
 
5
- from codex.serializers.browser.settings import BrowserSettingsFilterSerializer
5
+ from codex.serializers.browser.settings import BrowserFilterChoicesInputSerilalizer
6
6
  from codex.serializers.fields import TimestampField
7
7
  from codex.serializers.route import SimpleRouteSerializer
8
8
 
9
9
 
10
- class GroupsMtimeSerializer(Serializer):
10
+ class GroupsMtimeSerializer(BrowserFilterChoicesInputSerilalizer):
11
11
  """Groups Mtimes."""
12
12
 
13
- filters = BrowserSettingsFilterSerializer(required=False)
14
- q = CharField(allow_blank=True, required=False)
15
13
  groups = SimpleRouteSerializer(many=True, required=True)
16
14
 
17
15
 
@@ -1,17 +1,18 @@
1
1
  """Browser Page Serializer."""
2
2
 
3
- from rest_framework.serializers import (
3
+ from rest_framework.fields import (
4
4
  BooleanField,
5
5
  CharField,
6
6
  DecimalField,
7
7
  IntegerField,
8
- Serializer,
9
8
  )
9
+ from rest_framework.serializers import Serializer
10
10
 
11
11
  from codex.serializers.browser.mixins import (
12
12
  BrowserAggregateSerializerMixin,
13
13
  )
14
- from codex.serializers.fields import BreadcrumbsField, TimestampField
14
+ from codex.serializers.fields import TimestampField
15
+ from codex.serializers.fields.browser import BreadcrumbsField, TopGroupField
15
16
 
16
17
 
17
18
  class BrowserCardSerializer(BrowserAggregateSerializerMixin):
@@ -57,7 +58,7 @@ class BrowserPageSerializer(Serializer):
57
58
  title = BrowserTitleSerializer(read_only=True)
58
59
  zero_pad = IntegerField(read_only=True)
59
60
  libraries_exist = BooleanField(read_only=True)
60
- model_group = CharField(read_only=True)
61
+ model_group = TopGroupField(read_only=True)
61
62
  num_pages = IntegerField(read_only=True)
62
63
  groups = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
63
64
  books = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
@@ -8,9 +8,10 @@ from rest_framework.serializers import (
8
8
  Serializer,
9
9
  )
10
10
 
11
- from codex.choices import BROWSER_ORDER_BY_CHOICES
12
- from codex.serializers.browser.filters import BrowserSettingsFilterSerializer
13
- from codex.serializers.fields import BreadcrumbsField, TimestampField, TopGroupField
11
+ from codex.choices.browser import BROWSER_ORDER_BY_CHOICES
12
+ from codex.serializers.browser.filters import BrowserSettingsFilterInputSerializer
13
+ from codex.serializers.fields import TimestampField
14
+ from codex.serializers.fields.browser import BreadcrumbsField, TopGroupField
14
15
  from codex.serializers.route import SimpleRouteSerializer
15
16
 
16
17
 
@@ -26,7 +27,8 @@ class BrowserSettingsShowGroupFlagsSerializer(Serializer):
26
27
  class BrowserFilterChoicesInputSerilalizer(Serializer):
27
28
  """Browser Settings for the filter choices response."""
28
29
 
29
- filters = BrowserSettingsFilterSerializer(required=False)
30
+ filters = BrowserSettingsFilterInputSerializer(required=False)
31
+ # NOT Sanitized because so complex.
30
32
  q = CharField(allow_blank=True, required=False)
31
33
 
32
34
 
@@ -51,7 +53,6 @@ class BrowserCoverInputSerializer(BrowserCoverInputSerializerBase):
51
53
  class BrowserSettingsSerializerBase(BrowserCoverInputSerializerBase):
52
54
  """Base Serializer for Browser & OPDS Settings."""
53
55
 
54
- # search_results_limit = IntegerField(required=False)
55
56
  top_group = TopGroupField(required=False)
56
57
 
57
58
 
@@ -64,7 +65,8 @@ class OPDSSettingsSerializer(BrowserSettingsSerializerBase):
64
65
 
65
66
 
66
67
  class BrowserSettingsSerializer(BrowserSettingsSerializerBase):
67
- """Browser Settings that the user can change.
68
+ """
69
+ Browser Settings that the user can change.
68
70
 
69
71
  This is the only browse serializer that's submitted.
70
72
  """