codex 1.6.9a0__py3-none-any.whl → 1.6.10__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 (675) hide show
  1. codex/librarian/cron/crond.py +10 -13
  2. codex/librarian/importer/importerd.py +19 -24
  3. codex/librarian/importer/moved.py +73 -176
  4. codex/librarian/importer/query_fks.py +11 -17
  5. codex/librarian/janitor/janitor.py +4 -12
  6. codex/librarian/janitor/status.py +0 -1
  7. codex/librarian/janitor/tasks.py +7 -14
  8. codex/librarian/janitor/update.py +2 -21
  9. codex/librarian/janitor/vacuum.py +2 -1
  10. codex/librarian/telemeter/stats.py +16 -20
  11. codex/librarian/telemeter/telemeter.py +7 -10
  12. codex/librarian/watchdog/dir_snapshot_diff.py +17 -19
  13. codex/librarian/watchdog/emitter.py +14 -12
  14. codex/librarian/watchdog/events.py +1 -1
  15. codex/librarian/watchdog/observers.py +4 -4
  16. codex/search/backend.py +2 -9
  17. codex/serializers/browser/mixins.py +2 -1
  18. codex/settings/settings.py +1 -5
  19. codex/signals/django_signals.py +9 -0
  20. codex/startup.py +3 -1
  21. codex/static_root/assets/VCheckbox-BoSq-Gp8.45ea578c17b0.js +1 -0
  22. codex/static_root/assets/VCheckbox-BoSq-Gp8.45ea578c17b0.js.br +0 -0
  23. codex/static_root/assets/VCheckbox-BoSq-Gp8.45ea578c17b0.js.gz +0 -0
  24. codex/static_root/assets/VCheckbox-BoSq-Gp8.js +1 -0
  25. codex/static_root/assets/VCheckbox-BoSq-Gp8.js.br +0 -0
  26. codex/static_root/assets/VCheckbox-BoSq-Gp8.js.gz +0 -0
  27. codex/static_root/assets/VCheckboxBtn-k69hHqz6.a5c3131a12bf.js +1 -0
  28. codex/static_root/assets/VCheckboxBtn-k69hHqz6.a5c3131a12bf.js.br +0 -0
  29. codex/static_root/assets/VCheckboxBtn-k69hHqz6.a5c3131a12bf.js.gz +0 -0
  30. codex/static_root/assets/VCheckboxBtn-k69hHqz6.js +1 -0
  31. codex/static_root/assets/VCheckboxBtn-k69hHqz6.js.br +0 -0
  32. codex/static_root/assets/VCheckboxBtn-k69hHqz6.js.gz +0 -0
  33. codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css +1 -0
  34. codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css.br +0 -0
  35. codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css.gz +0 -0
  36. codex/static_root/assets/VCombobox-BksEr4D_.css +1 -0
  37. codex/static_root/assets/VCombobox-BksEr4D_.css.br +0 -0
  38. codex/static_root/assets/VCombobox-BksEr4D_.css.gz +0 -0
  39. codex/static_root/assets/VCombobox-CLEC9nAk.3ed4e3a0cb6e.js +1 -0
  40. codex/static_root/assets/VCombobox-CLEC9nAk.3ed4e3a0cb6e.js.br +0 -0
  41. codex/static_root/assets/VCombobox-CLEC9nAk.3ed4e3a0cb6e.js.gz +0 -0
  42. codex/static_root/assets/VCombobox-CLEC9nAk.js +1 -0
  43. codex/static_root/assets/VCombobox-CLEC9nAk.js.br +0 -0
  44. codex/static_root/assets/VCombobox-CLEC9nAk.js.gz +0 -0
  45. codex/static_root/assets/VDialog-Dir_-l5o.5de2881bebcc.js +1 -0
  46. codex/static_root/assets/VDialog-Dir_-l5o.5de2881bebcc.js.br +0 -0
  47. codex/static_root/assets/VDialog-Dir_-l5o.5de2881bebcc.js.gz +0 -0
  48. codex/static_root/assets/VDialog-Dir_-l5o.js +1 -0
  49. codex/static_root/assets/VDialog-Dir_-l5o.js.br +0 -0
  50. codex/static_root/assets/VDialog-Dir_-l5o.js.gz +0 -0
  51. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css +1 -0
  52. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
  53. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
  54. codex/static_root/assets/VDialog-lQ13FnRR.css +1 -0
  55. codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
  56. codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
  57. codex/static_root/assets/VExpansionPanels-DSywtH47.8bff92b5b38b.js +1 -0
  58. codex/static_root/assets/VExpansionPanels-DSywtH47.8bff92b5b38b.js.br +0 -0
  59. codex/static_root/assets/VExpansionPanels-DSywtH47.8bff92b5b38b.js.gz +0 -0
  60. codex/static_root/assets/VExpansionPanels-DSywtH47.js +1 -0
  61. codex/static_root/assets/VExpansionPanels-DSywtH47.js.br +0 -0
  62. codex/static_root/assets/VExpansionPanels-DSywtH47.js.gz +0 -0
  63. codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css +1 -0
  64. codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css.br +0 -0
  65. codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css.gz +0 -0
  66. codex/static_root/assets/VExpansionPanels-DsEXVcCv.css +1 -0
  67. codex/static_root/assets/VExpansionPanels-DsEXVcCv.css.br +0 -0
  68. codex/static_root/assets/VExpansionPanels-DsEXVcCv.css.gz +0 -0
  69. codex/static_root/assets/VRadioGroup-BwpxWbvH.1e52b683c2bf.js +1 -0
  70. codex/static_root/assets/VRadioGroup-BwpxWbvH.1e52b683c2bf.js.br +0 -0
  71. codex/static_root/assets/VRadioGroup-BwpxWbvH.1e52b683c2bf.js.gz +0 -0
  72. codex/static_root/assets/VRadioGroup-BwpxWbvH.js +1 -0
  73. codex/static_root/assets/VRadioGroup-BwpxWbvH.js.br +0 -0
  74. codex/static_root/assets/VRadioGroup-BwpxWbvH.js.gz +0 -0
  75. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +1 -0
  76. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  77. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  78. codex/static_root/assets/VSelect-ARDhJiQK.css +1 -0
  79. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  80. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  81. codex/static_root/assets/VSelect-cGleKlm7.82f880fcf439.js +1 -0
  82. codex/static_root/assets/VSelect-cGleKlm7.82f880fcf439.js.br +0 -0
  83. codex/static_root/assets/VSelect-cGleKlm7.82f880fcf439.js.gz +0 -0
  84. codex/static_root/assets/VSelect-cGleKlm7.js +1 -0
  85. codex/static_root/assets/VSelect-cGleKlm7.js.br +0 -0
  86. codex/static_root/assets/VSelect-cGleKlm7.js.gz +0 -0
  87. codex/static_root/assets/{VSelectionControl-CiViEYoC.c77bb19a4a06.css → VSelectionControl-BFq--28I.9687c77461fc.css} +1 -1
  88. codex/static_root/assets/VSelectionControl-BFq--28I.9687c77461fc.css.br +0 -0
  89. codex/static_root/assets/VSelectionControl-BFq--28I.9687c77461fc.css.gz +0 -0
  90. codex/static_root/assets/{VSelectionControl-CiViEYoC.css → VSelectionControl-BFq--28I.css} +1 -1
  91. codex/static_root/assets/VSelectionControl-BFq--28I.css.br +0 -0
  92. codex/static_root/assets/VSelectionControl-BFq--28I.css.gz +0 -0
  93. codex/static_root/assets/VSelectionControl-_6sv90xF.74f88fc610a2.js +1 -0
  94. codex/static_root/assets/VSelectionControl-_6sv90xF.74f88fc610a2.js.br +0 -0
  95. codex/static_root/assets/VSelectionControl-_6sv90xF.74f88fc610a2.js.gz +0 -0
  96. codex/static_root/assets/VSelectionControl-_6sv90xF.js +1 -0
  97. codex/static_root/assets/VSelectionControl-_6sv90xF.js.br +0 -0
  98. codex/static_root/assets/VSelectionControl-_6sv90xF.js.gz +0 -0
  99. codex/static_root/assets/{VSlideGroup-P_U-rmf2.ca3efe698702.js → VSlideGroup-DbQOqyzs.183374da65d9.js} +1 -1
  100. codex/static_root/assets/VSlideGroup-DbQOqyzs.183374da65d9.js.br +0 -0
  101. codex/static_root/assets/VSlideGroup-DbQOqyzs.183374da65d9.js.gz +0 -0
  102. codex/static_root/assets/{VSlideGroup-P_U-rmf2.js → VSlideGroup-DbQOqyzs.js} +1 -1
  103. codex/static_root/assets/VSlideGroup-DbQOqyzs.js.br +0 -0
  104. codex/static_root/assets/VSlideGroup-DbQOqyzs.js.gz +0 -0
  105. codex/static_root/assets/{VTable-Coq-QwtQ.2b04104cbf05.js → VTable-CF4zd2t_.3ade1377ce12.js} +1 -1
  106. codex/static_root/assets/VTable-CF4zd2t_.3ade1377ce12.js.br +0 -0
  107. codex/static_root/assets/VTable-CF4zd2t_.3ade1377ce12.js.gz +0 -0
  108. codex/static_root/assets/{VTable-Coq-QwtQ.js → VTable-CF4zd2t_.js} +1 -1
  109. codex/static_root/assets/VTable-CF4zd2t_.js.br +0 -0
  110. codex/static_root/assets/VTable-CF4zd2t_.js.gz +0 -0
  111. codex/static_root/assets/VTable-DflzroDq.160504d82195.css +1 -0
  112. codex/static_root/assets/VTable-DflzroDq.160504d82195.css.br +0 -0
  113. codex/static_root/assets/VTable-DflzroDq.160504d82195.css.gz +0 -0
  114. codex/static_root/assets/VTable-DflzroDq.css +1 -0
  115. codex/static_root/assets/VTable-DflzroDq.css.br +0 -0
  116. codex/static_root/assets/VTable-DflzroDq.css.gz +0 -0
  117. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +1 -0
  118. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  119. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  120. codex/static_root/assets/VTextField-BssVoDhB.css +1 -0
  121. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  122. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  123. codex/static_root/assets/VTextField-DcZhRvPB.5eba90d2c45f.js +1 -0
  124. codex/static_root/assets/VTextField-DcZhRvPB.5eba90d2c45f.js.br +0 -0
  125. codex/static_root/assets/VTextField-DcZhRvPB.5eba90d2c45f.js.gz +0 -0
  126. codex/static_root/assets/VTextField-DcZhRvPB.js +1 -0
  127. codex/static_root/assets/VTextField-DcZhRvPB.js.br +0 -0
  128. codex/static_root/assets/VTextField-DcZhRvPB.js.gz +0 -0
  129. codex/static_root/assets/VWindowItem-0P4rgYDy.b96b44e20f8d.js +1 -0
  130. codex/static_root/assets/VWindowItem-0P4rgYDy.b96b44e20f8d.js.br +0 -0
  131. codex/static_root/assets/VWindowItem-0P4rgYDy.b96b44e20f8d.js.gz +0 -0
  132. codex/static_root/assets/VWindowItem-0P4rgYDy.js +1 -0
  133. codex/static_root/assets/VWindowItem-0P4rgYDy.js.br +0 -0
  134. codex/static_root/assets/VWindowItem-0P4rgYDy.js.gz +0 -0
  135. codex/static_root/assets/admin-CM5v3ffD.1889b8c1df83.js +1 -0
  136. codex/static_root/assets/admin-CM5v3ffD.1889b8c1df83.js.br +0 -0
  137. codex/static_root/assets/admin-CM5v3ffD.1889b8c1df83.js.gz +0 -0
  138. codex/static_root/assets/admin-CM5v3ffD.js +1 -0
  139. codex/static_root/assets/admin-CM5v3ffD.js.br +0 -0
  140. codex/static_root/assets/admin-CM5v3ffD.js.gz +0 -0
  141. codex/static_root/assets/{admin-CWEjaFr5.7d4523387384.css → admin-D4sw3N4T.b590f88a62cf.css} +1 -1
  142. codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.br +0 -0
  143. codex/static_root/assets/admin-D4sw3N4T.b590f88a62cf.css.gz +0 -0
  144. codex/static_root/assets/{admin-CWEjaFr5.css → admin-D4sw3N4T.css} +1 -1
  145. codex/static_root/assets/admin-D4sw3N4T.css.br +0 -0
  146. codex/static_root/assets/admin-D4sw3N4T.css.gz +0 -0
  147. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css +1 -0
  148. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css.br +0 -0
  149. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css.gz +0 -0
  150. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css +1 -0
  151. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css.br +0 -0
  152. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css.gz +0 -0
  153. codex/static_root/assets/admin-drawer-panel-D_qGurp0.2904617d0407.js +61 -0
  154. codex/static_root/assets/admin-drawer-panel-D_qGurp0.2904617d0407.js.br +0 -0
  155. codex/static_root/assets/admin-drawer-panel-D_qGurp0.2904617d0407.js.gz +0 -0
  156. codex/static_root/assets/admin-drawer-panel-D_qGurp0.js +61 -0
  157. codex/static_root/assets/admin-drawer-panel-D_qGurp0.js.br +0 -0
  158. codex/static_root/assets/admin-drawer-panel-D_qGurp0.js.gz +0 -0
  159. codex/static_root/assets/{browser-Binc3n9H.313722d27ca3.css → browser-B61tfW_K.61b1d18f4479.css} +1 -1
  160. codex/static_root/assets/browser-B61tfW_K.61b1d18f4479.css.br +0 -0
  161. codex/static_root/assets/browser-B61tfW_K.61b1d18f4479.css.gz +0 -0
  162. codex/static_root/assets/{browser-Binc3n9H.css → browser-B61tfW_K.css} +1 -1
  163. codex/static_root/assets/browser-B61tfW_K.css.br +0 -0
  164. codex/static_root/assets/browser-B61tfW_K.css.gz +0 -0
  165. codex/static_root/assets/browser-DnLOg4a-.3d16651e4daa.js +1 -0
  166. codex/static_root/assets/browser-DnLOg4a-.3d16651e4daa.js.br +0 -0
  167. codex/static_root/assets/browser-DnLOg4a-.3d16651e4daa.js.gz +0 -0
  168. codex/static_root/assets/browser-DnLOg4a-.js +1 -0
  169. codex/static_root/assets/browser-DnLOg4a-.js.br +0 -0
  170. codex/static_root/assets/browser-DnLOg4a-.js.gz +0 -0
  171. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +1 -0
  172. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
  173. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
  174. codex/static_root/assets/change-password-dialog-0u6IJA87.css +1 -0
  175. codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
  176. codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
  177. codex/static_root/assets/change-password-dialog-B_FNP488.6009508df02f.js +1 -0
  178. codex/static_root/assets/change-password-dialog-B_FNP488.6009508df02f.js.br +0 -0
  179. codex/static_root/assets/change-password-dialog-B_FNP488.6009508df02f.js.gz +0 -0
  180. codex/static_root/assets/change-password-dialog-B_FNP488.js +1 -0
  181. codex/static_root/assets/change-password-dialog-B_FNP488.js.br +0 -0
  182. codex/static_root/assets/change-password-dialog-B_FNP488.js.gz +0 -0
  183. codex/static_root/assets/confirm-dialog-CEegFOPy.49243b6548c7.js +1 -0
  184. codex/static_root/assets/confirm-dialog-CEegFOPy.49243b6548c7.js.br +0 -0
  185. codex/static_root/assets/confirm-dialog-CEegFOPy.49243b6548c7.js.gz +0 -0
  186. codex/static_root/assets/confirm-dialog-CEegFOPy.js +1 -0
  187. codex/static_root/assets/confirm-dialog-CEegFOPy.js.br +0 -0
  188. codex/static_root/assets/confirm-dialog-CEegFOPy.js.gz +0 -0
  189. codex/static_root/assets/{datetime-column-BRZDPl2k.f6ae3ce94da7.js → datetime-column-C10LMvbg.dd6d719d5ceb.js} +1 -1
  190. codex/static_root/assets/datetime-column-C10LMvbg.dd6d719d5ceb.js.br +0 -0
  191. codex/static_root/assets/datetime-column-C10LMvbg.dd6d719d5ceb.js.gz +0 -0
  192. codex/static_root/assets/{datetime-column-BRZDPl2k.js → datetime-column-C10LMvbg.js} +1 -1
  193. codex/static_root/assets/datetime-column-C10LMvbg.js.br +0 -0
  194. codex/static_root/assets/datetime-column-C10LMvbg.js.gz +0 -0
  195. codex/static_root/assets/{filter-Tk2N9xgO.02f23827434b.js → filter-C96Ludgn.7238da6d21fd.js} +1 -1
  196. codex/static_root/assets/filter-C96Ludgn.7238da6d21fd.js.br +0 -0
  197. codex/static_root/assets/filter-C96Ludgn.7238da6d21fd.js.gz +0 -0
  198. codex/static_root/assets/{filter-Tk2N9xgO.js → filter-C96Ludgn.js} +1 -1
  199. codex/static_root/assets/filter-C96Ludgn.js.br +0 -0
  200. codex/static_root/assets/filter-C96Ludgn.js.gz +0 -0
  201. codex/static_root/assets/flag-tab-C3MlArdN.2a06f46f4951.js +1 -0
  202. codex/static_root/assets/flag-tab-C3MlArdN.2a06f46f4951.js.br +0 -0
  203. codex/static_root/assets/flag-tab-C3MlArdN.2a06f46f4951.js.gz +0 -0
  204. codex/static_root/assets/flag-tab-C3MlArdN.js +1 -0
  205. codex/static_root/assets/flag-tab-C3MlArdN.js.br +0 -0
  206. codex/static_root/assets/flag-tab-C3MlArdN.js.gz +0 -0
  207. codex/static_root/assets/group-tab-BcQtQB5p.41780aa82feb.js +1 -0
  208. codex/static_root/assets/group-tab-BcQtQB5p.41780aa82feb.js.br +0 -0
  209. codex/static_root/assets/group-tab-BcQtQB5p.41780aa82feb.js.gz +0 -0
  210. codex/static_root/assets/group-tab-BcQtQB5p.js +1 -0
  211. codex/static_root/assets/group-tab-BcQtQB5p.js.br +0 -0
  212. codex/static_root/assets/group-tab-BcQtQB5p.js.gz +0 -0
  213. codex/static_root/assets/http-error-DskCJS4d.e51aabff2171.js +1 -0
  214. codex/static_root/assets/http-error-DskCJS4d.e51aabff2171.js.br +0 -0
  215. codex/static_root/assets/http-error-DskCJS4d.e51aabff2171.js.gz +0 -0
  216. codex/static_root/assets/http-error-DskCJS4d.js +1 -0
  217. codex/static_root/assets/http-error-DskCJS4d.js.br +0 -0
  218. codex/static_root/assets/http-error-DskCJS4d.js.gz +0 -0
  219. codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css +1 -0
  220. codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.br +0 -0
  221. codex/static_root/assets/library-tab-BygwYPnO.94e92aa60bf2.css.gz +0 -0
  222. codex/static_root/assets/library-tab-BygwYPnO.css +1 -0
  223. codex/static_root/assets/library-tab-BygwYPnO.css.br +0 -0
  224. codex/static_root/assets/library-tab-BygwYPnO.css.gz +0 -0
  225. codex/static_root/assets/library-tab-ooPl8bku.bfcc0f7b4432.js +1 -0
  226. codex/static_root/assets/library-tab-ooPl8bku.bfcc0f7b4432.js.br +0 -0
  227. codex/static_root/assets/library-tab-ooPl8bku.bfcc0f7b4432.js.gz +0 -0
  228. codex/static_root/assets/library-tab-ooPl8bku.js +1 -0
  229. codex/static_root/assets/library-tab-ooPl8bku.js.br +0 -0
  230. codex/static_root/assets/library-tab-ooPl8bku.js.gz +0 -0
  231. codex/static_root/assets/main-C_v3V6g9.40fbf6d42a93.js +1 -0
  232. codex/static_root/assets/main-C_v3V6g9.40fbf6d42a93.js.br +0 -0
  233. codex/static_root/assets/main-C_v3V6g9.40fbf6d42a93.js.gz +0 -0
  234. codex/static_root/assets/main-C_v3V6g9.js +1 -0
  235. codex/static_root/assets/main-C_v3V6g9.js.br +0 -0
  236. codex/static_root/assets/main-C_v3V6g9.js.gz +0 -0
  237. codex/static_root/assets/{main-BZ73p_WW.css → main-Ztp855PW.css} +1 -1
  238. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  239. codex/static_root/assets/{main-BZ73p_WW.css.gz → main-Ztp855PW.css.gz} +0 -0
  240. codex/static_root/assets/{main-BZ73p_WW.d529e8b3f52a.css → main-Ztp855PW.d46d718fac45.css} +1 -1
  241. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  242. codex/static_root/assets/{main-BZ73p_WW.d529e8b3f52a.css.gz → main-Ztp855PW.d46d718fac45.css.gz} +0 -0
  243. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +1 -0
  244. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
  245. codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
  246. codex/static_root/assets/pagination-toolbar-BA9ifTed.css +1 -0
  247. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
  248. codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
  249. codex/static_root/assets/pagination-toolbar-DytNZNBD.be56c9194ecb.js +1 -0
  250. codex/static_root/assets/pagination-toolbar-DytNZNBD.be56c9194ecb.js.br +0 -0
  251. codex/static_root/assets/pagination-toolbar-DytNZNBD.be56c9194ecb.js.gz +0 -0
  252. codex/static_root/assets/pagination-toolbar-DytNZNBD.js +1 -0
  253. codex/static_root/assets/pagination-toolbar-DytNZNBD.js.br +0 -0
  254. codex/static_root/assets/pagination-toolbar-DytNZNBD.js.gz +0 -0
  255. codex/static_root/assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css +1 -0
  256. codex/static_root/assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css.br +0 -0
  257. codex/static_root/assets/pdf-doc-CMzaSV_K.8cd4b3e65f9a.css.gz +0 -0
  258. codex/static_root/assets/pdf-doc-CMzaSV_K.css +1 -0
  259. codex/static_root/assets/pdf-doc-CMzaSV_K.css.br +0 -0
  260. codex/static_root/assets/pdf-doc-CMzaSV_K.css.gz +0 -0
  261. codex/static_root/assets/pdf-doc-k6Gv5nwG.22b85beec022.js +305 -0
  262. codex/static_root/assets/pdf-doc-k6Gv5nwG.22b85beec022.js.br +0 -0
  263. codex/static_root/assets/pdf-doc-k6Gv5nwG.22b85beec022.js.gz +0 -0
  264. codex/static_root/assets/pdf-doc-k6Gv5nwG.js +305 -0
  265. codex/static_root/assets/pdf-doc-k6Gv5nwG.js.br +0 -0
  266. codex/static_root/assets/pdf-doc-k6Gv5nwG.js.gz +0 -0
  267. codex/static_root/assets/reader-RcTjScAE.41459ab8f803.js +2 -0
  268. codex/static_root/assets/reader-RcTjScAE.41459ab8f803.js.br +0 -0
  269. codex/static_root/assets/reader-RcTjScAE.41459ab8f803.js.gz +0 -0
  270. codex/static_root/assets/reader-RcTjScAE.js +2 -0
  271. codex/static_root/assets/reader-RcTjScAE.js.br +0 -0
  272. codex/static_root/assets/reader-RcTjScAE.js.gz +0 -0
  273. codex/static_root/assets/reader-YbzUiD0T.css +1 -0
  274. codex/static_root/assets/reader-YbzUiD0T.css.br +0 -0
  275. codex/static_root/assets/reader-YbzUiD0T.css.gz +0 -0
  276. codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css +1 -0
  277. codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.br +0 -0
  278. codex/static_root/assets/reader-YbzUiD0T.efb15835b39d.css.gz +0 -0
  279. codex/static_root/assets/{relation-chips-ByfWwuQc.01269a188e01.css → relation-chips-BGitp3yx.b3e7d178a615.css} +1 -1
  280. codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.br +0 -0
  281. codex/static_root/assets/relation-chips-BGitp3yx.b3e7d178a615.css.gz +0 -0
  282. codex/static_root/assets/{relation-chips-ByfWwuQc.css → relation-chips-BGitp3yx.css} +1 -1
  283. codex/static_root/assets/relation-chips-BGitp3yx.css.br +0 -0
  284. codex/static_root/assets/relation-chips-BGitp3yx.css.gz +0 -0
  285. codex/static_root/assets/relation-chips-DE08G87m.ea1b8391feff.js +1 -0
  286. codex/static_root/assets/relation-chips-DE08G87m.ea1b8391feff.js.br +0 -0
  287. codex/static_root/assets/relation-chips-DE08G87m.ea1b8391feff.js.gz +0 -0
  288. codex/static_root/assets/relation-chips-DE08G87m.js +1 -0
  289. codex/static_root/assets/relation-chips-DE08G87m.js.br +0 -0
  290. codex/static_root/assets/relation-chips-DE08G87m.js.gz +0 -0
  291. codex/static_root/assets/settings-drawer-BF5NdklY.0c3f4f51c12f.js +2 -0
  292. codex/static_root/assets/settings-drawer-BF5NdklY.0c3f4f51c12f.js.br +0 -0
  293. codex/static_root/assets/settings-drawer-BF5NdklY.0c3f4f51c12f.js.gz +0 -0
  294. codex/static_root/assets/settings-drawer-BF5NdklY.js +2 -0
  295. codex/static_root/assets/settings-drawer-BF5NdklY.js.br +0 -0
  296. codex/static_root/assets/settings-drawer-BF5NdklY.js.gz +0 -0
  297. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +1 -0
  298. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  299. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  300. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +1 -0
  301. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  302. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  303. codex/static_root/assets/{stats-tab-BXGisn5N.9b7a71e7fe28.css → stats-tab-BVZzfwi1.28f07f038e88.css} +1 -1
  304. codex/static_root/assets/stats-tab-BVZzfwi1.28f07f038e88.css.br +0 -0
  305. codex/static_root/assets/stats-tab-BVZzfwi1.28f07f038e88.css.gz +0 -0
  306. codex/static_root/assets/{stats-tab-BXGisn5N.css → stats-tab-BVZzfwi1.css} +1 -1
  307. codex/static_root/assets/stats-tab-BVZzfwi1.css.br +0 -0
  308. codex/static_root/assets/stats-tab-BVZzfwi1.css.gz +0 -0
  309. codex/static_root/assets/stats-tab-pGjIjQgL.f5f939abe108.js +1 -0
  310. codex/static_root/assets/stats-tab-pGjIjQgL.f5f939abe108.js.br +0 -0
  311. codex/static_root/assets/stats-tab-pGjIjQgL.f5f939abe108.js.gz +0 -0
  312. codex/static_root/assets/stats-tab-pGjIjQgL.js +1 -0
  313. codex/static_root/assets/stats-tab-pGjIjQgL.js.br +0 -0
  314. codex/static_root/assets/stats-tab-pGjIjQgL.js.gz +0 -0
  315. codex/static_root/assets/task-tab-DTZ-yJon.2000bd142ca2.js +1 -0
  316. codex/static_root/assets/task-tab-DTZ-yJon.2000bd142ca2.js.br +0 -0
  317. codex/static_root/assets/task-tab-DTZ-yJon.2000bd142ca2.js.gz +0 -0
  318. codex/static_root/assets/task-tab-DTZ-yJon.js +1 -0
  319. codex/static_root/assets/task-tab-DTZ-yJon.js.br +0 -0
  320. codex/static_root/assets/task-tab-DTZ-yJon.js.gz +0 -0
  321. codex/static_root/assets/unauthorized-BlNvdM7v.e3a8b210f100.js +1 -0
  322. codex/static_root/assets/unauthorized-BlNvdM7v.e3a8b210f100.js.br +0 -0
  323. codex/static_root/assets/unauthorized-BlNvdM7v.e3a8b210f100.js.gz +0 -0
  324. codex/static_root/assets/unauthorized-BlNvdM7v.js +1 -0
  325. codex/static_root/assets/unauthorized-BlNvdM7v.js.br +0 -0
  326. codex/static_root/assets/unauthorized-BlNvdM7v.js.gz +0 -0
  327. codex/static_root/assets/{unauthorized-muDsv-rC.2166ec8b3a32.css → unauthorized-YhYCb_lH.9b3ed0ae3331.css} +1 -1
  328. codex/static_root/assets/unauthorized-YhYCb_lH.9b3ed0ae3331.css.br +0 -0
  329. codex/static_root/assets/unauthorized-YhYCb_lH.9b3ed0ae3331.css.gz +0 -0
  330. codex/static_root/assets/{unauthorized-muDsv-rC.css → unauthorized-YhYCb_lH.css} +1 -1
  331. codex/static_root/assets/unauthorized-YhYCb_lH.css.br +0 -0
  332. codex/static_root/assets/unauthorized-YhYCb_lH.css.gz +0 -0
  333. codex/static_root/assets/user-tab-r6e-d5QN.30aebcf4898f.js +1 -0
  334. codex/static_root/assets/user-tab-r6e-d5QN.30aebcf4898f.js.br +0 -0
  335. codex/static_root/assets/user-tab-r6e-d5QN.30aebcf4898f.js.gz +0 -0
  336. codex/static_root/assets/user-tab-r6e-d5QN.js +1 -0
  337. codex/static_root/assets/user-tab-r6e-d5QN.js.br +0 -0
  338. codex/static_root/assets/user-tab-r6e-d5QN.js.gz +0 -0
  339. codex/static_root/js/choices-admin.1eca6d2c22cf.json +1 -0
  340. codex/static_root/js/choices-admin.1eca6d2c22cf.json.br +0 -0
  341. codex/static_root/js/choices-admin.1eca6d2c22cf.json.gz +0 -0
  342. codex/static_root/js/choices-admin.json +1 -1
  343. codex/static_root/js/choices-admin.json.br +0 -0
  344. codex/static_root/js/choices-admin.json.gz +0 -0
  345. codex/static_root/manifest.a300a8e1e4f3.json +609 -0
  346. codex/static_root/manifest.a300a8e1e4f3.json.br +0 -0
  347. codex/static_root/manifest.a300a8e1e4f3.json.gz +0 -0
  348. codex/static_root/manifest.json +282 -308
  349. codex/static_root/manifest.json.br +0 -0
  350. codex/static_root/manifest.json.gz +0 -0
  351. codex/static_root/staticfiles.json +1 -1
  352. codex/templates/pwa/serviceworker.js +4 -4
  353. codex/urls/root.py +6 -2
  354. codex/version.py +53 -0
  355. codex/views/admin/tasks.py +0 -2
  356. codex/views/admin/user.py +2 -4
  357. codex/views/bookmark.py +23 -16
  358. codex/views/browser/filters/annotations.py +4 -5
  359. codex/views/browser/validate.py +7 -4
  360. codex/views/const.py +2 -1
  361. codex/views/reader/books.py +2 -2
  362. codex/views/reader/init.py +2 -3
  363. codex/views/version.py +2 -10
  364. {codex-1.6.9a0.dist-info → codex-1.6.10.dist-info}/METADATA +8 -9
  365. {codex-1.6.9a0.dist-info → codex-1.6.10.dist-info}/RECORD +368 -375
  366. codex/librarian/janitor/latest_version.py +0 -54
  367. codex/static_root/assets/VCheckbox-BkQ2enlR.c750c7d8cecc.js +0 -1
  368. codex/static_root/assets/VCheckbox-BkQ2enlR.c750c7d8cecc.js.br +0 -0
  369. codex/static_root/assets/VCheckbox-BkQ2enlR.c750c7d8cecc.js.gz +0 -0
  370. codex/static_root/assets/VCheckbox-BkQ2enlR.js +0 -1
  371. codex/static_root/assets/VCheckbox-BkQ2enlR.js.br +0 -0
  372. codex/static_root/assets/VCheckbox-BkQ2enlR.js.gz +0 -0
  373. codex/static_root/assets/VCheckboxBtn-C4qxJC_o.ff4628662392.js +0 -1
  374. codex/static_root/assets/VCheckboxBtn-C4qxJC_o.ff4628662392.js.br +0 -0
  375. codex/static_root/assets/VCheckboxBtn-C4qxJC_o.ff4628662392.js.gz +0 -0
  376. codex/static_root/assets/VCheckboxBtn-C4qxJC_o.js +0 -1
  377. codex/static_root/assets/VCheckboxBtn-C4qxJC_o.js.br +0 -0
  378. codex/static_root/assets/VCheckboxBtn-C4qxJC_o.js.gz +0 -0
  379. codex/static_root/assets/VCombobox-CJYErhVj.5ae39362d05a.js +0 -1
  380. codex/static_root/assets/VCombobox-CJYErhVj.5ae39362d05a.js.br +0 -0
  381. codex/static_root/assets/VCombobox-CJYErhVj.5ae39362d05a.js.gz +0 -0
  382. codex/static_root/assets/VCombobox-CJYErhVj.js +0 -1
  383. codex/static_root/assets/VCombobox-CJYErhVj.js.br +0 -0
  384. codex/static_root/assets/VCombobox-CJYErhVj.js.gz +0 -0
  385. codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css +0 -1
  386. codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css.br +0 -0
  387. codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css.gz +0 -0
  388. codex/static_root/assets/VCombobox-bwEYrXCg.css +0 -1
  389. codex/static_root/assets/VCombobox-bwEYrXCg.css.br +0 -0
  390. codex/static_root/assets/VCombobox-bwEYrXCg.css.gz +0 -0
  391. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css +0 -1
  392. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.br +0 -0
  393. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.gz +0 -0
  394. codex/static_root/assets/VDialog-38np3TSA.css +0 -1
  395. codex/static_root/assets/VDialog-38np3TSA.css.br +0 -0
  396. codex/static_root/assets/VDialog-38np3TSA.css.gz +0 -0
  397. codex/static_root/assets/VDialog-U73OI2x0.a74b15bb2eb4.js +0 -1
  398. codex/static_root/assets/VDialog-U73OI2x0.a74b15bb2eb4.js.br +0 -0
  399. codex/static_root/assets/VDialog-U73OI2x0.a74b15bb2eb4.js.gz +0 -0
  400. codex/static_root/assets/VDialog-U73OI2x0.js +0 -1
  401. codex/static_root/assets/VDialog-U73OI2x0.js.br +0 -0
  402. codex/static_root/assets/VDialog-U73OI2x0.js.gz +0 -0
  403. codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css +0 -1
  404. codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css.br +0 -0
  405. codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css.gz +0 -0
  406. codex/static_root/assets/VExpansionPanels-6G1AUajL.css +0 -1
  407. codex/static_root/assets/VExpansionPanels-6G1AUajL.css.br +0 -0
  408. codex/static_root/assets/VExpansionPanels-6G1AUajL.css.gz +0 -0
  409. codex/static_root/assets/VExpansionPanels-CfbnFRnp.11d5ca74c514.js +0 -1
  410. codex/static_root/assets/VExpansionPanels-CfbnFRnp.11d5ca74c514.js.br +0 -0
  411. codex/static_root/assets/VExpansionPanels-CfbnFRnp.11d5ca74c514.js.gz +0 -0
  412. codex/static_root/assets/VExpansionPanels-CfbnFRnp.js +0 -1
  413. codex/static_root/assets/VExpansionPanels-CfbnFRnp.js.br +0 -0
  414. codex/static_root/assets/VExpansionPanels-CfbnFRnp.js.gz +0 -0
  415. codex/static_root/assets/VRadioGroup-Dk6ucuGh.b364223f1e2b.js +0 -1
  416. codex/static_root/assets/VRadioGroup-Dk6ucuGh.b364223f1e2b.js.br +0 -3
  417. codex/static_root/assets/VRadioGroup-Dk6ucuGh.b364223f1e2b.js.gz +0 -0
  418. codex/static_root/assets/VRadioGroup-Dk6ucuGh.js +0 -1
  419. codex/static_root/assets/VRadioGroup-Dk6ucuGh.js.br +0 -3
  420. codex/static_root/assets/VRadioGroup-Dk6ucuGh.js.gz +0 -0
  421. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css +0 -1
  422. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.br +0 -0
  423. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.gz +0 -0
  424. codex/static_root/assets/VSelect-Ca2bIOso.css +0 -1
  425. codex/static_root/assets/VSelect-Ca2bIOso.css.br +0 -0
  426. codex/static_root/assets/VSelect-Ca2bIOso.css.gz +0 -0
  427. codex/static_root/assets/VSelect-gqsRzrIv.f022ea270144.js +0 -1
  428. codex/static_root/assets/VSelect-gqsRzrIv.f022ea270144.js.br +0 -0
  429. codex/static_root/assets/VSelect-gqsRzrIv.f022ea270144.js.gz +0 -0
  430. codex/static_root/assets/VSelect-gqsRzrIv.js +0 -1
  431. codex/static_root/assets/VSelect-gqsRzrIv.js.br +0 -0
  432. codex/static_root/assets/VSelect-gqsRzrIv.js.gz +0 -0
  433. codex/static_root/assets/VSelectionControl-BVJLEOcO.5d398b4b3b1c.js +0 -1
  434. codex/static_root/assets/VSelectionControl-BVJLEOcO.5d398b4b3b1c.js.br +0 -0
  435. codex/static_root/assets/VSelectionControl-BVJLEOcO.5d398b4b3b1c.js.gz +0 -0
  436. codex/static_root/assets/VSelectionControl-BVJLEOcO.js +0 -1
  437. codex/static_root/assets/VSelectionControl-BVJLEOcO.js.br +0 -0
  438. codex/static_root/assets/VSelectionControl-BVJLEOcO.js.gz +0 -0
  439. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.br +0 -0
  440. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.gz +0 -0
  441. codex/static_root/assets/VSelectionControl-CiViEYoC.css.br +0 -0
  442. codex/static_root/assets/VSelectionControl-CiViEYoC.css.gz +0 -0
  443. codex/static_root/assets/VSlideGroup-P_U-rmf2.ca3efe698702.js.br +0 -0
  444. codex/static_root/assets/VSlideGroup-P_U-rmf2.ca3efe698702.js.gz +0 -0
  445. codex/static_root/assets/VSlideGroup-P_U-rmf2.js.br +0 -0
  446. codex/static_root/assets/VSlideGroup-P_U-rmf2.js.gz +0 -0
  447. codex/static_root/assets/VTable-Coq-QwtQ.2b04104cbf05.js.br +0 -0
  448. codex/static_root/assets/VTable-Coq-QwtQ.2b04104cbf05.js.gz +0 -0
  449. codex/static_root/assets/VTable-Coq-QwtQ.js.br +0 -0
  450. codex/static_root/assets/VTable-Coq-QwtQ.js.gz +0 -0
  451. codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css +0 -1
  452. codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css.br +0 -0
  453. codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css.gz +0 -0
  454. codex/static_root/assets/VTable-Y5Sc3hJd.css +0 -1
  455. codex/static_root/assets/VTable-Y5Sc3hJd.css.br +0 -0
  456. codex/static_root/assets/VTable-Y5Sc3hJd.css.gz +0 -0
  457. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +0 -1
  458. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
  459. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
  460. codex/static_root/assets/VTextField-Crc3gdNh.css +0 -1
  461. codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
  462. codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
  463. codex/static_root/assets/VTextField-D8612eNs.bff65c141149.js +0 -1
  464. codex/static_root/assets/VTextField-D8612eNs.bff65c141149.js.br +0 -0
  465. codex/static_root/assets/VTextField-D8612eNs.bff65c141149.js.gz +0 -0
  466. codex/static_root/assets/VTextField-D8612eNs.js +0 -1
  467. codex/static_root/assets/VTextField-D8612eNs.js.br +0 -0
  468. codex/static_root/assets/VTextField-D8612eNs.js.gz +0 -0
  469. codex/static_root/assets/VWindowItem-BViiLFXH.70619152a4a5.js +0 -1
  470. codex/static_root/assets/VWindowItem-BViiLFXH.70619152a4a5.js.br +0 -0
  471. codex/static_root/assets/VWindowItem-BViiLFXH.70619152a4a5.js.gz +0 -0
  472. codex/static_root/assets/VWindowItem-BViiLFXH.js +0 -1
  473. codex/static_root/assets/VWindowItem-BViiLFXH.js.br +0 -0
  474. codex/static_root/assets/VWindowItem-BViiLFXH.js.gz +0 -0
  475. codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.br +0 -0
  476. codex/static_root/assets/admin-CWEjaFr5.7d4523387384.css.gz +0 -0
  477. codex/static_root/assets/admin-CWEjaFr5.css.br +0 -0
  478. codex/static_root/assets/admin-CWEjaFr5.css.gz +0 -0
  479. codex/static_root/assets/admin-RVemMSzT.fcbdca9f6b46.js +0 -1
  480. codex/static_root/assets/admin-RVemMSzT.fcbdca9f6b46.js.br +0 -0
  481. codex/static_root/assets/admin-RVemMSzT.fcbdca9f6b46.js.gz +0 -0
  482. codex/static_root/assets/admin-RVemMSzT.js +0 -1
  483. codex/static_root/assets/admin-RVemMSzT.js.br +0 -0
  484. codex/static_root/assets/admin-RVemMSzT.js.gz +0 -0
  485. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css +0 -1
  486. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.br +0 -0
  487. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.3cb33056e994.css.gz +0 -0
  488. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css +0 -1
  489. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.br +0 -0
  490. codex/static_root/assets/admin-drawer-panel-Bs2wgKj3.css.gz +0 -0
  491. codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.0acaf708f24a.js +0 -30
  492. codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.0acaf708f24a.js.br +0 -0
  493. codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.0acaf708f24a.js.gz +0 -0
  494. codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.js +0 -30
  495. codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.js.br +0 -0
  496. codex/static_root/assets/admin-drawer-panel-Ul8BRcOp.js.gz +0 -0
  497. codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.br +0 -0
  498. codex/static_root/assets/browser-Binc3n9H.313722d27ca3.css.gz +0 -0
  499. codex/static_root/assets/browser-Binc3n9H.css.br +0 -0
  500. codex/static_root/assets/browser-Binc3n9H.css.gz +0 -0
  501. codex/static_root/assets/browser-C9pnPT8U.bc239343d007.js +0 -1
  502. codex/static_root/assets/browser-C9pnPT8U.bc239343d007.js.br +0 -0
  503. codex/static_root/assets/browser-C9pnPT8U.bc239343d007.js.gz +0 -0
  504. codex/static_root/assets/browser-C9pnPT8U.js +0 -1
  505. codex/static_root/assets/browser-C9pnPT8U.js.br +0 -0
  506. codex/static_root/assets/browser-C9pnPT8U.js.gz +0 -0
  507. codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css +0 -1
  508. codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.br +0 -0
  509. codex/static_root/assets/change-password-dialog-Cebek1-c.aff2975e31f8.css.gz +0 -0
  510. codex/static_root/assets/change-password-dialog-Cebek1-c.css +0 -1
  511. codex/static_root/assets/change-password-dialog-Cebek1-c.css.br +0 -0
  512. codex/static_root/assets/change-password-dialog-Cebek1-c.css.gz +0 -0
  513. codex/static_root/assets/change-password-dialog-DjxFqIRX.1a5ac7100192.js +0 -1
  514. codex/static_root/assets/change-password-dialog-DjxFqIRX.1a5ac7100192.js.br +0 -0
  515. codex/static_root/assets/change-password-dialog-DjxFqIRX.1a5ac7100192.js.gz +0 -0
  516. codex/static_root/assets/change-password-dialog-DjxFqIRX.js +0 -1
  517. codex/static_root/assets/change-password-dialog-DjxFqIRX.js.br +0 -0
  518. codex/static_root/assets/change-password-dialog-DjxFqIRX.js.gz +0 -0
  519. codex/static_root/assets/confirm-dialog-B-dLb_50.62d3ea64b93a.js +0 -1
  520. codex/static_root/assets/confirm-dialog-B-dLb_50.62d3ea64b93a.js.br +0 -0
  521. codex/static_root/assets/confirm-dialog-B-dLb_50.62d3ea64b93a.js.gz +0 -0
  522. codex/static_root/assets/confirm-dialog-B-dLb_50.js +0 -1
  523. codex/static_root/assets/confirm-dialog-B-dLb_50.js.br +0 -0
  524. codex/static_root/assets/confirm-dialog-B-dLb_50.js.gz +0 -0
  525. codex/static_root/assets/datetime-column-BRZDPl2k.f6ae3ce94da7.js.br +0 -0
  526. codex/static_root/assets/datetime-column-BRZDPl2k.f6ae3ce94da7.js.gz +0 -0
  527. codex/static_root/assets/datetime-column-BRZDPl2k.js.br +0 -0
  528. codex/static_root/assets/datetime-column-BRZDPl2k.js.gz +0 -0
  529. codex/static_root/assets/filter-Tk2N9xgO.02f23827434b.js.br +0 -0
  530. codex/static_root/assets/filter-Tk2N9xgO.02f23827434b.js.gz +0 -0
  531. codex/static_root/assets/filter-Tk2N9xgO.js.br +0 -0
  532. codex/static_root/assets/filter-Tk2N9xgO.js.gz +0 -0
  533. codex/static_root/assets/flag-tab-C_v4CugS.928c1035d6ce.js +0 -1
  534. codex/static_root/assets/flag-tab-C_v4CugS.928c1035d6ce.js.br +0 -0
  535. codex/static_root/assets/flag-tab-C_v4CugS.928c1035d6ce.js.gz +0 -0
  536. codex/static_root/assets/flag-tab-C_v4CugS.js +0 -1
  537. codex/static_root/assets/flag-tab-C_v4CugS.js.br +0 -0
  538. codex/static_root/assets/flag-tab-C_v4CugS.js.gz +0 -0
  539. codex/static_root/assets/group-tab-Dn5N4Ebx.d2cf9c300427.js +0 -1
  540. codex/static_root/assets/group-tab-Dn5N4Ebx.d2cf9c300427.js.br +0 -0
  541. codex/static_root/assets/group-tab-Dn5N4Ebx.d2cf9c300427.js.gz +0 -0
  542. codex/static_root/assets/group-tab-Dn5N4Ebx.js +0 -1
  543. codex/static_root/assets/group-tab-Dn5N4Ebx.js.br +0 -0
  544. codex/static_root/assets/group-tab-Dn5N4Ebx.js.gz +0 -0
  545. codex/static_root/assets/http-error-DbRCRWKA.c31ec2f67f3b.js +0 -1
  546. codex/static_root/assets/http-error-DbRCRWKA.c31ec2f67f3b.js.br +0 -0
  547. codex/static_root/assets/http-error-DbRCRWKA.c31ec2f67f3b.js.gz +0 -0
  548. codex/static_root/assets/http-error-DbRCRWKA.js +0 -1
  549. codex/static_root/assets/http-error-DbRCRWKA.js.br +0 -0
  550. codex/static_root/assets/http-error-DbRCRWKA.js.gz +0 -0
  551. codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css +0 -1
  552. codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.br +0 -2
  553. codex/static_root/assets/library-tab-Bh7ErAk7.7c44200ee727.css.gz +0 -0
  554. codex/static_root/assets/library-tab-Bh7ErAk7.css +0 -1
  555. codex/static_root/assets/library-tab-Bh7ErAk7.css.br +0 -2
  556. codex/static_root/assets/library-tab-Bh7ErAk7.css.gz +0 -0
  557. codex/static_root/assets/library-tab-CnvofvVA.419ab4e21896.js +0 -1
  558. codex/static_root/assets/library-tab-CnvofvVA.419ab4e21896.js.br +0 -0
  559. codex/static_root/assets/library-tab-CnvofvVA.419ab4e21896.js.gz +0 -0
  560. codex/static_root/assets/library-tab-CnvofvVA.js +0 -1
  561. codex/static_root/assets/library-tab-CnvofvVA.js.br +0 -0
  562. codex/static_root/assets/library-tab-CnvofvVA.js.gz +0 -0
  563. codex/static_root/assets/main-BHIo0ep5.6ec78d1380f0.js +0 -6
  564. codex/static_root/assets/main-BHIo0ep5.6ec78d1380f0.js.br +0 -0
  565. codex/static_root/assets/main-BHIo0ep5.6ec78d1380f0.js.gz +0 -0
  566. codex/static_root/assets/main-BHIo0ep5.js +0 -6
  567. codex/static_root/assets/main-BHIo0ep5.js.br +0 -0
  568. codex/static_root/assets/main-BHIo0ep5.js.gz +0 -0
  569. codex/static_root/assets/main-BZ73p_WW.css.br +0 -0
  570. codex/static_root/assets/main-BZ73p_WW.d529e8b3f52a.css.br +0 -0
  571. codex/static_root/assets/pager-full-pdf-DuOmJXkZ.5eaa11c55b1d.js +0 -1
  572. codex/static_root/assets/pager-full-pdf-DuOmJXkZ.5eaa11c55b1d.js.br +0 -0
  573. codex/static_root/assets/pager-full-pdf-DuOmJXkZ.5eaa11c55b1d.js.gz +0 -0
  574. codex/static_root/assets/pager-full-pdf-DuOmJXkZ.js +0 -1
  575. codex/static_root/assets/pager-full-pdf-DuOmJXkZ.js.br +0 -0
  576. codex/static_root/assets/pager-full-pdf-DuOmJXkZ.js.gz +0 -0
  577. codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css +0 -1
  578. codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.br +0 -0
  579. codex/static_root/assets/pagination-toolbar-C45-onwe.4722195bb60b.css.gz +0 -0
  580. codex/static_root/assets/pagination-toolbar-C45-onwe.css +0 -1
  581. codex/static_root/assets/pagination-toolbar-C45-onwe.css.br +0 -0
  582. codex/static_root/assets/pagination-toolbar-C45-onwe.css.gz +0 -0
  583. codex/static_root/assets/pagination-toolbar-CzvikaTz.a025f20590fa.js +0 -1
  584. codex/static_root/assets/pagination-toolbar-CzvikaTz.a025f20590fa.js.br +0 -0
  585. codex/static_root/assets/pagination-toolbar-CzvikaTz.a025f20590fa.js.gz +0 -0
  586. codex/static_root/assets/pagination-toolbar-CzvikaTz.js +0 -1
  587. codex/static_root/assets/pagination-toolbar-CzvikaTz.js.br +0 -0
  588. codex/static_root/assets/pagination-toolbar-CzvikaTz.js.gz +0 -0
  589. codex/static_root/assets/pdf-doc-B5yncCrs.97ba5cf21b63.js +0 -305
  590. codex/static_root/assets/pdf-doc-B5yncCrs.97ba5cf21b63.js.br +0 -0
  591. codex/static_root/assets/pdf-doc-B5yncCrs.97ba5cf21b63.js.gz +0 -0
  592. codex/static_root/assets/pdf-doc-B5yncCrs.js +0 -305
  593. codex/static_root/assets/pdf-doc-B5yncCrs.js.br +0 -0
  594. codex/static_root/assets/pdf-doc-B5yncCrs.js.gz +0 -0
  595. codex/static_root/assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css +0 -1
  596. codex/static_root/assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css.br +0 -1
  597. codex/static_root/assets/pdf-doc-CG5-AZ2M.b94aac7e3313.css.gz +0 -0
  598. codex/static_root/assets/pdf-doc-CG5-AZ2M.css +0 -1
  599. codex/static_root/assets/pdf-doc-CG5-AZ2M.css.br +0 -1
  600. codex/static_root/assets/pdf-doc-CG5-AZ2M.css.gz +0 -0
  601. codex/static_root/assets/reader-BR3TSv3h.b00087748514.js +0 -2
  602. codex/static_root/assets/reader-BR3TSv3h.b00087748514.js.br +0 -0
  603. codex/static_root/assets/reader-BR3TSv3h.b00087748514.js.gz +0 -0
  604. codex/static_root/assets/reader-BR3TSv3h.js +0 -2
  605. codex/static_root/assets/reader-BR3TSv3h.js.br +0 -0
  606. codex/static_root/assets/reader-BR3TSv3h.js.gz +0 -0
  607. codex/static_root/assets/reader-_stAWL-4.5089e9144795.css +0 -1
  608. codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.br +0 -0
  609. codex/static_root/assets/reader-_stAWL-4.5089e9144795.css.gz +0 -0
  610. codex/static_root/assets/reader-_stAWL-4.css +0 -1
  611. codex/static_root/assets/reader-_stAWL-4.css.br +0 -0
  612. codex/static_root/assets/reader-_stAWL-4.css.gz +0 -0
  613. codex/static_root/assets/relation-chips-ByfWwuQc.01269a188e01.css.br +0 -0
  614. codex/static_root/assets/relation-chips-ByfWwuQc.01269a188e01.css.gz +0 -0
  615. codex/static_root/assets/relation-chips-ByfWwuQc.css.br +0 -0
  616. codex/static_root/assets/relation-chips-ByfWwuQc.css.gz +0 -0
  617. codex/static_root/assets/relation-chips-C-62eg7F.d234f5d05881.js +0 -1
  618. codex/static_root/assets/relation-chips-C-62eg7F.d234f5d05881.js.br +0 -0
  619. codex/static_root/assets/relation-chips-C-62eg7F.d234f5d05881.js.gz +0 -0
  620. codex/static_root/assets/relation-chips-C-62eg7F.js +0 -1
  621. codex/static_root/assets/relation-chips-C-62eg7F.js.br +0 -0
  622. codex/static_root/assets/relation-chips-C-62eg7F.js.gz +0 -0
  623. codex/static_root/assets/settings-drawer-CpNFdMBP.e5b1af37e0a0.js +0 -2
  624. codex/static_root/assets/settings-drawer-CpNFdMBP.e5b1af37e0a0.js.br +0 -0
  625. codex/static_root/assets/settings-drawer-CpNFdMBP.e5b1af37e0a0.js.gz +0 -0
  626. codex/static_root/assets/settings-drawer-CpNFdMBP.js +0 -2
  627. codex/static_root/assets/settings-drawer-CpNFdMBP.js.br +0 -0
  628. codex/static_root/assets/settings-drawer-CpNFdMBP.js.gz +0 -0
  629. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css +0 -1
  630. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.br +0 -0
  631. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.gz +0 -0
  632. codex/static_root/assets/settings-drawer-DKncO_vX.css +0 -1
  633. codex/static_root/assets/settings-drawer-DKncO_vX.css.br +0 -0
  634. codex/static_root/assets/settings-drawer-DKncO_vX.css.gz +0 -0
  635. codex/static_root/assets/stats-tab-B0-L7btW.8f7914d5c8ac.js +0 -1
  636. codex/static_root/assets/stats-tab-B0-L7btW.8f7914d5c8ac.js.br +0 -0
  637. codex/static_root/assets/stats-tab-B0-L7btW.8f7914d5c8ac.js.gz +0 -0
  638. codex/static_root/assets/stats-tab-B0-L7btW.js +0 -1
  639. codex/static_root/assets/stats-tab-B0-L7btW.js.br +0 -0
  640. codex/static_root/assets/stats-tab-B0-L7btW.js.gz +0 -0
  641. codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.br +0 -0
  642. codex/static_root/assets/stats-tab-BXGisn5N.9b7a71e7fe28.css.gz +0 -0
  643. codex/static_root/assets/stats-tab-BXGisn5N.css.br +0 -0
  644. codex/static_root/assets/stats-tab-BXGisn5N.css.gz +0 -0
  645. codex/static_root/assets/task-tab-CJEMX4mU.31d0755cc8f7.js +0 -1
  646. codex/static_root/assets/task-tab-CJEMX4mU.31d0755cc8f7.js.br +0 -0
  647. codex/static_root/assets/task-tab-CJEMX4mU.31d0755cc8f7.js.gz +0 -0
  648. codex/static_root/assets/task-tab-CJEMX4mU.js +0 -1
  649. codex/static_root/assets/task-tab-CJEMX4mU.js.br +0 -0
  650. codex/static_root/assets/task-tab-CJEMX4mU.js.gz +0 -0
  651. codex/static_root/assets/unauthorized-U1yum5qV.4a44970b19c7.js +0 -1
  652. codex/static_root/assets/unauthorized-U1yum5qV.4a44970b19c7.js.br +0 -0
  653. codex/static_root/assets/unauthorized-U1yum5qV.4a44970b19c7.js.gz +0 -0
  654. codex/static_root/assets/unauthorized-U1yum5qV.js +0 -1
  655. codex/static_root/assets/unauthorized-U1yum5qV.js.br +0 -0
  656. codex/static_root/assets/unauthorized-U1yum5qV.js.gz +0 -0
  657. codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.br +0 -0
  658. codex/static_root/assets/unauthorized-muDsv-rC.2166ec8b3a32.css.gz +0 -0
  659. codex/static_root/assets/unauthorized-muDsv-rC.css.br +0 -0
  660. codex/static_root/assets/unauthorized-muDsv-rC.css.gz +0 -0
  661. codex/static_root/assets/user-tab-8DHYNhT5.3160ac8849a9.js +0 -1
  662. codex/static_root/assets/user-tab-8DHYNhT5.3160ac8849a9.js.br +0 -0
  663. codex/static_root/assets/user-tab-8DHYNhT5.3160ac8849a9.js.gz +0 -0
  664. codex/static_root/assets/user-tab-8DHYNhT5.js +0 -1
  665. codex/static_root/assets/user-tab-8DHYNhT5.js.br +0 -0
  666. codex/static_root/assets/user-tab-8DHYNhT5.js.gz +0 -0
  667. codex/static_root/js/choices-admin.1a20a5648f20.json +0 -1
  668. codex/static_root/js/choices-admin.1a20a5648f20.json.br +0 -0
  669. codex/static_root/js/choices-admin.1a20a5648f20.json.gz +0 -0
  670. codex/static_root/manifest.c82a23abe985.json +0 -635
  671. codex/static_root/manifest.c82a23abe985.json.br +0 -0
  672. codex/static_root/manifest.c82a23abe985.json.gz +0 -0
  673. {codex-1.6.9a0.dist-info → codex-1.6.10.dist-info}/LICENSE +0 -0
  674. {codex-1.6.9a0.dist-info → codex-1.6.10.dist-info}/WHEEL +0 -0
  675. {codex-1.6.9a0.dist-info → codex-1.6.10.dist-info}/entry_points.txt +0 -0
@@ -30,23 +30,21 @@ class CronThread(NamedThread):
30
30
  self._task_times = ()
31
31
  super().__init__(*args, name=self.__class__.__name__, daemon=True, **kwargs)
32
32
 
33
- def _create_task_times(self):
33
+ def _update_task_times(self):
34
34
  task_times = {}
35
35
  for task_class, func in _TASK_TIME_FUNCTION_MAP.items():
36
36
  if dttm := func(self.log):
37
37
  task_times[dttm] = task_class
38
38
 
39
- self._task_times = tuple(sorted(task_times.items()))
39
+ task_tuple = tuple(sorted(task_times.items()))
40
40
 
41
- def _get_timeout(self):
42
- if not self._task_times or not self._task_times[0]:
43
- self.log.warning("No scheduled jobs found. Not normal! Waiting a minute.")
44
- return 60
41
+ # There's always at least the janitor job
42
+ self._next_time = task_tuple[0][0]
43
+ self._task_times = task_tuple
45
44
 
46
- next_time = self._task_times[0][0]
45
+ def _get_timeout(self):
47
46
  now = django_timezone.now()
48
- delta = next_time - now
49
- self.log.debug(f"Next scheduled job at {next_time} in {delta}.")
47
+ delta = self._next_time - now
50
48
  return max(0, int(delta.total_seconds()))
51
49
 
52
50
  def _run_expired_jobs(self):
@@ -55,7 +53,6 @@ class CronThread(NamedThread):
55
53
  if dttm < now:
56
54
  self.librarian_queue.put(task_class())
57
55
  else:
58
- # Times are always ordered so stop checking at the first future job.
59
56
  break
60
57
 
61
58
  def run(self):
@@ -65,13 +62,13 @@ class CronThread(NamedThread):
65
62
  with self._cond:
66
63
  while not self._stop_event.is_set():
67
64
  self._run_expired_jobs()
68
- self._create_task_times()
69
- sleep(2) # try to fix double jobs
65
+ self._update_task_times()
70
66
  timeout = self._get_timeout()
67
+ self.log.debug(f"Next scheduled job at {self._next_time}.")
71
68
  self._cond.wait(timeout=timeout)
72
69
  if self._stop_event.is_set():
73
70
  break
74
- sleep(2) # fix time rounding problems
71
+ sleep(2) # fixes any time rounding problems
75
72
  except Exception:
76
73
  self.log.exception(f"In {self.__class__.__name__}")
77
74
  self.log.debug(f"Stopped {self.__class__.__name__}.")
@@ -1,7 +1,5 @@
1
1
  """Bulk import and move comics and folders."""
2
2
 
3
- from pathlib import Path
4
-
5
3
  from django.utils import timezone
6
4
 
7
5
  from codex.librarian.importer.importer import ComicImporter
@@ -48,15 +46,6 @@ class ComicImporterThread(QueuedThread):
48
46
  )
49
47
  self._import(task)
50
48
 
51
- def _update_groups(self, task):
52
- pks = Library.objects.filter(covers_only=False).values_list("pk", flat=True)
53
- start_time = task.start_time if task.start_time else timezone.now()
54
- for pk in pks:
55
- task = ImportDBDiffTask(library_id=pk)
56
- importer = self._create_importer(task)
57
- importer.update_all_groups({}, start_time)
58
- self.librarian_queue.put(LIBRARY_CHANGED_TASK)
59
-
60
49
  def _adopt_orphan_folders_for_library(self, library):
61
50
  """Adopt orphan folders for one library."""
62
51
  orphan_folder_paths = (
@@ -64,20 +53,17 @@ class ComicImporterThread(QueuedThread):
64
53
  .exclude(path=library.path)
65
54
  .values_list("path", flat=True)
66
55
  )
67
- # Move in place
68
- # Exclude deleted folders
69
- folders_moved = {
70
- path: path for path in orphan_folder_paths if Path(path).is_dir()
71
- }
72
-
73
- if folders_moved:
74
- self.log.debug(
75
- f"{len(folders_moved)} orphan folders found in {library.path}"
76
- )
77
- else:
56
+ if not orphan_folder_paths:
78
57
  self.log.debug(f"No orphan folders in {library.path}")
79
58
  return False
80
59
 
60
+ self.log.debug(
61
+ f"{len(orphan_folder_paths)} orphan folders found in {library.path}"
62
+ )
63
+
64
+ # Move in place
65
+ folders_moved = {path: path for path in orphan_folder_paths}
66
+
81
67
  # An abridged import task.
82
68
  task = ImportDBDiffTask(
83
69
  library_id=library.pk,
@@ -103,6 +89,15 @@ class ComicImporterThread(QueuedThread):
103
89
 
104
90
  self.status_controller.finish_many((moved_status, status))
105
91
 
92
+ def _update_groups(self, task):
93
+ pks = Library.objects.filter(covers_only=False).values_list("pk", flat=True)
94
+ start_time = task.start_time if task.start_time else timezone.now()
95
+ for pk in pks:
96
+ task = ImportDBDiffTask(library_id=pk)
97
+ importer = self._create_importer(task)
98
+ importer.update_all_groups({}, start_time)
99
+ self.librarian_queue.put(LIBRARY_CHANGED_TASK)
100
+
106
101
  def process_item(self, item):
107
102
  """Run the updater."""
108
103
  task = item
@@ -110,9 +105,9 @@ class ComicImporterThread(QueuedThread):
110
105
  self._import(task)
111
106
  elif isinstance(task, LazyImportComicsTask):
112
107
  self._lazy_import_metadata(task)
113
- elif isinstance(task, UpdateGroupsTask):
114
- self._update_groups(task)
115
108
  elif isinstance(task, AdoptOrphanFoldersTask):
116
109
  self._adopt_orphan_folders()
110
+ elif isinstance(task, UpdateGroupsTask):
111
+ self._update_groups(task)
117
112
  else:
118
113
  self.log.warning(f"Bad task sent to library updater {task}")
@@ -2,7 +2,6 @@
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- from bidict import bidict
6
5
  from django.db.models.functions import Now
7
6
 
8
7
  from codex.librarian.importer.aggregate import AggregateMetadataImporter
@@ -24,42 +23,20 @@ from codex.status import Status
24
23
  class MovedImporter(AggregateMetadataImporter):
25
24
  """Methods for moving comics and folders."""
26
25
 
27
- def _bulk_comics_moved_ensure_folders(self):
28
- """Ensure folders we're moving to exist."""
29
- dest_comic_paths = self.task.files_moved.values()
30
- if not dest_comic_paths:
31
- return
32
- # Not sending statues to the controller for now.
33
- status = Status(ImportStatusTypes.QUERY_MISSING_FKS)
34
- create_folder_paths = self.query_missing_folder_paths(dest_comic_paths, status)
35
- if not create_folder_paths:
26
+ def _bulk_comics_moved(self):
27
+ """Move comcis."""
28
+ num_files_moved = len(self.task.files_moved)
29
+ if not num_files_moved:
36
30
  return
37
- status = Status(ImportStatusTypes.CREATE_FKS, 0, len(create_folder_paths))
38
- self.log.debug(
39
- "Creating {len(create_folder_paths)} folders for {len(dest_comic_paths)} moved comics."
40
- )
41
- self.bulk_folders_create(create_folder_paths, status)
31
+ status = Status(ImportStatusTypes.FILES_MOVED, 0, num_files_moved)
32
+ self.status_controller.start(status)
33
+
34
+ # Prepare FKs
35
+ create_folder_paths = set()
36
+ self.query_missing_folder_paths(create_folder_paths, status)
37
+ self.bulk_folders_create(frozenset(create_folder_paths), status)
42
38
 
43
- def _prepare_moved_comic(self, comic, folder_m2m_links, updated_comics):
44
- """Prepare one comic for bulk update."""
45
- try:
46
- new_path = self.task.files_moved[comic.path]
47
- comic.path = new_path
48
- new_path = Path(new_path)
49
- comic.parent_folder = Folder.objects.get( # type: ignore
50
- path=new_path.parent
51
- )
52
- comic.updated_at = Now()
53
- comic.presave()
54
- folder_m2m_links[comic.pk] = Folder.objects.filter(
55
- path__in=new_path.parents
56
- ).values_list("pk", flat=True)
57
- updated_comics.append(comic)
58
- except Exception:
59
- self.log.exception(f"moving {comic.path}")
60
-
61
- def _bulk_comics_move_prepare(self):
62
- """Prepare Update Comics."""
39
+ # Update Comics
63
40
  comics = Comic.objects.filter(
64
41
  library=self.library, path__in=self.task.files_moved.keys()
65
42
  ).only("pk", "path", PARENT_FOLDER, FOLDERS_FIELD)
@@ -67,29 +44,29 @@ class MovedImporter(AggregateMetadataImporter):
67
44
  folder_m2m_links = {}
68
45
  updated_comics = []
69
46
  for comic in comics.iterator():
70
- self._prepare_moved_comic(comic, folder_m2m_links, updated_comics)
47
+ try:
48
+ new_path = self.task.files_moved[comic.path]
49
+ comic.path = new_path
50
+ new_path = Path(new_path)
51
+ comic.parent_folder = Folder.objects.get( # type: ignore
52
+ path=new_path.parent
53
+ )
54
+ comic.updated_at = Now()
55
+ comic.presave()
56
+ folder_m2m_links[comic.pk] = Folder.objects.filter(
57
+ path__in=new_path.parents
58
+ ).values_list("pk", flat=True)
59
+ updated_comics.append(comic)
60
+ except Exception:
61
+ self.log.exception(f"moving {comic.path}")
71
62
  self.task.files_moved = {}
72
- self.log.debug(f"Prepared {len(updated_comics)} for move...")
73
- return updated_comics, folder_m2m_links
74
-
75
- def _bulk_comics_moved(self):
76
- """Move comcis."""
77
- num_files_moved = len(self.task.files_moved)
78
- if not num_files_moved:
79
- return
80
- status = Status(ImportStatusTypes.FILES_MOVED, 0, num_files_moved)
81
- self.status_controller.start(status)
82
63
 
83
- # Prepare
84
- self._bulk_comics_moved_ensure_folders()
85
- updated_comics, folder_m2m_links = self._bulk_comics_move_prepare()
86
-
87
- # Update comics
88
64
  Comic.objects.bulk_update(updated_comics, MOVED_BULK_COMIC_UPDATE_FIELDS)
65
+
66
+ # Update m2m field
89
67
  if folder_m2m_links:
90
68
  self.bulk_fix_comic_m2m_field(FOLDERS_FIELD, folder_m2m_links, status)
91
-
92
- count = len(updated_comics)
69
+ count = len(comics)
93
70
  if count:
94
71
  self.log.info(f"Moved {count} comics.")
95
72
 
@@ -163,32 +140,60 @@ class MovedImporter(AggregateMetadataImporter):
163
140
  self.changed += count
164
141
  self.status_controller.finish(status)
165
142
 
166
- def _bulk_move_folders(
167
- self, src_folder_paths, dest_parent_folders_map, dirs_moved: bidict, status
168
- ):
169
- """Bulk move folders."""
170
- folders_to_move = (
171
- Folder.objects.filter(library=self.library, path__in=src_folder_paths)
172
- .only(*MOVED_BULK_FOLDER_UPDATE_FIELDS)
173
- .order_by("path")
143
+ def _get_parent_folders(self, dest_folder_paths, status):
144
+ """Get destination parent folders."""
145
+ # Determine parent folder paths.
146
+ dest_parent_folder_paths = set()
147
+ for dest_folder_path in dest_folder_paths:
148
+ dest_parent_path = str(Path(dest_folder_path).parent)
149
+ if dest_parent_path not in dest_folder_paths:
150
+ dest_parent_folder_paths.add(dest_parent_path)
151
+
152
+ # Create only intermediate subfolders.
153
+ existing_folder_paths = Folder.objects.filter(
154
+ library=self.library, path__in=dest_parent_folder_paths
155
+ ).values_list("path", flat=True)
156
+ create_folder_paths = frozenset(
157
+ dest_parent_folder_paths - frozenset(existing_folder_paths)
174
158
  )
159
+ self.bulk_folders_create(create_folder_paths, status)
175
160
 
176
- # TODO what if a folder with that path already exists in the db?
161
+ # get parent folders path to model obj dict
162
+ dest_parent_folders_objs = Folder.objects.filter(
163
+ path__in=dest_parent_folder_paths
164
+ ).only("path", "pk")
165
+ dest_parent_folders = {}
166
+ for folder in dest_parent_folders_objs.iterator():
167
+ dest_parent_folders[folder.path] = folder
168
+ return dest_parent_folders
169
+
170
+ def bulk_folders_moved(self):
171
+ """Move folders in the database instead of recreating them."""
172
+ num_dirs_moved = len(self.task.dirs_moved)
173
+ if not num_dirs_moved:
174
+ return
175
+ status = Status(ImportStatusTypes.DIRS_MOVED, None, num_dirs_moved)
176
+ self.status_controller.start(status)
177
+
178
+ dest_folder_paths = frozenset(self.task.dirs_moved.values())
179
+ dest_parent_folders = self._get_parent_folders(dest_folder_paths, status)
180
+
181
+ src_folder_paths = frozenset(self.task.dirs_moved.keys())
182
+ folders_to_move = Folder.objects.filter(
183
+ library=self.library, path__in=src_folder_paths
184
+ ).order_by("path")
177
185
 
178
- new_paths = set()
179
186
  update_folders = []
180
187
  for folder in folders_to_move.iterator():
181
- new_path = dirs_moved[folder.path]
182
- new_paths.add(new_path)
188
+ new_path = self.task.dirs_moved[folder.path]
183
189
  folder.name = Path(new_path).name
184
190
  folder.path = new_path
185
191
  parent_path_str = str(Path(new_path).parent)
186
- folder.parent_folder = dest_parent_folders_map.get(parent_path_str)
192
+ folder.parent_folder = dest_parent_folders.get(parent_path_str)
187
193
  folder.presave()
188
194
  folder.updated_at = Now()
189
195
  update_folders.append(folder)
190
-
191
- # delete_folders = Folder.objects.filter(library=self.library, path__in=new_paths)
196
+ self.task.dirs_moved = {}
192
197
 
193
198
  update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
194
199
 
@@ -197,112 +202,6 @@ class MovedImporter(AggregateMetadataImporter):
197
202
  if count:
198
203
  self.log.info(f"Moved {count} folders.")
199
204
  self.changed += count
200
- status.add_complete(count)
201
- self.status_controller.update(status, notify=False)
202
-
203
- def _bulk_move_folders_under_existing_parents(
204
- self, dest_parent_folder_paths_map, dirs_moved, status
205
- ):
206
- """Move folders under existing folders."""
207
- while True:
208
- # Get existing parent folders
209
- # dest_parent_paths = tuple(str(path) for path in dest_parent_folder_paths_map)
210
- dest_parent_paths = tuple(dest_parent_folder_paths_map.keys())
211
- extant_parent_folders = Folder.objects.filter(
212
- library=self.library, path__in=dest_parent_paths
213
- ).only("path")
214
-
215
- # Create a list of folders than can be moved under existing folders.
216
- dest_parent_folders_map = {}
217
- src_folder_paths = []
218
- for extant_parent_folder in extant_parent_folders:
219
- dest_parent_folders_map[extant_parent_folder.path] = (
220
- extant_parent_folder
221
- )
222
- if dest_folder_paths := dest_parent_folder_paths_map.pop(
223
- extant_parent_folder.path
224
- ):
225
- for dest_folder_path in dest_folder_paths:
226
- src_path = dirs_moved.inverse[dest_folder_path]
227
- src_folder_paths.append(src_path)
228
-
229
- if not src_folder_paths:
230
- return
231
- src_folder_paths = sorted(src_folder_paths)
232
- self.log.debug(f"Moving folders under existing parents: {src_folder_paths}")
233
-
234
- self._bulk_move_folders(
235
- src_folder_paths, dest_parent_folders_map, dirs_moved, status
236
- )
237
-
238
- def _get_move_create_folders_one_layer(self, dest_parent_folder_paths_map):
239
- """Find the next layer of folder paths to create."""
240
- create_folder_paths_one_layer = set()
241
- library_parts_len = len(Path(self.library.path).parts)
242
- for parent_path_str in sorted(dest_parent_folder_paths_map.keys()):
243
- parts = Path(parent_path_str).parts
244
- # Get one layer of folders from existing layers.
245
- possible_create_folder_path = ""
246
- for index in range(library_parts_len, len(parts) + 1):
247
- layer_parts = parts[:index]
248
- possible_create_folder_path = str(Path(*layer_parts))
249
- if (
250
- possible_create_folder_path in create_folder_paths_one_layer
251
- or not Folder.objects.filter(
252
- library=self.library, path=possible_create_folder_path
253
- ).exists()
254
- ):
255
- break
256
- else:
257
- possible_create_folder_path = ""
258
- if possible_create_folder_path:
259
- create_folder_paths_one_layer.add(possible_create_folder_path)
260
-
261
- return frozenset(create_folder_paths_one_layer)
262
-
263
- def _bulk_move_folders_and_create_parents(self, status):
264
- """Find folders that can be moved without creating parents."""
265
- dirs_moved = bidict(self.task.dirs_moved)
266
-
267
- dest_parent_folder_paths_map = {}
268
- for dest_path in sorted(set(dirs_moved.values())):
269
- parent = str(Path(dest_path).parent)
270
- if parent not in dest_parent_folder_paths_map:
271
- dest_parent_folder_paths_map[parent] = set()
272
- dest_parent_folder_paths_map[parent].add(dest_path)
273
-
274
- create_status = Status(ImportStatusTypes.CREATE_FKS)
275
- layer = 1
276
- while True:
277
- self._bulk_move_folders_under_existing_parents(
278
- dest_parent_folder_paths_map, dirs_moved, status
279
- )
280
-
281
- # All folders movable without creation have moved.
282
- if not dest_parent_folder_paths_map:
283
- break
284
- self.log.debug(
285
- f"Creating intermediate folder layer {layer} to move folders."
286
- )
287
- create_folder_paths_one_layer = self._get_move_create_folders_one_layer(
288
- dest_parent_folder_paths_map
289
- )
290
-
291
- # Create one layer of folders
292
- self.bulk_folders_create(create_folder_paths_one_layer, create_status)
293
- layer += 1
294
-
295
- def bulk_folders_moved(self):
296
- """Move folders in the database instead of recreating them."""
297
- num_dirs_moved = len(self.task.dirs_moved)
298
- if not num_dirs_moved:
299
- return
300
- status = Status(ImportStatusTypes.DIRS_MOVED, None, num_dirs_moved)
301
- self.status_controller.start(status)
302
-
303
- self._bulk_move_folders_and_create_parents(status)
304
- self.task.dirs_moved = {}
305
-
306
205
  self.status_controller.finish(status)
307
206
 
308
207
  def _bulk_folders_modified(self):
@@ -334,8 +233,6 @@ class MovedImporter(AggregateMetadataImporter):
334
233
 
335
234
  def move_and_modify_dirs(self):
336
235
  """Move files and dirs and modify dirs."""
337
- # It would be nice to move folders instead of recreating them but it would require
338
- # an inode map from the snapshots to do correctly.
339
236
  self.bulk_folders_moved()
340
237
  self._bulk_comics_moved()
341
238
  self._bulk_covers_moved()
@@ -544,22 +544,19 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
544
544
  base_cls = Comic
545
545
  fk_data = self.metadata[FK_CREATE][FKC_CREATE_FKS], base_cls, fk_field, "name"
546
546
  names = self.metadata[FKS].pop(fk_field)
547
- count = self._query_missing_simple_models(
548
- names,
549
- fk_data,
550
- status,
547
+ status.add_complete(
548
+ self._query_missing_simple_models(
549
+ names,
550
+ fk_data,
551
+ status,
552
+ )
551
553
  )
552
- status.add_complete(count)
553
554
  self.status_controller.update(status, notify=False)
554
555
 
555
556
  ###########
556
557
  # FOLDERS #
557
558
  ###########
558
- def query_missing_folder_paths(
559
- self,
560
- comic_paths,
561
- status,
562
- ):
559
+ def query_missing_folder_paths(self, comic_paths, status):
563
560
  """Find missing folder paths."""
564
561
  # Get the proposed folder_paths
565
562
  library_path = Path(self.library.path)
@@ -578,18 +575,15 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
578
575
  status,
579
576
  )
580
577
  create_folder_paths = create_folder_paths_dict.get(Folder, set())
581
- return frozenset(create_folder_paths)
582
-
583
- def _add_missing_folder_paths(self, comic_paths, status):
584
- create_folder_paths = self.query_missing_folder_paths(comic_paths, status)
585
- """Add missing folder_paths to create fks."""
586
578
  if FK_CREATE not in self.metadata:
587
579
  self.metadata[FK_CREATE] = {}
588
580
  if FKC_FOLDER_PATHS not in self.metadata[FK_CREATE]:
589
581
  self.metadata[FK_CREATE][FKC_FOLDER_PATHS] = set()
590
582
  self.metadata[FK_CREATE][FKC_FOLDER_PATHS] |= create_folder_paths
591
- if count := len(create_folder_paths):
583
+ count = len(create_folder_paths)
584
+ if count:
592
585
  self.log.info(f"Prepared {count} new Folders.")
586
+ return count
593
587
 
594
588
  def _get_create_fks_totals(self):
595
589
  fkc = self.metadata[FK_CREATE]
@@ -640,7 +634,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
640
634
  status,
641
635
  )
642
636
 
643
- self._add_missing_folder_paths(
637
+ self.query_missing_folder_paths(
644
638
  self.metadata[FKS].pop(COMIC_PATHS, ()), status
645
639
  )
646
640
 
@@ -8,7 +8,6 @@ from codex.librarian.importer.tasks import (
8
8
  )
9
9
  from codex.librarian.janitor.cleanup import TOTAL_NUM_FK_CLASSES, CleanupMixin
10
10
  from codex.librarian.janitor.failed_imports import UpdateFailedImportsMixin
11
- from codex.librarian.janitor.latest_version import LatestVersionMixin
12
11
  from codex.librarian.janitor.status import JanitorStatusTypes
13
12
  from codex.librarian.janitor.tasks import (
14
13
  ForceUpdateAllFailedImportsTask,
@@ -17,7 +16,6 @@ from codex.librarian.janitor.tasks import (
17
16
  JanitorCleanFKsTask,
18
17
  JanitorCleanupSessionsTask,
19
18
  JanitorClearStatusTask,
20
- JanitorLatestVersionTask,
21
19
  JanitorNightlyTask,
22
20
  JanitorRestartTask,
23
21
  JanitorShutdownTask,
@@ -49,13 +47,10 @@ _JANITOR_STATII = (
49
47
  Status(SearchIndexStatusTypes.SEARCH_INDEX_UPDATE),
50
48
  Status(SearchIndexStatusTypes.SEARCH_INDEX_REMOVE),
51
49
  Status(SearchIndexStatusTypes.SEARCH_INDEX_MERGE),
52
- Status(JanitorStatusTypes.CODEX_LATEST_VERSION),
53
50
  )
54
51
 
55
52
 
56
- class Janitor(
57
- CleanupMixin, LatestVersionMixin, UpdateMixin, UpdateFailedImportsMixin, VacuumMixin
58
- ):
53
+ class Janitor(CleanupMixin, UpdateMixin, VacuumMixin, UpdateFailedImportsMixin):
59
54
  """Janitor inline task runner."""
60
55
 
61
56
  def __init__(self, log_queue, librarian_queue):
@@ -70,15 +65,14 @@ class Janitor(
70
65
  ).on
71
66
  self.status_controller.start_many(_JANITOR_STATII)
72
67
  tasks = (
73
- JanitorLatestVersionTask(),
74
- JanitorUpdateTask(force=False),
75
68
  SearchIndexAbortTask(),
76
69
  JanitorCleanFKsTask(),
77
70
  JanitorCleanCoversTask(),
78
71
  JanitorCleanupSessionsTask(),
79
- AdoptOrphanFoldersTask(),
80
72
  JanitorVacuumTask(),
81
73
  JanitorBackupTask(),
74
+ JanitorUpdateTask(force=False),
75
+ AdoptOrphanFoldersTask(),
82
76
  CoverRemoveOrphansTask(),
83
77
  SearchIndexUpdateTask(False),
84
78
  SearchIndexMergeTask(optimize),
@@ -97,10 +91,8 @@ class Janitor(
97
91
  self.vacuum_db()
98
92
  case JanitorBackupTask():
99
93
  self.backup_db()
100
- case JanitorLatestVersionTask():
101
- self.update_latest_version(task.force)
102
94
  case JanitorUpdateTask():
103
- self.update_codex(task.force)
95
+ self.update_codex()
104
96
  case JanitorRestartTask():
105
97
  self.restart_codex()
106
98
  case JanitorShutdownTask():
@@ -7,7 +7,6 @@ class JanitorStatusTypes(Choices):
7
7
  """Janitor Status Types."""
8
8
 
9
9
  CLEANUP_FK = "JTD"
10
- CODEX_LATEST_VERSION = "JLV"
11
10
  CODEX_UPDATE = "JCU"
12
11
  CODEX_RESTART = "JCR"
13
12
  CODEX_STOP = "JCS"
@@ -8,20 +8,6 @@ class JanitorTask:
8
8
  """Tasks for the janitor."""
9
9
 
10
10
 
11
- @dataclass
12
- class JanitorLatestVersionTask(JanitorTask):
13
- """Get the latest version."""
14
-
15
- force: bool = False
16
-
17
-
18
- @dataclass
19
- class JanitorUpdateTask(JanitorTask):
20
- """Task for updater."""
21
-
22
- force: bool = False
23
-
24
-
25
11
  @dataclass
26
12
  class JanitorBackupTask(JanitorTask):
27
13
  """Backup the database."""
@@ -37,6 +23,13 @@ class JanitorShutdownTask(JanitorTask):
37
23
  """for shutdown."""
38
24
 
39
25
 
26
+ @dataclass
27
+ class JanitorUpdateTask(JanitorTask):
28
+ """Task for updater."""
29
+
30
+ force: bool
31
+
32
+
40
33
  @dataclass
41
34
  class JanitorVacuumTask(JanitorTask):
42
35
  """Vacuum the database."""
@@ -5,36 +5,17 @@ import signal
5
5
  import subprocess
6
6
  import sys
7
7
 
8
- from versio.version import Version
9
-
10
8
  from codex.librarian.janitor.status import JanitorStatusTypes
11
9
  from codex.librarian.tasks import LibrarianShutdownTask
12
10
  from codex.models import AdminFlag
13
- from codex.models.admin import Timestamp
14
11
  from codex.status import Status
15
- from codex.version import VERSION, get_version
12
+ from codex.version import PACKAGE_NAME, VERSION, get_version, is_outdated
16
13
  from codex.worker_base import WorkerBaseMixin
17
14
 
18
15
 
19
16
  class UpdateMixin(WorkerBaseMixin):
20
17
  """Update codex methods for janitor."""
21
18
 
22
- def _is_outdated(self):
23
- """Is codex outdated."""
24
- result = False
25
- ts = Timestamp.objects.get(key=Timestamp.TimestampChoices.CODEX_VERSION.value)
26
- latest_version = ts.version
27
- versio_latest_version = Version(latest_version)
28
-
29
- installed_versio_version = Version(VERSION)
30
- if versio_latest_version.parts[1] and not installed_versio_version.parts[1]: # type: ignore
31
- pre_blurb = "latest version is a prerelease. But installed version is not."
32
- else:
33
- result = versio_latest_version > installed_versio_version
34
- pre_blurb = ""
35
- self.log.info(f"{latest_version=} > {VERSION=} = {result}{pre_blurb}")
36
- return result
37
-
38
19
  def update_codex(self, force=False):
39
20
  """Update the package and restart everything if the version changed."""
40
21
  status = Status(JanitorStatusTypes.CODEX_UPDATE)
@@ -46,7 +27,7 @@ class UpdateMixin(WorkerBaseMixin):
46
27
  eau = AdminFlag.objects.only("on").get(
47
28
  key=AdminFlag.FlagChoices.AUTO_UPDATE.value
48
29
  )
49
- if not eau.on or not self._is_outdated():
30
+ if not eau.on or not is_outdated(PACKAGE_NAME):
50
31
  self.log.info("Codex is up to date.")
51
32
  return
52
33
 
@@ -21,7 +21,8 @@ class VacuumMixin(WorkerBaseMixin):
21
21
  self.status_controller.start(status)
22
22
  old_size = DB_PATH.stat().st_size
23
23
  with connection.cursor() as cursor:
24
- cursor.execute("PRAGMA optimize")
24
+ cursor.execute("PRAGMA analysis_limit=400;")
25
+ cursor.execute("PRAGMA optimize;")
25
26
  cursor.execute("VACUUM")
26
27
  cursor.execute("PRAGMA wal_checkpoint(TRUNCATE)")
27
28
  new_size = DB_PATH.stat().st_size