codex 1.6.0a4__py3-none-any.whl → 1.6.0a6__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 (568) hide show
  1. codex/exceptions.py +17 -6
  2. codex/integrity.py +67 -3
  3. codex/librarian/covers/coverd.py +2 -2
  4. codex/librarian/covers/create.py +29 -13
  5. codex/librarian/covers/path.py +8 -6
  6. codex/librarian/covers/purge.py +28 -18
  7. codex/librarian/covers/tasks.py +3 -2
  8. codex/librarian/importer/aggregate_metadata.py +2 -2
  9. codex/librarian/importer/const.py +31 -3
  10. codex/librarian/importer/covers.py +47 -0
  11. codex/librarian/importer/create_fks.py +88 -28
  12. codex/librarian/importer/deleted.py +71 -9
  13. codex/librarian/importer/importerd.py +133 -28
  14. codex/librarian/importer/link_comics.py +55 -0
  15. codex/librarian/importer/moved.py +78 -3
  16. codex/librarian/importer/query_fks.py +65 -1
  17. codex/librarian/importer/status.py +6 -0
  18. codex/librarian/importer/tasks.py +11 -3
  19. codex/librarian/importer/update_comics.py +1 -5
  20. codex/librarian/janitor/cleanup.py +24 -0
  21. codex/librarian/janitor/failed_imports.py +1 -1
  22. codex/librarian/janitor/janitor.py +5 -0
  23. codex/librarian/janitor/janitord.py +1 -1
  24. codex/librarian/janitor/status.py +1 -0
  25. codex/librarian/janitor/tasks.py +5 -0
  26. codex/librarian/librariand.py +3 -3
  27. codex/librarian/search/update.py +1 -1
  28. codex/librarian/watchdog/db_snapshot.py +9 -6
  29. codex/librarian/watchdog/emitter.py +3 -0
  30. codex/librarian/watchdog/event_batcherd.py +19 -3
  31. codex/librarian/watchdog/events.py +176 -36
  32. codex/librarian/watchdog/observers.py +13 -3
  33. codex/migrations/0027_sort_name.py +2 -12
  34. codex/migrations/0028_custom_covers.py +166 -0
  35. codex/migrations/0029_choices_adminflag_and_timestamp.py +44 -0
  36. codex/models/admin.py +0 -2
  37. codex/models/base.py +3 -0
  38. codex/models/comic.py +15 -9
  39. codex/models/groups.py +30 -14
  40. codex/models/library.py +11 -3
  41. codex/models/paths.py +62 -14
  42. codex/models/{const.py → util.py} +15 -2
  43. codex/serializers/admin.py +11 -2
  44. codex/serializers/browser/mixins.py +1 -0
  45. codex/serializers/browser/page.py +0 -1
  46. codex/serializers/browser/settings.py +3 -0
  47. codex/settings/settings.py +11 -5
  48. codex/startup.py +77 -3
  49. codex/static_root/assets/{VCheckbox-CN6Sn_k7.9bf27129091c.js → VCheckbox-BS5fdM4M.a5d446e4aeb3.js} +1 -1
  50. codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js.br +0 -0
  51. codex/static_root/assets/VCheckbox-BS5fdM4M.a5d446e4aeb3.js.gz +0 -0
  52. codex/static_root/assets/{VCheckbox-CN6Sn_k7.js → VCheckbox-BS5fdM4M.js} +1 -1
  53. codex/static_root/assets/VCheckbox-BS5fdM4M.js.br +0 -0
  54. codex/static_root/assets/VCheckbox-BS5fdM4M.js.gz +0 -0
  55. codex/static_root/assets/{VCheckboxBtn-CN1RPZPQ.b763c6a7abaf.js → VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js} +1 -1
  56. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js.br +0 -0
  57. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.f0c4a814ded8.js.gz +0 -0
  58. codex/static_root/assets/{VCheckboxBtn-CN1RPZPQ.js → VCheckboxBtn-m4IbdjEe.js} +1 -1
  59. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.js.br +0 -0
  60. codex/static_root/assets/VCheckboxBtn-m4IbdjEe.js.gz +0 -0
  61. codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js +1 -0
  62. codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js.br +0 -0
  63. codex/static_root/assets/VCombobox-jg7SI5mn.af66f18b4c54.js.gz +0 -0
  64. codex/static_root/assets/VCombobox-jg7SI5mn.js +1 -0
  65. codex/static_root/assets/VCombobox-jg7SI5mn.js.br +0 -0
  66. codex/static_root/assets/VCombobox-jg7SI5mn.js.gz +0 -0
  67. codex/static_root/assets/{VDialog-DC3KGsMT.71e2a460e44b.js → VDialog-DiixpkQL.f84fd1fd1f84.js} +1 -1
  68. codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js.br +0 -0
  69. codex/static_root/assets/VDialog-DiixpkQL.f84fd1fd1f84.js.gz +0 -0
  70. codex/static_root/assets/{VDialog-DC3KGsMT.js → VDialog-DiixpkQL.js} +1 -1
  71. codex/static_root/assets/VDialog-DiixpkQL.js.br +0 -0
  72. codex/static_root/assets/VDialog-DiixpkQL.js.gz +0 -0
  73. codex/static_root/assets/{VExpansionPanels-DJQYXCn2.f0d471a1b80a.js → VExpansionPanels-Db5mPFue.56d0ad094a62.js} +1 -1
  74. codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js.br +0 -0
  75. codex/static_root/assets/VExpansionPanels-Db5mPFue.56d0ad094a62.js.gz +0 -0
  76. codex/static_root/assets/{VExpansionPanels-DJQYXCn2.js → VExpansionPanels-Db5mPFue.js} +1 -1
  77. codex/static_root/assets/VExpansionPanels-Db5mPFue.js.br +0 -0
  78. codex/static_root/assets/VExpansionPanels-Db5mPFue.js.gz +0 -0
  79. codex/static_root/assets/{VRadioGroup-CgodTThy.b20290d57146.js → VRadioGroup-DULWiMSI.4ae535e01d2b.js} +1 -1
  80. codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js.br +0 -0
  81. codex/static_root/assets/VRadioGroup-DULWiMSI.4ae535e01d2b.js.gz +0 -0
  82. codex/static_root/assets/{VRadioGroup-CgodTThy.js → VRadioGroup-DULWiMSI.js} +1 -1
  83. codex/static_root/assets/VRadioGroup-DULWiMSI.js.br +0 -0
  84. codex/static_root/assets/VRadioGroup-DULWiMSI.js.gz +0 -0
  85. codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js +1 -0
  86. codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js.br +0 -0
  87. codex/static_root/assets/VSelect-BnoCfCLY.4d50fd11c77c.js.gz +0 -0
  88. codex/static_root/assets/VSelect-BnoCfCLY.js +1 -0
  89. codex/static_root/assets/VSelect-BnoCfCLY.js.br +0 -0
  90. codex/static_root/assets/VSelect-BnoCfCLY.js.gz +0 -0
  91. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css +1 -0
  92. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.br +0 -0
  93. codex/static_root/assets/VSelect-MGVSeLgr.7cabd30bc5e4.css.gz +0 -0
  94. codex/static_root/assets/VSelect-MGVSeLgr.css +1 -0
  95. codex/static_root/assets/VSelect-MGVSeLgr.css.br +0 -0
  96. codex/static_root/assets/VSelect-MGVSeLgr.css.gz +0 -0
  97. codex/static_root/assets/{VSelectionControl-zPp4DXWq.635b7f5aa882.js → VSelectionControl-BSL0qeL9.5b5835d286df.js} +1 -1
  98. codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js.br +0 -0
  99. codex/static_root/assets/VSelectionControl-BSL0qeL9.5b5835d286df.js.gz +0 -0
  100. codex/static_root/assets/{VSelectionControl-zPp4DXWq.js → VSelectionControl-BSL0qeL9.js} +1 -1
  101. codex/static_root/assets/VSelectionControl-BSL0qeL9.js.br +0 -0
  102. codex/static_root/assets/VSelectionControl-BSL0qeL9.js.gz +0 -0
  103. codex/static_root/assets/{VSlideGroup-pK9Xompk.67014b86fc74.js → VSlideGroup-BQtz8h9p.ff97dbf209e1.js} +1 -1
  104. codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js.br +0 -0
  105. codex/static_root/assets/VSlideGroup-BQtz8h9p.ff97dbf209e1.js.gz +0 -0
  106. codex/static_root/assets/{VSlideGroup-pK9Xompk.js → VSlideGroup-BQtz8h9p.js} +1 -1
  107. codex/static_root/assets/VSlideGroup-BQtz8h9p.js.br +0 -0
  108. codex/static_root/assets/VSlideGroup-BQtz8h9p.js.gz +0 -0
  109. codex/static_root/assets/{VTable-CMi6sDtP.8445f91d9023.js → VTable-C0gAriFq.fa6e4b60d4ef.js} +1 -1
  110. codex/static_root/assets/VTable-C0gAriFq.fa6e4b60d4ef.js.br +0 -0
  111. codex/static_root/assets/VTable-C0gAriFq.fa6e4b60d4ef.js.gz +0 -0
  112. codex/static_root/assets/{VTable-CMi6sDtP.js → VTable-C0gAriFq.js} +1 -1
  113. codex/static_root/assets/VTable-C0gAriFq.js.br +0 -0
  114. codex/static_root/assets/VTable-C0gAriFq.js.gz +0 -0
  115. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +1 -0
  116. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
  117. codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
  118. codex/static_root/assets/VTextField-BssVoDhB.css +1 -0
  119. codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
  120. codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
  121. codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js +1 -0
  122. codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js.br +0 -0
  123. codex/static_root/assets/VTextField-Da_Hs7ls.c295f24c251e.js.gz +0 -0
  124. codex/static_root/assets/VTextField-Da_Hs7ls.js +1 -0
  125. codex/static_root/assets/VTextField-Da_Hs7ls.js.br +0 -0
  126. codex/static_root/assets/VTextField-Da_Hs7ls.js.gz +0 -0
  127. codex/static_root/assets/{VWindowItem-DNsRQx9l.c42f636c386e.js → VWindowItem-CFhKCj49.b2458002a937.js} +1 -1
  128. codex/static_root/assets/VWindowItem-CFhKCj49.b2458002a937.js.br +0 -0
  129. codex/static_root/assets/VWindowItem-CFhKCj49.b2458002a937.js.gz +0 -0
  130. codex/static_root/assets/{VWindowItem-DNsRQx9l.js → VWindowItem-CFhKCj49.js} +1 -1
  131. codex/static_root/assets/VWindowItem-CFhKCj49.js.br +0 -0
  132. codex/static_root/assets/VWindowItem-CFhKCj49.js.gz +0 -0
  133. codex/static_root/assets/{admin-BbrOU9Y9.29936748af9e.css → admin-B8Bp2Uih.82c98714bcf1.css} +1 -1
  134. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
  135. codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
  136. codex/static_root/assets/{admin-BbrOU9Y9.css → admin-B8Bp2Uih.css} +1 -1
  137. codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
  138. codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
  139. codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js +1 -0
  140. codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js.br +0 -0
  141. codex/static_root/assets/admin-BpGFsvOH.d7193c78d0dd.js.gz +0 -0
  142. codex/static_root/assets/admin-BpGFsvOH.js +1 -0
  143. codex/static_root/assets/admin-BpGFsvOH.js.br +0 -0
  144. codex/static_root/assets/admin-BpGFsvOH.js.gz +0 -0
  145. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css +1 -0
  146. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.br +0 -0
  147. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.bd2c1d6148bb.css.gz +0 -0
  148. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css +1 -0
  149. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.br +0 -0
  150. codex/static_root/assets/admin-drawer-panel-BHdNPwzF.css.gz +0 -0
  151. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js +30 -0
  152. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js.br +0 -0
  153. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.113c50cdf2cc.js.gz +0 -0
  154. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js +30 -0
  155. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js.br +0 -0
  156. codex/static_root/assets/admin-drawer-panel-C34Ze3-5.js.gz +0 -0
  157. codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js +1 -0
  158. codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js.br +0 -0
  159. codex/static_root/assets/browser-CRPvEdqv.f355a71f4380.js.gz +0 -0
  160. codex/static_root/assets/browser-CRPvEdqv.js +1 -0
  161. codex/static_root/assets/browser-CRPvEdqv.js.br +0 -0
  162. codex/static_root/assets/browser-CRPvEdqv.js.gz +0 -0
  163. codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css +1 -0
  164. codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css.br +0 -0
  165. codex/static_root/assets/browser-Zqc9cR6T.b7d6ddeff130.css.gz +0 -0
  166. codex/static_root/assets/browser-Zqc9cR6T.css +1 -0
  167. codex/static_root/assets/browser-Zqc9cR6T.css.br +0 -0
  168. codex/static_root/assets/browser-Zqc9cR6T.css.gz +0 -0
  169. codex/static_root/assets/{change-password-dialog-BHTkpC6Z.2d5d5dd57ad3.js → change-password-dialog-JYM9aAR9.f71d378f4fa6.js} +1 -1
  170. codex/static_root/assets/change-password-dialog-JYM9aAR9.f71d378f4fa6.js.br +0 -0
  171. codex/static_root/assets/change-password-dialog-JYM9aAR9.f71d378f4fa6.js.gz +0 -0
  172. codex/static_root/assets/{change-password-dialog-BHTkpC6Z.js → change-password-dialog-JYM9aAR9.js} +1 -1
  173. codex/static_root/assets/change-password-dialog-JYM9aAR9.js.br +0 -0
  174. codex/static_root/assets/change-password-dialog-JYM9aAR9.js.gz +0 -0
  175. codex/static_root/assets/{confirm-dialog-DmXGDg4z.284812bd376d.js → confirm-dialog-ZAZ6P1Rc.515858dd21e8.js} +1 -1
  176. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.515858dd21e8.js.br +0 -0
  177. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.515858dd21e8.js.gz +0 -0
  178. codex/static_root/assets/{confirm-dialog-DmXGDg4z.js → confirm-dialog-ZAZ6P1Rc.js} +1 -1
  179. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.js.br +0 -0
  180. codex/static_root/assets/confirm-dialog-ZAZ6P1Rc.js.gz +0 -0
  181. codex/static_root/assets/{datetime-column-DKaH5S3z.5c6042016200.js → datetime-column-BT1MSP82.9617f6b94ebf.js} +1 -1
  182. codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js.br +0 -0
  183. codex/static_root/assets/datetime-column-BT1MSP82.9617f6b94ebf.js.gz +0 -0
  184. codex/static_root/assets/{datetime-column-DKaH5S3z.js → datetime-column-BT1MSP82.js} +1 -1
  185. codex/static_root/assets/datetime-column-BT1MSP82.js.br +0 -0
  186. codex/static_root/assets/datetime-column-BT1MSP82.js.gz +0 -0
  187. codex/static_root/assets/{filter-DUVATQW3.e1e7aa6d0b18.js → filter-frSNsexx.eab1570bd32d.js} +1 -1
  188. codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js.br +0 -0
  189. codex/static_root/assets/filter-frSNsexx.eab1570bd32d.js.gz +0 -0
  190. codex/static_root/assets/{filter-DUVATQW3.js → filter-frSNsexx.js} +1 -1
  191. codex/static_root/assets/filter-frSNsexx.js.br +0 -0
  192. codex/static_root/assets/filter-frSNsexx.js.gz +0 -0
  193. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +1 -0
  194. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
  195. codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
  196. codex/static_root/assets/flag-tab-BNwbLfrN.css +1 -0
  197. codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
  198. codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
  199. codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js +1 -0
  200. codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js.br +0 -0
  201. codex/static_root/assets/flag-tab-Ci_XcDT0.a5ea640a08d6.js.gz +0 -0
  202. codex/static_root/assets/flag-tab-Ci_XcDT0.js +1 -0
  203. codex/static_root/assets/flag-tab-Ci_XcDT0.js.br +0 -0
  204. codex/static_root/assets/flag-tab-Ci_XcDT0.js.gz +0 -0
  205. codex/static_root/assets/{group-tab-D3CyU-Il.fbf9ce5ef593.js → group-tab-DFma2R-E.879c37b15bbd.js} +1 -1
  206. codex/static_root/assets/group-tab-DFma2R-E.879c37b15bbd.js.br +0 -0
  207. codex/static_root/assets/group-tab-DFma2R-E.879c37b15bbd.js.gz +0 -0
  208. codex/static_root/assets/{group-tab-D3CyU-Il.js → group-tab-DFma2R-E.js} +1 -1
  209. codex/static_root/assets/group-tab-DFma2R-E.js.br +0 -0
  210. codex/static_root/assets/group-tab-DFma2R-E.js.gz +0 -0
  211. codex/static_root/assets/{http-error-0ef6-Ajc.e2879bcd242f.js → http-error-B3fQGxpt.9efc29496e30.js} +1 -1
  212. codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js.br +0 -0
  213. codex/static_root/assets/http-error-B3fQGxpt.9efc29496e30.js.gz +0 -0
  214. codex/static_root/assets/{http-error-0ef6-Ajc.js → http-error-B3fQGxpt.js} +1 -1
  215. codex/static_root/assets/http-error-B3fQGxpt.js.br +0 -0
  216. codex/static_root/assets/http-error-B3fQGxpt.js.gz +0 -0
  217. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +1 -0
  218. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
  219. codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
  220. codex/static_root/assets/library-tab-BN0SfPcH.css +1 -0
  221. codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
  222. codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
  223. codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js +1 -0
  224. codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js.br +0 -0
  225. codex/static_root/assets/library-tab-V8DUGoHN.809c548ff347.js.gz +0 -0
  226. codex/static_root/assets/library-tab-V8DUGoHN.js +1 -0
  227. codex/static_root/assets/library-tab-V8DUGoHN.js.br +0 -0
  228. codex/static_root/assets/library-tab-V8DUGoHN.js.gz +0 -0
  229. codex/static_root/assets/{main-CuzJev9T.js → main-Cavvqkyp.a9c4df906406.js} +6 -6
  230. codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js.br +0 -0
  231. codex/static_root/assets/main-Cavvqkyp.a9c4df906406.js.gz +0 -0
  232. codex/static_root/assets/{main-CuzJev9T.f58771cc37e2.js → main-Cavvqkyp.js} +6 -6
  233. codex/static_root/assets/main-Cavvqkyp.js.br +0 -0
  234. codex/static_root/assets/main-Cavvqkyp.js.gz +0 -0
  235. codex/static_root/assets/{pagination-toolbar-B3wS4_7p.7695fafdefce.css → pagination-toolbar-ClZui9Zf.697d7ae11c21.css} +1 -1
  236. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.br +0 -0
  237. codex/static_root/assets/pagination-toolbar-ClZui9Zf.697d7ae11c21.css.gz +0 -0
  238. codex/static_root/assets/{pagination-toolbar-B3wS4_7p.css → pagination-toolbar-ClZui9Zf.css} +1 -1
  239. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.br +0 -0
  240. codex/static_root/assets/pagination-toolbar-ClZui9Zf.css.gz +0 -0
  241. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js +1 -0
  242. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js.br +0 -0
  243. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.b2eb1e0c6985.js.gz +0 -0
  244. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js +1 -0
  245. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js.br +0 -0
  246. codex/static_root/assets/pagination-toolbar-Dg9tH1Ju.js.gz +0 -0
  247. codex/static_root/assets/{pdf-doc-BlU5mm5n.0431bcdf48d1.js → pdf-doc-4PKLN-Z3.62d8de4d1fd8.js} +1 -1
  248. codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js.br +0 -0
  249. codex/static_root/assets/pdf-doc-4PKLN-Z3.62d8de4d1fd8.js.gz +0 -0
  250. codex/static_root/assets/{pdf-doc-BlU5mm5n.js → pdf-doc-4PKLN-Z3.js} +1 -1
  251. codex/static_root/assets/pdf-doc-4PKLN-Z3.js.br +0 -0
  252. codex/static_root/assets/pdf-doc-4PKLN-Z3.js.gz +0 -0
  253. codex/static_root/assets/{reader-s7jttB_F.f7b43b04961a.js → reader-C39uGGxn.67b0f324cd5e.js} +2 -2
  254. codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js.br +0 -0
  255. codex/static_root/assets/reader-C39uGGxn.67b0f324cd5e.js.gz +0 -0
  256. codex/static_root/assets/{reader-s7jttB_F.js → reader-C39uGGxn.js} +2 -2
  257. codex/static_root/assets/reader-C39uGGxn.js.br +0 -0
  258. codex/static_root/assets/reader-C39uGGxn.js.gz +0 -0
  259. codex/static_root/assets/{reader-BiKxuxj7.css → reader-N2Ei_y2K.8c0cdc0d513f.css} +1 -1
  260. codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css.br +0 -0
  261. codex/static_root/assets/reader-N2Ei_y2K.8c0cdc0d513f.css.gz +0 -0
  262. codex/static_root/assets/{reader-BiKxuxj7.dabe592f46f1.css → reader-N2Ei_y2K.css} +1 -1
  263. codex/static_root/assets/reader-N2Ei_y2K.css.br +0 -0
  264. codex/static_root/assets/reader-N2Ei_y2K.css.gz +0 -0
  265. codex/static_root/assets/{relation-chips-D6T6FX7G.css → relation-chips-Bsc-gUxE.98e65384c3c9.css} +1 -1
  266. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
  267. codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
  268. codex/static_root/assets/{relation-chips-D6T6FX7G.db095b2497e4.css → relation-chips-Bsc-gUxE.css} +1 -1
  269. codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
  270. codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
  271. codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js +1 -0
  272. codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js.br +0 -0
  273. codex/static_root/assets/relation-chips-CAC5ytqH.f205d920ffea.js.gz +0 -0
  274. codex/static_root/assets/relation-chips-CAC5ytqH.js +1 -0
  275. codex/static_root/assets/relation-chips-CAC5ytqH.js.br +0 -0
  276. codex/static_root/assets/relation-chips-CAC5ytqH.js.gz +0 -0
  277. codex/static_root/assets/{settings-drawer-DbJuixzh.352aaaab5925.js → settings-drawer-CT73ieiV.fac6f69e86ff.js} +2 -2
  278. codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js.br +0 -0
  279. codex/static_root/assets/settings-drawer-CT73ieiV.fac6f69e86ff.js.gz +0 -0
  280. codex/static_root/assets/{settings-drawer-DbJuixzh.js → settings-drawer-CT73ieiV.js} +2 -2
  281. codex/static_root/assets/settings-drawer-CT73ieiV.js.br +0 -0
  282. codex/static_root/assets/settings-drawer-CT73ieiV.js.gz +0 -0
  283. codex/static_root/assets/{settings-drawer-BJOfMtqA.6471a50fc05b.css → settings-drawer-nZ9HvKSZ.01744e131806.css} +1 -1
  284. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
  285. codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
  286. codex/static_root/assets/{settings-drawer-BJOfMtqA.css → settings-drawer-nZ9HvKSZ.css} +1 -1
  287. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
  288. codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
  289. codex/static_root/assets/{stats-tab-BhRLh7XB.7be95b5021c3.js → stats-tab-C6qAnV6-.f08c46b5f141.js} +1 -1
  290. codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js.br +0 -0
  291. codex/static_root/assets/stats-tab-C6qAnV6-.f08c46b5f141.js.gz +0 -0
  292. codex/static_root/assets/{stats-tab-BhRLh7XB.js → stats-tab-C6qAnV6-.js} +1 -1
  293. codex/static_root/assets/stats-tab-C6qAnV6-.js.br +0 -0
  294. codex/static_root/assets/stats-tab-C6qAnV6-.js.gz +0 -0
  295. codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js +1 -0
  296. codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js.br +0 -0
  297. codex/static_root/assets/task-tab-JcG2vvgt.eed6e03db487.js.gz +0 -0
  298. codex/static_root/assets/task-tab-JcG2vvgt.js +1 -0
  299. codex/static_root/assets/task-tab-JcG2vvgt.js.br +0 -0
  300. codex/static_root/assets/task-tab-JcG2vvgt.js.gz +0 -0
  301. codex/static_root/assets/{unauthorized-BgXQnNpn.f63b36d526b5.js → unauthorized-28bmiwAU.c74825b5c16f.js} +1 -1
  302. codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js.br +0 -0
  303. codex/static_root/assets/unauthorized-28bmiwAU.c74825b5c16f.js.gz +0 -0
  304. codex/static_root/assets/{unauthorized-BgXQnNpn.js → unauthorized-28bmiwAU.js} +1 -1
  305. codex/static_root/assets/unauthorized-28bmiwAU.js.br +0 -0
  306. codex/static_root/assets/unauthorized-28bmiwAU.js.gz +0 -0
  307. codex/static_root/assets/{user-tab-cZoJDlb-.2a497e8b2b24.js → user-tab-BOq4BMMc.b64ac15dd772.js} +1 -1
  308. codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js.br +0 -0
  309. codex/static_root/assets/user-tab-BOq4BMMc.b64ac15dd772.js.gz +0 -0
  310. codex/static_root/assets/{user-tab-cZoJDlb-.js → user-tab-BOq4BMMc.js} +1 -1
  311. codex/static_root/assets/user-tab-BOq4BMMc.js.br +0 -0
  312. codex/static_root/assets/user-tab-BOq4BMMc.js.gz +0 -0
  313. codex/static_root/js/choices-admin.ef1ff3a8b9da.json +1 -0
  314. codex/static_root/js/choices-admin.ef1ff3a8b9da.json.br +0 -0
  315. codex/static_root/js/choices-admin.ef1ff3a8b9da.json.gz +0 -0
  316. codex/static_root/js/choices-admin.json +1 -1
  317. codex/static_root/js/choices-admin.json.br +0 -0
  318. codex/static_root/js/choices-admin.json.gz +0 -0
  319. codex/static_root/js/{choices.b60817ba0a93.json → choices.23fedac2b9ba.json} +1 -1
  320. codex/static_root/js/choices.23fedac2b9ba.json.br +0 -0
  321. codex/static_root/js/choices.23fedac2b9ba.json.gz +0 -0
  322. codex/static_root/js/choices.json +1 -1
  323. codex/static_root/js/choices.json.br +0 -0
  324. codex/static_root/js/choices.json.gz +0 -0
  325. codex/static_root/manifest.ad9da9714e46.json +642 -0
  326. codex/static_root/manifest.ad9da9714e46.json.br +0 -0
  327. codex/static_root/manifest.ad9da9714e46.json.gz +0 -0
  328. codex/static_root/manifest.json +256 -256
  329. codex/static_root/manifest.json.br +0 -0
  330. codex/static_root/manifest.json.gz +0 -0
  331. codex/static_root/staticfiles.json +1 -1
  332. codex/status_controller.py +11 -2
  333. codex/views/admin/library.py +47 -27
  334. codex/views/admin/stats.py +4 -1
  335. codex/views/admin/tasks.py +6 -1
  336. codex/views/browser/base.py +3 -3
  337. codex/views/browser/browser.py +1 -9
  338. codex/views/browser/browser_annotations.py +101 -20
  339. codex/views/browser/browser_breadcrumbs.py +2 -2
  340. codex/views/browser/browser_order_by.py +31 -20
  341. codex/views/browser/filters/field.py +3 -1
  342. codex/views/browser/filters/search.py +1 -1
  343. codex/views/browser/metadata.py +0 -1
  344. codex/views/cover.py +3 -2
  345. codex/views/session.py +1 -0
  346. {codex-1.6.0a4.dist-info → codex-1.6.0a6.dist-info}/METADATA +3 -2
  347. {codex-1.6.0a4.dist-info → codex-1.6.0a6.dist-info}/RECORD +350 -347
  348. codex/static_root/assets/VCheckbox-CN6Sn_k7.9bf27129091c.js.br +0 -0
  349. codex/static_root/assets/VCheckbox-CN6Sn_k7.9bf27129091c.js.gz +0 -0
  350. codex/static_root/assets/VCheckbox-CN6Sn_k7.js.br +0 -0
  351. codex/static_root/assets/VCheckbox-CN6Sn_k7.js.gz +0 -0
  352. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.b763c6a7abaf.js.br +0 -0
  353. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.b763c6a7abaf.js.gz +0 -0
  354. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.js.br +0 -0
  355. codex/static_root/assets/VCheckboxBtn-CN1RPZPQ.js.gz +0 -0
  356. codex/static_root/assets/VCombobox-CtLUGYtD.6e49097cea2c.js +0 -1
  357. codex/static_root/assets/VCombobox-CtLUGYtD.6e49097cea2c.js.br +0 -0
  358. codex/static_root/assets/VCombobox-CtLUGYtD.6e49097cea2c.js.gz +0 -0
  359. codex/static_root/assets/VCombobox-CtLUGYtD.js +0 -1
  360. codex/static_root/assets/VCombobox-CtLUGYtD.js.br +0 -0
  361. codex/static_root/assets/VCombobox-CtLUGYtD.js.gz +0 -0
  362. codex/static_root/assets/VDialog-DC3KGsMT.71e2a460e44b.js.br +0 -0
  363. codex/static_root/assets/VDialog-DC3KGsMT.71e2a460e44b.js.gz +0 -0
  364. codex/static_root/assets/VDialog-DC3KGsMT.js.br +0 -0
  365. codex/static_root/assets/VDialog-DC3KGsMT.js.gz +0 -0
  366. codex/static_root/assets/VExpansionPanels-DJQYXCn2.f0d471a1b80a.js.br +0 -0
  367. codex/static_root/assets/VExpansionPanels-DJQYXCn2.f0d471a1b80a.js.gz +0 -0
  368. codex/static_root/assets/VExpansionPanels-DJQYXCn2.js.br +0 -0
  369. codex/static_root/assets/VExpansionPanels-DJQYXCn2.js.gz +0 -0
  370. codex/static_root/assets/VRadioGroup-CgodTThy.b20290d57146.js.br +0 -0
  371. codex/static_root/assets/VRadioGroup-CgodTThy.b20290d57146.js.gz +0 -0
  372. codex/static_root/assets/VRadioGroup-CgodTThy.js.br +0 -0
  373. codex/static_root/assets/VRadioGroup-CgodTThy.js.gz +0 -0
  374. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css +0 -1
  375. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.br +0 -0
  376. codex/static_root/assets/VSelect-ARDhJiQK.372c098fb475.css.gz +0 -0
  377. codex/static_root/assets/VSelect-ARDhJiQK.css +0 -1
  378. codex/static_root/assets/VSelect-ARDhJiQK.css.br +0 -0
  379. codex/static_root/assets/VSelect-ARDhJiQK.css.gz +0 -0
  380. codex/static_root/assets/VSelect-B6s-x36h.de1030c82785.js +0 -1
  381. codex/static_root/assets/VSelect-B6s-x36h.de1030c82785.js.br +0 -0
  382. codex/static_root/assets/VSelect-B6s-x36h.de1030c82785.js.gz +0 -0
  383. codex/static_root/assets/VSelect-B6s-x36h.js +0 -1
  384. codex/static_root/assets/VSelect-B6s-x36h.js.br +0 -0
  385. codex/static_root/assets/VSelect-B6s-x36h.js.gz +0 -0
  386. codex/static_root/assets/VSelectionControl-zPp4DXWq.635b7f5aa882.js.br +0 -0
  387. codex/static_root/assets/VSelectionControl-zPp4DXWq.635b7f5aa882.js.gz +0 -0
  388. codex/static_root/assets/VSelectionControl-zPp4DXWq.js.br +0 -0
  389. codex/static_root/assets/VSelectionControl-zPp4DXWq.js.gz +0 -0
  390. codex/static_root/assets/VSlideGroup-pK9Xompk.67014b86fc74.js.br +0 -0
  391. codex/static_root/assets/VSlideGroup-pK9Xompk.67014b86fc74.js.gz +0 -0
  392. codex/static_root/assets/VSlideGroup-pK9Xompk.js.br +0 -0
  393. codex/static_root/assets/VSlideGroup-pK9Xompk.js.gz +0 -0
  394. codex/static_root/assets/VTable-CMi6sDtP.8445f91d9023.js.br +0 -0
  395. codex/static_root/assets/VTable-CMi6sDtP.8445f91d9023.js.gz +0 -0
  396. codex/static_root/assets/VTable-CMi6sDtP.js.br +0 -0
  397. codex/static_root/assets/VTable-CMi6sDtP.js.gz +0 -0
  398. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +0 -1
  399. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
  400. codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
  401. codex/static_root/assets/VTextField-DuIIp-VY.css +0 -1
  402. codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
  403. codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
  404. codex/static_root/assets/VTextField-dMTizQEN.e93eefec0fdf.js +0 -1
  405. codex/static_root/assets/VTextField-dMTizQEN.e93eefec0fdf.js.br +0 -0
  406. codex/static_root/assets/VTextField-dMTizQEN.e93eefec0fdf.js.gz +0 -0
  407. codex/static_root/assets/VTextField-dMTizQEN.js +0 -1
  408. codex/static_root/assets/VTextField-dMTizQEN.js.br +0 -0
  409. codex/static_root/assets/VTextField-dMTizQEN.js.gz +0 -0
  410. codex/static_root/assets/VWindowItem-DNsRQx9l.c42f636c386e.js.br +0 -0
  411. codex/static_root/assets/VWindowItem-DNsRQx9l.c42f636c386e.js.gz +0 -0
  412. codex/static_root/assets/VWindowItem-DNsRQx9l.js.br +0 -0
  413. codex/static_root/assets/VWindowItem-DNsRQx9l.js.gz +0 -0
  414. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
  415. codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
  416. codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
  417. codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
  418. codex/static_root/assets/admin-CApphpty.5a41cbf0eefc.js +0 -1
  419. codex/static_root/assets/admin-CApphpty.5a41cbf0eefc.js.br +0 -0
  420. codex/static_root/assets/admin-CApphpty.5a41cbf0eefc.js.gz +0 -0
  421. codex/static_root/assets/admin-CApphpty.js +0 -1
  422. codex/static_root/assets/admin-CApphpty.js.br +0 -0
  423. codex/static_root/assets/admin-CApphpty.js.gz +0 -0
  424. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +0 -1
  425. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
  426. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
  427. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +0 -1
  428. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
  429. codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
  430. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.a79eda3055a7.js +0 -30
  431. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.a79eda3055a7.js.br +0 -0
  432. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.a79eda3055a7.js.gz +0 -0
  433. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.js +0 -30
  434. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.js.br +0 -0
  435. codex/static_root/assets/admin-drawer-panel-Dp8KD9D7.js.gz +0 -0
  436. codex/static_root/assets/browser-C_PHQbyN.bb065e2ce21a.js +0 -1
  437. codex/static_root/assets/browser-C_PHQbyN.bb065e2ce21a.js.br +0 -0
  438. codex/static_root/assets/browser-C_PHQbyN.bb065e2ce21a.js.gz +0 -0
  439. codex/static_root/assets/browser-C_PHQbyN.js +0 -1
  440. codex/static_root/assets/browser-C_PHQbyN.js.br +0 -0
  441. codex/static_root/assets/browser-C_PHQbyN.js.gz +0 -0
  442. codex/static_root/assets/browser-DVI4LUNv.css +0 -1
  443. codex/static_root/assets/browser-DVI4LUNv.css.br +0 -0
  444. codex/static_root/assets/browser-DVI4LUNv.css.gz +0 -0
  445. codex/static_root/assets/browser-DVI4LUNv.fef2b997145a.css +0 -1
  446. codex/static_root/assets/browser-DVI4LUNv.fef2b997145a.css.br +0 -0
  447. codex/static_root/assets/browser-DVI4LUNv.fef2b997145a.css.gz +0 -0
  448. codex/static_root/assets/change-password-dialog-BHTkpC6Z.2d5d5dd57ad3.js.br +0 -0
  449. codex/static_root/assets/change-password-dialog-BHTkpC6Z.2d5d5dd57ad3.js.gz +0 -0
  450. codex/static_root/assets/change-password-dialog-BHTkpC6Z.js.br +0 -0
  451. codex/static_root/assets/change-password-dialog-BHTkpC6Z.js.gz +0 -0
  452. codex/static_root/assets/confirm-dialog-DmXGDg4z.284812bd376d.js.br +0 -0
  453. codex/static_root/assets/confirm-dialog-DmXGDg4z.284812bd376d.js.gz +0 -0
  454. codex/static_root/assets/confirm-dialog-DmXGDg4z.js.br +0 -0
  455. codex/static_root/assets/confirm-dialog-DmXGDg4z.js.gz +0 -0
  456. codex/static_root/assets/datetime-column-DKaH5S3z.5c6042016200.js.br +0 -0
  457. codex/static_root/assets/datetime-column-DKaH5S3z.5c6042016200.js.gz +0 -0
  458. codex/static_root/assets/datetime-column-DKaH5S3z.js.br +0 -0
  459. codex/static_root/assets/datetime-column-DKaH5S3z.js.gz +0 -0
  460. codex/static_root/assets/filter-DUVATQW3.e1e7aa6d0b18.js.br +0 -0
  461. codex/static_root/assets/filter-DUVATQW3.e1e7aa6d0b18.js.gz +0 -0
  462. codex/static_root/assets/filter-DUVATQW3.js.br +0 -0
  463. codex/static_root/assets/filter-DUVATQW3.js.gz +0 -0
  464. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +0 -1
  465. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
  466. codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
  467. codex/static_root/assets/flag-tab-BacZLlVi.css +0 -1
  468. codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
  469. codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
  470. codex/static_root/assets/flag-tab-CSKMJEn-.5d5c0604541b.js +0 -1
  471. codex/static_root/assets/flag-tab-CSKMJEn-.5d5c0604541b.js.br +0 -0
  472. codex/static_root/assets/flag-tab-CSKMJEn-.5d5c0604541b.js.gz +0 -0
  473. codex/static_root/assets/flag-tab-CSKMJEn-.js +0 -1
  474. codex/static_root/assets/flag-tab-CSKMJEn-.js.br +0 -0
  475. codex/static_root/assets/flag-tab-CSKMJEn-.js.gz +0 -0
  476. codex/static_root/assets/group-tab-D3CyU-Il.fbf9ce5ef593.js.br +0 -0
  477. codex/static_root/assets/group-tab-D3CyU-Il.fbf9ce5ef593.js.gz +0 -0
  478. codex/static_root/assets/group-tab-D3CyU-Il.js.br +0 -0
  479. codex/static_root/assets/group-tab-D3CyU-Il.js.gz +0 -0
  480. codex/static_root/assets/http-error-0ef6-Ajc.e2879bcd242f.js.br +0 -0
  481. codex/static_root/assets/http-error-0ef6-Ajc.e2879bcd242f.js.gz +0 -0
  482. codex/static_root/assets/http-error-0ef6-Ajc.js.br +0 -0
  483. codex/static_root/assets/http-error-0ef6-Ajc.js.gz +0 -0
  484. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +0 -1
  485. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +0 -2
  486. codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
  487. codex/static_root/assets/library-tab-CXbkxNZt.css +0 -1
  488. codex/static_root/assets/library-tab-CXbkxNZt.css.br +0 -2
  489. codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
  490. codex/static_root/assets/library-tab-IdDfLn4k.70b55460b8e1.js +0 -1
  491. codex/static_root/assets/library-tab-IdDfLn4k.70b55460b8e1.js.br +0 -0
  492. codex/static_root/assets/library-tab-IdDfLn4k.70b55460b8e1.js.gz +0 -0
  493. codex/static_root/assets/library-tab-IdDfLn4k.js +0 -1
  494. codex/static_root/assets/library-tab-IdDfLn4k.js.br +0 -0
  495. codex/static_root/assets/library-tab-IdDfLn4k.js.gz +0 -0
  496. codex/static_root/assets/main-CuzJev9T.f58771cc37e2.js.br +0 -0
  497. codex/static_root/assets/main-CuzJev9T.f58771cc37e2.js.gz +0 -0
  498. codex/static_root/assets/main-CuzJev9T.js.br +0 -0
  499. codex/static_root/assets/main-CuzJev9T.js.gz +0 -0
  500. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
  501. codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
  502. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
  503. codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
  504. codex/static_root/assets/pagination-toolbar-CLr9RVHd.66d3b756d974.js +0 -1
  505. codex/static_root/assets/pagination-toolbar-CLr9RVHd.66d3b756d974.js.br +0 -0
  506. codex/static_root/assets/pagination-toolbar-CLr9RVHd.66d3b756d974.js.gz +0 -0
  507. codex/static_root/assets/pagination-toolbar-CLr9RVHd.js +0 -1
  508. codex/static_root/assets/pagination-toolbar-CLr9RVHd.js.br +0 -0
  509. codex/static_root/assets/pagination-toolbar-CLr9RVHd.js.gz +0 -0
  510. codex/static_root/assets/pdf-doc-BlU5mm5n.0431bcdf48d1.js.br +0 -0
  511. codex/static_root/assets/pdf-doc-BlU5mm5n.0431bcdf48d1.js.gz +0 -0
  512. codex/static_root/assets/pdf-doc-BlU5mm5n.js.br +0 -0
  513. codex/static_root/assets/pdf-doc-BlU5mm5n.js.gz +0 -0
  514. codex/static_root/assets/reader-BiKxuxj7.css.br +0 -0
  515. codex/static_root/assets/reader-BiKxuxj7.css.gz +0 -0
  516. codex/static_root/assets/reader-BiKxuxj7.dabe592f46f1.css.br +0 -0
  517. codex/static_root/assets/reader-BiKxuxj7.dabe592f46f1.css.gz +0 -0
  518. codex/static_root/assets/reader-s7jttB_F.f7b43b04961a.js.br +0 -0
  519. codex/static_root/assets/reader-s7jttB_F.f7b43b04961a.js.gz +0 -0
  520. codex/static_root/assets/reader-s7jttB_F.js.br +0 -0
  521. codex/static_root/assets/reader-s7jttB_F.js.gz +0 -0
  522. codex/static_root/assets/relation-chips-BiVsYCd9.27eca8616ba4.js +0 -1
  523. codex/static_root/assets/relation-chips-BiVsYCd9.27eca8616ba4.js.br +0 -0
  524. codex/static_root/assets/relation-chips-BiVsYCd9.27eca8616ba4.js.gz +0 -0
  525. codex/static_root/assets/relation-chips-BiVsYCd9.js +0 -1
  526. codex/static_root/assets/relation-chips-BiVsYCd9.js.br +0 -0
  527. codex/static_root/assets/relation-chips-BiVsYCd9.js.gz +0 -0
  528. codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
  529. codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
  530. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
  531. codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
  532. codex/static_root/assets/settings-drawer-BJOfMtqA.6471a50fc05b.css.br +0 -0
  533. codex/static_root/assets/settings-drawer-BJOfMtqA.6471a50fc05b.css.gz +0 -0
  534. codex/static_root/assets/settings-drawer-BJOfMtqA.css.br +0 -0
  535. codex/static_root/assets/settings-drawer-BJOfMtqA.css.gz +0 -0
  536. codex/static_root/assets/settings-drawer-DbJuixzh.352aaaab5925.js.br +0 -0
  537. codex/static_root/assets/settings-drawer-DbJuixzh.352aaaab5925.js.gz +0 -0
  538. codex/static_root/assets/settings-drawer-DbJuixzh.js.br +0 -0
  539. codex/static_root/assets/settings-drawer-DbJuixzh.js.gz +0 -0
  540. codex/static_root/assets/stats-tab-BhRLh7XB.7be95b5021c3.js.br +0 -0
  541. codex/static_root/assets/stats-tab-BhRLh7XB.7be95b5021c3.js.gz +0 -0
  542. codex/static_root/assets/stats-tab-BhRLh7XB.js.br +0 -0
  543. codex/static_root/assets/stats-tab-BhRLh7XB.js.gz +0 -0
  544. codex/static_root/assets/task-tab-DLrd54z2.c574111658b9.js +0 -1
  545. codex/static_root/assets/task-tab-DLrd54z2.c574111658b9.js.br +0 -0
  546. codex/static_root/assets/task-tab-DLrd54z2.c574111658b9.js.gz +0 -0
  547. codex/static_root/assets/task-tab-DLrd54z2.js +0 -1
  548. codex/static_root/assets/task-tab-DLrd54z2.js.br +0 -0
  549. codex/static_root/assets/task-tab-DLrd54z2.js.gz +0 -0
  550. codex/static_root/assets/unauthorized-BgXQnNpn.f63b36d526b5.js.br +0 -0
  551. codex/static_root/assets/unauthorized-BgXQnNpn.f63b36d526b5.js.gz +0 -0
  552. codex/static_root/assets/unauthorized-BgXQnNpn.js.br +0 -0
  553. codex/static_root/assets/unauthorized-BgXQnNpn.js.gz +0 -0
  554. codex/static_root/assets/user-tab-cZoJDlb-.2a497e8b2b24.js.br +0 -0
  555. codex/static_root/assets/user-tab-cZoJDlb-.2a497e8b2b24.js.gz +0 -0
  556. codex/static_root/assets/user-tab-cZoJDlb-.js.br +0 -0
  557. codex/static_root/assets/user-tab-cZoJDlb-.js.gz +0 -0
  558. codex/static_root/js/choices-admin.248384ece3f6.json +0 -1
  559. codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
  560. codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
  561. codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
  562. codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
  563. codex/static_root/manifest.3c2f342d5f5a.json +0 -642
  564. codex/static_root/manifest.3c2f342d5f5a.json.br +0 -0
  565. codex/static_root/manifest.3c2f342d5f5a.json.gz +0 -0
  566. {codex-1.6.0a4.dist-info → codex-1.6.0a6.dist-info}/LICENSE +0 -0
  567. {codex-1.6.0a4.dist-info → codex-1.6.0a6.dist-info}/WHEEL +0 -0
  568. {codex-1.6.0a4.dist-info → codex-1.6.0a6.dist-info}/entry_points.txt +0 -0
@@ -2,30 +2,93 @@
2
2
 
3
3
  import re
4
4
  from os import fsdecode
5
+ from pathlib import Path
6
+ from types import MappingProxyType
5
7
 
6
8
  from comicbox.box import Comicbox
7
9
  from watchdog.events import (
8
10
  EVENT_TYPE_CLOSED,
9
11
  EVENT_TYPE_CREATED,
12
+ EVENT_TYPE_DELETED,
13
+ EVENT_TYPE_MODIFIED,
10
14
  EVENT_TYPE_MOVED,
11
15
  EVENT_TYPE_OPENED,
12
16
  FileCreatedEvent,
13
17
  FileDeletedEvent,
18
+ FileModifiedEvent,
19
+ FileMovedEvent,
14
20
  FileSystemEventHandler,
15
21
  )
16
22
 
17
23
  from codex.librarian.watchdog.tasks import WatchdogEventTask
18
24
  from codex.logger.logging import get_logger
19
25
  from codex.logger_base import LoggerBaseMixin
26
+ from codex.models import CustomCover
27
+ from codex.settings.settings import CUSTOM_COVERS_DIR, CUSTOM_COVERS_GROUP_DIRS
20
28
 
21
29
  LOG = get_logger(__name__)
30
+ _IMAGE_EXTS = frozenset({"jpg", "jpeg", "webp", "png", "gif", "bmp"})
31
+ _GROUP_COVERS_DIRS = frozenset(CUSTOM_COVERS_GROUP_DIRS)
22
32
 
23
33
 
24
- class CodexLibraryEventHandler(FileSystemEventHandler, LoggerBaseMixin):
25
- """Handle watchdog events for comics in a library."""
34
+ class CoverMovedEvent(FileMovedEvent):
35
+ """Cover Modified."""
36
+
37
+ is_cover = True
38
+ is_synthetic = True
39
+
40
+
41
+ class CoverModifiedEvent(FileModifiedEvent):
42
+ """Cover Modified."""
43
+
44
+ is_cover = True
45
+ is_synthetic = True
46
+
47
+
48
+ class CoverCreatedEvent(FileCreatedEvent):
49
+ """Cover Created."""
50
+
51
+ is_cover = True
52
+ is_synthetic = True
53
+
54
+
55
+ class CoverDeletedEvent(FileDeletedEvent):
56
+ """Cover Deleted."""
57
+
58
+ is_cover = True
59
+ is_synthetic = True
60
+
61
+
62
+ COVERS_EVENT_TYPE_MAP = MappingProxyType(
63
+ {
64
+ EVENT_TYPE_MODIFIED: CoverModifiedEvent,
65
+ EVENT_TYPE_CREATED: CoverCreatedEvent,
66
+ EVENT_TYPE_DELETED: CoverDeletedEvent,
67
+ }
68
+ )
69
+
70
+
71
+ class CodexEventHandlerBase(FileSystemEventHandler, LoggerBaseMixin):
72
+ """Base class for Codex Event Handlers."""
26
73
 
27
74
  IGNORED_EVENTS = frozenset({EVENT_TYPE_CLOSED, EVENT_TYPE_OPENED})
28
75
 
76
+ def __init__(self, library, *args, **kwargs):
77
+ """Let us send along he library id."""
78
+ self.library_pk = library.pk
79
+ self.librarian_queue = kwargs.pop("librarian_queue")
80
+ log_queue = kwargs.pop("log_queue")
81
+ self.init_logger(log_queue)
82
+ super().__init__(*args, **kwargs)
83
+
84
+ @staticmethod
85
+ def _match_image_suffix(path: Path) -> bool:
86
+ return path and len(path.suffix) > 1 and path.suffix[1:] in _IMAGE_EXTS
87
+
88
+
89
+ class CodexLibraryEventHandler(CodexEventHandlerBase):
90
+ """Handle watchdog events for comics in a library."""
91
+
29
92
  def _set_comic_matcher(self):
30
93
  comic_regex = r"\.(cb[zt"
31
94
  unsupported = []
@@ -45,12 +108,8 @@ class CodexLibraryEventHandler(FileSystemEventHandler, LoggerBaseMixin):
45
108
  un_str = ", ".join(unsupported)
46
109
  LOG.warning(f"Cannot detect or read from {un_str} archives")
47
110
 
48
- def __init__(self, library, *args, **kwargs):
111
+ def __init__(self, *args, **kwargs):
49
112
  """Let us send along he library id."""
50
- self.library_pk = library.pk
51
- self.librarian_queue = kwargs.pop("librarian_queue")
52
- log_queue = kwargs.pop("log_queue")
53
- self.init_logger(log_queue)
54
113
  self._set_comic_matcher()
55
114
  super().__init__(*args, **kwargs)
56
115
 
@@ -63,48 +122,129 @@ class CodexLibraryEventHandler(FileSystemEventHandler, LoggerBaseMixin):
63
122
  suffix = fsdecode(suffix)
64
123
  return self._comic_matcher.match(suffix) is not None
65
124
 
125
+ @classmethod
126
+ def _match_folder_cover(cls, path_str: str) -> bool:
127
+ if not path_str:
128
+ return False
129
+ path = Path(path_str)
130
+ return path.stem == CustomCover.FOLDER_COVER_STEM and cls._match_image_suffix(
131
+ path
132
+ )
133
+
134
+ def _transform_file_move_event(
135
+ self,
136
+ event,
137
+ events,
138
+ source_match_comic,
139
+ ):
140
+ """Create file events for file moves."""
141
+ dest_match_comic = self._match_comic_suffix(event.dest_path)
142
+ if not source_match_comic and dest_match_comic:
143
+ # Moved from an ignored file extension into a comic type,
144
+ # so create a new comic.
145
+ events.append(FileCreatedEvent(event.dest_path))
146
+ elif source_match_comic and not dest_match_comic:
147
+ # moved into something that's not a comic name so delete
148
+ events.append(FileDeletedEvent(event.src_path))
149
+ elif source_match_comic and dest_match_comic:
150
+ events.append(event)
151
+
152
+ def _transform_file_move_event_to_cover_event(self, event, events):
153
+ """Create cover events for file moves."""
154
+ source_match_cover = self._match_folder_cover(event.src_path)
155
+ dest_match_cover = self._match_folder_cover(event.dest_path)
156
+ if source_match_cover and not dest_match_cover:
157
+ events.append(CoverDeletedEvent(event.src_path))
158
+ elif not source_match_cover and dest_match_cover:
159
+ events.append(CoverCreatedEvent(event.dest_path))
160
+ elif source_match_cover and dest_match_cover:
161
+ events.append(CoverMovedEvent(event.src_path, event.dest_path))
162
+
66
163
  def _transform_file_event(self, event):
67
164
  """Transform file events into other events."""
68
- source_match = self._match_comic_suffix(event.src_path)
165
+ events = []
166
+ source_match_comic = self._match_comic_suffix(event.src_path)
69
167
  if event.event_type == EVENT_TYPE_MOVED:
70
- # Some types of file moves need to be cast as other events.
71
- dest_match = self._match_comic_suffix(event.dest_path)
72
- if not source_match and dest_match:
73
- # Moved from an ignored file extension into a comic type,
74
- # so create a new comic.
75
- event = FileCreatedEvent(event.dest_path)
76
- elif source_match and not dest_match:
77
- # moved into something that's not a comic name so delete
78
- event = FileDeletedEvent(event.src_path)
79
- elif not source_match:
80
- # Don't process non-moved, non-comic files at all
81
- event = None
82
- return event
83
-
84
- def _transform_event(self, event):
85
- """Transform events into other events."""
86
- if event.event_type in self.IGNORED_EVENTS:
87
- event = None
88
- elif event.is_directory:
89
- if event.event_type == EVENT_TYPE_CREATED:
90
- # Directories are only created by comics
91
- event = None
168
+ self._transform_file_move_event(event, events, source_match_comic)
169
+ self._transform_file_move_event_to_cover_event(event, events)
170
+ elif source_match_comic:
171
+ events.append(event)
92
172
  else:
93
- event = self._transform_file_event(event)
94
- return event
173
+ source_match_cover = self._match_folder_cover(event.src_path)
174
+ if source_match_cover and (
175
+ event_class := COVERS_EVENT_TYPE_MAP.get(event.event_type)
176
+ ):
177
+ # Convert to cover type
178
+ event = event_class(event.src_path)
179
+ events.append(event)
180
+ return events
95
181
 
96
182
  def dispatch(self, event):
97
183
  """Send only valid codex events to the EventBatcher."""
98
184
  try:
99
- event = self._transform_event(event)
100
- if not event:
185
+ if event.event_type in self.IGNORED_EVENTS or (
186
+ event.is_directory and event.event_type == EVENT_TYPE_CREATED
187
+ ):
188
+ # Directories are only created by comics
101
189
  return
102
190
 
191
+ events = self._transform_file_event(event)
192
+
103
193
  # Send it to the EventBatcher
104
- task = WatchdogEventTask(self.library_pk, event)
105
- self.librarian_queue.put(task)
194
+ for event in events:
195
+ task = WatchdogEventTask(self.library_pk, event)
196
+ self.librarian_queue.put(task)
106
197
 
107
198
  # Calls stub event dispatchers
108
199
  # super().dispatch(event)
109
200
  except Exception:
110
201
  self.log.exception(f"{self.__class__.__name__} dispatch")
202
+
203
+
204
+ class CodexCustomCoverEventHandler(CodexEventHandlerBase):
205
+ """Special event handler for the custom cover dir."""
206
+
207
+ @classmethod
208
+ def _match_group_cover_image(cls, path_str: str) -> bool:
209
+ path = Path(path_str)
210
+ parent = path.parent
211
+ if (
212
+ parent.parent != CUSTOM_COVERS_DIR
213
+ or str(parent.name) not in _GROUP_COVERS_DIRS
214
+ ):
215
+ return False
216
+ return cls._match_image_suffix(path)
217
+
218
+ @classmethod
219
+ def _transform_event_moved(cls, event, src_cover_match):
220
+ """Get a cover event for a file moved event."""
221
+ send_event = None
222
+ dest_cover_match = cls._match_group_cover_image(event.dest_path)
223
+ if src_cover_match and dest_cover_match:
224
+ send_event = CoverMovedEvent(event.src_path, event.dest_path)
225
+ elif not src_cover_match and dest_cover_match:
226
+ send_event = CoverCreatedEvent(event.dest_path)
227
+ elif src_cover_match and not dest_cover_match:
228
+ send_event = CoverDeletedEvent(event.src_path)
229
+ return send_event
230
+
231
+ def dispatch(self, event):
232
+ """Send only valid cover events to the EventBatcher."""
233
+ send_event = None
234
+ try:
235
+ if event.is_directory or event.event_type in self.IGNORED_EVENTS:
236
+ return
237
+ src_cover_match = self._match_group_cover_image(event.src_path)
238
+ if event.event_type == EVENT_TYPE_MOVED:
239
+ send_event = self._transform_event_moved(event, src_cover_match)
240
+ elif src_cover_match:
241
+ event_class = COVERS_EVENT_TYPE_MAP.get(event.event_type)
242
+ if not event_class:
243
+ return
244
+ send_event = event_class(event.src_path)
245
+
246
+ if send_event:
247
+ task = WatchdogEventTask(self.library_pk, send_event)
248
+ self.librarian_queue.put(task)
249
+ except Exception:
250
+ self.log.exception(f"{self.__class__.__name__} dispatch")
@@ -8,7 +8,10 @@ from watchdog.observers.api import (
8
8
  )
9
9
 
10
10
  from codex.librarian.watchdog.emitter import DatabasePollingEmitter
11
- from codex.librarian.watchdog.events import CodexLibraryEventHandler
11
+ from codex.librarian.watchdog.events import (
12
+ CodexCustomCoverEventHandler,
13
+ CodexLibraryEventHandler,
14
+ )
12
15
  from codex.models import Library
13
16
  from codex.worker_base import WorkerBaseMixin
14
17
 
@@ -50,7 +53,12 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
50
53
  return
51
54
 
52
55
  # Set up the watch
53
- handler = CodexLibraryEventHandler(
56
+ handler_class = (
57
+ CodexCustomCoverEventHandler
58
+ if library.covers_only
59
+ else CodexLibraryEventHandler
60
+ )
61
+ handler = handler_class(
54
62
  library,
55
63
  librarian_queue=self.librarian_queue,
56
64
  log_queue=self.log_queue,
@@ -109,12 +117,14 @@ class UatuMixin(BaseObserver, WorkerBaseMixin):
109
117
 
110
118
  # If we don't have an emitter for this watch already, create it.
111
119
  if self._emitter_for_watch.get(watch) is None:
120
+ covers_only = isinstance(event_handler, CodexCustomCoverEventHandler)
112
121
  emitter = self._emitter_class(
113
122
  event_queue=self.event_queue,
114
123
  watch=watch,
115
124
  timeout=self.timeout,
116
125
  log_queue=self.log_queue,
117
126
  librarian_queue=self.librarian_queue,
127
+ covers_only=covers_only,
118
128
  )
119
129
  self._add_emitter(emitter)
120
130
  if self.is_alive():
@@ -149,7 +159,7 @@ class LibraryPollingObserver(UatuMixin):
149
159
  def poll(self, library_pks, force=False):
150
160
  """Poll each requested emitter."""
151
161
  try:
152
- qs = Library.objects
162
+ qs = Library.objects.all()
153
163
  if library_pks:
154
164
  qs = qs.filter(pk__in=library_pks)
155
165
  paths = frozenset(qs.values_list("path", flat=True))
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
 
7
7
  from django.db import migrations, models
8
8
 
9
- from codex.models.const import ARTICLES
9
+ from codex.models.util import get_sort_name
10
10
 
11
11
  CONFIG_PATH = Path(os.environ.get("CODEX_CONFIG_DIR", Path.cwd() / "config"))
12
12
  COVER_ROOT = CONFIG_PATH / "cache" / "covers"
@@ -23,17 +23,7 @@ _GROUP_MODEL_NAMES = (
23
23
 
24
24
  def _set_sort_name(obj):
25
25
  """Create sort_name for model."""
26
- # Duplicate code from BaseGroupModel
27
- sort_name = ""
28
- lower_name = obj.name.lower()
29
- sort_name = lower_name
30
- name_parts = lower_name.split()
31
- if len(name_parts) > 1:
32
- first_word = name_parts[0]
33
- if first_word in ARTICLES:
34
- sort_name = " ".join(name_parts[1:])
35
- sort_name += ", " + first_word
36
- obj.sort_name = sort_name
26
+ obj.sort_name = get_sort_name(obj.name)
37
27
 
38
28
 
39
29
  def _generate_sort_name(apps, _schema_editor):
@@ -0,0 +1,166 @@
1
+ """Generated by Django 5.0.6 on 2024-05-23 03:50."""
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ """Migrate db."""
9
+
10
+ dependencies = [
11
+ ("codex", "0027_sort_name"),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.RemoveField(
16
+ model_name="failedimport",
17
+ name="sort_name",
18
+ ),
19
+ migrations.AddField(
20
+ model_name="library",
21
+ name="covers_only",
22
+ field=models.BooleanField(db_index=True, default=False),
23
+ ),
24
+ migrations.AlterField(
25
+ model_name="librarianstatus",
26
+ name="status_type",
27
+ field=models.CharField(
28
+ choices=[
29
+ ("CCC", "Create Covers"),
30
+ ("CCD", "Purge Covers"),
31
+ ("CFO", "Find Orphan"),
32
+ ("IDM", "Dirs Moved"),
33
+ ("IFM", "Files Moved"),
34
+ ("ITR", "Aggregate Tags"),
35
+ ("ITQ", "Query Missing Fks"),
36
+ ("ITC", "Create Fks"),
37
+ ("IDU", "Dirs Modified"),
38
+ ("IFU", "Files Modified"),
39
+ ("IFC", "Files Created"),
40
+ ("IMQ", "Query M2M Fields"),
41
+ ("IMC", "Link M2M Fields"),
42
+ ("IDD", "Dirs Deleted"),
43
+ ("IFD", "Files Deleted"),
44
+ ("IFI", "Failed Imports"),
45
+ ("ICQ", "Query Missing Covers"),
46
+ ("ICM", "Covers Moved"),
47
+ ("ICU", "Covers Modified"),
48
+ ("ICC", "Covers Created"),
49
+ ("ICD", "Covers Deleted"),
50
+ ("ICL", "Covers Link"),
51
+ ("JTD", "Cleanup Fk"),
52
+ ("JCU", "Codex Update"),
53
+ ("JCR", "Codex Restart"),
54
+ ("JCS", "Codex Stop"),
55
+ ("JDO", "Db Optimize"),
56
+ ("JDB", "Db Backup"),
57
+ ("JSD", "Cleanup Sessions"),
58
+ ("JCD", "Cleanup Covers"),
59
+ ("SIX", "Search Index Clear"),
60
+ ("SIU", "Search Index Update"),
61
+ ("SID", "Search Index Remove"),
62
+ ("SIM", "Search Index Merge"),
63
+ ("WPO", "Poll"),
64
+ ],
65
+ db_index=True,
66
+ max_length=3,
67
+ ),
68
+ ),
69
+ migrations.CreateModel(
70
+ name="CustomCover",
71
+ fields=[
72
+ (
73
+ "id",
74
+ models.AutoField(
75
+ auto_created=True,
76
+ primary_key=True,
77
+ serialize=False,
78
+ verbose_name="ID",
79
+ ),
80
+ ),
81
+ ("created_at", models.DateTimeField(auto_now_add=True)),
82
+ ("updated_at", models.DateTimeField(auto_now=True)),
83
+ ("path", models.CharField(db_index=True, max_length=4095)),
84
+ ("stat", models.JSONField(null=True)),
85
+ (
86
+ "group",
87
+ models.CharField(
88
+ choices=[
89
+ ("p", "P"),
90
+ ("i", "I"),
91
+ ("s", "S"),
92
+ ("a", "A"),
93
+ ("f", "F"),
94
+ ],
95
+ db_index=True,
96
+ max_length=1,
97
+ ),
98
+ ),
99
+ (
100
+ "sort_name",
101
+ models.CharField(db_index=True, default="", max_length=128),
102
+ ),
103
+ (
104
+ "library",
105
+ models.ForeignKey(
106
+ on_delete=django.db.models.deletion.CASCADE, to="codex.library"
107
+ ),
108
+ ),
109
+ ],
110
+ options={
111
+ "get_latest_by": "updated_at",
112
+ "abstract": False,
113
+ "unique_together": {("library", "path")},
114
+ },
115
+ ),
116
+ migrations.AddField(
117
+ model_name="folder",
118
+ name="custom_cover",
119
+ field=models.ForeignKey(
120
+ default=None,
121
+ null=True,
122
+ on_delete=django.db.models.deletion.SET_DEFAULT,
123
+ to="codex.customcover",
124
+ ),
125
+ ),
126
+ migrations.AddField(
127
+ model_name="imprint",
128
+ name="custom_cover",
129
+ field=models.ForeignKey(
130
+ default=None,
131
+ null=True,
132
+ on_delete=django.db.models.deletion.SET_DEFAULT,
133
+ to="codex.customcover",
134
+ ),
135
+ ),
136
+ migrations.AddField(
137
+ model_name="publisher",
138
+ name="custom_cover",
139
+ field=models.ForeignKey(
140
+ default=None,
141
+ null=True,
142
+ on_delete=django.db.models.deletion.SET_DEFAULT,
143
+ to="codex.customcover",
144
+ ),
145
+ ),
146
+ migrations.AddField(
147
+ model_name="series",
148
+ name="custom_cover",
149
+ field=models.ForeignKey(
150
+ default=None,
151
+ null=True,
152
+ on_delete=django.db.models.deletion.SET_DEFAULT,
153
+ to="codex.customcover",
154
+ ),
155
+ ),
156
+ migrations.AddField(
157
+ model_name="storyarc",
158
+ name="custom_cover",
159
+ field=models.ForeignKey(
160
+ default=None,
161
+ null=True,
162
+ on_delete=django.db.models.deletion.SET_DEFAULT,
163
+ to="codex.customcover",
164
+ ),
165
+ ),
166
+ ]
@@ -0,0 +1,44 @@
1
+ """Generated by Django 5.0.6 on 2024-05-25 21:19."""
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ """Migrate db."""
8
+
9
+ dependencies = [
10
+ ("codex", "0028_custom_covers"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name="adminflag",
16
+ name="key",
17
+ field=models.CharField(
18
+ choices=[
19
+ ("FV", "Folder View"),
20
+ ("RG", "Registration"),
21
+ ("NU", "Non Users"),
22
+ ("AU", "Auto Update"),
23
+ ("SO", "Search Index Optimize"),
24
+ ("IM", "Import Metadata"),
25
+ ],
26
+ db_index=True,
27
+ max_length=2,
28
+ ),
29
+ ),
30
+ migrations.AlterField(
31
+ model_name="timestamp",
32
+ name="key",
33
+ field=models.CharField(
34
+ choices=[
35
+ ("JA", "Janitor"),
36
+ ("VR", "Codex Version"),
37
+ ("SI", "Search Index UUID"),
38
+ ("AP", "API Key"),
39
+ ],
40
+ db_index=True,
41
+ max_length=2,
42
+ ),
43
+ ),
44
+ ]
codex/models/admin.py CHANGED
@@ -39,7 +39,6 @@ class AdminFlag(BaseModel):
39
39
  AUTO_UPDATE = "AU"
40
40
  SEARCH_INDEX_OPTIMIZE = "SO"
41
41
  IMPORT_METADATA = "IM"
42
- DYNAMIC_GROUP_COVERS = "DC"
43
42
 
44
43
  FALSE_DEFAULTS = frozenset({FlagChoices.AUTO_UPDATE})
45
44
 
@@ -83,7 +82,6 @@ class Timestamp(BaseModel):
83
82
  class TimestampChoices(TextChoices):
84
83
  """Choices for Timestamps."""
85
84
 
86
- COVERS = "CV", _("Covers")
87
85
  JANITOR = "JA", _("Janitor")
88
86
  CODEX_VERSION = "VR", _("Codex Version")
89
87
  SEARCH_INDEX_UUID = "SI", _("Search Index UUID")
codex/models/base.py CHANGED
@@ -23,3 +23,6 @@ class BaseModel(Model):
23
23
 
24
24
  abstract = True
25
25
  get_latest_by = "updated_at"
26
+
27
+ def presave(self):
28
+ """Create values before save."""
codex/models/comic.py CHANGED
@@ -22,7 +22,14 @@ from django.db.models import (
22
22
  TextField,
23
23
  )
24
24
 
25
- from codex.models.groups import Imprint, Publisher, Series, Volume
25
+ from codex.models.groups import (
26
+ Folder,
27
+ Imprint,
28
+ Publisher,
29
+ Series,
30
+ Volume,
31
+ WatchedPathBrowserGroup,
32
+ )
26
33
  from codex.models.named import (
27
34
  AgeRating,
28
35
  Character,
@@ -41,7 +48,6 @@ from codex.models.named import (
41
48
  Tagger,
42
49
  Team,
43
50
  )
44
- from codex.models.paths import Folder, WatchedPath
45
51
 
46
52
  __all__ = ("Comic",)
47
53
 
@@ -55,7 +61,7 @@ class ReadingDirection(Choices):
55
61
  BTT = ReadingDirectionEnum.BTT.value
56
62
 
57
63
 
58
- class Comic(WatchedPath):
64
+ class Comic(WatchedPathBrowserGroup):
59
65
  """Comic metadata."""
60
66
 
61
67
  class FileType(Choices):
@@ -168,10 +174,14 @@ class Comic(WatchedPath):
168
174
  default="",
169
175
  )
170
176
 
171
- class Meta(WatchedPath.Meta):
177
+ # Not useful
178
+ custom_cover = None
179
+
180
+ class Meta(WatchedPathBrowserGroup.Meta):
172
181
  """Constraints."""
173
182
 
174
- unique_together = ("library", "path")
183
+ # TODO see if this inherits properly
184
+ # unique_together = ("library", "path")
175
185
  verbose_name = "Issue"
176
186
 
177
187
  def _set_date(self):
@@ -279,7 +289,3 @@ class Comic(WatchedPath):
279
289
  if self.issue_suffix:
280
290
  res += self.issue_suffix
281
291
  return res
282
-
283
- def search_path(self) -> str:
284
- """Relative path for search index."""
285
- return self.path.removeprefix(self.library.path)