codex 1.8.5__py3-none-any.whl → 1.8.8__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 (542) hide show
  1. codex/choices/admin.py +1 -0
  2. codex/librarian/scribe/importer/const.py +7 -3
  3. codex/librarian/scribe/importer/create/comics.py +0 -3
  4. codex/librarian/scribe/importer/delete/comics.py +7 -4
  5. codex/librarian/scribe/importer/delete/folders.py +2 -1
  6. codex/librarian/scribe/importer/read/aggregate_path.py +53 -13
  7. codex/librarian/scribe/importer/read/extract.py +16 -13
  8. codex/librarian/scribe/importer/search/prepare.py +0 -7
  9. codex/librarian/scribe/importer/search/sync_m2m.py +2 -1
  10. codex/librarian/scribe/importer/search/update.py +18 -81
  11. codex/librarian/scribe/scribed.py +2 -13
  12. codex/librarian/scribe/{importer/search → search}/const.py +1 -13
  13. codex/librarian/scribe/search/prepare.py +162 -0
  14. codex/librarian/scribe/search/remove.py +22 -2
  15. codex/librarian/scribe/search/sync.py +88 -179
  16. codex/librarian/status_controller.py +25 -27
  17. codex/librarian/watchdog/event_batcherd.py +1 -1
  18. codex/migrations/0035_fts_optmize.py +136 -0
  19. codex/models/comic.py +0 -16
  20. codex/models/functions.py +3 -1
  21. codex/serializers/admin/tasks.py +1 -1
  22. codex/settings/__init__.py +26 -11
  23. codex/static_root/assets/VCheckbox-MwE_xq11.b2e2086608d9.js +1 -0
  24. codex/static_root/assets/VCheckbox-MwE_xq11.b2e2086608d9.js.br +0 -0
  25. codex/static_root/assets/VCheckbox-MwE_xq11.b2e2086608d9.js.gz +0 -0
  26. codex/static_root/assets/VCheckbox-MwE_xq11.js +1 -0
  27. codex/static_root/assets/VCheckbox-MwE_xq11.js.br +0 -0
  28. codex/static_root/assets/VCheckbox-MwE_xq11.js.gz +0 -0
  29. codex/static_root/assets/{VCheckboxBtn-BE6yrQ80.4a42f2c068f5.js → VCheckboxBtn-HUjl2ti_.f3e7b85503c6.js} +1 -1
  30. codex/static_root/assets/VCheckboxBtn-HUjl2ti_.f3e7b85503c6.js.br +0 -0
  31. codex/static_root/assets/VCheckboxBtn-HUjl2ti_.f3e7b85503c6.js.gz +0 -0
  32. codex/static_root/assets/{VCheckboxBtn-BE6yrQ80.js → VCheckboxBtn-HUjl2ti_.js} +1 -1
  33. codex/static_root/assets/VCheckboxBtn-HUjl2ti_.js.br +0 -0
  34. codex/static_root/assets/VCheckboxBtn-HUjl2ti_.js.gz +0 -0
  35. codex/static_root/assets/VCombobox-C1Osy24P.73168f28d07b.js +1 -0
  36. codex/static_root/assets/VCombobox-C1Osy24P.73168f28d07b.js.br +0 -0
  37. codex/static_root/assets/VCombobox-C1Osy24P.73168f28d07b.js.gz +0 -0
  38. codex/static_root/assets/VCombobox-C1Osy24P.js +1 -0
  39. codex/static_root/assets/VCombobox-C1Osy24P.js.br +0 -0
  40. codex/static_root/assets/VCombobox-C1Osy24P.js.gz +0 -0
  41. codex/static_root/assets/VDialog-c2XXqNJH.a4156ca20133.js +1 -0
  42. codex/static_root/assets/VDialog-c2XXqNJH.a4156ca20133.js.br +0 -0
  43. codex/static_root/assets/VDialog-c2XXqNJH.a4156ca20133.js.gz +0 -0
  44. codex/static_root/assets/VDialog-c2XXqNJH.js +1 -0
  45. codex/static_root/assets/VDialog-c2XXqNJH.js.br +0 -0
  46. codex/static_root/assets/VDialog-c2XXqNJH.js.gz +0 -0
  47. codex/static_root/assets/{VDivider-BEpvozL4.c9fd95940cb5.js → VDivider-C06z3Tn-.d5e3ed421a68.js} +1 -1
  48. codex/static_root/assets/VDivider-C06z3Tn-.d5e3ed421a68.js.br +0 -0
  49. codex/static_root/assets/VDivider-C06z3Tn-.d5e3ed421a68.js.gz +0 -0
  50. codex/static_root/assets/{VDivider-BEpvozL4.js → VDivider-C06z3Tn-.js} +1 -1
  51. codex/static_root/assets/VDivider-C06z3Tn-.js.br +0 -0
  52. codex/static_root/assets/VDivider-C06z3Tn-.js.gz +0 -0
  53. codex/static_root/assets/{VExpansionPanels-BzyHQUL4.7e54cedf021c.js → VExpansionPanels-B8gzbonk.3532c6d08817.js} +1 -1
  54. codex/static_root/assets/VExpansionPanels-B8gzbonk.3532c6d08817.js.br +0 -0
  55. codex/static_root/assets/VExpansionPanels-B8gzbonk.3532c6d08817.js.gz +0 -0
  56. codex/static_root/assets/{VExpansionPanels-BzyHQUL4.js → VExpansionPanels-B8gzbonk.js} +1 -1
  57. codex/static_root/assets/VExpansionPanels-B8gzbonk.js.br +0 -0
  58. codex/static_root/assets/VExpansionPanels-B8gzbonk.js.gz +0 -0
  59. codex/static_root/assets/{VForm-CmVlbmJK.887eb47d3534.js → VForm-Chf0FU2x.3e8425bff3d0.js} +1 -1
  60. codex/static_root/assets/VForm-Chf0FU2x.3e8425bff3d0.js.br +0 -0
  61. codex/static_root/assets/VForm-Chf0FU2x.3e8425bff3d0.js.gz +0 -0
  62. codex/static_root/assets/{VForm-CmVlbmJK.js → VForm-Chf0FU2x.js} +1 -1
  63. codex/static_root/assets/VForm-Chf0FU2x.js.br +0 -0
  64. codex/static_root/assets/VForm-Chf0FU2x.js.gz +0 -0
  65. codex/static_root/assets/{VRadioGroup-DWzKkq9W.daaabd353889.js → VRadioGroup-CHteIFqx.23cb51ee43e1.js} +1 -1
  66. codex/static_root/assets/VRadioGroup-CHteIFqx.23cb51ee43e1.js.br +0 -0
  67. codex/static_root/assets/VRadioGroup-CHteIFqx.23cb51ee43e1.js.gz +0 -0
  68. codex/static_root/assets/{VRadioGroup-DWzKkq9W.js → VRadioGroup-CHteIFqx.js} +1 -1
  69. codex/static_root/assets/VRadioGroup-CHteIFqx.js.br +0 -0
  70. codex/static_root/assets/VRadioGroup-CHteIFqx.js.gz +0 -0
  71. codex/static_root/assets/VSelect-BqS1Prvd.36bbebae4f0d.js +1 -0
  72. codex/static_root/assets/VSelect-BqS1Prvd.36bbebae4f0d.js.br +0 -0
  73. codex/static_root/assets/VSelect-BqS1Prvd.36bbebae4f0d.js.gz +0 -0
  74. codex/static_root/assets/VSelect-BqS1Prvd.js +1 -0
  75. codex/static_root/assets/VSelect-BqS1Prvd.js.br +0 -0
  76. codex/static_root/assets/VSelect-BqS1Prvd.js.gz +0 -0
  77. codex/static_root/assets/VSelectionControl-BRWozKsT.d10531c1a243.js +1 -0
  78. codex/static_root/assets/VSelectionControl-BRWozKsT.d10531c1a243.js.br +0 -0
  79. codex/static_root/assets/VSelectionControl-BRWozKsT.d10531c1a243.js.gz +0 -0
  80. codex/static_root/assets/VSelectionControl-BRWozKsT.js +1 -0
  81. codex/static_root/assets/VSelectionControl-BRWozKsT.js.br +0 -0
  82. codex/static_root/assets/VSelectionControl-BRWozKsT.js.gz +0 -0
  83. codex/static_root/assets/VTable-BT6w_ifJ.ee6b710fb398.js +1 -0
  84. codex/static_root/assets/VTable-BT6w_ifJ.ee6b710fb398.js.br +0 -0
  85. codex/static_root/assets/VTable-BT6w_ifJ.ee6b710fb398.js.gz +0 -0
  86. codex/static_root/assets/VTable-BT6w_ifJ.js +1 -0
  87. codex/static_root/assets/VTable-BT6w_ifJ.js.br +0 -0
  88. codex/static_root/assets/VTable-BT6w_ifJ.js.gz +0 -0
  89. codex/static_root/assets/VTable-BsI6FsWH.css +1 -0
  90. codex/static_root/assets/VTable-BsI6FsWH.css.br +0 -0
  91. codex/static_root/assets/VTable-BsI6FsWH.css.gz +0 -0
  92. codex/static_root/assets/VTable-BsI6FsWH.d4c54d44a5ab.css +1 -0
  93. codex/static_root/assets/VTable-BsI6FsWH.d4c54d44a5ab.css.br +0 -0
  94. codex/static_root/assets/VTable-BsI6FsWH.d4c54d44a5ab.css.gz +0 -0
  95. codex/static_root/assets/VWindowItem-QQWUVqMX.c22e707ac99c.css +1 -0
  96. codex/static_root/assets/VWindowItem-QQWUVqMX.c22e707ac99c.css.br +5 -0
  97. codex/static_root/assets/VWindowItem-QQWUVqMX.c22e707ac99c.css.gz +0 -0
  98. codex/static_root/assets/VWindowItem-QQWUVqMX.css +1 -0
  99. codex/static_root/assets/VWindowItem-QQWUVqMX.css.br +5 -0
  100. codex/static_root/assets/VWindowItem-QQWUVqMX.css.gz +0 -0
  101. codex/static_root/assets/{VWindowItem-DBU8ce0X.4bbcffa75b9a.js → VWindowItem-sfdsG_YK.ee17f6723988.js} +1 -1
  102. codex/static_root/assets/VWindowItem-sfdsG_YK.ee17f6723988.js.br +0 -0
  103. codex/static_root/assets/VWindowItem-sfdsG_YK.ee17f6723988.js.gz +0 -0
  104. codex/static_root/assets/{VWindowItem-DBU8ce0X.js → VWindowItem-sfdsG_YK.js} +1 -1
  105. codex/static_root/assets/VWindowItem-sfdsG_YK.js.br +0 -0
  106. codex/static_root/assets/VWindowItem-sfdsG_YK.js.gz +0 -0
  107. codex/static_root/assets/{admin-CfqMNEyt.bebc609836d0.js → admin-DuEKXO1g.5c6bfe166705.js} +1 -1
  108. codex/static_root/assets/admin-DuEKXO1g.5c6bfe166705.js.br +0 -0
  109. codex/static_root/assets/admin-DuEKXO1g.5c6bfe166705.js.gz +0 -0
  110. codex/static_root/assets/{admin-CfqMNEyt.js → admin-DuEKXO1g.js} +1 -1
  111. codex/static_root/assets/admin-DuEKXO1g.js.br +0 -0
  112. codex/static_root/assets/admin-DuEKXO1g.js.gz +0 -0
  113. codex/static_root/assets/admin-menu-DBce75Uy.be45a0a53c1a.js +1 -0
  114. codex/static_root/assets/admin-menu-DBce75Uy.be45a0a53c1a.js.br +0 -0
  115. codex/static_root/assets/admin-menu-DBce75Uy.be45a0a53c1a.js.gz +0 -0
  116. codex/static_root/assets/admin-menu-DBce75Uy.js +1 -0
  117. codex/static_root/assets/admin-menu-DBce75Uy.js.br +0 -0
  118. codex/static_root/assets/admin-menu-DBce75Uy.js.gz +0 -0
  119. codex/static_root/assets/{admin-Bt81XU3H.ca36b1a1b799.js → admin-pnPIiLQj.b791e7869cf5.js} +1 -1
  120. codex/static_root/assets/admin-pnPIiLQj.b791e7869cf5.js.br +0 -0
  121. codex/static_root/assets/admin-pnPIiLQj.b791e7869cf5.js.gz +0 -0
  122. codex/static_root/assets/{admin-Bt81XU3H.js → admin-pnPIiLQj.js} +1 -1
  123. codex/static_root/assets/admin-pnPIiLQj.js.br +0 -0
  124. codex/static_root/assets/admin-pnPIiLQj.js.gz +0 -0
  125. codex/static_root/assets/{admin-settings-button-progress-BPQGNinn.53e05963f557.js → admin-settings-button-progress-C4VmW80I.c8d9033b8bc5.js} +1 -1
  126. codex/static_root/assets/admin-settings-button-progress-C4VmW80I.c8d9033b8bc5.js.br +0 -0
  127. codex/static_root/assets/admin-settings-button-progress-C4VmW80I.c8d9033b8bc5.js.gz +0 -0
  128. codex/static_root/assets/{admin-settings-button-progress-BPQGNinn.js → admin-settings-button-progress-C4VmW80I.js} +1 -1
  129. codex/static_root/assets/admin-settings-button-progress-C4VmW80I.js.br +0 -0
  130. codex/static_root/assets/admin-settings-button-progress-C4VmW80I.js.gz +0 -0
  131. codex/static_root/assets/browser-DOuRKBxy.c9a72e4f9498.js +1 -0
  132. codex/static_root/assets/browser-DOuRKBxy.c9a72e4f9498.js.br +0 -0
  133. codex/static_root/assets/browser-DOuRKBxy.c9a72e4f9498.js.gz +0 -0
  134. codex/static_root/assets/browser-DOuRKBxy.js +1 -0
  135. codex/static_root/assets/browser-DOuRKBxy.js.br +0 -0
  136. codex/static_root/assets/browser-DOuRKBxy.js.gz +0 -0
  137. codex/static_root/assets/{browser-Cdi7U5ku.bab9fccf0326.css → browser-TRzEFz_k.css} +1 -1
  138. codex/static_root/assets/browser-TRzEFz_k.css.br +0 -0
  139. codex/static_root/assets/browser-TRzEFz_k.css.gz +0 -0
  140. codex/static_root/assets/{browser-Cdi7U5ku.css → browser-TRzEFz_k.e8ae750c4a41.css} +1 -1
  141. codex/static_root/assets/browser-TRzEFz_k.e8ae750c4a41.css.br +0 -0
  142. codex/static_root/assets/browser-TRzEFz_k.e8ae750c4a41.css.gz +0 -0
  143. codex/static_root/assets/change-password-dialog-B4tB0bVX.5b476ac6a873.js +1 -0
  144. codex/static_root/assets/change-password-dialog-B4tB0bVX.5b476ac6a873.js.br +0 -0
  145. codex/static_root/assets/change-password-dialog-B4tB0bVX.5b476ac6a873.js.gz +0 -0
  146. codex/static_root/assets/change-password-dialog-B4tB0bVX.js +1 -0
  147. codex/static_root/assets/change-password-dialog-B4tB0bVX.js.br +0 -0
  148. codex/static_root/assets/change-password-dialog-B4tB0bVX.js.gz +0 -0
  149. codex/static_root/assets/{confirm-dialog-CgSU27R5.0127ce62d24c.js → confirm-dialog-C0DHEcnB.e2d9968f6fea.js} +1 -1
  150. codex/static_root/assets/confirm-dialog-C0DHEcnB.e2d9968f6fea.js.br +0 -0
  151. codex/static_root/assets/confirm-dialog-C0DHEcnB.e2d9968f6fea.js.gz +0 -0
  152. codex/static_root/assets/{confirm-dialog-CgSU27R5.js → confirm-dialog-C0DHEcnB.js} +1 -1
  153. codex/static_root/assets/confirm-dialog-C0DHEcnB.js.br +0 -0
  154. codex/static_root/assets/confirm-dialog-C0DHEcnB.js.gz +0 -0
  155. codex/static_root/assets/{datetime-column-Dw9zxZLR.cd8791cda91b.js → datetime-column-j_CRGEuQ.cc499deb52f7.js} +1 -1
  156. codex/static_root/assets/datetime-column-j_CRGEuQ.cc499deb52f7.js.br +4 -0
  157. codex/static_root/assets/datetime-column-j_CRGEuQ.cc499deb52f7.js.gz +0 -0
  158. codex/static_root/assets/{datetime-column-Dw9zxZLR.js → datetime-column-j_CRGEuQ.js} +1 -1
  159. codex/static_root/assets/datetime-column-j_CRGEuQ.js.br +4 -0
  160. codex/static_root/assets/datetime-column-j_CRGEuQ.js.gz +0 -0
  161. codex/static_root/assets/{filter-BNTCrH8l.c00298a8fccf.js → filter-Baea_DNH.985ef541d6af.js} +1 -1
  162. codex/static_root/assets/filter-Baea_DNH.985ef541d6af.js.br +0 -0
  163. codex/static_root/assets/filter-Baea_DNH.985ef541d6af.js.gz +0 -0
  164. codex/static_root/assets/{filter-BNTCrH8l.js → filter-Baea_DNH.js} +1 -1
  165. codex/static_root/assets/filter-Baea_DNH.js.br +0 -0
  166. codex/static_root/assets/filter-Baea_DNH.js.gz +0 -0
  167. codex/static_root/assets/{flag-tab-Xpq_xAF5.3f14de61afcc.js → flag-tab-DefdNLmr.0cf5a73c1c8c.js} +1 -1
  168. codex/static_root/assets/flag-tab-DefdNLmr.0cf5a73c1c8c.js.br +0 -0
  169. codex/static_root/assets/flag-tab-DefdNLmr.0cf5a73c1c8c.js.gz +0 -0
  170. codex/static_root/assets/{flag-tab-Xpq_xAF5.js → flag-tab-DefdNLmr.js} +1 -1
  171. codex/static_root/assets/flag-tab-DefdNLmr.js.br +0 -0
  172. codex/static_root/assets/flag-tab-DefdNLmr.js.gz +0 -0
  173. codex/static_root/assets/forwardRefs-Bvy6478W.409df679a82f.js +1 -0
  174. codex/static_root/assets/forwardRefs-Bvy6478W.409df679a82f.js.br +0 -0
  175. codex/static_root/assets/forwardRefs-Bvy6478W.409df679a82f.js.gz +0 -0
  176. codex/static_root/assets/forwardRefs-Bvy6478W.js +1 -0
  177. codex/static_root/assets/forwardRefs-Bvy6478W.js.br +0 -0
  178. codex/static_root/assets/forwardRefs-Bvy6478W.js.gz +0 -0
  179. codex/static_root/assets/{forwardRefs-B_sUWZn6.5114f206ca9c.css → forwardRefs-C3Z4qL85.48fb131ece85.css} +1 -1
  180. codex/static_root/assets/forwardRefs-C3Z4qL85.48fb131ece85.css.br +0 -0
  181. codex/static_root/assets/forwardRefs-C3Z4qL85.48fb131ece85.css.gz +0 -0
  182. codex/static_root/assets/{forwardRefs-B_sUWZn6.css → forwardRefs-C3Z4qL85.css} +1 -1
  183. codex/static_root/assets/forwardRefs-C3Z4qL85.css.br +0 -0
  184. codex/static_root/assets/forwardRefs-C3Z4qL85.css.gz +0 -0
  185. codex/static_root/assets/{group-tab-E_R0glEI.82e1d92be747.js → group-tab-C71MBY7x.483482eaaf8c.js} +1 -1
  186. codex/static_root/assets/group-tab-C71MBY7x.483482eaaf8c.js.br +0 -0
  187. codex/static_root/assets/group-tab-C71MBY7x.483482eaaf8c.js.gz +0 -0
  188. codex/static_root/assets/{group-tab-E_R0glEI.js → group-tab-C71MBY7x.js} +1 -1
  189. codex/static_root/assets/group-tab-C71MBY7x.js.br +0 -0
  190. codex/static_root/assets/group-tab-C71MBY7x.js.gz +0 -0
  191. codex/static_root/assets/http-error-mPrHO762.70c41cde5ee1.js +1 -0
  192. codex/static_root/assets/http-error-mPrHO762.70c41cde5ee1.js.br +0 -0
  193. codex/static_root/assets/http-error-mPrHO762.70c41cde5ee1.js.gz +0 -0
  194. codex/static_root/assets/http-error-mPrHO762.js +1 -0
  195. codex/static_root/assets/http-error-mPrHO762.js.br +0 -0
  196. codex/static_root/assets/http-error-mPrHO762.js.gz +0 -0
  197. codex/static_root/assets/{index-j9jeRHe0.294b614eaea1.js → index-Bxqss4CY.5f797cedb404.js} +1 -1
  198. codex/static_root/assets/index-Bxqss4CY.5f797cedb404.js.br +0 -0
  199. codex/static_root/assets/index-Bxqss4CY.5f797cedb404.js.gz +0 -0
  200. codex/static_root/assets/{index-j9jeRHe0.js → index-Bxqss4CY.js} +1 -1
  201. codex/static_root/assets/index-Bxqss4CY.js.br +0 -0
  202. codex/static_root/assets/index-Bxqss4CY.js.gz +0 -0
  203. codex/static_root/assets/library-tab-DG2bhQ_A.e831e11418e2.js +1 -0
  204. codex/static_root/assets/library-tab-DG2bhQ_A.e831e11418e2.js.br +0 -0
  205. codex/static_root/assets/library-tab-DG2bhQ_A.e831e11418e2.js.gz +0 -0
  206. codex/static_root/assets/library-tab-DG2bhQ_A.js +1 -0
  207. codex/static_root/assets/library-tab-DG2bhQ_A.js.br +0 -0
  208. codex/static_root/assets/library-tab-DG2bhQ_A.js.gz +0 -0
  209. codex/static_root/assets/main-RxUHaWll.d7d671319f8d.js +39 -0
  210. codex/static_root/assets/main-RxUHaWll.d7d671319f8d.js.br +0 -0
  211. codex/static_root/assets/main-RxUHaWll.d7d671319f8d.js.gz +0 -0
  212. codex/static_root/assets/main-RxUHaWll.js +39 -0
  213. codex/static_root/assets/main-RxUHaWll.js.br +0 -0
  214. codex/static_root/assets/main-RxUHaWll.js.gz +0 -0
  215. codex/static_root/assets/pager-full-pdf-DYbA0-qc.b66019cafd05.js +1 -0
  216. codex/static_root/assets/pager-full-pdf-DYbA0-qc.b66019cafd05.js.br +0 -0
  217. codex/static_root/assets/pager-full-pdf-DYbA0-qc.b66019cafd05.js.gz +0 -0
  218. codex/static_root/assets/pager-full-pdf-DYbA0-qc.js +1 -0
  219. codex/static_root/assets/pager-full-pdf-DYbA0-qc.js.br +0 -0
  220. codex/static_root/assets/pager-full-pdf-DYbA0-qc.js.gz +0 -0
  221. codex/static_root/assets/pagination-toolbar-ChW-jj1Q.0cf6985b630e.js +1 -0
  222. codex/static_root/assets/pagination-toolbar-ChW-jj1Q.0cf6985b630e.js.br +0 -0
  223. codex/static_root/assets/pagination-toolbar-ChW-jj1Q.0cf6985b630e.js.gz +0 -0
  224. codex/static_root/assets/pagination-toolbar-ChW-jj1Q.js +1 -0
  225. codex/static_root/assets/pagination-toolbar-ChW-jj1Q.js.br +0 -0
  226. codex/static_root/assets/pagination-toolbar-ChW-jj1Q.js.gz +0 -0
  227. codex/static_root/assets/{pagination-toolbar-741OafNw.4f9155469274.css → pagination-toolbar-rUdD4-Qi.3a224c7806a5.css} +1 -1
  228. codex/static_root/assets/pagination-toolbar-rUdD4-Qi.3a224c7806a5.css.br +0 -0
  229. codex/static_root/assets/pagination-toolbar-rUdD4-Qi.3a224c7806a5.css.gz +0 -0
  230. codex/static_root/assets/{pagination-toolbar-741OafNw.css → pagination-toolbar-rUdD4-Qi.css} +1 -1
  231. codex/static_root/assets/pagination-toolbar-rUdD4-Qi.css.br +0 -0
  232. codex/static_root/assets/pagination-toolbar-rUdD4-Qi.css.gz +0 -0
  233. codex/static_root/assets/{pdf-doc-XAg2nqnS.5a33f918a28a.js → pdf-doc-CfiAAYT8.c4fb1945d1c5.js} +1 -1
  234. codex/static_root/assets/pdf-doc-CfiAAYT8.c4fb1945d1c5.js.br +0 -0
  235. codex/static_root/assets/{pdf-doc-XAg2nqnS.5a33f918a28a.js.gz → pdf-doc-CfiAAYT8.c4fb1945d1c5.js.gz} +0 -0
  236. codex/static_root/assets/{pdf-doc-XAg2nqnS.js → pdf-doc-CfiAAYT8.js} +1 -1
  237. codex/static_root/assets/pdf-doc-CfiAAYT8.js.br +0 -0
  238. codex/static_root/assets/{pdf-doc-XAg2nqnS.js.gz → pdf-doc-CfiAAYT8.js.gz} +0 -0
  239. codex/static_root/assets/reader-BNwsMjad.78e4ad034141.js +2 -0
  240. codex/static_root/assets/reader-BNwsMjad.78e4ad034141.js.br +0 -0
  241. codex/static_root/assets/reader-BNwsMjad.78e4ad034141.js.gz +0 -0
  242. codex/static_root/assets/reader-BNwsMjad.js +2 -0
  243. codex/static_root/assets/reader-BNwsMjad.js.br +0 -0
  244. codex/static_root/assets/reader-BNwsMjad.js.gz +0 -0
  245. codex/static_root/assets/relation-chips-Cfk3FVE5.eadbc380a353.js +1 -0
  246. codex/static_root/assets/relation-chips-Cfk3FVE5.eadbc380a353.js.br +0 -0
  247. codex/static_root/assets/relation-chips-Cfk3FVE5.eadbc380a353.js.gz +0 -0
  248. codex/static_root/assets/relation-chips-Cfk3FVE5.js +1 -0
  249. codex/static_root/assets/relation-chips-Cfk3FVE5.js.br +0 -0
  250. codex/static_root/assets/relation-chips-Cfk3FVE5.js.gz +0 -0
  251. codex/static_root/assets/{relation-chips-CcFgPQOM.0b468249b62e.css → relation-chips-CpiT_4k-.b72d78f512e6.css} +1 -1
  252. codex/static_root/assets/relation-chips-CpiT_4k-.b72d78f512e6.css.br +0 -0
  253. codex/static_root/assets/relation-chips-CpiT_4k-.b72d78f512e6.css.gz +0 -0
  254. codex/static_root/assets/{relation-chips-CcFgPQOM.css → relation-chips-CpiT_4k-.css} +1 -1
  255. codex/static_root/assets/relation-chips-CpiT_4k-.css.br +0 -0
  256. codex/static_root/assets/relation-chips-CpiT_4k-.css.gz +0 -0
  257. codex/static_root/assets/{settings-drawer-CpS_aX4Z.9d4d9533d033.css → settings-drawer-B-pwZRSy.8fc8b237bb52.css} +1 -1
  258. codex/static_root/assets/settings-drawer-B-pwZRSy.8fc8b237bb52.css.br +0 -0
  259. codex/static_root/assets/settings-drawer-B-pwZRSy.8fc8b237bb52.css.gz +0 -0
  260. codex/static_root/assets/{settings-drawer-CpS_aX4Z.css → settings-drawer-B-pwZRSy.css} +1 -1
  261. codex/static_root/assets/settings-drawer-B-pwZRSy.css.br +0 -0
  262. codex/static_root/assets/settings-drawer-B-pwZRSy.css.gz +0 -0
  263. codex/static_root/assets/settings-drawer-BvHMmEFL.6b6d9bf0e2ad.js +2 -0
  264. codex/static_root/assets/settings-drawer-BvHMmEFL.6b6d9bf0e2ad.js.br +0 -0
  265. codex/static_root/assets/settings-drawer-BvHMmEFL.6b6d9bf0e2ad.js.gz +0 -0
  266. codex/static_root/assets/settings-drawer-BvHMmEFL.js +2 -0
  267. codex/static_root/assets/settings-drawer-BvHMmEFL.js.br +0 -0
  268. codex/static_root/assets/settings-drawer-BvHMmEFL.js.gz +0 -0
  269. codex/static_root/assets/{ssrBoot-CoIsTnhp.ab2c4669478d.js → ssrBoot-BBuVfjys.ee6df7695a63.js} +1 -1
  270. codex/static_root/assets/ssrBoot-BBuVfjys.ee6df7695a63.js.br +0 -0
  271. codex/static_root/assets/ssrBoot-BBuVfjys.ee6df7695a63.js.gz +0 -0
  272. codex/static_root/assets/{ssrBoot-CoIsTnhp.js → ssrBoot-BBuVfjys.js} +1 -1
  273. codex/static_root/assets/ssrBoot-BBuVfjys.js.br +0 -0
  274. codex/static_root/assets/ssrBoot-BBuVfjys.js.gz +0 -0
  275. codex/static_root/assets/{stats-tab-Bncgetpc.94f90b6a111b.js → stats-tab-BW725H_T.61e34db4cc39.js} +1 -1
  276. codex/static_root/assets/stats-tab-BW725H_T.61e34db4cc39.js.br +0 -0
  277. codex/static_root/assets/stats-tab-BW725H_T.61e34db4cc39.js.gz +0 -0
  278. codex/static_root/assets/{stats-tab-Bncgetpc.js → stats-tab-BW725H_T.js} +1 -1
  279. codex/static_root/assets/stats-tab-BW725H_T.js.br +0 -0
  280. codex/static_root/assets/stats-tab-BW725H_T.js.gz +0 -0
  281. codex/static_root/assets/task-tab-P3x-M3d5.59e0479f2f1e.js +1 -0
  282. codex/static_root/assets/task-tab-P3x-M3d5.59e0479f2f1e.js.br +0 -0
  283. codex/static_root/assets/task-tab-P3x-M3d5.59e0479f2f1e.js.gz +0 -0
  284. codex/static_root/assets/task-tab-P3x-M3d5.js +1 -0
  285. codex/static_root/assets/task-tab-P3x-M3d5.js.br +0 -0
  286. codex/static_root/assets/task-tab-P3x-M3d5.js.gz +0 -0
  287. codex/static_root/assets/{unauthorized-IXAh-oLS.be484d21eb2a.js → unauthorized-CC6sHmUB.394dffd78457.js} +1 -1
  288. codex/static_root/assets/unauthorized-CC6sHmUB.394dffd78457.js.br +0 -0
  289. codex/static_root/assets/unauthorized-CC6sHmUB.394dffd78457.js.gz +0 -0
  290. codex/static_root/assets/{unauthorized-IXAh-oLS.js → unauthorized-CC6sHmUB.js} +1 -1
  291. codex/static_root/assets/unauthorized-CC6sHmUB.js.br +0 -0
  292. codex/static_root/assets/unauthorized-CC6sHmUB.js.gz +0 -0
  293. codex/static_root/assets/user-tab-OaDKeq2f.ac3d682c35f1.js +1 -0
  294. codex/static_root/assets/user-tab-OaDKeq2f.ac3d682c35f1.js.br +0 -0
  295. codex/static_root/assets/user-tab-OaDKeq2f.ac3d682c35f1.js.gz +0 -0
  296. codex/static_root/assets/user-tab-OaDKeq2f.js +1 -0
  297. codex/static_root/assets/user-tab-OaDKeq2f.js.br +0 -0
  298. codex/static_root/assets/user-tab-OaDKeq2f.js.gz +0 -0
  299. codex/static_root/{manifest.92b1b461eee2.json → manifest.d51267c3637d.json} +310 -310
  300. codex/static_root/manifest.d51267c3637d.json.br +0 -0
  301. codex/static_root/manifest.d51267c3637d.json.gz +0 -0
  302. codex/static_root/manifest.json +310 -310
  303. codex/static_root/manifest.json.br +0 -0
  304. codex/static_root/manifest.json.gz +0 -0
  305. codex/static_root/staticfiles.json +1 -1
  306. codex/views/browser/filters/filter.py +1 -1
  307. codex/views/browser/filters/search/field/column.py +3 -3
  308. codex/views/browser/filters/search/parse.py +15 -10
  309. {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/METADATA +5 -5
  310. {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/RECORD +314 -312
  311. codex/static_root/assets/VCheckbox-DxJUMufz.9a175cfa72c8.js +0 -1
  312. codex/static_root/assets/VCheckbox-DxJUMufz.9a175cfa72c8.js.br +0 -0
  313. codex/static_root/assets/VCheckbox-DxJUMufz.9a175cfa72c8.js.gz +0 -0
  314. codex/static_root/assets/VCheckbox-DxJUMufz.js +0 -1
  315. codex/static_root/assets/VCheckbox-DxJUMufz.js.br +0 -0
  316. codex/static_root/assets/VCheckbox-DxJUMufz.js.gz +0 -0
  317. codex/static_root/assets/VCheckboxBtn-BE6yrQ80.4a42f2c068f5.js.br +0 -0
  318. codex/static_root/assets/VCheckboxBtn-BE6yrQ80.4a42f2c068f5.js.gz +0 -0
  319. codex/static_root/assets/VCheckboxBtn-BE6yrQ80.js.br +0 -0
  320. codex/static_root/assets/VCheckboxBtn-BE6yrQ80.js.gz +0 -0
  321. codex/static_root/assets/VCombobox-Bfyp0szs.b51553d29c8c.js +0 -1
  322. codex/static_root/assets/VCombobox-Bfyp0szs.b51553d29c8c.js.br +0 -0
  323. codex/static_root/assets/VCombobox-Bfyp0szs.b51553d29c8c.js.gz +0 -0
  324. codex/static_root/assets/VCombobox-Bfyp0szs.js +0 -1
  325. codex/static_root/assets/VCombobox-Bfyp0szs.js.br +0 -0
  326. codex/static_root/assets/VCombobox-Bfyp0szs.js.gz +0 -0
  327. codex/static_root/assets/VDialog-Cy6wFjb9.9253a3f54f22.js +0 -1
  328. codex/static_root/assets/VDialog-Cy6wFjb9.9253a3f54f22.js.br +0 -0
  329. codex/static_root/assets/VDialog-Cy6wFjb9.9253a3f54f22.js.gz +0 -0
  330. codex/static_root/assets/VDialog-Cy6wFjb9.js +0 -1
  331. codex/static_root/assets/VDialog-Cy6wFjb9.js.br +0 -0
  332. codex/static_root/assets/VDialog-Cy6wFjb9.js.gz +0 -0
  333. codex/static_root/assets/VDivider-BEpvozL4.c9fd95940cb5.js.br +0 -0
  334. codex/static_root/assets/VDivider-BEpvozL4.c9fd95940cb5.js.gz +0 -0
  335. codex/static_root/assets/VDivider-BEpvozL4.js.br +0 -0
  336. codex/static_root/assets/VDivider-BEpvozL4.js.gz +0 -0
  337. codex/static_root/assets/VExpansionPanels-BzyHQUL4.7e54cedf021c.js.br +0 -0
  338. codex/static_root/assets/VExpansionPanels-BzyHQUL4.7e54cedf021c.js.gz +0 -0
  339. codex/static_root/assets/VExpansionPanels-BzyHQUL4.js.br +0 -0
  340. codex/static_root/assets/VExpansionPanels-BzyHQUL4.js.gz +0 -0
  341. codex/static_root/assets/VForm-CmVlbmJK.887eb47d3534.js.br +0 -0
  342. codex/static_root/assets/VForm-CmVlbmJK.887eb47d3534.js.gz +0 -0
  343. codex/static_root/assets/VForm-CmVlbmJK.js.br +0 -0
  344. codex/static_root/assets/VForm-CmVlbmJK.js.gz +0 -0
  345. codex/static_root/assets/VRadioGroup-DWzKkq9W.daaabd353889.js.br +0 -0
  346. codex/static_root/assets/VRadioGroup-DWzKkq9W.daaabd353889.js.gz +0 -0
  347. codex/static_root/assets/VRadioGroup-DWzKkq9W.js.br +0 -0
  348. codex/static_root/assets/VRadioGroup-DWzKkq9W.js.gz +0 -0
  349. codex/static_root/assets/VSelect-Cqjdw0PV.ab01020c5ef4.js +0 -1
  350. codex/static_root/assets/VSelect-Cqjdw0PV.ab01020c5ef4.js.br +0 -0
  351. codex/static_root/assets/VSelect-Cqjdw0PV.ab01020c5ef4.js.gz +0 -0
  352. codex/static_root/assets/VSelect-Cqjdw0PV.js +0 -1
  353. codex/static_root/assets/VSelect-Cqjdw0PV.js.br +0 -0
  354. codex/static_root/assets/VSelect-Cqjdw0PV.js.gz +0 -0
  355. codex/static_root/assets/VSelectionControl-Dk52iIPb.fe2504c50db4.js +0 -1
  356. codex/static_root/assets/VSelectionControl-Dk52iIPb.fe2504c50db4.js.br +0 -0
  357. codex/static_root/assets/VSelectionControl-Dk52iIPb.fe2504c50db4.js.gz +0 -0
  358. codex/static_root/assets/VSelectionControl-Dk52iIPb.js +0 -1
  359. codex/static_root/assets/VSelectionControl-Dk52iIPb.js.br +0 -0
  360. codex/static_root/assets/VSelectionControl-Dk52iIPb.js.gz +0 -0
  361. codex/static_root/assets/VTable-CQyv0wyO.1740239d481b.css +0 -1
  362. codex/static_root/assets/VTable-CQyv0wyO.1740239d481b.css.br +0 -0
  363. codex/static_root/assets/VTable-CQyv0wyO.1740239d481b.css.gz +0 -0
  364. codex/static_root/assets/VTable-CQyv0wyO.css +0 -1
  365. codex/static_root/assets/VTable-CQyv0wyO.css.br +0 -0
  366. codex/static_root/assets/VTable-CQyv0wyO.css.gz +0 -0
  367. codex/static_root/assets/VTable-Ch5ZSNsk.c38a2cb589fa.js +0 -1
  368. codex/static_root/assets/VTable-Ch5ZSNsk.c38a2cb589fa.js.br +0 -0
  369. codex/static_root/assets/VTable-Ch5ZSNsk.c38a2cb589fa.js.gz +0 -0
  370. codex/static_root/assets/VTable-Ch5ZSNsk.js +0 -1
  371. codex/static_root/assets/VTable-Ch5ZSNsk.js.br +0 -0
  372. codex/static_root/assets/VTable-Ch5ZSNsk.js.gz +0 -0
  373. codex/static_root/assets/VWindowItem-ChYLiXSE.a9c679a2977e.css +0 -1
  374. codex/static_root/assets/VWindowItem-ChYLiXSE.a9c679a2977e.css.br +0 -0
  375. codex/static_root/assets/VWindowItem-ChYLiXSE.a9c679a2977e.css.gz +0 -0
  376. codex/static_root/assets/VWindowItem-ChYLiXSE.css +0 -1
  377. codex/static_root/assets/VWindowItem-ChYLiXSE.css.br +0 -0
  378. codex/static_root/assets/VWindowItem-ChYLiXSE.css.gz +0 -0
  379. codex/static_root/assets/VWindowItem-DBU8ce0X.4bbcffa75b9a.js.br +0 -0
  380. codex/static_root/assets/VWindowItem-DBU8ce0X.4bbcffa75b9a.js.gz +0 -0
  381. codex/static_root/assets/VWindowItem-DBU8ce0X.js.br +0 -0
  382. codex/static_root/assets/VWindowItem-DBU8ce0X.js.gz +0 -0
  383. codex/static_root/assets/admin-Bt81XU3H.ca36b1a1b799.js.br +0 -0
  384. codex/static_root/assets/admin-Bt81XU3H.ca36b1a1b799.js.gz +0 -0
  385. codex/static_root/assets/admin-Bt81XU3H.js.br +0 -0
  386. codex/static_root/assets/admin-Bt81XU3H.js.gz +0 -0
  387. codex/static_root/assets/admin-CfqMNEyt.bebc609836d0.js.br +0 -0
  388. codex/static_root/assets/admin-CfqMNEyt.bebc609836d0.js.gz +0 -0
  389. codex/static_root/assets/admin-CfqMNEyt.js.br +0 -0
  390. codex/static_root/assets/admin-CfqMNEyt.js.gz +0 -0
  391. codex/static_root/assets/admin-menu-Db5b3nba.09d034934b88.js +0 -1
  392. codex/static_root/assets/admin-menu-Db5b3nba.09d034934b88.js.br +0 -0
  393. codex/static_root/assets/admin-menu-Db5b3nba.09d034934b88.js.gz +0 -0
  394. codex/static_root/assets/admin-menu-Db5b3nba.js +0 -1
  395. codex/static_root/assets/admin-menu-Db5b3nba.js.br +0 -0
  396. codex/static_root/assets/admin-menu-Db5b3nba.js.gz +0 -0
  397. codex/static_root/assets/admin-settings-button-progress-BPQGNinn.53e05963f557.js.br +0 -0
  398. codex/static_root/assets/admin-settings-button-progress-BPQGNinn.53e05963f557.js.gz +0 -0
  399. codex/static_root/assets/admin-settings-button-progress-BPQGNinn.js.br +0 -0
  400. codex/static_root/assets/admin-settings-button-progress-BPQGNinn.js.gz +0 -0
  401. codex/static_root/assets/browser-C19hTg9r.9b5d0eadd6d4.js +0 -1
  402. codex/static_root/assets/browser-C19hTg9r.9b5d0eadd6d4.js.br +0 -0
  403. codex/static_root/assets/browser-C19hTg9r.9b5d0eadd6d4.js.gz +0 -0
  404. codex/static_root/assets/browser-C19hTg9r.js +0 -1
  405. codex/static_root/assets/browser-C19hTg9r.js.br +0 -0
  406. codex/static_root/assets/browser-C19hTg9r.js.gz +0 -0
  407. codex/static_root/assets/browser-Cdi7U5ku.bab9fccf0326.css.br +0 -0
  408. codex/static_root/assets/browser-Cdi7U5ku.bab9fccf0326.css.gz +0 -0
  409. codex/static_root/assets/browser-Cdi7U5ku.css.br +0 -0
  410. codex/static_root/assets/browser-Cdi7U5ku.css.gz +0 -0
  411. codex/static_root/assets/change-password-dialog-DPAIPGJa.7c4c436e259e.js +0 -1
  412. codex/static_root/assets/change-password-dialog-DPAIPGJa.7c4c436e259e.js.br +0 -0
  413. codex/static_root/assets/change-password-dialog-DPAIPGJa.7c4c436e259e.js.gz +0 -0
  414. codex/static_root/assets/change-password-dialog-DPAIPGJa.js +0 -1
  415. codex/static_root/assets/change-password-dialog-DPAIPGJa.js.br +0 -0
  416. codex/static_root/assets/change-password-dialog-DPAIPGJa.js.gz +0 -0
  417. codex/static_root/assets/confirm-dialog-CgSU27R5.0127ce62d24c.js.br +0 -0
  418. codex/static_root/assets/confirm-dialog-CgSU27R5.0127ce62d24c.js.gz +0 -0
  419. codex/static_root/assets/confirm-dialog-CgSU27R5.js.br +0 -0
  420. codex/static_root/assets/confirm-dialog-CgSU27R5.js.gz +0 -0
  421. codex/static_root/assets/datetime-column-Dw9zxZLR.cd8791cda91b.js.br +0 -0
  422. codex/static_root/assets/datetime-column-Dw9zxZLR.cd8791cda91b.js.gz +0 -0
  423. codex/static_root/assets/datetime-column-Dw9zxZLR.js.br +0 -0
  424. codex/static_root/assets/datetime-column-Dw9zxZLR.js.gz +0 -0
  425. codex/static_root/assets/filter-BNTCrH8l.c00298a8fccf.js.br +0 -0
  426. codex/static_root/assets/filter-BNTCrH8l.c00298a8fccf.js.gz +0 -0
  427. codex/static_root/assets/filter-BNTCrH8l.js.br +0 -0
  428. codex/static_root/assets/filter-BNTCrH8l.js.gz +0 -0
  429. codex/static_root/assets/flag-tab-Xpq_xAF5.3f14de61afcc.js.br +0 -0
  430. codex/static_root/assets/flag-tab-Xpq_xAF5.3f14de61afcc.js.gz +0 -0
  431. codex/static_root/assets/flag-tab-Xpq_xAF5.js.br +0 -0
  432. codex/static_root/assets/flag-tab-Xpq_xAF5.js.gz +0 -0
  433. codex/static_root/assets/forwardRefs-B_sUWZn6.5114f206ca9c.css.br +0 -0
  434. codex/static_root/assets/forwardRefs-B_sUWZn6.5114f206ca9c.css.gz +0 -0
  435. codex/static_root/assets/forwardRefs-B_sUWZn6.css.br +0 -0
  436. codex/static_root/assets/forwardRefs-B_sUWZn6.css.gz +0 -0
  437. codex/static_root/assets/forwardRefs-fUake9mW.12c1018cd1e3.js +0 -1
  438. codex/static_root/assets/forwardRefs-fUake9mW.12c1018cd1e3.js.br +0 -0
  439. codex/static_root/assets/forwardRefs-fUake9mW.12c1018cd1e3.js.gz +0 -0
  440. codex/static_root/assets/forwardRefs-fUake9mW.js +0 -1
  441. codex/static_root/assets/forwardRefs-fUake9mW.js.br +0 -0
  442. codex/static_root/assets/forwardRefs-fUake9mW.js.gz +0 -0
  443. codex/static_root/assets/group-tab-E_R0glEI.82e1d92be747.js.br +0 -0
  444. codex/static_root/assets/group-tab-E_R0glEI.82e1d92be747.js.gz +0 -0
  445. codex/static_root/assets/group-tab-E_R0glEI.js.br +0 -0
  446. codex/static_root/assets/group-tab-E_R0glEI.js.gz +0 -0
  447. codex/static_root/assets/http-error-C6bKTzfv.168d047d5219.js +0 -1
  448. codex/static_root/assets/http-error-C6bKTzfv.168d047d5219.js.br +0 -5
  449. codex/static_root/assets/http-error-C6bKTzfv.168d047d5219.js.gz +0 -0
  450. codex/static_root/assets/http-error-C6bKTzfv.js +0 -1
  451. codex/static_root/assets/http-error-C6bKTzfv.js.br +0 -5
  452. codex/static_root/assets/http-error-C6bKTzfv.js.gz +0 -0
  453. codex/static_root/assets/index-j9jeRHe0.294b614eaea1.js.br +0 -0
  454. codex/static_root/assets/index-j9jeRHe0.294b614eaea1.js.gz +0 -0
  455. codex/static_root/assets/index-j9jeRHe0.js.br +0 -0
  456. codex/static_root/assets/index-j9jeRHe0.js.gz +0 -0
  457. codex/static_root/assets/library-tab-DPYjRIWp.58e434354594.js +0 -1
  458. codex/static_root/assets/library-tab-DPYjRIWp.58e434354594.js.br +0 -0
  459. codex/static_root/assets/library-tab-DPYjRIWp.58e434354594.js.gz +0 -0
  460. codex/static_root/assets/library-tab-DPYjRIWp.js +0 -1
  461. codex/static_root/assets/library-tab-DPYjRIWp.js.br +0 -0
  462. codex/static_root/assets/library-tab-DPYjRIWp.js.gz +0 -0
  463. codex/static_root/assets/main-Cnq2ldv_.1c0766500281.js +0 -39
  464. codex/static_root/assets/main-Cnq2ldv_.1c0766500281.js.br +0 -0
  465. codex/static_root/assets/main-Cnq2ldv_.1c0766500281.js.gz +0 -0
  466. codex/static_root/assets/main-Cnq2ldv_.js +0 -39
  467. codex/static_root/assets/main-Cnq2ldv_.js.br +0 -0
  468. codex/static_root/assets/main-Cnq2ldv_.js.gz +0 -0
  469. codex/static_root/assets/pager-full-pdf-CdHAL6P3.cb6bc541ca5c.js +0 -1
  470. codex/static_root/assets/pager-full-pdf-CdHAL6P3.cb6bc541ca5c.js.br +0 -0
  471. codex/static_root/assets/pager-full-pdf-CdHAL6P3.cb6bc541ca5c.js.gz +0 -0
  472. codex/static_root/assets/pager-full-pdf-CdHAL6P3.js +0 -1
  473. codex/static_root/assets/pager-full-pdf-CdHAL6P3.js.br +0 -0
  474. codex/static_root/assets/pager-full-pdf-CdHAL6P3.js.gz +0 -0
  475. codex/static_root/assets/pagination-toolbar-741OafNw.4f9155469274.css.br +0 -0
  476. codex/static_root/assets/pagination-toolbar-741OafNw.4f9155469274.css.gz +0 -0
  477. codex/static_root/assets/pagination-toolbar-741OafNw.css.br +0 -0
  478. codex/static_root/assets/pagination-toolbar-741OafNw.css.gz +0 -0
  479. codex/static_root/assets/pagination-toolbar-BFBKOWOc.3355103910ff.js +0 -1
  480. codex/static_root/assets/pagination-toolbar-BFBKOWOc.3355103910ff.js.br +0 -0
  481. codex/static_root/assets/pagination-toolbar-BFBKOWOc.3355103910ff.js.gz +0 -0
  482. codex/static_root/assets/pagination-toolbar-BFBKOWOc.js +0 -1
  483. codex/static_root/assets/pagination-toolbar-BFBKOWOc.js.br +0 -0
  484. codex/static_root/assets/pagination-toolbar-BFBKOWOc.js.gz +0 -0
  485. codex/static_root/assets/pdf-doc-XAg2nqnS.5a33f918a28a.js.br +0 -0
  486. codex/static_root/assets/pdf-doc-XAg2nqnS.js.br +0 -0
  487. codex/static_root/assets/reader-BZ4Dp-_5.ecd9d42077af.js +0 -2
  488. codex/static_root/assets/reader-BZ4Dp-_5.ecd9d42077af.js.br +0 -0
  489. codex/static_root/assets/reader-BZ4Dp-_5.ecd9d42077af.js.gz +0 -0
  490. codex/static_root/assets/reader-BZ4Dp-_5.js +0 -2
  491. codex/static_root/assets/reader-BZ4Dp-_5.js.br +0 -0
  492. codex/static_root/assets/reader-BZ4Dp-_5.js.gz +0 -0
  493. codex/static_root/assets/relation-chips-B_z9ixOK.8f144f1cf119.js +0 -1
  494. codex/static_root/assets/relation-chips-B_z9ixOK.8f144f1cf119.js.br +0 -0
  495. codex/static_root/assets/relation-chips-B_z9ixOK.8f144f1cf119.js.gz +0 -0
  496. codex/static_root/assets/relation-chips-B_z9ixOK.js +0 -1
  497. codex/static_root/assets/relation-chips-B_z9ixOK.js.br +0 -0
  498. codex/static_root/assets/relation-chips-B_z9ixOK.js.gz +0 -0
  499. codex/static_root/assets/relation-chips-CcFgPQOM.0b468249b62e.css.br +0 -0
  500. codex/static_root/assets/relation-chips-CcFgPQOM.0b468249b62e.css.gz +0 -0
  501. codex/static_root/assets/relation-chips-CcFgPQOM.css.br +0 -0
  502. codex/static_root/assets/relation-chips-CcFgPQOM.css.gz +0 -0
  503. codex/static_root/assets/settings-drawer-Cd3M0s-h.c5216209feb4.js +0 -2
  504. codex/static_root/assets/settings-drawer-Cd3M0s-h.c5216209feb4.js.br +0 -0
  505. codex/static_root/assets/settings-drawer-Cd3M0s-h.c5216209feb4.js.gz +0 -0
  506. codex/static_root/assets/settings-drawer-Cd3M0s-h.js +0 -2
  507. codex/static_root/assets/settings-drawer-Cd3M0s-h.js.br +0 -0
  508. codex/static_root/assets/settings-drawer-Cd3M0s-h.js.gz +0 -0
  509. codex/static_root/assets/settings-drawer-CpS_aX4Z.9d4d9533d033.css.br +0 -0
  510. codex/static_root/assets/settings-drawer-CpS_aX4Z.9d4d9533d033.css.gz +0 -0
  511. codex/static_root/assets/settings-drawer-CpS_aX4Z.css.br +0 -0
  512. codex/static_root/assets/settings-drawer-CpS_aX4Z.css.gz +0 -0
  513. codex/static_root/assets/ssrBoot-CoIsTnhp.ab2c4669478d.js.br +0 -0
  514. codex/static_root/assets/ssrBoot-CoIsTnhp.ab2c4669478d.js.gz +0 -0
  515. codex/static_root/assets/ssrBoot-CoIsTnhp.js.br +0 -0
  516. codex/static_root/assets/ssrBoot-CoIsTnhp.js.gz +0 -0
  517. codex/static_root/assets/stats-tab-Bncgetpc.94f90b6a111b.js.br +0 -0
  518. codex/static_root/assets/stats-tab-Bncgetpc.94f90b6a111b.js.gz +0 -0
  519. codex/static_root/assets/stats-tab-Bncgetpc.js.br +0 -0
  520. codex/static_root/assets/stats-tab-Bncgetpc.js.gz +0 -0
  521. codex/static_root/assets/task-tab-BWkw24-u.0e981a767773.js +0 -1
  522. codex/static_root/assets/task-tab-BWkw24-u.0e981a767773.js.br +0 -0
  523. codex/static_root/assets/task-tab-BWkw24-u.0e981a767773.js.gz +0 -0
  524. codex/static_root/assets/task-tab-BWkw24-u.js +0 -1
  525. codex/static_root/assets/task-tab-BWkw24-u.js.br +0 -0
  526. codex/static_root/assets/task-tab-BWkw24-u.js.gz +0 -0
  527. codex/static_root/assets/unauthorized-IXAh-oLS.be484d21eb2a.js.br +0 -0
  528. codex/static_root/assets/unauthorized-IXAh-oLS.be484d21eb2a.js.gz +0 -0
  529. codex/static_root/assets/unauthorized-IXAh-oLS.js.br +0 -0
  530. codex/static_root/assets/unauthorized-IXAh-oLS.js.gz +0 -0
  531. codex/static_root/assets/user-tab-CtQh6PJE.6b9a1d0e733a.js +0 -1
  532. codex/static_root/assets/user-tab-CtQh6PJE.6b9a1d0e733a.js.br +0 -0
  533. codex/static_root/assets/user-tab-CtQh6PJE.6b9a1d0e733a.js.gz +0 -0
  534. codex/static_root/assets/user-tab-CtQh6PJE.js +0 -1
  535. codex/static_root/assets/user-tab-CtQh6PJE.js.br +0 -0
  536. codex/static_root/assets/user-tab-CtQh6PJE.js.gz +0 -0
  537. codex/static_root/manifest.92b1b461eee2.json.br +0 -0
  538. codex/static_root/manifest.92b1b461eee2.json.gz +0 -0
  539. /codex/librarian/{watchdog/memory.py → memory.py} +0 -0
  540. {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/WHEEL +0 -0
  541. {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/entry_points.txt +0 -0
  542. {codex-1.8.5.dist-info → codex-1.8.8.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,13 @@
1
1
  """Search Index cleanup."""
2
2
 
3
+ from django.db.models import F, Max
4
+
3
5
  from codex.librarian.scribe.search.optimize import SearchIndexerOptimize
4
6
  from codex.librarian.scribe.search.status import (
5
7
  SearchIndexCleanStatus,
6
8
  SearchIndexClearStatus,
7
9
  )
8
- from codex.models.comic import ComicFTS
10
+ from codex.models.comic import Comic, ComicFTS
9
11
 
10
12
 
11
13
  class SearchIndexerRemove(SearchIndexerOptimize):
@@ -22,7 +24,9 @@ class SearchIndexerRemove(SearchIndexerOptimize):
22
24
  """Remove records not in the database from the index."""
23
25
  self.status_controller.start(status)
24
26
  self.log.debug("Finding stale records to remove...")
25
- delete_comicfts = ComicFTS.objects.filter(comic__isnull=True)
27
+ delete_comicfts = ComicFTS.objects.exclude(
28
+ comic_id__in=Comic.objects.only("pk")
29
+ )
26
30
  status.total = delete_comicfts.count()
27
31
  self.status_controller.update(status)
28
32
  if status.total:
@@ -42,3 +46,19 @@ class SearchIndexerRemove(SearchIndexerOptimize):
42
46
  finally:
43
47
  self.status_controller.finish(status)
44
48
  return count
49
+
50
+ def remove_duplicate_records(self) -> int:
51
+ """Remove duplicate FTS records."""
52
+ self.log.debug("Looking for duplicate search entries...")
53
+ duplicates = ComicFTS.objects.annotate(max_updated_at=Max("updated_at")).filter(
54
+ updated_at__lt=F("max_updated_at")
55
+ )
56
+
57
+ num_dupes = duplicates.count()
58
+ self.log.debug(f"Found {num_dupes} duplicate search entries")
59
+ count = num_dupes
60
+ if num_dupes:
61
+ count, _ = duplicates.delete()
62
+ if count:
63
+ self.log.info(f"Deleted {count} duplicate search entries")
64
+ return count
@@ -1,17 +1,21 @@
1
1
  """Search Index update."""
2
2
 
3
3
  from datetime import datetime
4
+ from math import floor
4
5
  from time import time
6
+ from typing import TYPE_CHECKING
5
7
  from zoneinfo import ZoneInfo
6
8
 
7
- from comicbox.enums.maps.identifiers import ID_SOURCE_NAME_MAP
9
+ from django.db.models import Q
8
10
  from django.db.models.aggregates import Max
9
11
  from django.db.models.expressions import F, Value
10
12
  from django.db.models.functions import Concat
11
- from django.db.models.functions.datetime import Now
12
13
  from django.db.models.query import QuerySet
13
14
  from humanize import intcomma, naturaldelta
14
15
 
16
+ from codex.librarian.memory import get_mem_limit
17
+ from codex.librarian.scribe.search.const import COMICFTS_UPDATE_FIELDS
18
+ from codex.librarian.scribe.search.prepare import SearchEntryPrepare
15
19
  from codex.librarian.scribe.search.remove import SearchIndexerRemove
16
20
  from codex.librarian.scribe.search.status import (
17
21
  SEARCH_INDEX_STATII,
@@ -20,56 +24,16 @@ from codex.librarian.scribe.search.status import (
20
24
  SearchIndexSyncCreateStatus,
21
25
  SearchIndexSyncUpdateStatus,
22
26
  )
23
- from codex.librarian.status import Status
24
27
  from codex.models import Comic
25
28
  from codex.models.comic import ComicFTS
26
29
  from codex.models.functions import GroupConcat
27
- from codex.serializers.fields.browser import CountryField, LanguageField, PyCountryField
28
- from codex.settings import SEARCH_INDEX_BATCH_SIZE
30
+ from codex.settings import SEARCH_SYNC_BATCH_MEMORY_RATIO
31
+
32
+ if TYPE_CHECKING:
33
+ from codex.librarian.status import Status
29
34
 
30
- _COMICFTS_ATTRIBUTES = (
31
- "collection_title",
32
- "file_type",
33
- "issue",
34
- "name",
35
- "notes",
36
- "reading_direction",
37
- "review",
38
- "summary",
39
- "updated_at",
40
- )
41
- _COMICFTS_FKS = (
42
- "publisher",
43
- "imprint",
44
- "series",
45
- "age_rating",
46
- "country",
47
- "language",
48
- "original_format",
49
- "scan_info",
50
- "tagger",
51
- )
52
- _COMICFTS_M2MS = (
53
- "characters",
54
- "credits",
55
- "genres",
56
- "identifiers",
57
- "locations",
58
- "series_groups",
59
- "sources",
60
- "stories",
61
- "story_arcs",
62
- "tags",
63
- "teams",
64
- "universes",
65
- )
66
- _COMICFTS_UPDATE_FIELDS = (
67
- *_COMICFTS_ATTRIBUTES,
68
- *_COMICFTS_FKS,
69
- *_COMICFTS_M2MS,
70
- )
71
35
  _MIN_UTC_DATE = datetime.min.replace(tzinfo=ZoneInfo("UTC"))
72
- _CHUNK_HUMAN_SIZE = intcomma(SEARCH_INDEX_BATCH_SIZE)
36
+ _ALL_FTS_COMIC_IDS_QUERY = Q(pk__in=ComicFTS.objects.values_list("comic_id", flat=True))
73
37
 
74
38
 
75
39
  class SearchIndexerSync(SearchIndexerRemove):
@@ -167,22 +131,14 @@ class SearchIndexerSync(SearchIndexerRemove):
167
131
  ),
168
132
  )
169
133
 
170
- @staticmethod
171
- def _get_pycountry_fts_field(field: PyCountryField, iso_code) -> str:
172
- if not iso_code:
173
- return ""
174
- return ",".join((iso_code, field.to_representation(iso_code)))
175
-
176
- @staticmethod
177
- def _get_sources_fts_field(sources) -> str | None:
178
- names = []
179
- if not sources:
180
- return None
181
- for source in sources.split(","):
182
- names.append(source)
183
- if long_name := ID_SOURCE_NAME_MAP.get(source):
184
- names.append(long_name)
185
- return ",".join(names)
134
+ def _update_search_index_operate_get_status(
135
+ self, total_comics: int, chunk_human_size: str, *, create: bool
136
+ ):
137
+ status_class = (
138
+ SearchIndexSyncCreateStatus if create else SearchIndexSyncUpdateStatus
139
+ )
140
+ subtitle = f"Chunks of {chunk_human_size}" if total_comics else ""
141
+ return status_class(total=total_comics, subtitle=subtitle)
186
142
 
187
143
  def _update_search_index_create_or_update(
188
144
  self,
@@ -202,133 +158,75 @@ class SearchIndexerSync(SearchIndexerRemove):
202
158
  if create:
203
159
  ComicFTS.objects.bulk_create(obj_list)
204
160
  else:
205
- ComicFTS.objects.bulk_update(obj_list, _COMICFTS_UPDATE_FIELDS)
206
- obj_list.clear()
207
-
208
- def _create_comicfts_entry(
209
- self,
210
- comic,
211
- obj_list: list[ComicFTS],
212
- status: Status,
213
- *,
214
- create: bool,
215
- ):
216
- country_field: PyCountryField = CountryField() # pyright: ignore[reportAssignmentType]
217
- language_field: PyCountryField = LanguageField() # pyright: ignore[reportAssignmentType]
218
-
219
- country = self._get_pycountry_fts_field(country_field, comic.fts_country)
220
- language = self._get_pycountry_fts_field(language_field, comic.fts_language)
221
- sources = self._get_sources_fts_field(comic.fts_sources)
222
-
223
- now = Now()
224
- comicfts = ComicFTS(
225
- comic_id=comic.pk,
226
- # Attributes
227
- collection_title=comic.collection_title,
228
- file_type=comic.file_type,
229
- issue=comic.issue(),
230
- name=comic.name,
231
- notes=comic.notes,
232
- reading_direction=comic.reading_direction,
233
- review=comic.review,
234
- summary=comic.summary,
235
- updated_at=now,
236
- # Group FKs
237
- publisher=comic.fts_publisher,
238
- imprint=comic.fts_imprint,
239
- series=comic.fts_series,
240
- # FKS
241
- age_rating=comic.fts_age_rating,
242
- country=country,
243
- language=language,
244
- original_format=comic.fts_original_format,
245
- scan_info=comic.fts_scan_info,
246
- tagger=comic.fts_tagger,
247
- # ManyToMany
248
- characters=comic.fts_characters,
249
- credits=comic.fts_credits,
250
- genres=comic.fts_genres,
251
- identifiers=comic.fts_identifiers,
252
- sources=sources, # comic.fts_sources,
253
- locations=comic.fts_locations,
254
- series_groups=comic.fts_series_groups,
255
- stories=comic.fts_stories,
256
- story_arcs=comic.fts_story_arcs,
257
- tags=comic.fts_tags,
258
- teams=comic.fts_teams,
259
- universes=comic.fts_universes,
260
- )
261
- if create:
262
- comicfts.created_at = now
161
+ ComicFTS.objects.bulk_update(obj_list, COMICFTS_UPDATE_FIELDS)
263
162
 
264
- obj_list.append(comicfts)
265
- status.increment_complete()
266
- self.status_controller.update(status)
163
+ status.increment_complete(num_comic_fts)
164
+ self.status_controller.update(status, notify=True)
267
165
 
268
- def _update_search_index_operate_get_status(
269
- self, total_comics: int, *, create: bool
270
- ):
271
- status_class = (
272
- SearchIndexSyncCreateStatus if create else SearchIndexSyncUpdateStatus
273
- )
274
- subtitle = f"Chunks of {_CHUNK_HUMAN_SIZE}" if total_comics else ""
275
- return status_class(total=total_comics, subtitle=subtitle)
166
+ @staticmethod
167
+ def _get_operation_comics_query(qs, *, create: bool):
168
+ if create and not ComicFTS.objects.exists():
169
+ qs = Comic.objects.all()
170
+ return qs
276
171
 
277
172
  def _update_search_index_operate(
278
173
  self, comics_filtered_qs: QuerySet, *, create: bool
279
174
  ):
280
- total_comics = comics_filtered_qs.count()
175
+ # Smaller systems may run out of virtual memory unless this is auto governed.
176
+ mem_limit_gb = get_mem_limit("g")
177
+ search_index_batch_size = floor(
178
+ (mem_limit_gb / SEARCH_SYNC_BATCH_MEMORY_RATIO) * 1000
179
+ )
180
+ chunk_human_size = intcomma(search_index_batch_size)
281
181
 
282
182
  verb = "create" if create else "update"
183
+ self.log.debug(f"Counting total search index entries to {verb}...")
184
+ total_comics = self._get_operation_comics_query(
185
+ comics_filtered_qs, create=create
186
+ )
187
+ total_comics = total_comics.count()
283
188
  status = self._update_search_index_operate_get_status(
284
- total_comics, create=create
189
+ total_comics, chunk_human_size, create=create
285
190
  )
286
191
 
287
192
  try:
288
- if total_comics:
289
- reason = (
290
- f"Batching this search engine {verb} operation in to chunks of {_CHUNK_HUMAN_SIZE}."
291
- f" Search engine {verb}s run much faster as one large batch but then there's no progress updates."
292
- " You may adjust the batch size with the environment variable CODEX_SEARCH_INDEX_BATCH_SIZE."
293
- )
294
- else:
295
- reason = f"No search entries to {verb}."
296
- self.log.debug(reason)
297
193
  if not total_comics:
194
+ self.log.debug(f"No search entries to {verb}.")
298
195
  return total_comics
299
196
 
300
- self.status_controller.start(status)
301
- comics = self._select_related_fts_query(comics_filtered_qs)
302
- comics = self._prefetch_related_fts_query(comics)
303
- comics = self._annotate_fts_query(comics)
304
- comics = comics.order_by("pk")
305
-
306
- obj_list = []
307
- prep_num = min(SEARCH_INDEX_BATCH_SIZE, total_comics)
308
- self.log.debug(f"Preparing {prep_num} comics for search indexing...")
309
- for comic in comics.iterator(chunk_size=SEARCH_INDEX_BATCH_SIZE):
197
+ self.status_controller.start(status, notify=True)
198
+ start = 0
199
+ while start < total_comics:
200
+ obj_list = []
310
201
  if self.abort_event.is_set():
311
202
  break
312
- self._create_comicfts_entry(comic, obj_list, status, create=create)
313
- if len(obj_list) >= SEARCH_INDEX_BATCH_SIZE:
314
- self._update_search_index_create_or_update(
315
- obj_list,
316
- status,
317
- create=create,
203
+ # Not using standard iterator chunking to control memory and really
204
+ # do this in batches.
205
+ self.log.debug(
206
+ f"Preparing up to {chunk_human_size} comics for search indexing..."
207
+ )
208
+ # This query is supposed to get only comics that don't need creating but it doesn't
209
+ # So the start total exit assists it with that.
210
+ comics = self._get_operation_comics_query(
211
+ comics_filtered_qs, create=create
212
+ )
213
+ comics = self._prefetch_related_fts_query(comics)
214
+ comics = comics.order_by("pk")
215
+ comics = comics[:search_index_batch_size]
216
+ comics = self._annotate_fts_query(comics)
217
+ for comic in comics.values():
218
+ SearchEntryPrepare.prepare_sync_fts_entry(
219
+ comic, obj_list, create=create
318
220
  )
319
- complete = status.complete or 0
320
- if prep_num := min(
321
- SEARCH_INDEX_BATCH_SIZE, total_comics - complete
322
- ):
323
- self.log.debug(
324
- f"Preparing {prep_num} comics for search indexing..."
325
- )
326
221
 
327
- self._update_search_index_create_or_update(
328
- obj_list,
329
- status,
330
- create=create,
331
- )
222
+ if not obj_list:
223
+ break
224
+ self._update_search_index_create_or_update(
225
+ obj_list,
226
+ status,
227
+ create=create,
228
+ )
229
+ start += search_index_batch_size
332
230
 
333
231
  finally:
334
232
  self.status_controller.finish(status)
@@ -336,25 +234,36 @@ class SearchIndexerSync(SearchIndexerRemove):
336
234
 
337
235
  def _update_search_index_update(self):
338
236
  """Update out of date search entries."""
237
+ out_of_date_comics = Comic.objects.filter(_ALL_FTS_COMIC_IDS_QUERY)
238
+ self.log.debug("Looking for search index watermark...")
339
239
  fts_watermark = ComicFTS.objects.aggregate(max=Max("updated_at"))["max"]
340
- since = fts_watermark or "the fracturing of the multiverse"
240
+ if fts_watermark:
241
+ since = fts_watermark
242
+ out_of_date_comics = out_of_date_comics.filter(updated_at__gt=fts_watermark)
243
+ else:
244
+ since = "the fracturing of the multiverse"
245
+ fts_watermark = fts_watermark or _MIN_UTC_DATE
341
246
  self.log.info(f"Looking for search entries to update since {since}...")
342
- fts_watermark = fts_watermark or _MIN_UTC_DATE
343
- out_of_date_comics = (
344
- Comic.objects.select_related("comicfts")
345
- .exclude(comicfts__isnull=True)
346
- .filter(updated_at__gt=fts_watermark)
347
- )
247
+ count = out_of_date_comics.count()
248
+ self.log.debug(f"Found {count} comics with out of date search entries.")
249
+
348
250
  return self._update_search_index_operate(out_of_date_comics, create=False)
349
251
 
350
252
  def _update_search_index_create(self):
351
253
  """Create missing search entries."""
352
254
  self.log.info("Looking for missing search entries to create...")
353
- missing_comics = Comic.objects.filter(comicfts__isnull=True)
255
+ missing_comics = Comic.objects.all()
256
+ count = None if ComicFTS.objects.exists() else missing_comics.count()
257
+ missing_comics = missing_comics.exclude(_ALL_FTS_COMIC_IDS_QUERY)
258
+ if count is None:
259
+ count = missing_comics.count()
260
+
261
+ self.log.debug(f"Found {count} comics missing from the search index.")
354
262
  return self._update_search_index_operate(missing_comics, create=True)
355
263
 
356
264
  def _update_search_index(self, *, rebuild: bool):
357
265
  """Update or Rebuild the search index."""
266
+ self.log.debug("In update search index before init statii.")
358
267
  start_time = time()
359
268
  self._init_statuses(rebuild)
360
269
 
@@ -111,6 +111,29 @@ class StatusController:
111
111
  return
112
112
  self._update(status, notify=notify)
113
113
 
114
+ @staticmethod
115
+ def _finish_status_prepare(
116
+ positive_statii: MappingProxyType[str, Status | type[Status]],
117
+ updates: dict[str, Any],
118
+ ):
119
+ # Filter all active or preactive statii
120
+ ls_filter = Q(active__isnull=False) | Q(preactive__isnull=False)
121
+ if positive_statii:
122
+ ls_filter_dict: dict[str, Any] = {"status_type__in": positive_statii.keys()}
123
+ # Filter on status codes
124
+ ls_filter &= Q(**ls_filter_dict)
125
+ lses = LibrarianStatus.objects.filter(ls_filter)
126
+ update_ls = []
127
+ log_statii = []
128
+ for ls in lses.iterator():
129
+ for key, value in updates.items():
130
+ setattr(ls, key, value)
131
+ update_ls.append(ls)
132
+ status = positive_statii[ls.status_type]
133
+ if isinstance(status, Status):
134
+ log_statii.append(status)
135
+ return update_ls, log_statii
136
+
114
137
  def _log_finish(self, status: Status, *, clear_subtitle: bool):
115
138
  """Log finish of status with stats."""
116
139
  level = "INFO"
@@ -140,31 +163,6 @@ class StatusController:
140
163
 
141
164
  self.log.log(level, f"{prefix}{suffix}.")
142
165
 
143
- @staticmethod
144
- def _finish_status_prepare(
145
- positive_statii: MappingProxyType[str, Status | type[Status]],
146
- updates: dict[str, Any],
147
- ):
148
- # Filter on submitted codes or all statii if nothing.
149
- ls_filter_dict: dict[str, Any] = (
150
- {"status_type__in": positive_statii.keys()} if positive_statii else {}
151
- )
152
- # Filter all active or preactive statii
153
- ls_filter = (Q(active__isnull=False) | Q(preactive__isnull=False)) & Q(
154
- **ls_filter_dict
155
- )
156
- lses = LibrarianStatus.objects.filter(ls_filter)
157
- update_ls = []
158
- log_statii = []
159
- for ls in lses.iterator():
160
- for key, value in updates.items():
161
- setattr(ls, key, value)
162
- update_ls.append(ls)
163
- status = positive_statii[ls.status_type]
164
- if isinstance(status, Status):
165
- log_statii.append(status)
166
- return update_ls, log_statii
167
-
168
166
  def finish_many(
169
167
  self,
170
168
  statii: Iterable[Status | type[Status] | None],
@@ -191,8 +189,8 @@ class StatusController:
191
189
  self.log.info("Cleared all librarian statuses")
192
190
  for status in log_statii:
193
191
  self._log_finish(status, clear_subtitle=clear_subtitle)
194
- except Exception as exc:
195
- self.log.warning(f"Finish status {positive_statii}: {exc}")
192
+ except Exception:
193
+ self.log.exception(f"Finish status {positive_statii}")
196
194
 
197
195
  def finish(
198
196
  self, status: Status | None, *, notify: bool = True, clear_subtitle=True
@@ -14,6 +14,7 @@ from types import MappingProxyType
14
14
  from typing_extensions import override
15
15
  from watchdog.events import EVENT_TYPE_MOVED, FileSystemEvent
16
16
 
17
+ from codex.librarian.memory import get_mem_limit
17
18
  from codex.librarian.scribe.importer.tasks import ImportTask
18
19
  from codex.librarian.threads import AggregateMessageQueuedThread
19
20
  from codex.librarian.watchdog.const import EVENT_CLASS_DIFF_ALL_MAP
@@ -21,7 +22,6 @@ from codex.librarian.watchdog.events import (
21
22
  EVENT_TYPE_FINISH_POLL,
22
23
  EVENT_TYPE_START_POLL,
23
24
  )
24
- from codex.librarian.watchdog.memory import get_mem_limit
25
25
 
26
26
  _IMPORT_TASK_PARAMS = MappingProxyType(
27
27
  {
@@ -0,0 +1,136 @@
1
+ """Generated by Django 5.2.4 on 2025-07-21 15:05."""
2
+
3
+ from itertools import chain
4
+
5
+ import django.db.models.deletion
6
+ from comicbox.enums.maps.identifiers import ID_SOURCE_NAME_MAP, get_id_source_by_alias
7
+ from django.db import migrations, models
8
+
9
+
10
+ def _map_identifiers_to_canonical_names(apps):
11
+ id_source_model = apps.get_model("codex", "identifiersource")
12
+ identifier_source_map = {}
13
+
14
+ for source in id_source_model.objects.all():
15
+ if id_source_enum := get_id_source_by_alias(source.name, default=None):
16
+ canon_name = ID_SOURCE_NAME_MAP.get(id_source_enum, source.name)
17
+ if canon_name != source.name:
18
+ if canon_name not in identifier_source_map:
19
+ identifier_source_map[canon_name] = set()
20
+ identifier_source_map[canon_name] |= set(
21
+ source.identifier_set.values_list("pk", flat=True)
22
+ )
23
+ return identifier_source_map
24
+
25
+
26
+ def _prepare_canonical_id_sources(apps, identifier_source_map):
27
+ id_source_model = apps.get_model("codex", "identifiersource")
28
+ source_names = identifier_source_map.keys()
29
+ existing_id_sources = id_source_model.objects.filter(name__in=source_names).only(
30
+ "name"
31
+ )
32
+ existing_id_source_names = id_source_model.objects.filter(
33
+ name__in=source_names
34
+ ).values_list("name", flat=True)
35
+ create_id_source_names = sorted(
36
+ frozenset(set(identifier_source_map.keys()) - set(existing_id_source_names))
37
+ )
38
+ return tuple(
39
+ id_source_model(name=name) for name in create_id_source_names
40
+ ), existing_id_sources
41
+
42
+
43
+ def _create_link_map(identifier_source_map, sources):
44
+ link_map = {}
45
+ for id_source in chain(*sources):
46
+ for id_pk in identifier_source_map[id_source.name]:
47
+ link_map[id_pk] = id_source
48
+ return link_map
49
+
50
+
51
+ def _prepare_updatatable_identifiers(apps, identifier_source_map, sources):
52
+ link_map = _create_link_map(identifier_source_map, sources)
53
+ identifier_model = apps.get_model("codex", "identifier")
54
+ updateable_identifiers = identifier_model.objects.filter(pk__in=link_map.keys())
55
+ obj_list = []
56
+ for identifier in updateable_identifiers:
57
+ identifier.source = link_map[identifier.pk]
58
+ obj_list.append(identifier)
59
+ return tuple(obj_list)
60
+
61
+
62
+ def _create_canonical_sources(apps):
63
+ print("Examining identifier sources for conversion to canonical sources...")
64
+ identifier_source_map = _map_identifiers_to_canonical_names(apps)
65
+ print("Preparing missing canonical identifiers sources for creation...")
66
+ obj_list, existing_id_sources = _prepare_canonical_id_sources(
67
+ apps, identifier_source_map
68
+ )
69
+ id_source_model = apps.get_model("codex", "identifiersource")
70
+ created_id_sources = id_source_model.objects.bulk_create(obj_list)
71
+ count = len(obj_list)
72
+ print(f"Created {count} canonical identifier sources")
73
+ return identifier_source_map, (existing_id_sources, created_id_sources)
74
+
75
+
76
+ def _update_identifiers_with_canonical_sources(apps, identifier_source_map, sources):
77
+ print("Preparing identifiers for update with canonical sources...")
78
+ obj_list = _prepare_updatatable_identifiers(apps, identifier_source_map, sources)
79
+ identifier_model = apps.get_model("codex", "identifier")
80
+ identifier_model.objects.bulk_update(obj_list, fields=("source",))
81
+ count = len(obj_list)
82
+ print(f"Updated {count} identifiers with canonical sources.")
83
+
84
+
85
+ def _convert_identifier_sources(apps, _schema_editor):
86
+ identifier_source_map, sources = _create_canonical_sources(apps)
87
+ _update_identifiers_with_canonical_sources(apps, identifier_source_map, sources)
88
+
89
+
90
+ class Migration(migrations.Migration):
91
+ """Migrate db."""
92
+
93
+ dependencies = [
94
+ ("codex", "0034_comicbox2"),
95
+ ]
96
+
97
+ operations = [
98
+ migrations.RunPython(_convert_identifier_sources),
99
+ migrations.SeparateDatabaseAndState(
100
+ database_operations=[
101
+ migrations.RunSQL(
102
+ sql="DROP TABLE IF EXISTS codex_comicfts;",
103
+ ),
104
+ migrations.RunSQL(
105
+ sql="CREATE VIRTUAL TABLE codex_comicfts USING fts5(comic_id UNINDEXED, created_at UNINDEXED, updated_at UNINDEXED, publisher, imprint, series, name, collection_title, age_rating, country, language, original_format, review, scan_info, summary, tagger, characters, credits, genres, locations, roles, series_groups, stories, story_arcs, tags, teams, universes, sources)",
106
+ reverse_sql="DROP TABLE IF EXISTS codex_comicfts",
107
+ ),
108
+ ],
109
+ state_operations=[
110
+ migrations.DeleteModel(
111
+ name="ComicFTS",
112
+ ),
113
+ migrations.CreateModel(
114
+ name="ComicFTS",
115
+ fields=[
116
+ ("created_at", models.DateTimeField(auto_now_add=True)),
117
+ ("updated_at", models.DateTimeField(auto_now=True)),
118
+ (
119
+ "comic",
120
+ models.OneToOneField(
121
+ on_delete=django.db.models.deletion.CASCADE,
122
+ primary_key=True,
123
+ serialize=False,
124
+ to="codex.comic",
125
+ ),
126
+ ),
127
+ ("body", models.TextField()),
128
+ ],
129
+ options={
130
+ "get_latest_by": "updated_at",
131
+ "managed": False,
132
+ },
133
+ ),
134
+ ],
135
+ ),
136
+ ]
codex/models/comic.py CHANGED
@@ -326,23 +326,12 @@ class Comic(WatchedPathBrowserGroup):
326
326
  """Most common text representation for logging."""
327
327
  return self.get_title(self, volume=True, name=True, filename_fallback=True)
328
328
 
329
- def issue(self) -> str:
330
- """Combine issue parts for search."""
331
- res = ""
332
- if self.issue_number is not None:
333
- res += str(self.issue_number.normalize())
334
- if self.issue_suffix:
335
- res += self.issue_suffix
336
- return res
337
-
338
329
 
339
330
  class ComicFTS(BaseModel):
340
331
  comic = OneToOneField(primary_key=True, to=Comic, on_delete=CASCADE)
341
332
  # Attributes
342
333
  collection_title = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
343
- issue = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
344
334
  name = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
345
- notes = TextField(db_collation="nocase")
346
335
  review = TextField(db_collation="nocase")
347
336
  summary = TextField(db_collation="nocase")
348
337
  # FK groups
@@ -352,7 +341,6 @@ class ComicFTS(BaseModel):
352
341
  # FK
353
342
  age_rating = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
354
343
  country = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
355
- file_type = CharField(db_collation="nocase", max_length=max_choices_len(FileType))
356
344
  language = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
357
345
  original_format = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
358
346
  scan_info = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
@@ -361,12 +349,8 @@ class ComicFTS(BaseModel):
361
349
  characters = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
362
350
  credits = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
363
351
  genres = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
364
- identifiers = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
365
352
  sources = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
366
353
  locations = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
367
- reading_direction = CharField(
368
- db_collation="nocase", max_length=max_choices_len(ReadingDirection)
369
- )
370
354
  series_groups = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
371
355
  stories = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)
372
356
  story_arcs = CharField(db_collation="nocase", max_length=MAX_NAME_LEN)