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
@@ -186,7 +186,8 @@ class LinkComicsImporter(LinkCoversImporter):
186
186
  return tms, all_del_pks
187
187
 
188
188
  def bulk_fix_comic_m2m_field(self, field_name, m2m_links, status):
189
- """Recreate an m2m field for a set of comics.
189
+ """
190
+ Recreate an m2m field for a set of comics.
190
191
 
191
192
  Since we can't bulk_update or bulk_create m2m fields use a trick.
192
193
  bulk_create() on the through table:
@@ -46,9 +46,7 @@ class MovedImporter(AggregateMetadataImporter):
46
46
  new_path = self.task.files_moved[comic.path]
47
47
  comic.path = new_path
48
48
  new_path = Path(new_path)
49
- comic.parent_folder = Folder.objects.get( # type: ignore
50
- path=new_path.parent
51
- )
49
+ comic.parent_folder = Folder.objects.get(path=new_path.parent)
52
50
  comic.updated_at = Now()
53
51
  comic.presave()
54
52
  folder_m2m_links[comic.pk] = Folder.objects.filter(
@@ -137,7 +135,7 @@ class MovedImporter(AggregateMetadataImporter):
137
135
  f"Unlinked {len(unlink_groups)} {model.__name__} moved custom covers."
138
136
  )
139
137
 
140
- self._remove_covers(unlink_pks, custom=True) # type: ignore
138
+ self._remove_covers(unlink_pks, custom=True)
141
139
 
142
140
  def _bulk_covers_moved(self, status=None):
143
141
  """Move covers."""
@@ -194,8 +192,6 @@ class MovedImporter(AggregateMetadataImporter):
194
192
  folder.updated_at = Now()
195
193
  update_folders.append(folder)
196
194
 
197
- # delete_folders = Folder.objects.filter(library=self.library, path__in=new_paths)
198
-
199
195
  update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
200
196
 
201
197
  Folder.objects.bulk_update(update_folders, MOVED_BULK_FOLDER_UPDATE_FIELDS)
@@ -212,7 +208,6 @@ class MovedImporter(AggregateMetadataImporter):
212
208
  """Move folders under existing folders."""
213
209
  while True:
214
210
  # Get existing parent folders
215
- # dest_parent_paths = tuple(str(path) for path in dest_parent_folder_paths_map)
216
211
  dest_parent_paths = tuple(dest_parent_folder_paths_map.keys())
217
212
  extant_parent_folders = Folder.objects.filter(
218
213
  library=self.library, path__in=dest_parent_paths
@@ -229,9 +229,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
229
229
 
230
230
  group_filter = Q()
231
231
  for group_tree, count_value in update_group_trees.items():
232
- compare_filter = {}
233
- for field_name, value in zip(compare_fields, group_tree, strict=False):
234
- compare_filter[field_name] = value
232
+ compare_filter = dict(zip(compare_fields, group_tree, strict=False))
235
233
  compare_filter[count_field_name] = count_value
236
234
  group_filter |= Q(**compare_filter)
237
235
  return group_filter
@@ -494,7 +492,6 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
494
492
 
495
493
  def _query_missing_simple_models(self, names, fk_data, status):
496
494
  """Find missing named models and folders."""
497
- # count = 0
498
495
  if not names:
499
496
  return 0
500
497
  create_fks, base_cls, field, fk_field = fk_data
@@ -18,7 +18,7 @@ class ImportDBDiffTask(ImportTask):
18
18
 
19
19
  dirs_moved: Mapping[str, str] = field(default_factory=dict)
20
20
  dirs_modified: frozenset[str] = frozenset()
21
- # dirs_created: frozenset[str] | None = frozenset()
21
+ # dirs_created: frozenset[str] | None = frozenset() # noqa: ERA001
22
22
  dirs_deleted: frozenset[str] = frozenset()
23
23
 
24
24
  files_moved: Mapping[str, str] = field(default_factory=dict)
@@ -2,6 +2,7 @@
2
2
  # Uses app.get_model() because functions may also be called before the models are ready on startup.
3
3
 
4
4
  import re
5
+ from typing import TYPE_CHECKING
5
6
 
6
7
  from django.apps import apps
7
8
  from django.db import DEFAULT_DB_ALIAS, connections
@@ -19,6 +20,13 @@ from codex.settings.settings import (
19
20
  from codex.status import Status
20
21
  from codex.worker_base import WorkerBaseMixin
21
22
 
23
+ if TYPE_CHECKING:
24
+ from django.db.models.manager import BaseManager
25
+
26
+ from codex.models.base import BaseModel
27
+ from codex.models.comic import Comic
28
+
29
+
22
30
  REPAIR_FLAG_PATH = CONFIG_PATH / "rebuild_db"
23
31
  REBUILT_DB_PATH = DB_PATH.parent / (DB_PATH.name + ".rebuilt")
24
32
  BACKUP_DB_PATH = DB_PATH.parent / (DB_PATH.name + ".bak")
@@ -105,7 +113,7 @@ def _null_bad_fk_rels_table(table_name, bad_rows, log):
105
113
  fields = model._meta.fields
106
114
  field_names = {}
107
115
  update_objs = []
108
- objs = model.objects.filter(pk__in=bad_rows.keys())
116
+ objs: BaseManager[BaseModel] = model.objects.filter(pk__in=bad_rows.keys()) # type: ignore[reportAssignmentType]
109
117
  update_fields = {"updated_at"}
110
118
  now = Now()
111
119
  for obj in objs:
@@ -119,7 +127,7 @@ def _null_bad_fk_rels_table(table_name, bad_rows, log):
119
127
  field_name = field_names[fkid]
120
128
  setattr(obj, field_name, None)
121
129
  update_fields.add(field_name)
122
- obj.updated_at = now # type: ignore
130
+ obj.updated_at = now
123
131
  update_objs.append(obj)
124
132
  except Exception as exc:
125
133
  log.warning(
@@ -142,10 +150,10 @@ def _null_bad_fk_rels(bad_fk_rels, log):
142
150
  for table_name, bad_rows in bad_fk_rels.items():
143
151
  try:
144
152
  fix_comic_pks |= _null_bad_fk_rels_table(table_name, bad_rows, log)
145
- except Exception as exc:
153
+ except Exception:
146
154
  pks = sorted(bad_rows.keys())
147
- log.error( # noqa: TRY400
148
- f"Unable to null {len(pks)} {table_name} rows with bad foreign keys - {exc}"
155
+ log.exception(
156
+ f"Unable to null {len(pks)} {table_name} rows with bad foreign keys"
149
157
  )
150
158
  log.error(f"{table_name}: {pks}") # noqa: TRY400
151
159
  return fix_comic_pks
@@ -175,10 +183,8 @@ def _delete_bad_m2m_rows(bad_m2m_rows, log):
175
183
  for table_name, ids in bad_m2m_rows.items():
176
184
  try:
177
185
  count += _delete_bad_m2m_rows_table(table_name, ids, fix_comic_pks, log)
178
- except Exception as exc:
179
- log.error( # noqa: TRY400
180
- f"Could not delete bad foreign keys in table {table_name} - {exc}"
181
- )
186
+ except Exception:
187
+ log.exception(f"Could not delete bad foreign keys in table {table_name}")
182
188
  if count:
183
189
  log.info(f"Removed {count} bad foreign key relations.")
184
190
  return fix_comic_pks
@@ -189,7 +195,9 @@ def _mark_comics_for_update(fix_comic_pks, log):
189
195
  if not fix_comic_pks:
190
196
  return
191
197
  comic_model = apps.get_model(app_label="codex", model_name="comic")
192
- outdated_comics = comic_model.objects.filter(pk__in=fix_comic_pks).only(
198
+ outdated_comics: BaseManager[Comic] = comic_model.objects.filter(
199
+ pk__in=fix_comic_pks
200
+ ).only( # type: ignore[reportAssignmentType]
193
201
  "stat", "updated_at"
194
202
  )
195
203
  if not outdated_comics:
@@ -198,12 +206,12 @@ def _mark_comics_for_update(fix_comic_pks, log):
198
206
  update_comics = []
199
207
  now = Now()
200
208
  for comic in outdated_comics:
201
- stat_list = comic.stat # type: ignore
209
+ stat_list = comic.stat
202
210
  if not stat_list:
203
211
  continue
204
212
  stat_list[8] = 0.0
205
- comic.stat = stat_list # type: ignore
206
- comic.updated_at = now # type: ignore
213
+ comic.stat = stat_list # type: ignore[reportAttributeAccessIssue]
214
+ comic.updated_at = now
207
215
  update_comics.append(comic)
208
216
 
209
217
  if update_comics:
@@ -227,8 +235,8 @@ def fix_foreign_keys(log=None):
227
235
  fix_comic_pks |= _delete_bad_m2m_rows(bad_m2m_rows, log)
228
236
  try:
229
237
  _mark_comics_for_update(fix_comic_pks, log)
230
- except Exception as exc:
231
- LOG.warning(f"Could not mark comics with bad relations for update: {exc}")
238
+ except Exception:
239
+ LOG.exception("Could not mark comics with bad relations for update")
232
240
 
233
241
 
234
242
  def _repair_extra_custom_cover_libraries(library_model, log):
@@ -277,7 +285,7 @@ def _is_integrity_ok(results):
277
285
  )
278
286
 
279
287
 
280
- def integrity_check(long=True, log=None):
288
+ def integrity_check(long, log=None):
281
289
  """Run sqlite3 integrity check."""
282
290
  if not log:
283
291
  log = LOG
@@ -318,9 +326,8 @@ def fts_integrity_check(log=None):
318
326
  if results:
319
327
  # I'm not sure if this raises or puts the error in the results.
320
328
  raise ValueError(results) # noqa: TRY301
321
- except Exception as exc:
322
- log.warning("Full Text Search Index failed integrity check.")
323
- log.warning(exc)
329
+ except Exception:
330
+ log.exception("Full Text Search Index failed integrity check.")
324
331
  else:
325
332
  log.info("Full Text Search Index passed integrity check.")
326
333
  success = True
@@ -340,7 +347,7 @@ class IntegrityMixin(WorkerBaseMixin):
340
347
  finally:
341
348
  self.status_controller.finish(status)
342
349
 
343
- def integrity_check(self, long=False):
350
+ def integrity_check(self, long: bool):
344
351
  """Integrity check task."""
345
352
  subtitle = "" if long else "Quick"
346
353
  status = Status(JanitorStatusTypes.INTEGRITY_CHECK, subtitle=subtitle)
@@ -109,7 +109,7 @@ class Janitor(
109
109
  case JanitorVacuumTask():
110
110
  self.vacuum_db()
111
111
  case JanitorBackupTask():
112
- self.backup_db()
112
+ self.backup_db(show_status=True)
113
113
  case JanitorLatestVersionTask():
114
114
  self.update_latest_version(task.force)
115
115
  case JanitorUpdateTask():
@@ -28,7 +28,7 @@ class LatestVersionMixin(WorkerBaseMixin):
28
28
  response = requests.get(_REPO_URL, timeout=_REPO_TIMEOUT)
29
29
  return json.loads(response.text)["info"]["version"]
30
30
 
31
- def update_latest_version(self, force=False):
31
+ def update_latest_version(self, force: bool):
32
32
  """Get the latest version from a remote repo using a cache."""
33
33
  status = Status(JanitorStatusTypes.CODEX_LATEST_VERSION)
34
34
  try:
@@ -22,12 +22,15 @@ class UpdateMixin(WorkerBaseMixin):
22
22
  def _is_outdated(self):
23
23
  """Is codex outdated."""
24
24
  result = False
25
+ if VERSION is None:
26
+ self.log.warning("Cannot determine installed Codex version.")
27
+ return result
25
28
  ts = Timestamp.objects.get(key=Timestamp.TimestampChoices.CODEX_VERSION.value)
26
29
  latest_version = ts.version
27
30
  versio_latest_version = Version(latest_version)
28
31
 
29
32
  installed_versio_version = Version(VERSION)
30
- if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore
33
+ if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore[reportIndexIssue]
31
34
  pre_blurb = "latest version is a prerelease. But installed version is not."
32
35
  else:
33
36
  result = versio_latest_version > installed_versio_version
@@ -35,7 +38,7 @@ class UpdateMixin(WorkerBaseMixin):
35
38
  self.log.debug(f"{latest_version=} > {VERSION=} = {result}{pre_blurb}")
36
39
  return result
37
40
 
38
- def update_codex(self, force=False):
41
+ def update_codex(self, force: bool):
39
42
  """Update the package and restart everything if the version changed."""
40
43
  status = Status(JanitorStatusTypes.CODEX_UPDATE)
41
44
  try:
@@ -30,7 +30,7 @@ class VacuumMixin(WorkerBaseMixin):
30
30
  finally:
31
31
  self.status_controller.finish(status)
32
32
 
33
- def backup_db(self, backup_path=BACKUP_DB_PATH, show_status=True):
33
+ def backup_db(self, show_status: bool, backup_path=BACKUP_DB_PATH):
34
34
  """Backup the database."""
35
35
  status = Status(JanitorStatusTypes.DB_BACKUP) if show_status else ""
36
36
  try:
@@ -79,7 +79,7 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
79
79
  startup_tasks = (
80
80
  AdoptOrphanFoldersTask(),
81
81
  WatchdogSyncTask(),
82
- SearchIndexUpdateTask(False),
82
+ SearchIndexUpdateTask(rebuild=False),
83
83
  )
84
84
 
85
85
  for task in startup_tasks:
@@ -193,7 +193,8 @@ class LibrarianDaemon(Process, LoggerBaseMixin):
193
193
  self.log_queue.join_thread()
194
194
 
195
195
  def run(self):
196
- """Process tasks from the queue.
196
+ """
197
+ Process tasks from the queue.
197
198
 
198
199
  This process also runs the crond thread and the Watchdog Observer
199
200
  threads.
@@ -16,7 +16,8 @@ class NotifierThread(AggregateMessageQueuedThread):
16
16
  self.cache[item.text] = item
17
17
 
18
18
  def _send_task(self, task):
19
- """Send a group_send message to the mulitprocess broadcast channel.
19
+ """
20
+ Send a group_send message to the mulitprocess broadcast channel.
20
21
 
21
22
  A random consumer awaiting the broadcast channel will consume it,
22
23
  and do a group_send with it's message.
@@ -2,6 +2,7 @@
2
2
 
3
3
  from dataclasses import dataclass
4
4
 
5
+ from codex.choices.notifications import Notifications
5
6
  from codex.websockets.consumers import ChannelGroups
6
7
 
7
8
 
@@ -13,6 +14,19 @@ class NotifierTask:
13
14
  group: str
14
15
 
15
16
 
16
- LIBRARY_CHANGED_TASK = NotifierTask("LIBRARY_CHANGED", ChannelGroups.ALL.name)
17
- LIBRARIAN_STATUS_TASK = NotifierTask("LIBRARIAN_STATUS", ChannelGroups.ADMIN.name)
18
- FAILED_IMPORTS_TASK = NotifierTask("FAILED_IMPORTS", ChannelGroups.ADMIN.name)
17
+ ADMIN_FLAGS_CHANGED_TASK = NotifierTask(
18
+ Notifications.ADMIN_FLAGS.value, ChannelGroups.ALL.name
19
+ )
20
+ COVERS_CHANGED_TASK = NotifierTask(Notifications.COVERS.value, ChannelGroups.ALL.name)
21
+ FAILED_IMPORTS_CHANGED_TASK = NotifierTask(
22
+ Notifications.FAILED_IMPORTS.value, ChannelGroups.ADMIN.name
23
+ )
24
+ GROUPS_CHANGED_TASK = NotifierTask(Notifications.GROUPS.value, ChannelGroups.ALL.name)
25
+ LIBRARIAN_STATUS_TASK = NotifierTask(
26
+ Notifications.LIBRARIAN_STATUS.value, ChannelGroups.ADMIN.name
27
+ )
28
+ LIBRARY_CHANGED_TASK = NotifierTask(Notifications.LIBRARY.value, ChannelGroups.ALL.name)
29
+ USERS_CHANGED_TASK = NotifierTask(Notifications.USERS.value, ChannelGroups.ALL.name)
30
+ ADMIN_USERS_CHANGED_TASK = NotifierTask(
31
+ Notifications.USERS.value, ChannelGroups.ADMIN.name
32
+ )
@@ -22,7 +22,7 @@ class OptimizeMixin(QueuedThread):
22
22
  self.abort_event = abort_event
23
23
  super().__init__(*args, **kwargs)
24
24
 
25
- def optimize(self, janitor=False):
25
+ def optimize(self, janitor: bool):
26
26
  """Remove records not in the database from the index, trapping exceptions."""
27
27
  start_time = time()
28
28
  status = Status(SearchIndexStatusTypes.SEARCH_INDEX_OPTIMIZE)
@@ -21,7 +21,7 @@ class RemoveMixin(OptimizeMixin):
21
21
  self.status_controller.finish(clear_status)
22
22
  self.log.info("Old search index cleared.")
23
23
 
24
- def _remove_stale_records(self, status): # type: ignore
24
+ def _remove_stale_records(self, status):
25
25
  """Remove records not in the database from the index."""
26
26
  start_time = time()
27
27
  delete_comicfts = ComicFTS.objects.filter(comic__isnull=True)
@@ -14,7 +14,7 @@ from codex.librarian.search.status import SearchIndexStatusTypes
14
14
  from codex.models import Comic, Library
15
15
  from codex.models.comic import ComicFTS
16
16
  from codex.models.functions import GroupConcat
17
- from codex.serializers.fields import CountryField, LanguageField, PyCountryField
17
+ from codex.serializers.fields.browser import CountryField, LanguageField, PyCountryField
18
18
  from codex.settings.settings import SEARCH_INDEX_BATCH_SIZE
19
19
  from codex.status import Status
20
20
 
@@ -159,7 +159,7 @@ class FTSUpdateMixin(RemoveMixin):
159
159
  obj_list.append(comicfts)
160
160
  self.log.debug(f"{len(obj_list)}/{comics.count()} entries prepped.")
161
161
 
162
- def _get_comicfts_list(self, comics, create=False):
162
+ def _get_comicfts_list(self, comics, create: bool):
163
163
  """Create a ComicFTS object for bulk_create or bulk_update."""
164
164
  country_field = CountryField()
165
165
  language_field = LanguageField()
@@ -178,7 +178,7 @@ class FTSUpdateMixin(RemoveMixin):
178
178
  self.log.debug(f"{verb} no search entries.")
179
179
  self.status_controller.finish(status)
180
180
 
181
- def _update_search_index_operate(self, comics_qs, create=False):
181
+ def _update_search_index_operate(self, comics_qs, create: bool):
182
182
  count = comics_qs.count()
183
183
  verb = "create" if create else "update"
184
184
  if not count:
@@ -285,7 +285,7 @@ class FTSUpdateMixin(RemoveMixin):
285
285
  elapsed = naturaldelta(elapsed_time)
286
286
  self.log.info(f"Search index updated in {elapsed}.")
287
287
 
288
- def update_search_index(self, rebuild=False):
288
+ def update_search_index(self, rebuild: bool):
289
289
  """Update or Rebuild the search index."""
290
290
  start_time = time()
291
291
  self.abort_event.clear()
@@ -62,11 +62,12 @@ class CodexStats:
62
62
  request_model_set = self.params.get(key, {})
63
63
  all_models = _KEY_MODELS_MAP[key]
64
64
  if request_model_set:
65
- models = []
66
- for model_name in request_model_set:
67
- for model in all_models:
68
- if model.__name__.lower() == model_name.lower():
69
- models.append(model)
65
+ models = [
66
+ model
67
+ for model in all_models
68
+ for model_name in request_model_set
69
+ if model.__name__.lower() == model_name.lower()
70
+ ]
70
71
  else:
71
72
  models = all_models
72
73
  return tuple(models)
@@ -149,7 +150,7 @@ class CodexStats:
149
150
 
150
151
  def _add_file_types(self, obj):
151
152
  """Query for file types."""
152
- if self.params and "fileTypes" not in self.params:
153
+ if self.params and "file_types" not in self.params:
153
154
  return
154
155
  file_types = {}
155
156
  qs = (
@@ -68,7 +68,6 @@ def _send_telemetry(log):
68
68
  stats = CodexStats().get()
69
69
  data = {"stats": stats, "uuid": ts.version}
70
70
  _post_stats(log, data)
71
- # log.debug("Sent anonymous stats")
72
71
  ts.save() # update updated_at
73
72
 
74
73
 
@@ -61,12 +61,12 @@ class CodexDatabaseSnapshot(DirectorySnapshot, LoggerBaseMixin):
61
61
  def __init__(
62
62
  self,
63
63
  path,
64
- _recursive=True, # unused, always recursive
64
+ _recursive=True, # noqa: FBT002 unused, always recursive
65
65
  stat=os.stat,
66
66
  _listdir=os.listdir, # unused for database
67
- force=False,
67
+ force=False, # noqa: FBT002
68
68
  log_queue=None,
69
- covers_only=False,
69
+ covers_only=False, # noqa: FBT002
70
70
  ):
71
71
  """Initialize like DirectorySnapshot but use a database walk."""
72
72
  self._covers_only = covers_only
@@ -33,7 +33,8 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
33
33
  return self._get_inode(data.ref, path) == self._get_inode(data.snapshot, path)
34
34
 
35
35
  def _is_stats_equal(self, data, old_path, new_path):
36
- """Return if the mtime and size are equal.
36
+ """
37
+ Return if the mtime and size are equal.
37
38
 
38
39
  For old paths in the ref and new paths in the snapshot.
39
40
  """
@@ -84,9 +85,7 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
84
85
  if not self._is_stats_equal(data, old_path, new_path):
85
86
  data.modified.add(new_path)
86
87
 
87
- def __init__( # C901, PLR0912
88
- self, ref, snapshot, ignore_device=False, inode_only_modified=False
89
- ):
88
+ def __init__(self, ref, snapshot, ignore_device: bool, inode_only_modified: bool):
90
89
  """Create diff object."""
91
90
  self._ignore_device = ignore_device
92
91
  self._inode_only_modified = inode_only_modified
@@ -46,14 +46,14 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
46
46
 
47
47
  _DIR_NOT_FOUND_TIMEOUT = 15 * 60
48
48
 
49
- def __init__( # noqa PLR0913
49
+ def __init__( # noqa: PLR0913
50
50
  self,
51
51
  event_queue,
52
52
  watch,
53
53
  timeout=DEFAULT_EMITTER_TIMEOUT,
54
54
  log_queue=None,
55
55
  librarian_queue=None,
56
- covers_only=False,
56
+ covers_only=False, # noqa: FBT002
57
57
  ):
58
58
  """Initialize snapshot methods."""
59
59
  self.init_worker(log_queue, librarian_queue)
@@ -72,7 +72,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
72
72
  # default stat and listdir params
73
73
  )
74
74
 
75
- def poll(self, force=False):
75
+ def poll(self, force: bool):
76
76
  """Poll now, sooner than timeout."""
77
77
  self._force = force
78
78
  with self._poll_cond:
@@ -118,7 +118,7 @@ class DatabasePollingEmitter(EventEmitter, WorkerBaseMixin):
118
118
  return ok
119
119
 
120
120
  @property
121
- def timeout(self) -> int | None: # type: ignore
121
+ def timeout(self) -> int | None: # type: ignore[reportIncompatibleMethodOverride]
122
122
  """Get the timeout for this emitter from its library."""
123
123
  # The timeout from the constructor, self._timeout, is thrown away in favor
124
124
  # of a dynamic timeout from the database.
@@ -1,4 +1,5 @@
1
- """Batch watchdog events into bulk database tasks.
1
+ """
2
+ Batch watchdog events into bulk database tasks.
2
3
 
3
4
  Watchdog actually starts events as bulk events with the DirSnapshotDiff
4
5
  but the built-in filesystem event emitters serialize them, so the most
@@ -198,8 +198,7 @@ class CodexLibraryEventHandler(CodexEventHandlerBase):
198
198
  task = WatchdogEventTask(self.library_pk, sub_event)
199
199
  self.librarian_queue.put(task)
200
200
 
201
- # Calls stub event dispatchers
202
- # super().dispatch(event)
201
+ # Do not call super dispatch to call stub event dispatchers
203
202
  except Exception:
204
203
  self.log.exception(f"{self.__class__.__name__} dispatch")
205
204
 
@@ -1,5 +1,6 @@
1
1
  """The Codex Library Watchdog Observer threads."""
2
2
 
3
+ from watchdog.events import FileSystemEvent, FileSystemEventHandler
3
4
  from watchdog.observers import Observer
4
5
  from watchdog.observers.api import (
5
6
  DEFAULT_OBSERVER_TIMEOUT,
@@ -98,14 +99,16 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
98
99
  except Exception:
99
100
  self.log.exception(f"{self.__class__.__name__} sync library watches")
100
101
 
101
- def schedule( # type: ignore
102
+ def schedule(
102
103
  self,
103
- event_handler,
104
- path,
105
- recursive=False,
106
- _event_filter=None,
107
- ):
108
- """Override BaseObserver for Codex emitter class.
104
+ event_handler: FileSystemEventHandler,
105
+ path: str,
106
+ *,
107
+ recursive: bool = False,
108
+ event_filter: list[type[FileSystemEvent]] | None = None, # noqa: ARG002, F841, RUF100
109
+ ) -> ObservedWatch:
110
+ """
111
+ Override BaseObserver for Codex emitter class.
109
112
 
110
113
  https://pythonhosted.org/watchdog/_modules/watchdog/observers/api.html#BaseObserver
111
114
  """
@@ -137,9 +140,11 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
137
140
  # watchdog class structure doesn't work that way.
138
141
 
139
142
 
140
- class LibraryEventObserver(UatuMixin, Observer): # type: ignore
143
+ class LibraryEventObserver(UatuMixin, Observer): # type: ignore[reportGeneralTypeIssues]
141
144
  """Regular observer."""
142
145
 
146
+ # Observer is a dynamically generated class by platform at runtime.
147
+
143
148
  ENABLE_FIELD = "events"
144
149
 
145
150
 
@@ -156,7 +161,7 @@ class LibraryPollingObserver(UatuMixin):
156
161
  *args, emitter_class=DatabasePollingEmitter, timeout=timeout, **kwargs
157
162
  )
158
163
 
159
- def poll(self, library_pks, force=False):
164
+ def poll(self, library_pks, force: bool):
160
165
  """Poll each requested emitter."""
161
166
  try:
162
167
  qs = Library.objects.all()
@@ -165,7 +170,7 @@ class LibraryPollingObserver(UatuMixin):
165
170
  paths = frozenset(qs.values_list("path", flat=True))
166
171
 
167
172
  for emitter in self.emitters:
168
- polling_emitter: DatabasePollingEmitter = emitter # type: ignore
173
+ polling_emitter: DatabasePollingEmitter = emitter # type: ignore[reportAssignmentType]
169
174
  if emitter.watch.path in paths:
170
175
  polling_emitter.poll(force)
171
176
  except Exception:
codex/logger/logging.py CHANGED
@@ -20,7 +20,7 @@ class CodexQueueHandler(QueueHandler):
20
20
  super().enqueue(record)
21
21
 
22
22
 
23
- LOG_HANDLER = CodexQueueHandler(LOG_QUEUE) # type: ignore
23
+ LOG_HANDLER = CodexQueueHandler(LOG_QUEUE)
24
24
 
25
25
 
26
26
  def _ensure_handler(logger, queue):
codex/memory.py CHANGED
@@ -26,7 +26,7 @@ def get_cgroups1_mem_limit():
26
26
  with Path(MEMORY_STAT_PATH).open("r") as mem_stat_file:
27
27
  for line in mem_stat_file:
28
28
  parts = line.split()
29
- if not parts or len(parts) < 2: # noqa PLR2004
29
+ if not parts or len(parts) < 2: # noqa: PLR2004
30
30
  continue
31
31
  if "hierarchical_memory_limit" in parts[0]:
32
32
  return int(parts[1])
@@ -34,7 +34,8 @@ def get_cgroups1_mem_limit():
34
34
 
35
35
 
36
36
  def get_mem_limit(divisor="b"):
37
- """Get the current memlimit.
37
+ """
38
+ Get the current memlimit.
38
39
 
39
40
  If we're in a container set the limit too.
40
41
  """
@@ -632,8 +632,7 @@ class Migration(migrations.Migration):
632
632
  default=None,
633
633
  max_length=6,
634
634
  null=True,
635
- # Removed in the future
636
- # validators=[codex.models.validate_fit_to_choice],
635
+ # Old code dependent validators removed in the future
637
636
  ),
638
637
  ),
639
638
  ("two_pages", models.BooleanField(default=None, null=True)),
@@ -151,8 +151,7 @@ class Migration(migrations.Migration):
151
151
  field=models.CharField(
152
152
  default="comic",
153
153
  max_length=5,
154
- # Removed in the future
155
- # validators=[codex.models.validate_file_format_choice],
154
+ # codex dependent validators removed in the future
156
155
  ),
157
156
  ),
158
157
  migrations.AddField(
@@ -28,8 +28,7 @@ class Migration(migrations.Migration):
28
28
  blank=True,
29
29
  default="",
30
30
  max_length=6,
31
- # removed in the future
32
- # validators=[codex.models.validate_fit_to_choice],
31
+ # Code dependent validators removed in the future
33
32
  ),
34
33
  ),
35
34
  migrations.AlterField(