codex 1.6.15__py3-none-any.whl → 1.6.17__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 (548) hide show
  1. codex/librarian/cron/crond.py +13 -10
  2. codex/librarian/importer/importerd.py +24 -19
  3. codex/librarian/importer/moved.py +176 -73
  4. codex/librarian/importer/query_fks.py +17 -11
  5. codex/librarian/janitor/update.py +1 -1
  6. codex/librarian/watchdog/dir_snapshot_diff.py +19 -17
  7. codex/startup.py +1 -3
  8. codex/static_root/assets/{VCheckbox-XREaWEHP.d331a6f4aab2.js → VCheckbox-D8eqoc1z.62941f086d95.js} +1 -1
  9. codex/static_root/assets/VCheckbox-D8eqoc1z.62941f086d95.js.br +0 -0
  10. codex/static_root/assets/VCheckbox-D8eqoc1z.62941f086d95.js.gz +0 -0
  11. codex/static_root/assets/{VCheckbox-XREaWEHP.js → VCheckbox-D8eqoc1z.js} +1 -1
  12. codex/static_root/assets/VCheckbox-D8eqoc1z.js.br +0 -0
  13. codex/static_root/assets/VCheckbox-D8eqoc1z.js.gz +0 -0
  14. codex/static_root/assets/{VCheckboxBtn-xs0GjiCS.0495df2dbd32.js → VCheckboxBtn-BaQC30AI.ea98c97505c2.js} +1 -1
  15. codex/static_root/assets/VCheckboxBtn-BaQC30AI.ea98c97505c2.js.br +0 -0
  16. codex/static_root/assets/VCheckboxBtn-BaQC30AI.ea98c97505c2.js.gz +0 -0
  17. codex/static_root/assets/{VCheckboxBtn-xs0GjiCS.js → VCheckboxBtn-BaQC30AI.js} +1 -1
  18. codex/static_root/assets/VCheckboxBtn-BaQC30AI.js.br +0 -0
  19. codex/static_root/assets/VCheckboxBtn-BaQC30AI.js.gz +0 -0
  20. codex/static_root/assets/{VCombobox-Dg0rRTsJ.a0134fe49dc6.js → VCombobox-BTG361j0.b2b9962de820.js} +1 -1
  21. codex/static_root/assets/VCombobox-BTG361j0.b2b9962de820.js.br +0 -0
  22. codex/static_root/assets/VCombobox-BTG361j0.b2b9962de820.js.gz +0 -0
  23. codex/static_root/assets/{VCombobox-Dg0rRTsJ.js → VCombobox-BTG361j0.js} +1 -1
  24. codex/static_root/assets/VCombobox-BTG361j0.js.br +0 -0
  25. codex/static_root/assets/VCombobox-BTG361j0.js.gz +0 -0
  26. codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css +1 -0
  27. codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css.br +0 -0
  28. codex/static_root/assets/VCombobox-bwEYrXCg.9f25a34994e2.css.gz +0 -0
  29. codex/static_root/assets/VCombobox-bwEYrXCg.css +1 -0
  30. codex/static_root/assets/VCombobox-bwEYrXCg.css.br +0 -0
  31. codex/static_root/assets/VCombobox-bwEYrXCg.css.gz +0 -0
  32. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css +1 -0
  33. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.br +0 -0
  34. codex/static_root/assets/VDialog-38np3TSA.5a0cb7a9e56d.css.gz +0 -0
  35. codex/static_root/assets/VDialog-38np3TSA.css +1 -0
  36. codex/static_root/assets/VDialog-38np3TSA.css.br +0 -0
  37. codex/static_root/assets/VDialog-38np3TSA.css.gz +0 -0
  38. codex/static_root/assets/{VDialog-CjdqXHdN.6b67273d69a3.js → VDialog-BWXBR7iM.80372742901b.js} +1 -1
  39. codex/static_root/assets/VDialog-BWXBR7iM.80372742901b.js.br +0 -0
  40. codex/static_root/assets/VDialog-BWXBR7iM.80372742901b.js.gz +0 -0
  41. codex/static_root/assets/{VDialog-CjdqXHdN.js → VDialog-BWXBR7iM.js} +1 -1
  42. codex/static_root/assets/VDialog-BWXBR7iM.js.br +0 -0
  43. codex/static_root/assets/VDialog-BWXBR7iM.js.gz +0 -0
  44. codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css +1 -0
  45. codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css.br +0 -0
  46. codex/static_root/assets/VExpansionPanels-6G1AUajL.5b17872981df.css.gz +0 -0
  47. codex/static_root/assets/VExpansionPanels-6G1AUajL.css +1 -0
  48. codex/static_root/assets/VExpansionPanels-6G1AUajL.css.br +0 -0
  49. codex/static_root/assets/VExpansionPanels-6G1AUajL.css.gz +0 -0
  50. codex/static_root/assets/{VExpansionPanels-DnzHPu5d.f949d412e326.js → VExpansionPanels-W4VN2Ykh.a3bf34bd9c01.js} +1 -1
  51. codex/static_root/assets/VExpansionPanels-W4VN2Ykh.a3bf34bd9c01.js.br +0 -0
  52. codex/static_root/assets/VExpansionPanels-W4VN2Ykh.a3bf34bd9c01.js.gz +0 -0
  53. codex/static_root/assets/{VExpansionPanels-DnzHPu5d.js → VExpansionPanels-W4VN2Ykh.js} +1 -1
  54. codex/static_root/assets/VExpansionPanels-W4VN2Ykh.js.br +0 -0
  55. codex/static_root/assets/VExpansionPanels-W4VN2Ykh.js.gz +0 -0
  56. codex/static_root/assets/{VRadioGroup-DpsttRwx.b5dff4bc597f.js → VRadioGroup-B8chw_xV.768aef5d30cd.js} +1 -1
  57. codex/static_root/assets/VRadioGroup-B8chw_xV.768aef5d30cd.js.br +0 -0
  58. codex/static_root/assets/VRadioGroup-B8chw_xV.768aef5d30cd.js.gz +0 -0
  59. codex/static_root/assets/{VRadioGroup-DpsttRwx.js → VRadioGroup-B8chw_xV.js} +1 -1
  60. codex/static_root/assets/VRadioGroup-B8chw_xV.js.br +0 -0
  61. codex/static_root/assets/VRadioGroup-B8chw_xV.js.gz +0 -0
  62. codex/static_root/assets/VSelect-BpWdXMPq.1921062294e6.js +1 -0
  63. codex/static_root/assets/VSelect-BpWdXMPq.1921062294e6.js.br +0 -0
  64. codex/static_root/assets/VSelect-BpWdXMPq.1921062294e6.js.gz +0 -0
  65. codex/static_root/assets/VSelect-BpWdXMPq.js +1 -0
  66. codex/static_root/assets/VSelect-BpWdXMPq.js.br +0 -0
  67. codex/static_root/assets/VSelect-BpWdXMPq.js.gz +0 -0
  68. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css +1 -0
  69. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.br +0 -0
  70. codex/static_root/assets/VSelect-Ca2bIOso.6613989e4c45.css.gz +0 -0
  71. codex/static_root/assets/VSelect-Ca2bIOso.css +1 -0
  72. codex/static_root/assets/VSelect-Ca2bIOso.css.br +0 -0
  73. codex/static_root/assets/VSelect-Ca2bIOso.css.gz +0 -0
  74. codex/static_root/assets/{VSelectionControl-BFq--28I.9687c77461fc.css → VSelectionControl-CiViEYoC.c77bb19a4a06.css} +1 -1
  75. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.br +0 -0
  76. codex/static_root/assets/VSelectionControl-CiViEYoC.c77bb19a4a06.css.gz +0 -0
  77. codex/static_root/assets/{VSelectionControl-BFq--28I.css → VSelectionControl-CiViEYoC.css} +1 -1
  78. codex/static_root/assets/VSelectionControl-CiViEYoC.css.br +0 -0
  79. codex/static_root/assets/VSelectionControl-CiViEYoC.css.gz +0 -0
  80. codex/static_root/assets/{VSelectionControl-Vy5G2zUU.74880d4099ef.js → VSelectionControl-FmTy_2ch.7e005af872d4.js} +1 -1
  81. codex/static_root/assets/VSelectionControl-FmTy_2ch.7e005af872d4.js.br +0 -0
  82. codex/static_root/assets/VSelectionControl-FmTy_2ch.7e005af872d4.js.gz +0 -0
  83. codex/static_root/assets/{VSelectionControl-Vy5G2zUU.js → VSelectionControl-FmTy_2ch.js} +1 -1
  84. codex/static_root/assets/VSelectionControl-FmTy_2ch.js.br +0 -0
  85. codex/static_root/assets/VSelectionControl-FmTy_2ch.js.gz +0 -0
  86. codex/static_root/assets/{VSlideGroup-Ce0R0Z5m.1dd2179cad7f.js → VSlideGroup-B9zEPv1H.627ca1e091fb.js} +1 -1
  87. codex/static_root/assets/VSlideGroup-B9zEPv1H.627ca1e091fb.js.br +0 -0
  88. codex/static_root/assets/VSlideGroup-B9zEPv1H.627ca1e091fb.js.gz +0 -0
  89. codex/static_root/assets/{VSlideGroup-Ce0R0Z5m.js → VSlideGroup-B9zEPv1H.js} +1 -1
  90. codex/static_root/assets/VSlideGroup-B9zEPv1H.js.br +0 -0
  91. codex/static_root/assets/VSlideGroup-B9zEPv1H.js.gz +0 -0
  92. codex/static_root/assets/{VTable-0gObCQ_t.07fd5a62df8a.js → VTable-D85UV0cA.c9b0d82847a0.js} +1 -1
  93. codex/static_root/assets/VTable-D85UV0cA.c9b0d82847a0.js.br +0 -0
  94. codex/static_root/assets/VTable-D85UV0cA.c9b0d82847a0.js.gz +0 -0
  95. codex/static_root/assets/{VTable-0gObCQ_t.js → VTable-D85UV0cA.js} +1 -1
  96. codex/static_root/assets/VTable-D85UV0cA.js.br +0 -0
  97. codex/static_root/assets/VTable-D85UV0cA.js.gz +0 -0
  98. codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css +1 -0
  99. codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css.br +0 -0
  100. codex/static_root/assets/VTable-Y5Sc3hJd.c36f0a6a30cf.css.gz +0 -0
  101. codex/static_root/assets/VTable-Y5Sc3hJd.css +1 -0
  102. codex/static_root/assets/VTable-Y5Sc3hJd.css.br +0 -0
  103. codex/static_root/assets/VTable-Y5Sc3hJd.css.gz +0 -0
  104. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css +1 -0
  105. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.br +0 -0
  106. codex/static_root/assets/VTextField-Crc3gdNh.39995ccf4d52.css.gz +0 -0
  107. codex/static_root/assets/VTextField-Crc3gdNh.css +1 -0
  108. codex/static_root/assets/VTextField-Crc3gdNh.css.br +0 -0
  109. codex/static_root/assets/VTextField-Crc3gdNh.css.gz +0 -0
  110. codex/static_root/assets/{VTextField-BqqJyXv9.bf7e872bdeaf.js → VTextField-DAe7a2au.04013b823a64.js} +1 -1
  111. codex/static_root/assets/VTextField-DAe7a2au.04013b823a64.js.br +0 -0
  112. codex/static_root/assets/VTextField-DAe7a2au.04013b823a64.js.gz +0 -0
  113. codex/static_root/assets/{VTextField-BqqJyXv9.js → VTextField-DAe7a2au.js} +1 -1
  114. codex/static_root/assets/VTextField-DAe7a2au.js.br +0 -0
  115. codex/static_root/assets/VTextField-DAe7a2au.js.gz +0 -0
  116. codex/static_root/assets/{VWindowItem-CWdXxb60.f094a97cee4e.js → VWindowItem-BOT7VsDv.061276a5a285.js} +1 -1
  117. codex/static_root/assets/VWindowItem-BOT7VsDv.061276a5a285.js.br +0 -0
  118. codex/static_root/assets/VWindowItem-BOT7VsDv.061276a5a285.js.gz +0 -0
  119. codex/static_root/assets/{VWindowItem-CWdXxb60.js → VWindowItem-BOT7VsDv.js} +1 -1
  120. codex/static_root/assets/VWindowItem-BOT7VsDv.js.br +0 -0
  121. codex/static_root/assets/VWindowItem-BOT7VsDv.js.gz +0 -0
  122. codex/static_root/assets/{admin-DlVORuxh.f6f9c3335f24.js → admin-DCoV7mGR.d949114bed68.js} +1 -1
  123. codex/static_root/assets/admin-DCoV7mGR.d949114bed68.js.br +0 -0
  124. codex/static_root/assets/admin-DCoV7mGR.d949114bed68.js.gz +0 -0
  125. codex/static_root/assets/{admin-DlVORuxh.js → admin-DCoV7mGR.js} +1 -1
  126. codex/static_root/assets/admin-DCoV7mGR.js.br +0 -0
  127. codex/static_root/assets/admin-DCoV7mGR.js.gz +0 -0
  128. codex/static_root/assets/admin-drawer-panel-COV3Ru1J.b0cd2b71000c.css +1 -0
  129. codex/static_root/assets/admin-drawer-panel-COV3Ru1J.b0cd2b71000c.css.br +0 -0
  130. codex/static_root/assets/admin-drawer-panel-COV3Ru1J.b0cd2b71000c.css.gz +0 -0
  131. codex/static_root/assets/admin-drawer-panel-COV3Ru1J.css +1 -0
  132. codex/static_root/assets/admin-drawer-panel-COV3Ru1J.css.br +0 -0
  133. codex/static_root/assets/admin-drawer-panel-COV3Ru1J.css.gz +0 -0
  134. codex/static_root/assets/admin-drawer-panel-Dmnn03CQ.52ea4aad3985.js +30 -0
  135. codex/static_root/assets/admin-drawer-panel-Dmnn03CQ.52ea4aad3985.js.br +0 -0
  136. codex/static_root/assets/admin-drawer-panel-Dmnn03CQ.52ea4aad3985.js.gz +0 -0
  137. codex/static_root/assets/admin-drawer-panel-Dmnn03CQ.js +30 -0
  138. codex/static_root/assets/admin-drawer-panel-Dmnn03CQ.js.br +0 -0
  139. codex/static_root/assets/admin-drawer-panel-Dmnn03CQ.js.gz +0 -0
  140. codex/static_root/assets/browser--OxfD4LA.d6c438cacb9a.js +1 -0
  141. codex/static_root/assets/browser--OxfD4LA.d6c438cacb9a.js.br +0 -0
  142. codex/static_root/assets/browser--OxfD4LA.d6c438cacb9a.js.gz +0 -0
  143. codex/static_root/assets/browser--OxfD4LA.js +1 -0
  144. codex/static_root/assets/browser--OxfD4LA.js.br +0 -0
  145. codex/static_root/assets/browser--OxfD4LA.js.gz +0 -0
  146. codex/static_root/assets/{browser-zaYTL_k7.3272235689e3.css → browser-DWoo5pGT.29bee2bbd49b.css} +1 -1
  147. codex/static_root/assets/browser-DWoo5pGT.29bee2bbd49b.css.br +0 -0
  148. codex/static_root/assets/browser-DWoo5pGT.29bee2bbd49b.css.gz +0 -0
  149. codex/static_root/assets/{browser-zaYTL_k7.css → browser-DWoo5pGT.css} +1 -1
  150. codex/static_root/assets/browser-DWoo5pGT.css.br +0 -0
  151. codex/static_root/assets/browser-DWoo5pGT.css.gz +0 -0
  152. codex/static_root/assets/change-password-dialog-Bosys_6j.036c0eea895d.css +1 -0
  153. codex/static_root/assets/change-password-dialog-Bosys_6j.036c0eea895d.css.br +0 -0
  154. codex/static_root/assets/change-password-dialog-Bosys_6j.036c0eea895d.css.gz +0 -0
  155. codex/static_root/assets/change-password-dialog-Bosys_6j.css +1 -0
  156. codex/static_root/assets/change-password-dialog-Bosys_6j.css.br +0 -0
  157. codex/static_root/assets/change-password-dialog-Bosys_6j.css.gz +0 -0
  158. codex/static_root/assets/change-password-dialog-HW0jK0gr.bbc3ed89fb31.js +1 -0
  159. codex/static_root/assets/change-password-dialog-HW0jK0gr.bbc3ed89fb31.js.br +0 -0
  160. codex/static_root/assets/change-password-dialog-HW0jK0gr.bbc3ed89fb31.js.gz +0 -0
  161. codex/static_root/assets/change-password-dialog-HW0jK0gr.js +1 -0
  162. codex/static_root/assets/change-password-dialog-HW0jK0gr.js.br +0 -0
  163. codex/static_root/assets/change-password-dialog-HW0jK0gr.js.gz +0 -0
  164. codex/static_root/assets/{confirm-dialog-DDql2Okm.9a10b377f5e0.js → confirm-dialog-BJlNq6ku.42441ece74ab.js} +1 -1
  165. codex/static_root/assets/confirm-dialog-BJlNq6ku.42441ece74ab.js.br +0 -0
  166. codex/static_root/assets/confirm-dialog-BJlNq6ku.42441ece74ab.js.gz +0 -0
  167. codex/static_root/assets/{confirm-dialog-DDql2Okm.js → confirm-dialog-BJlNq6ku.js} +1 -1
  168. codex/static_root/assets/confirm-dialog-BJlNq6ku.js.br +0 -0
  169. codex/static_root/assets/confirm-dialog-BJlNq6ku.js.gz +0 -0
  170. codex/static_root/assets/{datetime-column-LnDRlNck.a89a38ca43a8.js → datetime-column-BdnHcY-4.12fb4188de00.js} +1 -1
  171. codex/static_root/assets/datetime-column-BdnHcY-4.12fb4188de00.js.br +0 -0
  172. codex/static_root/assets/datetime-column-BdnHcY-4.12fb4188de00.js.gz +0 -0
  173. codex/static_root/assets/{datetime-column-LnDRlNck.js → datetime-column-BdnHcY-4.js} +1 -1
  174. codex/static_root/assets/datetime-column-BdnHcY-4.js.br +0 -0
  175. codex/static_root/assets/datetime-column-BdnHcY-4.js.gz +0 -0
  176. codex/static_root/assets/{filter-DA11td9b.26d25f4b88e1.js → filter-BL9RMUvn.b92d240c4512.js} +1 -1
  177. codex/static_root/assets/filter-BL9RMUvn.b92d240c4512.js.br +0 -0
  178. codex/static_root/assets/filter-BL9RMUvn.b92d240c4512.js.gz +0 -0
  179. codex/static_root/assets/{filter-DA11td9b.js → filter-BL9RMUvn.js} +1 -1
  180. codex/static_root/assets/filter-BL9RMUvn.js.br +0 -0
  181. codex/static_root/assets/filter-BL9RMUvn.js.gz +0 -0
  182. codex/static_root/assets/{flag-tab-D_VrM7zT.240eab4456f0.js → flag-tab-BWJ5tGcV.6f4c1dd9d1bb.js} +1 -1
  183. codex/static_root/assets/flag-tab-BWJ5tGcV.6f4c1dd9d1bb.js.br +0 -0
  184. codex/static_root/assets/flag-tab-BWJ5tGcV.6f4c1dd9d1bb.js.gz +0 -0
  185. codex/static_root/assets/{flag-tab-D_VrM7zT.js → flag-tab-BWJ5tGcV.js} +1 -1
  186. codex/static_root/assets/flag-tab-BWJ5tGcV.js.br +0 -0
  187. codex/static_root/assets/flag-tab-BWJ5tGcV.js.gz +0 -0
  188. codex/static_root/assets/{group-tab-BQJUrW7O.7d77e88195ff.js → group-tab-CQiGzI43.d88e38af13b4.js} +1 -1
  189. codex/static_root/assets/group-tab-CQiGzI43.d88e38af13b4.js.br +0 -0
  190. codex/static_root/assets/group-tab-CQiGzI43.d88e38af13b4.js.gz +0 -0
  191. codex/static_root/assets/{group-tab-BQJUrW7O.js → group-tab-CQiGzI43.js} +1 -1
  192. codex/static_root/assets/group-tab-CQiGzI43.js.br +0 -0
  193. codex/static_root/assets/group-tab-CQiGzI43.js.gz +0 -0
  194. codex/static_root/assets/{http-error-DvYPiCry.fb306a0f546b.js → http-error-B0-GHW9C.81a3af2c7f7c.js} +1 -1
  195. codex/static_root/assets/http-error-B0-GHW9C.81a3af2c7f7c.js.br +0 -0
  196. codex/static_root/assets/http-error-B0-GHW9C.81a3af2c7f7c.js.gz +0 -0
  197. codex/static_root/assets/{http-error-DvYPiCry.js → http-error-B0-GHW9C.js} +1 -1
  198. codex/static_root/assets/http-error-B0-GHW9C.js.br +0 -0
  199. codex/static_root/assets/http-error-B0-GHW9C.js.gz +0 -0
  200. codex/static_root/assets/library-tab-VMgJSPRy.5e7d6c6baff6.css +1 -0
  201. codex/static_root/assets/library-tab-VMgJSPRy.5e7d6c6baff6.css.br +0 -0
  202. codex/static_root/assets/library-tab-VMgJSPRy.5e7d6c6baff6.css.gz +0 -0
  203. codex/static_root/assets/library-tab-VMgJSPRy.css +1 -0
  204. codex/static_root/assets/library-tab-VMgJSPRy.css.br +0 -0
  205. codex/static_root/assets/library-tab-VMgJSPRy.css.gz +0 -0
  206. codex/static_root/assets/library-tab-czIX7PK1.540a657518c8.js +1 -0
  207. codex/static_root/assets/library-tab-czIX7PK1.540a657518c8.js.br +0 -0
  208. codex/static_root/assets/library-tab-czIX7PK1.540a657518c8.js.gz +0 -0
  209. codex/static_root/assets/library-tab-czIX7PK1.js +1 -0
  210. codex/static_root/assets/library-tab-czIX7PK1.js.br +0 -0
  211. codex/static_root/assets/library-tab-czIX7PK1.js.gz +0 -0
  212. codex/static_root/assets/{main-Ztp855PW.css → main-BZ73p_WW.css} +1 -1
  213. codex/static_root/assets/main-BZ73p_WW.css.br +0 -0
  214. codex/static_root/assets/{main-Ztp855PW.css.gz → main-BZ73p_WW.css.gz} +0 -0
  215. codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-BZ73p_WW.d529e8b3f52a.css} +1 -1
  216. codex/static_root/assets/main-BZ73p_WW.d529e8b3f52a.css.br +0 -0
  217. codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-BZ73p_WW.d529e8b3f52a.css.gz} +0 -0
  218. codex/static_root/assets/main-C0t41OAE.9c108cc29c65.js +6 -0
  219. codex/static_root/assets/main-C0t41OAE.9c108cc29c65.js.br +0 -0
  220. codex/static_root/assets/main-C0t41OAE.9c108cc29c65.js.gz +0 -0
  221. codex/static_root/assets/main-C0t41OAE.js +6 -0
  222. codex/static_root/assets/main-C0t41OAE.js.br +0 -0
  223. codex/static_root/assets/main-C0t41OAE.js.gz +0 -0
  224. codex/static_root/assets/pagination-toolbar-BNiqJX4c.c62f4bb07236.js +1 -0
  225. codex/static_root/assets/pagination-toolbar-BNiqJX4c.c62f4bb07236.js.br +0 -0
  226. codex/static_root/assets/pagination-toolbar-BNiqJX4c.c62f4bb07236.js.gz +0 -0
  227. codex/static_root/assets/pagination-toolbar-BNiqJX4c.js +1 -0
  228. codex/static_root/assets/pagination-toolbar-BNiqJX4c.js.br +0 -0
  229. codex/static_root/assets/pagination-toolbar-BNiqJX4c.js.gz +0 -0
  230. codex/static_root/assets/{pagination-toolbar-0xaXKIHc.css → pagination-toolbar-D6v3_jVe.4f0e62bbb1e5.css} +1 -1
  231. codex/static_root/assets/pagination-toolbar-D6v3_jVe.4f0e62bbb1e5.css.br +0 -0
  232. codex/static_root/assets/pagination-toolbar-D6v3_jVe.4f0e62bbb1e5.css.gz +0 -0
  233. codex/static_root/assets/{pagination-toolbar-0xaXKIHc.acec3ef3a951.css → pagination-toolbar-D6v3_jVe.css} +1 -1
  234. codex/static_root/assets/pagination-toolbar-D6v3_jVe.css.br +0 -0
  235. codex/static_root/assets/pagination-toolbar-D6v3_jVe.css.gz +0 -0
  236. codex/static_root/assets/{pdf-doc-C3OfGN5a.54692f4bda33.js → pdf-doc-BDi_S8C8.ec2572c47556.js} +1 -1
  237. codex/static_root/assets/pdf-doc-BDi_S8C8.ec2572c47556.js.br +0 -0
  238. codex/static_root/assets/pdf-doc-BDi_S8C8.ec2572c47556.js.gz +0 -0
  239. codex/static_root/assets/{pdf-doc-C3OfGN5a.js → pdf-doc-BDi_S8C8.js} +1 -1
  240. codex/static_root/assets/pdf-doc-BDi_S8C8.js.br +0 -0
  241. codex/static_root/assets/pdf-doc-BDi_S8C8.js.gz +0 -0
  242. codex/static_root/assets/{reader-Do0jgF5k.aae2010130b4.css → reader-BORfYc0l.css} +1 -1
  243. codex/static_root/assets/reader-BORfYc0l.css.br +0 -0
  244. codex/static_root/assets/{reader-Do0jgF5k.aae2010130b4.css.gz → reader-BORfYc0l.css.gz} +0 -0
  245. codex/static_root/assets/{reader-Do0jgF5k.css → reader-BORfYc0l.e66ce0ded0b6.css} +1 -1
  246. codex/static_root/assets/reader-BORfYc0l.e66ce0ded0b6.css.br +0 -0
  247. codex/static_root/assets/{reader-Do0jgF5k.css.gz → reader-BORfYc0l.e66ce0ded0b6.css.gz} +0 -0
  248. codex/static_root/assets/{reader-Dr7IAvkA.js → reader-Dg5VFhMO.d0ece6c59cb6.js} +2 -2
  249. codex/static_root/assets/reader-Dg5VFhMO.d0ece6c59cb6.js.br +0 -0
  250. codex/static_root/assets/reader-Dg5VFhMO.d0ece6c59cb6.js.gz +0 -0
  251. codex/static_root/assets/{reader-Dr7IAvkA.5349d84214d8.js → reader-Dg5VFhMO.js} +2 -2
  252. codex/static_root/assets/reader-Dg5VFhMO.js.br +0 -0
  253. codex/static_root/assets/reader-Dg5VFhMO.js.gz +0 -0
  254. codex/static_root/assets/relation-chips-nc41xEEd.eadca8fe8606.js +1 -0
  255. codex/static_root/assets/relation-chips-nc41xEEd.eadca8fe8606.js.br +0 -0
  256. codex/static_root/assets/relation-chips-nc41xEEd.eadca8fe8606.js.gz +0 -0
  257. codex/static_root/assets/relation-chips-nc41xEEd.js +1 -0
  258. codex/static_root/assets/relation-chips-nc41xEEd.js.br +0 -0
  259. codex/static_root/assets/relation-chips-nc41xEEd.js.gz +0 -0
  260. codex/static_root/assets/settings-drawer-ChnWWcfy.627fb9c17389.js +2 -0
  261. codex/static_root/assets/settings-drawer-ChnWWcfy.627fb9c17389.js.br +0 -0
  262. codex/static_root/assets/settings-drawer-ChnWWcfy.627fb9c17389.js.gz +0 -0
  263. codex/static_root/assets/settings-drawer-ChnWWcfy.js +2 -0
  264. codex/static_root/assets/settings-drawer-ChnWWcfy.js.br +0 -0
  265. codex/static_root/assets/settings-drawer-ChnWWcfy.js.gz +0 -0
  266. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css +1 -0
  267. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.br +0 -0
  268. codex/static_root/assets/settings-drawer-DKncO_vX.0a2cd24cd904.css.gz +0 -0
  269. codex/static_root/assets/settings-drawer-DKncO_vX.css +1 -0
  270. codex/static_root/assets/settings-drawer-DKncO_vX.css.br +0 -0
  271. codex/static_root/assets/settings-drawer-DKncO_vX.css.gz +0 -0
  272. codex/static_root/assets/{stats-tab-BoCyyFVC.7db4689bd9e3.js → stats-tab-Ep3dyw2f.1c5c48751a8d.js} +1 -1
  273. codex/static_root/assets/stats-tab-Ep3dyw2f.1c5c48751a8d.js.br +0 -0
  274. codex/static_root/assets/stats-tab-Ep3dyw2f.1c5c48751a8d.js.gz +0 -0
  275. codex/static_root/assets/{stats-tab-BoCyyFVC.js → stats-tab-Ep3dyw2f.js} +1 -1
  276. codex/static_root/assets/stats-tab-Ep3dyw2f.js.br +0 -0
  277. codex/static_root/assets/stats-tab-Ep3dyw2f.js.gz +0 -0
  278. codex/static_root/assets/{task-tab-DrDBVQJ2.b9031c4f7ea8.js → task-tab-CZM8h2Cr.d61b2f38a5c7.js} +1 -1
  279. codex/static_root/assets/task-tab-CZM8h2Cr.d61b2f38a5c7.js.br +0 -0
  280. codex/static_root/assets/task-tab-CZM8h2Cr.d61b2f38a5c7.js.gz +0 -0
  281. codex/static_root/assets/{task-tab-DrDBVQJ2.js → task-tab-CZM8h2Cr.js} +1 -1
  282. codex/static_root/assets/task-tab-CZM8h2Cr.js.br +0 -0
  283. codex/static_root/assets/task-tab-CZM8h2Cr.js.gz +0 -0
  284. codex/static_root/assets/{unauthorized-D87B0TXf.85c5665072a2.css → unauthorized-CQL7W5pA.75124be66863.css} +1 -1
  285. codex/static_root/assets/unauthorized-CQL7W5pA.75124be66863.css.br +0 -0
  286. codex/static_root/assets/unauthorized-CQL7W5pA.75124be66863.css.gz +0 -0
  287. codex/static_root/assets/{unauthorized-D87B0TXf.css → unauthorized-CQL7W5pA.css} +1 -1
  288. codex/static_root/assets/unauthorized-CQL7W5pA.css.br +0 -0
  289. codex/static_root/assets/unauthorized-CQL7W5pA.css.gz +0 -0
  290. codex/static_root/assets/{unauthorized-DrrtXMYF.f39bd5105e4d.js → unauthorized-DnNuMsHM.2e4595f10736.js} +1 -1
  291. codex/static_root/assets/unauthorized-DnNuMsHM.2e4595f10736.js.br +0 -0
  292. codex/static_root/assets/unauthorized-DnNuMsHM.2e4595f10736.js.gz +0 -0
  293. codex/static_root/assets/{unauthorized-DrrtXMYF.js → unauthorized-DnNuMsHM.js} +1 -1
  294. codex/static_root/assets/unauthorized-DnNuMsHM.js.br +0 -0
  295. codex/static_root/assets/unauthorized-DnNuMsHM.js.gz +0 -0
  296. codex/static_root/assets/{user-tab-BPTm1S-a.1ba9fb20134c.js → user-tab-CmdeTcr_.e7929862d979.js} +1 -1
  297. codex/static_root/assets/user-tab-CmdeTcr_.e7929862d979.js.br +0 -0
  298. codex/static_root/assets/user-tab-CmdeTcr_.e7929862d979.js.gz +0 -0
  299. codex/static_root/assets/{user-tab-BPTm1S-a.js → user-tab-CmdeTcr_.js} +1 -1
  300. codex/static_root/assets/user-tab-CmdeTcr_.js.br +0 -0
  301. codex/static_root/assets/user-tab-CmdeTcr_.js.gz +0 -0
  302. codex/static_root/manifest.9e3c56e33cdb.json +611 -0
  303. codex/static_root/manifest.9e3c56e33cdb.json.br +0 -0
  304. codex/static_root/manifest.9e3c56e33cdb.json.gz +0 -0
  305. codex/static_root/manifest.json +241 -241
  306. codex/static_root/manifest.json.br +0 -0
  307. codex/static_root/manifest.json.gz +0 -0
  308. codex/static_root/staticfiles.json +1 -1
  309. codex/version.py +21 -11
  310. codex/views/browser/filters/annotations.py +5 -4
  311. {codex-1.6.15.dist-info → codex-1.6.17.dist-info}/METADATA +3 -2
  312. {codex-1.6.15.dist-info → codex-1.6.17.dist-info}/RECORD +315 -315
  313. codex/static_root/assets/VCheckbox-XREaWEHP.d331a6f4aab2.js.br +0 -0
  314. codex/static_root/assets/VCheckbox-XREaWEHP.d331a6f4aab2.js.gz +0 -0
  315. codex/static_root/assets/VCheckbox-XREaWEHP.js.br +0 -0
  316. codex/static_root/assets/VCheckbox-XREaWEHP.js.gz +0 -0
  317. codex/static_root/assets/VCheckboxBtn-xs0GjiCS.0495df2dbd32.js.br +0 -0
  318. codex/static_root/assets/VCheckboxBtn-xs0GjiCS.0495df2dbd32.js.gz +0 -0
  319. codex/static_root/assets/VCheckboxBtn-xs0GjiCS.js.br +0 -0
  320. codex/static_root/assets/VCheckboxBtn-xs0GjiCS.js.gz +0 -0
  321. codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css +0 -1
  322. codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css.br +0 -0
  323. codex/static_root/assets/VCombobox-BksEr4D_.73429bb75f42.css.gz +0 -0
  324. codex/static_root/assets/VCombobox-BksEr4D_.css +0 -1
  325. codex/static_root/assets/VCombobox-BksEr4D_.css.br +0 -0
  326. codex/static_root/assets/VCombobox-BksEr4D_.css.gz +0 -0
  327. codex/static_root/assets/VCombobox-Dg0rRTsJ.a0134fe49dc6.js.br +0 -0
  328. codex/static_root/assets/VCombobox-Dg0rRTsJ.a0134fe49dc6.js.gz +0 -0
  329. codex/static_root/assets/VCombobox-Dg0rRTsJ.js.br +0 -0
  330. codex/static_root/assets/VCombobox-Dg0rRTsJ.js.gz +0 -0
  331. codex/static_root/assets/VDialog-CjdqXHdN.6b67273d69a3.js.br +0 -0
  332. codex/static_root/assets/VDialog-CjdqXHdN.6b67273d69a3.js.gz +0 -0
  333. codex/static_root/assets/VDialog-CjdqXHdN.js.br +0 -0
  334. codex/static_root/assets/VDialog-CjdqXHdN.js.gz +0 -0
  335. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css +0 -1
  336. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
  337. codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
  338. codex/static_root/assets/VDialog-lQ13FnRR.css +0 -1
  339. codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
  340. codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
  341. codex/static_root/assets/VExpansionPanels-DnzHPu5d.f949d412e326.js.br +0 -0
  342. codex/static_root/assets/VExpansionPanels-DnzHPu5d.f949d412e326.js.gz +0 -0
  343. codex/static_root/assets/VExpansionPanels-DnzHPu5d.js.br +0 -0
  344. codex/static_root/assets/VExpansionPanels-DnzHPu5d.js.gz +0 -0
  345. codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css +0 -1
  346. codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css.br +0 -0
  347. codex/static_root/assets/VExpansionPanels-DsEXVcCv.20b475255e66.css.gz +0 -0
  348. codex/static_root/assets/VExpansionPanels-DsEXVcCv.css +0 -1
  349. codex/static_root/assets/VExpansionPanels-DsEXVcCv.css.br +0 -0
  350. codex/static_root/assets/VExpansionPanels-DsEXVcCv.css.gz +0 -0
  351. codex/static_root/assets/VRadioGroup-DpsttRwx.b5dff4bc597f.js.br +0 -0
  352. codex/static_root/assets/VRadioGroup-DpsttRwx.b5dff4bc597f.js.gz +0 -0
  353. codex/static_root/assets/VRadioGroup-DpsttRwx.js.br +0 -0
  354. codex/static_root/assets/VRadioGroup-DpsttRwx.js.gz +0 -0
  355. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +0 -1
  356. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  357. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  358. codex/static_root/assets/VSelect-ARDhJiQK.css +0 -1
  359. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  360. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  361. codex/static_root/assets/VSelect-ClH-v875.ebe915372730.js +0 -1
  362. codex/static_root/assets/VSelect-ClH-v875.ebe915372730.js.br +0 -0
  363. codex/static_root/assets/VSelect-ClH-v875.ebe915372730.js.gz +0 -0
  364. codex/static_root/assets/VSelect-ClH-v875.js +0 -1
  365. codex/static_root/assets/VSelect-ClH-v875.js.br +0 -0
  366. codex/static_root/assets/VSelect-ClH-v875.js.gz +0 -0
  367. codex/static_root/assets/VSelectionControl-BFq--28I.9687c77461fc.css.br +0 -0
  368. codex/static_root/assets/VSelectionControl-BFq--28I.9687c77461fc.css.gz +0 -0
  369. codex/static_root/assets/VSelectionControl-BFq--28I.css.br +0 -0
  370. codex/static_root/assets/VSelectionControl-BFq--28I.css.gz +0 -0
  371. codex/static_root/assets/VSelectionControl-Vy5G2zUU.74880d4099ef.js.br +0 -0
  372. codex/static_root/assets/VSelectionControl-Vy5G2zUU.74880d4099ef.js.gz +0 -0
  373. codex/static_root/assets/VSelectionControl-Vy5G2zUU.js.br +0 -0
  374. codex/static_root/assets/VSelectionControl-Vy5G2zUU.js.gz +0 -0
  375. codex/static_root/assets/VSlideGroup-Ce0R0Z5m.1dd2179cad7f.js.br +0 -0
  376. codex/static_root/assets/VSlideGroup-Ce0R0Z5m.1dd2179cad7f.js.gz +0 -0
  377. codex/static_root/assets/VSlideGroup-Ce0R0Z5m.js.br +0 -0
  378. codex/static_root/assets/VSlideGroup-Ce0R0Z5m.js.gz +0 -0
  379. codex/static_root/assets/VTable-0gObCQ_t.07fd5a62df8a.js.br +0 -0
  380. codex/static_root/assets/VTable-0gObCQ_t.07fd5a62df8a.js.gz +0 -0
  381. codex/static_root/assets/VTable-0gObCQ_t.js.br +0 -0
  382. codex/static_root/assets/VTable-0gObCQ_t.js.gz +0 -0
  383. codex/static_root/assets/VTable-DflzroDq.160504d82195.css +0 -1
  384. codex/static_root/assets/VTable-DflzroDq.160504d82195.css.br +0 -0
  385. codex/static_root/assets/VTable-DflzroDq.160504d82195.css.gz +0 -0
  386. codex/static_root/assets/VTable-DflzroDq.css +0 -1
  387. codex/static_root/assets/VTable-DflzroDq.css.br +0 -0
  388. codex/static_root/assets/VTable-DflzroDq.css.gz +0 -0
  389. codex/static_root/assets/VTextField-BqqJyXv9.bf7e872bdeaf.js.br +0 -0
  390. codex/static_root/assets/VTextField-BqqJyXv9.bf7e872bdeaf.js.gz +0 -0
  391. codex/static_root/assets/VTextField-BqqJyXv9.js.br +0 -0
  392. codex/static_root/assets/VTextField-BqqJyXv9.js.gz +0 -0
  393. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
  394. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  395. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  396. codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
  397. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  398. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  399. codex/static_root/assets/VWindowItem-CWdXxb60.f094a97cee4e.js.br +0 -0
  400. codex/static_root/assets/VWindowItem-CWdXxb60.f094a97cee4e.js.gz +0 -0
  401. codex/static_root/assets/VWindowItem-CWdXxb60.js.br +0 -0
  402. codex/static_root/assets/VWindowItem-CWdXxb60.js.gz +0 -0
  403. codex/static_root/assets/admin-DlVORuxh.f6f9c3335f24.js.br +0 -0
  404. codex/static_root/assets/admin-DlVORuxh.f6f9c3335f24.js.gz +0 -0
  405. codex/static_root/assets/admin-DlVORuxh.js.br +0 -0
  406. codex/static_root/assets/admin-DlVORuxh.js.gz +0 -0
  407. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css +0 -1
  408. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css.br +0 -0
  409. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.3a7af8ec4462.css.gz +0 -0
  410. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css +0 -1
  411. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css.br +0 -0
  412. codex/static_root/assets/admin-drawer-panel-DOrhl9Nb.css.gz +0 -0
  413. codex/static_root/assets/admin-drawer-panel-oz6FwrsQ.b89b5fe1204a.js +0 -30
  414. codex/static_root/assets/admin-drawer-panel-oz6FwrsQ.b89b5fe1204a.js.br +0 -0
  415. codex/static_root/assets/admin-drawer-panel-oz6FwrsQ.b89b5fe1204a.js.gz +0 -0
  416. codex/static_root/assets/admin-drawer-panel-oz6FwrsQ.js +0 -30
  417. codex/static_root/assets/admin-drawer-panel-oz6FwrsQ.js.br +0 -0
  418. codex/static_root/assets/admin-drawer-panel-oz6FwrsQ.js.gz +0 -0
  419. codex/static_root/assets/browser-ZfiFIS4C.e672d2eba94c.js +0 -1
  420. codex/static_root/assets/browser-ZfiFIS4C.e672d2eba94c.js.br +0 -0
  421. codex/static_root/assets/browser-ZfiFIS4C.e672d2eba94c.js.gz +0 -0
  422. codex/static_root/assets/browser-ZfiFIS4C.js +0 -1
  423. codex/static_root/assets/browser-ZfiFIS4C.js.br +0 -0
  424. codex/static_root/assets/browser-ZfiFIS4C.js.gz +0 -0
  425. codex/static_root/assets/browser-zaYTL_k7.3272235689e3.css.br +0 -0
  426. codex/static_root/assets/browser-zaYTL_k7.3272235689e3.css.gz +0 -0
  427. codex/static_root/assets/browser-zaYTL_k7.css.br +0 -0
  428. codex/static_root/assets/browser-zaYTL_k7.css.gz +0 -0
  429. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
  430. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
  431. codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
  432. codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
  433. codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
  434. codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
  435. codex/static_root/assets/change-password-dialog-Da7BzDbN.7c3b835790fb.js +0 -1
  436. codex/static_root/assets/change-password-dialog-Da7BzDbN.7c3b835790fb.js.br +0 -0
  437. codex/static_root/assets/change-password-dialog-Da7BzDbN.7c3b835790fb.js.gz +0 -0
  438. codex/static_root/assets/change-password-dialog-Da7BzDbN.js +0 -1
  439. codex/static_root/assets/change-password-dialog-Da7BzDbN.js.br +0 -0
  440. codex/static_root/assets/change-password-dialog-Da7BzDbN.js.gz +0 -0
  441. codex/static_root/assets/confirm-dialog-DDql2Okm.9a10b377f5e0.js.br +0 -0
  442. codex/static_root/assets/confirm-dialog-DDql2Okm.9a10b377f5e0.js.gz +0 -0
  443. codex/static_root/assets/confirm-dialog-DDql2Okm.js.br +0 -0
  444. codex/static_root/assets/confirm-dialog-DDql2Okm.js.gz +0 -0
  445. codex/static_root/assets/datetime-column-LnDRlNck.a89a38ca43a8.js.br +0 -0
  446. codex/static_root/assets/datetime-column-LnDRlNck.a89a38ca43a8.js.gz +0 -0
  447. codex/static_root/assets/datetime-column-LnDRlNck.js.br +0 -0
  448. codex/static_root/assets/datetime-column-LnDRlNck.js.gz +0 -0
  449. codex/static_root/assets/filter-DA11td9b.26d25f4b88e1.js.br +0 -0
  450. codex/static_root/assets/filter-DA11td9b.26d25f4b88e1.js.gz +0 -0
  451. codex/static_root/assets/filter-DA11td9b.js.br +0 -0
  452. codex/static_root/assets/filter-DA11td9b.js.gz +0 -0
  453. codex/static_root/assets/flag-tab-D_VrM7zT.240eab4456f0.js.br +0 -0
  454. codex/static_root/assets/flag-tab-D_VrM7zT.240eab4456f0.js.gz +0 -0
  455. codex/static_root/assets/flag-tab-D_VrM7zT.js.br +0 -0
  456. codex/static_root/assets/flag-tab-D_VrM7zT.js.gz +0 -0
  457. codex/static_root/assets/group-tab-BQJUrW7O.7d77e88195ff.js.br +0 -0
  458. codex/static_root/assets/group-tab-BQJUrW7O.7d77e88195ff.js.gz +0 -0
  459. codex/static_root/assets/group-tab-BQJUrW7O.js.br +0 -0
  460. codex/static_root/assets/group-tab-BQJUrW7O.js.gz +0 -0
  461. codex/static_root/assets/http-error-DvYPiCry.fb306a0f546b.js.br +0 -0
  462. codex/static_root/assets/http-error-DvYPiCry.fb306a0f546b.js.gz +0 -0
  463. codex/static_root/assets/http-error-DvYPiCry.js.br +0 -0
  464. codex/static_root/assets/http-error-DvYPiCry.js.gz +0 -0
  465. codex/static_root/assets/library-tab-7vaPOM7P.88b38ad4ec71.js +0 -1
  466. codex/static_root/assets/library-tab-7vaPOM7P.88b38ad4ec71.js.br +0 -0
  467. codex/static_root/assets/library-tab-7vaPOM7P.88b38ad4ec71.js.gz +0 -0
  468. codex/static_root/assets/library-tab-7vaPOM7P.js +0 -1
  469. codex/static_root/assets/library-tab-7vaPOM7P.js.br +0 -0
  470. codex/static_root/assets/library-tab-7vaPOM7P.js.gz +0 -0
  471. codex/static_root/assets/library-tab-C_6VqUnE.513fe55019e7.css +0 -1
  472. codex/static_root/assets/library-tab-C_6VqUnE.513fe55019e7.css.br +0 -0
  473. codex/static_root/assets/library-tab-C_6VqUnE.513fe55019e7.css.gz +0 -0
  474. codex/static_root/assets/library-tab-C_6VqUnE.css +0 -1
  475. codex/static_root/assets/library-tab-C_6VqUnE.css.br +0 -0
  476. codex/static_root/assets/library-tab-C_6VqUnE.css.gz +0 -0
  477. codex/static_root/assets/main-Dki8E4GN.aa194275f982.js +0 -6
  478. codex/static_root/assets/main-Dki8E4GN.aa194275f982.js.br +0 -0
  479. codex/static_root/assets/main-Dki8E4GN.aa194275f982.js.gz +0 -0
  480. codex/static_root/assets/main-Dki8E4GN.js +0 -6
  481. codex/static_root/assets/main-Dki8E4GN.js.br +0 -0
  482. codex/static_root/assets/main-Dki8E4GN.js.gz +0 -0
  483. codex/static_root/assets/main-Ztp855PW.css.br +0 -0
  484. codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
  485. codex/static_root/assets/pagination-toolbar-0xaXKIHc.acec3ef3a951.css.br +0 -0
  486. codex/static_root/assets/pagination-toolbar-0xaXKIHc.acec3ef3a951.css.gz +0 -0
  487. codex/static_root/assets/pagination-toolbar-0xaXKIHc.css.br +0 -0
  488. codex/static_root/assets/pagination-toolbar-0xaXKIHc.css.gz +0 -0
  489. codex/static_root/assets/pagination-toolbar-DDK-oY2X.b1a32f09b1dc.js +0 -1
  490. codex/static_root/assets/pagination-toolbar-DDK-oY2X.b1a32f09b1dc.js.br +0 -0
  491. codex/static_root/assets/pagination-toolbar-DDK-oY2X.b1a32f09b1dc.js.gz +0 -0
  492. codex/static_root/assets/pagination-toolbar-DDK-oY2X.js +0 -1
  493. codex/static_root/assets/pagination-toolbar-DDK-oY2X.js.br +0 -0
  494. codex/static_root/assets/pagination-toolbar-DDK-oY2X.js.gz +0 -0
  495. codex/static_root/assets/pdf-doc-C3OfGN5a.54692f4bda33.js.br +0 -0
  496. codex/static_root/assets/pdf-doc-C3OfGN5a.54692f4bda33.js.gz +0 -0
  497. codex/static_root/assets/pdf-doc-C3OfGN5a.js.br +0 -0
  498. codex/static_root/assets/pdf-doc-C3OfGN5a.js.gz +0 -0
  499. codex/static_root/assets/reader-Do0jgF5k.aae2010130b4.css.br +0 -0
  500. codex/static_root/assets/reader-Do0jgF5k.css.br +0 -0
  501. codex/static_root/assets/reader-Dr7IAvkA.5349d84214d8.js.br +0 -0
  502. codex/static_root/assets/reader-Dr7IAvkA.5349d84214d8.js.gz +0 -0
  503. codex/static_root/assets/reader-Dr7IAvkA.js.br +0 -0
  504. codex/static_root/assets/reader-Dr7IAvkA.js.gz +0 -0
  505. codex/static_root/assets/relation-chips-xtLhd8kQ.2db5427bf360.js +0 -1
  506. codex/static_root/assets/relation-chips-xtLhd8kQ.2db5427bf360.js.br +0 -0
  507. codex/static_root/assets/relation-chips-xtLhd8kQ.2db5427bf360.js.gz +0 -0
  508. codex/static_root/assets/relation-chips-xtLhd8kQ.js +0 -1
  509. codex/static_root/assets/relation-chips-xtLhd8kQ.js.br +0 -0
  510. codex/static_root/assets/relation-chips-xtLhd8kQ.js.gz +0 -0
  511. codex/static_root/assets/settings-drawer-IqskIoNa.d76e15190dc3.js +0 -2
  512. codex/static_root/assets/settings-drawer-IqskIoNa.d76e15190dc3.js.br +0 -0
  513. codex/static_root/assets/settings-drawer-IqskIoNa.d76e15190dc3.js.gz +0 -0
  514. codex/static_root/assets/settings-drawer-IqskIoNa.js +0 -2
  515. codex/static_root/assets/settings-drawer-IqskIoNa.js.br +0 -0
  516. codex/static_root/assets/settings-drawer-IqskIoNa.js.gz +0 -0
  517. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
  518. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  519. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  520. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
  521. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  522. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  523. codex/static_root/assets/stats-tab-BoCyyFVC.7db4689bd9e3.js.br +0 -0
  524. codex/static_root/assets/stats-tab-BoCyyFVC.7db4689bd9e3.js.gz +0 -0
  525. codex/static_root/assets/stats-tab-BoCyyFVC.js.br +0 -0
  526. codex/static_root/assets/stats-tab-BoCyyFVC.js.gz +0 -0
  527. codex/static_root/assets/task-tab-DrDBVQJ2.b9031c4f7ea8.js.br +0 -0
  528. codex/static_root/assets/task-tab-DrDBVQJ2.b9031c4f7ea8.js.gz +0 -0
  529. codex/static_root/assets/task-tab-DrDBVQJ2.js.br +0 -0
  530. codex/static_root/assets/task-tab-DrDBVQJ2.js.gz +0 -0
  531. codex/static_root/assets/unauthorized-D87B0TXf.85c5665072a2.css.br +0 -0
  532. codex/static_root/assets/unauthorized-D87B0TXf.85c5665072a2.css.gz +0 -0
  533. codex/static_root/assets/unauthorized-D87B0TXf.css.br +0 -0
  534. codex/static_root/assets/unauthorized-D87B0TXf.css.gz +0 -0
  535. codex/static_root/assets/unauthorized-DrrtXMYF.f39bd5105e4d.js.br +0 -0
  536. codex/static_root/assets/unauthorized-DrrtXMYF.f39bd5105e4d.js.gz +0 -0
  537. codex/static_root/assets/unauthorized-DrrtXMYF.js.br +0 -0
  538. codex/static_root/assets/unauthorized-DrrtXMYF.js.gz +0 -0
  539. codex/static_root/assets/user-tab-BPTm1S-a.1ba9fb20134c.js.br +0 -0
  540. codex/static_root/assets/user-tab-BPTm1S-a.1ba9fb20134c.js.gz +0 -0
  541. codex/static_root/assets/user-tab-BPTm1S-a.js.br +0 -0
  542. codex/static_root/assets/user-tab-BPTm1S-a.js.gz +0 -0
  543. codex/static_root/manifest.18955e51373f.json +0 -611
  544. codex/static_root/manifest.18955e51373f.json.br +0 -0
  545. codex/static_root/manifest.18955e51373f.json.gz +0 -0
  546. {codex-1.6.15.dist-info → codex-1.6.17.dist-info}/LICENSE +0 -0
  547. {codex-1.6.15.dist-info → codex-1.6.17.dist-info}/WHEEL +0 -0
  548. {codex-1.6.15.dist-info → codex-1.6.17.dist-info}/entry_points.txt +0 -0
@@ -30,21 +30,23 @@ class CronThread(NamedThread):
30
30
  self._task_times = ()
31
31
  super().__init__(*args, name=self.__class__.__name__, daemon=True, **kwargs)
32
32
 
33
- def _update_task_times(self):
33
+ def _create_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
- task_tuple = tuple(sorted(task_times.items()))
40
-
41
- # There's always at least the janitor job
42
- self._next_time = task_tuple[0][0]
43
- self._task_times = task_tuple
39
+ self._task_times = tuple(sorted(task_times.items()))
44
40
 
45
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
45
+
46
+ next_time = self._task_times[0][0]
46
47
  now = django_timezone.now()
47
- delta = self._next_time - now
48
+ delta = next_time - now
49
+ self.log.debug(f"Next scheduled job at {next_time} in {delta}.")
48
50
  return max(0, int(delta.total_seconds()))
49
51
 
50
52
  def _run_expired_jobs(self):
@@ -53,6 +55,7 @@ class CronThread(NamedThread):
53
55
  if dttm < now:
54
56
  self.librarian_queue.put(task_class())
55
57
  else:
58
+ # Times are always ordered so stop checking at the first future job.
56
59
  break
57
60
 
58
61
  def run(self):
@@ -62,13 +65,13 @@ class CronThread(NamedThread):
62
65
  with self._cond:
63
66
  while not self._stop_event.is_set():
64
67
  self._run_expired_jobs()
65
- self._update_task_times()
68
+ self._create_task_times()
69
+ sleep(2) # try to fix double jobs
66
70
  timeout = self._get_timeout()
67
- self.log.debug(f"Next scheduled job at {self._next_time}.")
68
71
  self._cond.wait(timeout=timeout)
69
72
  if self._stop_event.is_set():
70
73
  break
71
- sleep(2) # fixes any time rounding problems
74
+ sleep(2) # fix time rounding problems
72
75
  except Exception:
73
76
  self.log.exception(f"In {self.__class__.__name__}")
74
77
  self.log.debug(f"Stopped {self.__class__.__name__}.")
@@ -1,5 +1,7 @@
1
1
  """Bulk import and move comics and folders."""
2
2
 
3
+ from pathlib import Path
4
+
3
5
  from django.utils import timezone
4
6
 
5
7
  from codex.librarian.importer.importer import ComicImporter
@@ -46,6 +48,15 @@ class ComicImporterThread(QueuedThread):
46
48
  )
47
49
  self._import(task)
48
50
 
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
+
49
60
  def _adopt_orphan_folders_for_library(self, library):
50
61
  """Adopt orphan folders for one library."""
51
62
  orphan_folder_paths = (
@@ -53,17 +64,20 @@ class ComicImporterThread(QueuedThread):
53
64
  .exclude(path=library.path)
54
65
  .values_list("path", flat=True)
55
66
  )
56
- if not orphan_folder_paths:
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:
57
78
  self.log.debug(f"No orphan folders in {library.path}")
58
79
  return False
59
80
 
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
-
67
81
  # An abridged import task.
68
82
  task = ImportDBDiffTask(
69
83
  library_id=library.pk,
@@ -89,15 +103,6 @@ class ComicImporterThread(QueuedThread):
89
103
 
90
104
  self.status_controller.finish_many((moved_status, status))
91
105
 
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
-
101
106
  def process_item(self, item):
102
107
  """Run the updater."""
103
108
  task = item
@@ -105,9 +110,9 @@ class ComicImporterThread(QueuedThread):
105
110
  self._import(task)
106
111
  elif isinstance(task, LazyImportComicsTask):
107
112
  self._lazy_import_metadata(task)
108
- elif isinstance(task, AdoptOrphanFoldersTask):
109
- self._adopt_orphan_folders()
110
113
  elif isinstance(task, UpdateGroupsTask):
111
114
  self._update_groups(task)
115
+ elif isinstance(task, AdoptOrphanFoldersTask):
116
+ self._adopt_orphan_folders()
112
117
  else:
113
118
  self.log.warning(f"Bad task sent to library updater {task}")
@@ -2,6 +2,7 @@
2
2
 
3
3
  from pathlib import Path
4
4
 
5
+ from bidict import bidict
5
6
  from django.db.models.functions import Now
6
7
 
7
8
  from codex.librarian.importer.aggregate import AggregateMetadataImporter
@@ -23,20 +24,42 @@ from codex.status import Status
23
24
  class MovedImporter(AggregateMetadataImporter):
24
25
  """Methods for moving comics and folders."""
25
26
 
26
- def _bulk_comics_moved(self):
27
- """Move comcis."""
28
- num_files_moved = len(self.task.files_moved)
29
- if not num_files_moved:
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:
30
31
  return
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)
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:
36
+ 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)
38
42
 
39
- # Update Comics
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."""
40
63
  comics = Comic.objects.filter(
41
64
  library=self.library, path__in=self.task.files_moved.keys()
42
65
  ).only("pk", "path", PARENT_FOLDER, FOLDERS_FIELD)
@@ -44,29 +67,29 @@ class MovedImporter(AggregateMetadataImporter):
44
67
  folder_m2m_links = {}
45
68
  updated_comics = []
46
69
  for comic in comics.iterator():
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}")
70
+ self._prepare_moved_comic(comic, folder_m2m_links, updated_comics)
62
71
  self.task.files_moved = {}
72
+ self.log.debug(f"Prepared {len(updated_comics)} for move...")
73
+ return updated_comics, folder_m2m_links
63
74
 
64
- Comic.objects.bulk_update(updated_comics, MOVED_BULK_COMIC_UPDATE_FIELDS)
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)
65
82
 
66
- # Update m2m field
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
+ Comic.objects.bulk_update(updated_comics, MOVED_BULK_COMIC_UPDATE_FIELDS)
67
89
  if folder_m2m_links:
68
90
  self.bulk_fix_comic_m2m_field(FOLDERS_FIELD, folder_m2m_links, status)
69
- count = len(comics)
91
+
92
+ count = len(updated_comics)
70
93
  if count:
71
94
  self.log.info(f"Moved {count} comics.")
72
95
 
@@ -140,60 +163,32 @@ class MovedImporter(AggregateMetadataImporter):
140
163
  self.changed += count
141
164
  self.status_controller.finish(status)
142
165
 
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)
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")
158
174
  )
159
- self.bulk_folders_create(create_folder_paths, status)
160
175
 
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")
176
+ # TODO what if a folder with that path already exists in the db?
185
177
 
178
+ new_paths = set()
186
179
  update_folders = []
187
180
  for folder in folders_to_move.iterator():
188
- new_path = self.task.dirs_moved[folder.path]
181
+ new_path = dirs_moved[folder.path]
182
+ new_paths.add(new_path)
189
183
  folder.name = Path(new_path).name
190
184
  folder.path = new_path
191
185
  parent_path_str = str(Path(new_path).parent)
192
- folder.parent_folder = dest_parent_folders.get(parent_path_str)
186
+ folder.parent_folder = dest_parent_folders_map.get(parent_path_str)
193
187
  folder.presave()
194
188
  folder.updated_at = Now()
195
189
  update_folders.append(folder)
196
- self.task.dirs_moved = {}
190
+
191
+ # delete_folders = Folder.objects.filter(library=self.library, path__in=new_paths)
197
192
 
198
193
  update_folders = sorted(update_folders, key=lambda x: len(Path(x.path).parts))
199
194
 
@@ -202,6 +197,112 @@ class MovedImporter(AggregateMetadataImporter):
202
197
  if count:
203
198
  self.log.info(f"Moved {count} folders.")
204
199
  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
+
205
306
  self.status_controller.finish(status)
206
307
 
207
308
  def _bulk_folders_modified(self):
@@ -233,6 +334,8 @@ class MovedImporter(AggregateMetadataImporter):
233
334
 
234
335
  def move_and_modify_dirs(self):
235
336
  """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.
236
339
  self.bulk_folders_moved()
237
340
  self._bulk_comics_moved()
238
341
  self._bulk_covers_moved()
@@ -544,19 +544,22 @@ 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
- status.add_complete(
548
- self._query_missing_simple_models(
549
- names,
550
- fk_data,
551
- status,
552
- )
547
+ count = self._query_missing_simple_models(
548
+ names,
549
+ fk_data,
550
+ status,
553
551
  )
552
+ status.add_complete(count)
554
553
  self.status_controller.update(status, notify=False)
555
554
 
556
555
  ###########
557
556
  # FOLDERS #
558
557
  ###########
559
- def query_missing_folder_paths(self, comic_paths, status):
558
+ def query_missing_folder_paths(
559
+ self,
560
+ comic_paths,
561
+ status,
562
+ ):
560
563
  """Find missing folder paths."""
561
564
  # Get the proposed folder_paths
562
565
  library_path = Path(self.library.path)
@@ -575,15 +578,18 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
575
578
  status,
576
579
  )
577
580
  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."""
578
586
  if FK_CREATE not in self.metadata:
579
587
  self.metadata[FK_CREATE] = {}
580
588
  if FKC_FOLDER_PATHS not in self.metadata[FK_CREATE]:
581
589
  self.metadata[FK_CREATE][FKC_FOLDER_PATHS] = set()
582
590
  self.metadata[FK_CREATE][FKC_FOLDER_PATHS] |= create_folder_paths
583
- count = len(create_folder_paths)
584
- if count:
591
+ if count := len(create_folder_paths):
585
592
  self.log.info(f"Prepared {count} new Folders.")
586
- return count
587
593
 
588
594
  def _get_create_fks_totals(self):
589
595
  fkc = self.metadata[FK_CREATE]
@@ -634,7 +640,7 @@ class QueryForeignKeysImporter(QueryCustomCoversImporter):
634
640
  status,
635
641
  )
636
642
 
637
- self.query_missing_folder_paths(
643
+ self._add_missing_folder_paths(
638
644
  self.metadata[FKS].pop(COMIC_PATHS, ()), status
639
645
  )
640
646
 
@@ -27,7 +27,7 @@ class UpdateMixin(WorkerBaseMixin):
27
27
  eau = AdminFlag.objects.only("on").get(
28
28
  key=AdminFlag.FlagChoices.AUTO_UPDATE.value
29
29
  )
30
- if not eau.on or not is_outdated(PACKAGE_NAME):
30
+ if not eau.on or not is_outdated(PACKAGE_NAME, self.log):
31
31
  self.log.info("Codex is up to date.")
32
32
  return
33
33
 
@@ -21,9 +21,9 @@ class SnapshotDiffData:
21
21
  class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
22
22
  """Custom Diff allows inode only changes to be 'modified'."""
23
23
 
24
- def _get_inode(self, directory, full_path):
24
+ def _get_inode(self, snapshot, full_path):
25
25
  """Get inode optionally ignoring device stats."""
26
- result = directory.inode(full_path)
26
+ result = snapshot.inode(full_path)
27
27
  if self._ignore_device:
28
28
  result = result[0]
29
29
  return result
@@ -33,7 +33,7 @@ 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 mtimze and size are equal.
36
+ """Return if the mtime and size are equal.
37
37
 
38
38
  For old paths in the ref and new paths in the snapshot.
39
39
  """
@@ -48,25 +48,27 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
48
48
  if self._inode_only_modified:
49
49
  data.modified.add(path)
50
50
  else:
51
+ # Add these to created and deleted. But...
52
+ # The _find_moved_paths() inode tracker should
53
+ # sort them out.
54
+ # This is the same as in upstream dirsnapshot.py
51
55
  data.created.add(path)
52
56
  data.deleted.add(path)
53
57
 
54
58
  def _find_moved_paths(self, data):
55
- """Find moved paths."""
56
- for path in tuple(data.deleted):
57
- inode = data.ref.inode(path)
58
- new_path = data.snapshot.path(inode)
59
- if new_path:
59
+ """Find moved paths in deleted and created."""
60
+ for old_path in tuple(data.deleted):
61
+ inode = self._get_inode(data.ref, old_path)
62
+ if new_path := data.snapshot.path(inode):
60
63
  # file is not deleted but moved
61
- data.deleted.remove(path)
62
- data.moved.add((path, new_path))
64
+ data.deleted.remove(old_path)
65
+ data.moved.add((old_path, new_path))
63
66
 
64
- for path in tuple(data.created):
65
- inode = data.snapshot.inode(path)
66
- old_path = data.ref.path(inode)
67
- if old_path:
68
- data.created.remove(path)
69
- data.moved.add((old_path, path))
67
+ for new_path in tuple(data.created):
68
+ inode = self._get_inode(data.snapshot, new_path)
69
+ if old_path := data.ref.path(inode):
70
+ data.created.remove(new_path)
71
+ data.moved.add((old_path, new_path))
70
72
 
71
73
  def _find_modified_paths(self, data):
72
74
  """Find modified paths."""
@@ -80,7 +82,7 @@ class CodexDirectorySnapshotDiff(DirectorySnapshotDiff):
80
82
  # Check moved paths for modificiations.
81
83
  for old_path, new_path in data.moved:
82
84
  if not self._is_stats_equal(data, old_path, new_path):
83
- data.modified.add(old_path)
85
+ data.modified.add(new_path)
84
86
 
85
87
  def __init__( # C901, PLR0912
86
88
  self, ref, snapshot, ignore_device=False, inode_only_modified=False
codex/startup.py CHANGED
@@ -87,9 +87,7 @@ def init_admin_flags():
87
87
  _delete_orphans(AdminFlag, "key", AdminFlag.FlagChoices.values)
88
88
 
89
89
  for key in AdminFlag.FlagChoices.values:
90
- defaults = {}
91
- if key in AdminFlag.FALSE_DEFAULTS:
92
- defaults["on"] = False
90
+ defaults = {"key": key, "on": key not in AdminFlag.FALSE_DEFAULTS}
93
91
  flag, created = AdminFlag.objects.get_or_create(defaults=defaults, key=key)
94
92
  if created:
95
93
  title = CHOICES["admin"]["adminFlags"][flag.key]
@@ -1 +1 @@
1
- import{m as g,V as t}from"./VCheckboxBtn-xs0GjiCS.js";import{a as A,u as I,V as l}from"./VSelectionControl-Vy5G2zUU.js";import{F as B,am as U,P as D,au as R,G as q,h as G,S as M,cD as N,T as u,aq as r}from"./admin-drawer-panel-oz6FwrsQ.js";const S=B({...A(),...U(g(),["inline"])},"VCheckbox"),w=D()({name:"VCheckbox",inheritAttrs:!1,props:S(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(e,c){let{attrs:d,slots:a}=c;const s=R(e,"modelValue"),{isFocused:n,focus:i,blur:m}=I(e),V=q(),p=G(()=>e.id||`checkbox-${V}`);return M(()=>{const[b,f]=N(d),v=l.filterProps(e),k=t.filterProps(e);return u(l,r({class:["v-checkbox",e.class]},b,v,{modelValue:s.value,"onUpdate:modelValue":o=>s.value=o,id:p.value,focused:n.value,style:e.style}),{...a,default:o=>{let{id:h,messagesId:x,isDisabled:P,isReadonly:C,isValid:y}=o;return u(t,r(k,{id:h.value,"aria-describedby":x.value,disabled:P.value,readonly:C.value},f,{error:y.value===!1,modelValue:s.value,"onUpdate:modelValue":F=>s.value=F,onFocus:i,onBlur:m}),a)}})}),{}}});export{w as V};
1
+ import{m as g,V as t}from"./VCheckboxBtn-BaQC30AI.js";import{a as A,u as I,V as l}from"./VSelectionControl-FmTy_2ch.js";import{F as B,am as U,P as D,au as R,G as q,h as G,S as M,cD as N,T as u,aq as r}from"./admin-drawer-panel-Dmnn03CQ.js";const S=B({...A(),...U(g(),["inline"])},"VCheckbox"),w=D()({name:"VCheckbox",inheritAttrs:!1,props:S(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(e,c){let{attrs:d,slots:a}=c;const s=R(e,"modelValue"),{isFocused:n,focus:i,blur:m}=I(e),V=q(),p=G(()=>e.id||`checkbox-${V}`);return M(()=>{const[b,f]=N(d),v=l.filterProps(e),k=t.filterProps(e);return u(l,r({class:["v-checkbox",e.class]},b,v,{modelValue:s.value,"onUpdate:modelValue":o=>s.value=o,id:p.value,focused:n.value,style:e.style}),{...a,default:o=>{let{id:h,messagesId:x,isDisabled:P,isReadonly:C,isValid:y}=o;return u(t,r(k,{id:h.value,"aria-describedby":x.value,disabled:P.value,readonly:C.value},f,{error:y.value===!1,modelValue:s.value,"onUpdate:modelValue":F=>s.value=F,onFocus:i,onBlur:m}),a)}})}),{}}});export{w as V};
@@ -1 +1 @@
1
- import{m as g,V as t}from"./VCheckboxBtn-xs0GjiCS.js";import{a as A,u as I,V as l}from"./VSelectionControl-Vy5G2zUU.js";import{F as B,am as U,P as D,au as R,G as q,h as G,S as M,cD as N,T as u,aq as r}from"./admin-drawer-panel-oz6FwrsQ.js";const S=B({...A(),...U(g(),["inline"])},"VCheckbox"),w=D()({name:"VCheckbox",inheritAttrs:!1,props:S(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(e,c){let{attrs:d,slots:a}=c;const s=R(e,"modelValue"),{isFocused:n,focus:i,blur:m}=I(e),V=q(),p=G(()=>e.id||`checkbox-${V}`);return M(()=>{const[b,f]=N(d),v=l.filterProps(e),k=t.filterProps(e);return u(l,r({class:["v-checkbox",e.class]},b,v,{modelValue:s.value,"onUpdate:modelValue":o=>s.value=o,id:p.value,focused:n.value,style:e.style}),{...a,default:o=>{let{id:h,messagesId:x,isDisabled:P,isReadonly:C,isValid:y}=o;return u(t,r(k,{id:h.value,"aria-describedby":x.value,disabled:P.value,readonly:C.value},f,{error:y.value===!1,modelValue:s.value,"onUpdate:modelValue":F=>s.value=F,onFocus:i,onBlur:m}),a)}})}),{}}});export{w as V};
1
+ import{m as g,V as t}from"./VCheckboxBtn-BaQC30AI.js";import{a as A,u as I,V as l}from"./VSelectionControl-FmTy_2ch.js";import{F as B,am as U,P as D,au as R,G as q,h as G,S as M,cD as N,T as u,aq as r}from"./admin-drawer-panel-Dmnn03CQ.js";const S=B({...A(),...U(g(),["inline"])},"VCheckbox"),w=D()({name:"VCheckbox",inheritAttrs:!1,props:S(),emits:{"update:modelValue":e=>!0,"update:focused":e=>!0},setup(e,c){let{attrs:d,slots:a}=c;const s=R(e,"modelValue"),{isFocused:n,focus:i,blur:m}=I(e),V=q(),p=G(()=>e.id||`checkbox-${V}`);return M(()=>{const[b,f]=N(d),v=l.filterProps(e),k=t.filterProps(e);return u(l,r({class:["v-checkbox",e.class]},b,v,{modelValue:s.value,"onUpdate:modelValue":o=>s.value=o,id:p.value,focused:n.value,style:e.style}),{...a,default:o=>{let{id:h,messagesId:x,isDisabled:P,isReadonly:C,isValid:y}=o;return u(t,r(k,{id:h.value,"aria-describedby":x.value,disabled:P.value,readonly:C.value},f,{error:y.value===!1,modelValue:s.value,"onUpdate:modelValue":F=>s.value=F,onFocus:i,onBlur:m}),a)}})}),{}}});export{w as V};
@@ -1 +1 @@
1
- import{c as f,d as o}from"./VSelectionControl-Vy5G2zUU.js";import{F as V,bd as v,P as I,au as c,h as l,S as h,am as k,T as x,aq as b}from"./admin-drawer-panel-oz6FwrsQ.js";const C=V({indeterminate:Boolean,indeterminateIcon:{type:v,default:"$checkboxIndeterminate"},...f({falseIcon:"$checkboxOff",trueIcon:"$checkboxOn"})},"VCheckboxBtn"),B=I()({name:"VCheckboxBtn",props:C(),emits:{"update:modelValue":e=>!0,"update:indeterminate":e=>!0},setup(e,r){let{slots:u}=r;const t=c(e,"indeterminate"),n=c(e,"modelValue");function s(a){t.value&&(t.value=!1)}const d=l(()=>t.value?e.indeterminateIcon:e.falseIcon),i=l(()=>t.value?e.indeterminateIcon:e.trueIcon);return h(()=>{const a=k(o.filterProps(e),["modelValue"]);return x(o,b(a,{modelValue:n.value,"onUpdate:modelValue":[m=>n.value=m,s],class:["v-checkbox-btn",e.class],style:e.style,type:"checkbox",falseIcon:d.value,trueIcon:i.value,"aria-checked":t.value?"mixed":void 0}),u)}),{}}});export{B as V,C as m};
1
+ import{c as f,d as o}from"./VSelectionControl-FmTy_2ch.js";import{F as V,bd as v,P as I,au as c,h as l,S as h,am as k,T as x,aq as b}from"./admin-drawer-panel-Dmnn03CQ.js";const C=V({indeterminate:Boolean,indeterminateIcon:{type:v,default:"$checkboxIndeterminate"},...f({falseIcon:"$checkboxOff",trueIcon:"$checkboxOn"})},"VCheckboxBtn"),B=I()({name:"VCheckboxBtn",props:C(),emits:{"update:modelValue":e=>!0,"update:indeterminate":e=>!0},setup(e,r){let{slots:u}=r;const t=c(e,"indeterminate"),n=c(e,"modelValue");function s(a){t.value&&(t.value=!1)}const d=l(()=>t.value?e.indeterminateIcon:e.falseIcon),i=l(()=>t.value?e.indeterminateIcon:e.trueIcon);return h(()=>{const a=k(o.filterProps(e),["modelValue"]);return x(o,b(a,{modelValue:n.value,"onUpdate:modelValue":[m=>n.value=m,s],class:["v-checkbox-btn",e.class],style:e.style,type:"checkbox",falseIcon:d.value,trueIcon:i.value,"aria-checked":t.value?"mixed":void 0}),u)}),{}}});export{B as V,C as m};
@@ -1 +1 @@
1
- import{c as f,d as o}from"./VSelectionControl-Vy5G2zUU.js";import{F as V,bd as v,P as I,au as c,h as l,S as h,am as k,T as x,aq as b}from"./admin-drawer-panel-oz6FwrsQ.js";const C=V({indeterminate:Boolean,indeterminateIcon:{type:v,default:"$checkboxIndeterminate"},...f({falseIcon:"$checkboxOff",trueIcon:"$checkboxOn"})},"VCheckboxBtn"),B=I()({name:"VCheckboxBtn",props:C(),emits:{"update:modelValue":e=>!0,"update:indeterminate":e=>!0},setup(e,r){let{slots:u}=r;const t=c(e,"indeterminate"),n=c(e,"modelValue");function s(a){t.value&&(t.value=!1)}const d=l(()=>t.value?e.indeterminateIcon:e.falseIcon),i=l(()=>t.value?e.indeterminateIcon:e.trueIcon);return h(()=>{const a=k(o.filterProps(e),["modelValue"]);return x(o,b(a,{modelValue:n.value,"onUpdate:modelValue":[m=>n.value=m,s],class:["v-checkbox-btn",e.class],style:e.style,type:"checkbox",falseIcon:d.value,trueIcon:i.value,"aria-checked":t.value?"mixed":void 0}),u)}),{}}});export{B as V,C as m};
1
+ import{c as f,d as o}from"./VSelectionControl-FmTy_2ch.js";import{F as V,bd as v,P as I,au as c,h as l,S as h,am as k,T as x,aq as b}from"./admin-drawer-panel-Dmnn03CQ.js";const C=V({indeterminate:Boolean,indeterminateIcon:{type:v,default:"$checkboxIndeterminate"},...f({falseIcon:"$checkboxOff",trueIcon:"$checkboxOn"})},"VCheckboxBtn"),B=I()({name:"VCheckboxBtn",props:C(),emits:{"update:modelValue":e=>!0,"update:indeterminate":e=>!0},setup(e,r){let{slots:u}=r;const t=c(e,"indeterminate"),n=c(e,"modelValue");function s(a){t.value&&(t.value=!1)}const d=l(()=>t.value?e.indeterminateIcon:e.falseIcon),i=l(()=>t.value?e.indeterminateIcon:e.trueIcon);return h(()=>{const a=k(o.filterProps(e),["modelValue"]);return x(o,b(a,{modelValue:n.value,"onUpdate:modelValue":[m=>n.value=m,s],class:["v-checkbox-btn",e.class],style:e.style,type:"checkbox",falseIcon:d.value,trueIcon:i.value,"aria-checked":t.value?"mixed":void 0}),u)}),{}}});export{B as V,C as m};