django-spire 0.23.7__py3-none-any.whl → 0.23.9__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.
Files changed (542) hide show
  1. django_spire/ai/admin.py +11 -11
  2. django_spire/ai/chat/apps.py +1 -0
  3. django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +1 -1
  4. django_spire/ai/chat/tests/factories.py +15 -0
  5. django_spire/ai/chat/tests/test_controller.py +45 -0
  6. django_spire/ai/chat/tests/test_models.py +301 -0
  7. django_spire/ai/chat/tests/test_prompts.py +48 -0
  8. django_spire/ai/chat/tests/test_responses.py +208 -0
  9. django_spire/ai/chat/tests/test_router/test_base_chat_router.py +66 -6
  10. django_spire/ai/chat/tests/test_router/test_chat_workflow.py +73 -3
  11. django_spire/ai/chat/tests/test_router/test_integration.py +86 -6
  12. django_spire/ai/chat/tests/test_router/test_intent_decoder.py +93 -1
  13. django_spire/ai/chat/tests/test_router/test_message_intel.py +60 -1
  14. django_spire/ai/chat/tests/test_router/test_spire_chat_router.py +110 -0
  15. django_spire/ai/chat/tests/test_urls/test_json_urls.py +202 -1
  16. django_spire/ai/context/tests/__init__.py +0 -0
  17. django_spire/ai/context/tests/test_context.py +188 -0
  18. django_spire/ai/decorators.py +7 -6
  19. django_spire/ai/prompt/tests/test_bots.py +100 -10
  20. django_spire/ai/prompt/tests/test_prompt_intel.py +83 -0
  21. django_spire/ai/prompt/tests/test_prompt_tuning.py +126 -0
  22. django_spire/ai/sms/decorators.py +8 -2
  23. django_spire/ai/sms/tests/test_sms.py +240 -16
  24. django_spire/ai/sms/tests/test_sms_intel.py +42 -0
  25. django_spire/ai/sms/tests/test_webhook.py +155 -7
  26. django_spire/ai/sms/views.py +23 -24
  27. django_spire/ai/tests/test_ai.py +131 -7
  28. django_spire/auth/apps.py +4 -2
  29. django_spire/auth/controller/controller.py +36 -23
  30. django_spire/auth/controller/exceptions.py +9 -0
  31. django_spire/auth/group/admin.py +1 -0
  32. django_spire/auth/group/apps.py +2 -0
  33. django_spire/auth/group/factories.py +17 -8
  34. django_spire/auth/group/forms.py +7 -0
  35. django_spire/auth/group/tests/test_factories.py +146 -0
  36. django_spire/auth/group/tests/test_forms.py +282 -0
  37. django_spire/auth/group/tests/test_models.py +192 -0
  38. django_spire/auth/group/tests/test_querysets.py +98 -0
  39. django_spire/auth/group/tests/test_utils.py +341 -0
  40. django_spire/auth/group/tests/test_views.py +377 -0
  41. django_spire/auth/group/urls/__init__.py +3 -1
  42. django_spire/auth/group/urls/form_urls.py +2 -0
  43. django_spire/auth/group/urls/json_urls.py +3 -0
  44. django_spire/auth/group/urls/page_urls.py +2 -0
  45. django_spire/auth/group/utils.py +6 -2
  46. django_spire/auth/group/views/form_views.py +6 -3
  47. django_spire/auth/group/views/json_views.py +6 -2
  48. django_spire/auth/mfa/admin.py +2 -0
  49. django_spire/auth/mfa/apps.py +2 -0
  50. django_spire/auth/mfa/forms.py +1 -0
  51. django_spire/auth/mfa/querysets.py +9 -2
  52. django_spire/auth/mfa/tests/test_models.py +233 -0
  53. django_spire/auth/mfa/tests/test_utils.py +106 -0
  54. django_spire/auth/mfa/urls/__init__.py +2 -0
  55. django_spire/auth/mfa/urls/page_urls.py +2 -0
  56. django_spire/auth/mfa/urls/redirect_urls.py +2 -0
  57. django_spire/auth/mfa/views/page_views.py +2 -1
  58. django_spire/auth/permissions/consts.py +2 -2
  59. django_spire/auth/permissions/decorators.py +8 -8
  60. django_spire/auth/permissions/permissions.py +28 -35
  61. django_spire/auth/permissions/tests/test_decorators.py +333 -0
  62. django_spire/auth/permissions/tests/test_permissions.py +337 -0
  63. django_spire/auth/permissions/tests/test_tools.py +305 -0
  64. django_spire/auth/permissions/tools.py +21 -15
  65. django_spire/auth/seeding/seed.py +3 -0
  66. django_spire/auth/seeding/seeder.py +2 -0
  67. django_spire/auth/tests/test_controller.py +323 -0
  68. django_spire/auth/tests/test_url_endpoints.py +9 -9
  69. django_spire/auth/tests/test_views.py +406 -0
  70. django_spire/auth/urls/admin_urls.py +2 -0
  71. django_spire/auth/urls/redirect_urls.py +2 -0
  72. django_spire/auth/user/apps.py +2 -0
  73. django_spire/auth/user/forms.py +9 -0
  74. django_spire/auth/user/models.py +1 -1
  75. django_spire/auth/user/services/services.py +1 -0
  76. django_spire/auth/user/tests/factories.py +14 -13
  77. django_spire/auth/user/tests/test_factories.py +166 -2
  78. django_spire/auth/user/tests/test_forms.py +573 -0
  79. django_spire/auth/user/tests/test_models.py +257 -0
  80. django_spire/auth/user/tests/test_services.py +200 -0
  81. django_spire/auth/user/tests/test_tools.py +153 -0
  82. django_spire/auth/user/tests/test_user_factories.py +139 -0
  83. django_spire/auth/user/tests/test_views.py +363 -0
  84. django_spire/auth/user/tools.py +7 -1
  85. django_spire/auth/user/urls/form_urls.py +3 -0
  86. django_spire/auth/user/urls/page_urls.py +3 -0
  87. django_spire/auth/user/views/form_views.py +19 -10
  88. django_spire/auth/user/views/page_views.py +8 -2
  89. django_spire/auth/views/redirect_views.py +14 -9
  90. django_spire/comment/admin.py +2 -0
  91. django_spire/comment/apps.py +2 -0
  92. django_spire/comment/templatetags/comment_tags.py +1 -0
  93. django_spire/comment/tests/test_forms.py +27 -0
  94. django_spire/comment/tests/test_models.py +215 -0
  95. django_spire/comment/tests/test_querysets.py +101 -0
  96. django_spire/comment/tests/test_utils.py +90 -0
  97. django_spire/comment/urls.py +2 -0
  98. django_spire/comment/utils.py +22 -13
  99. django_spire/comment/views.py +1 -1
  100. django_spire/conf.py +8 -6
  101. django_spire/consts.py +1 -1
  102. django_spire/contrib/breadcrumb/apps.py +2 -0
  103. django_spire/contrib/breadcrumb/breadcrumbs.py +18 -18
  104. django_spire/contrib/breadcrumb/tests/test_breadcrumbs.py +198 -0
  105. django_spire/contrib/constructor/__init__.py +3 -3
  106. django_spire/contrib/constructor/constructor.py +15 -15
  107. django_spire/contrib/constructor/django_model_constructor.py +5 -4
  108. django_spire/contrib/constructor/exceptions.py +5 -3
  109. django_spire/contrib/constructor/tests/__init__.py +0 -0
  110. django_spire/contrib/constructor/tests/test_constructor.py +193 -0
  111. django_spire/contrib/form/tests/__init__.py +0 -0
  112. django_spire/contrib/form/tests/test_forms.py +203 -0
  113. django_spire/contrib/generic_views/modal_views.py +2 -1
  114. django_spire/contrib/generic_views/portal_views.py +20 -19
  115. django_spire/contrib/generic_views/tests/__init__.py +0 -0
  116. django_spire/contrib/generic_views/tests/test_views.py +459 -0
  117. django_spire/contrib/help/apps.py +2 -0
  118. django_spire/contrib/help/templatetags/help.py +1 -0
  119. django_spire/contrib/help/tests/__init__.py +0 -0
  120. django_spire/contrib/help/tests/test_templatetags.py +100 -0
  121. django_spire/contrib/options/mixins.py +6 -5
  122. django_spire/contrib/options/tests/factories.py +5 -1
  123. django_spire/contrib/options/tests/test_options.py +234 -0
  124. django_spire/contrib/ordering/exceptions.py +7 -3
  125. django_spire/contrib/ordering/mixins.py +2 -0
  126. django_spire/contrib/ordering/querysets.py +3 -1
  127. django_spire/contrib/ordering/services/processor_service.py +8 -4
  128. django_spire/contrib/ordering/services/service.py +1 -2
  129. django_spire/contrib/ordering/tests/__init__.py +0 -0
  130. django_spire/contrib/ordering/tests/test_ordering.py +165 -0
  131. django_spire/contrib/ordering/validators.py +6 -6
  132. django_spire/contrib/pagination/templatetags/pagination_tags.py +12 -5
  133. django_spire/contrib/pagination/tests/__init__.py +0 -0
  134. django_spire/contrib/pagination/tests/test_pagination.py +179 -0
  135. django_spire/contrib/performance/decorators.py +16 -6
  136. django_spire/contrib/performance/tests/__init__.py +0 -0
  137. django_spire/contrib/performance/tests/test_performance.py +107 -0
  138. django_spire/contrib/queryset/enums.py +3 -1
  139. django_spire/contrib/queryset/filter_tools.py +10 -5
  140. django_spire/contrib/queryset/mixins.py +16 -16
  141. django_spire/contrib/queryset/tests/__init__.py +0 -0
  142. django_spire/contrib/queryset/tests/test_queryset.py +137 -0
  143. django_spire/contrib/seeding/field/base.py +13 -7
  144. django_spire/contrib/seeding/field/callable.py +8 -1
  145. django_spire/contrib/seeding/field/cleaners.py +5 -5
  146. django_spire/contrib/seeding/field/custom.py +20 -10
  147. django_spire/contrib/seeding/field/django/seeder.py +8 -6
  148. django_spire/contrib/seeding/field/enums.py +7 -5
  149. django_spire/contrib/seeding/field/override.py +16 -6
  150. django_spire/contrib/seeding/field/static.py +9 -2
  151. django_spire/contrib/seeding/field/tests/test_base.py +18 -14
  152. django_spire/contrib/seeding/field/tests/test_callable.py +13 -9
  153. django_spire/contrib/seeding/field/tests/test_cleaners.py +51 -38
  154. django_spire/contrib/seeding/field/tests/test_static.py +13 -9
  155. django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +2 -0
  156. django_spire/contrib/seeding/intelligence/intel.py +5 -1
  157. django_spire/contrib/seeding/intelligence/prompts/factory.py +6 -1
  158. django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +6 -1
  159. django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +2 -0
  160. django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +7 -1
  161. django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +6 -2
  162. django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +8 -2
  163. django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +2 -0
  164. django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +6 -1
  165. django_spire/contrib/seeding/management/commands/seeding.py +9 -3
  166. django_spire/contrib/seeding/management/example.py +2 -0
  167. django_spire/contrib/seeding/model/base.py +16 -7
  168. django_spire/contrib/seeding/model/config.py +31 -15
  169. django_spire/contrib/seeding/model/django/config.py +13 -13
  170. django_spire/contrib/seeding/model/django/seeder.py +4 -4
  171. django_spire/contrib/seeding/model/django/tests/test_seeder.py +34 -23
  172. django_spire/contrib/seeding/model/enums.py +2 -0
  173. django_spire/contrib/seeding/tests/test_config.py +71 -0
  174. django_spire/contrib/seeding/tests/test_custom.py +35 -0
  175. django_spire/contrib/seeding/tests/test_enums.py +40 -0
  176. django_spire/contrib/seeding/tests/test_intel.py +32 -0
  177. django_spire/contrib/seeding/tests/test_override.py +63 -0
  178. django_spire/contrib/service/__init__.py +2 -2
  179. django_spire/contrib/service/django_model_service.py +16 -15
  180. django_spire/contrib/service/exceptions.py +5 -3
  181. django_spire/contrib/service/tests/__init__.py +0 -0
  182. django_spire/contrib/service/tests/test_service.py +153 -0
  183. django_spire/contrib/session/apps.py +2 -0
  184. django_spire/contrib/session/controller.py +48 -42
  185. django_spire/contrib/session/templatetags/session_tags.py +11 -2
  186. django_spire/contrib/session/tests/test_session_controller.py +117 -53
  187. django_spire/contrib/tests/__init__.py +0 -0
  188. django_spire/contrib/tests/test_utils.py +37 -0
  189. django_spire/contrib/utils.py +4 -1
  190. django_spire/core/apps.py +2 -0
  191. django_spire/core/converters/tests/test_to_data.py +353 -0
  192. django_spire/core/converters/tests/test_to_enums.py +61 -41
  193. django_spire/core/converters/tests/test_to_pydantic.py +138 -109
  194. django_spire/core/converters/to_data.py +29 -10
  195. django_spire/core/converters/to_enums.py +4 -2
  196. django_spire/core/converters/to_pydantic.py +22 -22
  197. django_spire/core/decorators.py +19 -6
  198. django_spire/core/forms/widgets.py +4 -0
  199. django_spire/core/management/commands/spire_startapp_pkg/template/app/apps.py.template +2 -0
  200. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/__init__.py.template +2 -0
  201. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/form_urls.py.template +2 -0
  202. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/page_urls.py.template +2 -0
  203. django_spire/core/maps.py +3 -1
  204. django_spire/core/middleware/maintenance.py +3 -3
  205. django_spire/core/middleware.py +8 -6
  206. django_spire/core/redirect/__init__.py +5 -0
  207. django_spire/core/redirect/generic_redirect.py +1 -2
  208. django_spire/core/redirect/tests/__init__.py +0 -0
  209. django_spire/core/redirect/tests/test_generic_redirect.py +34 -0
  210. django_spire/core/{tests/tests_redirect.py → redirect/tests/test_safe_redirect.py} +55 -81
  211. django_spire/core/shortcuts.py +3 -3
  212. django_spire/core/static/django_spire/css/app-layout.css +1 -1
  213. django_spire/core/static/django_spire/css/app-navigation.css +3 -3
  214. django_spire/core/static/django_spire/css/bootstrap-override.css +4 -0
  215. django_spire/core/tag/admin.py +12 -0
  216. django_spire/core/tag/intelligence/tag_set_bot.py +2 -0
  217. django_spire/core/tag/mixins.py +2 -0
  218. django_spire/core/tag/models.py +2 -0
  219. django_spire/core/tag/querysets.py +2 -0
  220. django_spire/core/tag/service/tag_service.py +6 -3
  221. django_spire/core/tag/tests/test_intelligence.py +9 -9
  222. django_spire/core/tag/tests/test_tags.py +44 -54
  223. django_spire/core/tag/tests/test_tools.py +191 -0
  224. django_spire/core/tag/tools.py +3 -0
  225. django_spire/core/templates/django_spire/card/card.html +5 -2
  226. django_spire/core/templates/django_spire/card/title_card.html +12 -4
  227. django_spire/core/templates/django_spire/infinite_scroll/base.html +1 -0
  228. django_spire/core/templates/django_spire/navigation/side_navigation.html +19 -24
  229. django_spire/core/templates/django_spire/page/full_page.html +46 -16
  230. django_spire/core/templates/django_spire/table/base.html +5 -3
  231. django_spire/core/templatetags/json.py +6 -2
  232. django_spire/core/templatetags/message.py +13 -8
  233. django_spire/core/templatetags/string_formating.py +8 -5
  234. django_spire/core/templatetags/tests/__init__.py +0 -0
  235. django_spire/core/templatetags/tests/test_templatetags.py +427 -0
  236. django_spire/core/templatetags/variable_types.py +17 -9
  237. django_spire/core/tests/test_cases.py +1 -1
  238. django_spire/core/tests/test_conf.py +43 -0
  239. django_spire/core/tests/test_consts.py +28 -0
  240. django_spire/core/tests/test_context_processors.py +93 -0
  241. django_spire/core/tests/test_decorators.py +95 -0
  242. django_spire/core/tests/test_django_spire_utils.py +56 -0
  243. django_spire/core/tests/test_exceptions.py +37 -0
  244. django_spire/core/tests/test_models.py +54 -0
  245. django_spire/core/tests/test_settings.py +45 -0
  246. django_spire/core/tests/test_shortcuts.py +74 -0
  247. django_spire/core/tests/test_urls.py +16 -0
  248. django_spire/core/tests/test_utils.py +58 -0
  249. django_spire/core/urls.py +4 -1
  250. django_spire/core/utils.py +12 -8
  251. django_spire/exceptions.py +16 -1
  252. django_spire/file/admin.py +4 -2
  253. django_spire/file/apps.py +8 -10
  254. django_spire/file/fields.py +7 -7
  255. django_spire/file/forms.py +1 -1
  256. django_spire/file/interfaces.py +15 -15
  257. django_spire/file/mixins.py +1 -4
  258. django_spire/file/models.py +3 -5
  259. django_spire/file/tests/factories.py +59 -0
  260. django_spire/file/tests/test_admin.py +69 -0
  261. django_spire/file/tests/test_apps.py +24 -0
  262. django_spire/file/tests/test_fields.py +114 -0
  263. django_spire/file/tests/test_forms.py +20 -0
  264. django_spire/file/tests/test_interfaces.py +183 -0
  265. django_spire/file/tests/test_models.py +82 -0
  266. django_spire/file/tests/test_querysets.py +102 -0
  267. django_spire/file/tests/test_utils.py +32 -0
  268. django_spire/file/tests/test_views.py +145 -0
  269. django_spire/file/tests/test_widgets.py +82 -0
  270. django_spire/file/tools.py +8 -2
  271. django_spire/file/views.py +7 -3
  272. django_spire/file/widgets.py +12 -12
  273. django_spire/help_desk/admin.py +15 -0
  274. django_spire/help_desk/apps.py +2 -0
  275. django_spire/help_desk/auth/controller.py +2 -0
  276. django_spire/help_desk/choices.py +2 -0
  277. django_spire/help_desk/enums.py +2 -0
  278. django_spire/help_desk/exceptions.py +31 -3
  279. django_spire/help_desk/forms.py +2 -0
  280. django_spire/help_desk/models.py +2 -0
  281. django_spire/help_desk/querysets.py +4 -1
  282. django_spire/help_desk/services/notification_service.py +26 -27
  283. django_spire/help_desk/services/service.py +2 -3
  284. django_spire/help_desk/tests/factories.py +8 -3
  285. django_spire/help_desk/tests/test_admin.py +41 -0
  286. django_spire/help_desk/tests/test_apps.py +41 -0
  287. django_spire/help_desk/tests/test_choices.py +50 -0
  288. django_spire/help_desk/tests/test_controller.py +87 -0
  289. django_spire/help_desk/tests/test_enums.py +18 -0
  290. django_spire/help_desk/tests/test_exceptions.py +37 -0
  291. django_spire/help_desk/tests/test_forms.py +89 -0
  292. django_spire/help_desk/tests/test_models.py +59 -0
  293. django_spire/help_desk/tests/test_querysets.py +38 -0
  294. django_spire/help_desk/tests/test_services/test_notification_service.py +15 -8
  295. django_spire/help_desk/tests/test_services/test_service.py +92 -0
  296. django_spire/help_desk/tests/test_urls/test_form_urls.py +6 -6
  297. django_spire/help_desk/tests/test_urls/test_page_urls.py +8 -9
  298. django_spire/help_desk/tests/test_views/test_form_views.py +46 -19
  299. django_spire/help_desk/tests/test_views/test_page_views.py +32 -9
  300. django_spire/help_desk/urls/__init__.py +4 -1
  301. django_spire/help_desk/urls/form_urls.py +3 -0
  302. django_spire/help_desk/urls/page_urls.py +3 -0
  303. django_spire/help_desk/views/form_views.py +13 -5
  304. django_spire/help_desk/views/page_views.py +11 -3
  305. django_spire/history/activity/admin.py +2 -0
  306. django_spire/history/activity/apps.py +3 -1
  307. django_spire/history/activity/mixins.py +13 -7
  308. django_spire/history/activity/models.py +6 -5
  309. django_spire/history/activity/querysets.py +2 -0
  310. django_spire/history/activity/tests/__init__.py +0 -0
  311. django_spire/history/activity/tests/test_activity.py +176 -0
  312. django_spire/history/admin.py +9 -2
  313. django_spire/history/choices.py +3 -0
  314. django_spire/history/models.py +5 -5
  315. django_spire/history/tests/test_admin.py +93 -0
  316. django_spire/history/tests/test_history.py +101 -0
  317. django_spire/history/tests/test_mixins.py +84 -0
  318. django_spire/history/viewed/admin.py +3 -1
  319. django_spire/history/viewed/apps.py +3 -1
  320. django_spire/history/viewed/models.py +2 -0
  321. django_spire/history/viewed/tests/__init__.py +0 -0
  322. django_spire/history/viewed/tests/test_viewed.py +46 -0
  323. django_spire/knowledge/auth/tests/__init__.py +0 -0
  324. django_spire/knowledge/auth/tests/test_controller.py +116 -0
  325. django_spire/knowledge/collection/admin.py +5 -1
  326. django_spire/knowledge/collection/models.py +3 -1
  327. django_spire/knowledge/collection/seeding/seed.py +1 -0
  328. django_spire/knowledge/collection/services/factory_service.py +10 -11
  329. django_spire/knowledge/collection/services/ordering_service.py +1 -2
  330. django_spire/knowledge/collection/services/service.py +5 -10
  331. django_spire/knowledge/collection/services/tag_service.py +5 -2
  332. django_spire/knowledge/collection/tests/factories.py +28 -1
  333. django_spire/knowledge/collection/tests/test_models.py +48 -0
  334. django_spire/knowledge/collection/tests/test_querysets.py +93 -0
  335. django_spire/knowledge/collection/tests/test_services/test_factory_service.py +100 -0
  336. django_spire/knowledge/collection/tests/test_services/test_services.py +160 -0
  337. django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +21 -3
  338. django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +39 -1
  339. django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +12 -4
  340. django_spire/knowledge/collection/urls/__init__.py +3 -0
  341. django_spire/knowledge/collection/urls/form_urls.py +2 -0
  342. django_spire/knowledge/collection/urls/json_urls.py +2 -0
  343. django_spire/knowledge/collection/urls/page_urls.py +2 -0
  344. django_spire/knowledge/collection/views/form_views.py +4 -4
  345. django_spire/knowledge/collection/views/json_views.py +5 -1
  346. django_spire/knowledge/collection/views/page_views.py +5 -2
  347. django_spire/knowledge/entry/admin.py +7 -1
  348. django_spire/knowledge/entry/forms.py +2 -0
  349. django_spire/knowledge/entry/models.py +2 -0
  350. django_spire/knowledge/entry/seeding/seed.py +3 -0
  351. django_spire/knowledge/entry/services/automation_service.py +5 -4
  352. django_spire/knowledge/entry/services/factory_service.py +7 -5
  353. django_spire/knowledge/entry/services/service.py +4 -7
  354. django_spire/knowledge/entry/services/tag_service.py +0 -1
  355. django_spire/knowledge/entry/services/tool_service.py +1 -0
  356. django_spire/knowledge/entry/services/transformation_services.py +1 -5
  357. django_spire/knowledge/entry/tests/factories.py +1 -2
  358. django_spire/knowledge/entry/tests/test_factory_service.py +20 -0
  359. django_spire/knowledge/entry/tests/test_models.py +41 -0
  360. django_spire/knowledge/entry/tests/test_querysets.py +71 -0
  361. django_spire/knowledge/entry/tests/test_services.py +94 -0
  362. django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +9 -14
  363. django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +48 -5
  364. django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +6 -8
  365. django_spire/knowledge/entry/tests/test_urls/test_template_urls.py +40 -0
  366. django_spire/knowledge/entry/urls/form_urls.py +2 -0
  367. django_spire/knowledge/entry/urls/json_urls.py +2 -0
  368. django_spire/knowledge/entry/urls/page_urls.py +2 -0
  369. django_spire/knowledge/entry/urls/template_urls.py +2 -0
  370. django_spire/knowledge/entry/version/block/choices.py +2 -0
  371. django_spire/knowledge/entry/version/block/data/data.py +1 -0
  372. django_spire/knowledge/entry/version/block/data/list/data.py +8 -13
  373. django_spire/knowledge/entry/version/block/data/list/maps.py +3 -0
  374. django_spire/knowledge/entry/version/block/data/list/meta.py +1 -2
  375. django_spire/knowledge/entry/version/block/data/list/tests/__init__.py +0 -0
  376. django_spire/knowledge/entry/version/block/data/list/tests/test_maps.py +32 -0
  377. django_spire/knowledge/entry/version/block/data/list/tests/test_meta.py +58 -0
  378. django_spire/knowledge/entry/version/block/data/maps.py +3 -6
  379. django_spire/knowledge/entry/version/block/models.py +7 -5
  380. django_spire/knowledge/entry/version/block/seeding/constants.py +5 -4
  381. django_spire/knowledge/entry/version/block/services/service.py +2 -3
  382. django_spire/knowledge/entry/version/block/tests/factories.py +4 -10
  383. django_spire/knowledge/entry/version/block/tests/test_choices.py +56 -0
  384. django_spire/knowledge/entry/version/block/tests/test_data.py +90 -0
  385. django_spire/knowledge/entry/version/block/tests/test_maps.py +37 -0
  386. django_spire/knowledge/entry/version/block/tests/test_models.py +55 -0
  387. django_spire/knowledge/entry/version/block/tests/test_querysets.py +35 -0
  388. django_spire/knowledge/entry/version/block/tests/test_services.py +65 -0
  389. django_spire/knowledge/entry/version/choices.py +2 -0
  390. django_spire/knowledge/entry/version/converters/converter.py +1 -1
  391. django_spire/knowledge/entry/version/converters/docx_converter.py +4 -7
  392. django_spire/knowledge/entry/version/converters/markdown_converter.py +20 -20
  393. django_spire/knowledge/entry/version/maps.py +4 -5
  394. django_spire/knowledge/entry/version/querysets.py +1 -1
  395. django_spire/knowledge/entry/version/seeding/seeder.py +1 -2
  396. django_spire/knowledge/entry/version/services/processor_service.py +5 -4
  397. django_spire/knowledge/entry/version/services/service.py +1 -2
  398. django_spire/knowledge/entry/version/tests/factories.py +2 -2
  399. django_spire/knowledge/entry/version/tests/test_choices.py +18 -0
  400. django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +56 -8
  401. django_spire/knowledge/entry/version/tests/test_converters/test_markdown_converter.py +78 -0
  402. django_spire/knowledge/entry/version/tests/test_maps.py +58 -0
  403. django_spire/knowledge/entry/version/tests/test_models.py +23 -0
  404. django_spire/knowledge/entry/version/tests/test_querysets.py +26 -0
  405. django_spire/knowledge/entry/version/tests/test_services.py +62 -0
  406. django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +27 -8
  407. django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +15 -8
  408. django_spire/knowledge/entry/version/tests/test_urls/test_redirect_urls.py +38 -0
  409. django_spire/knowledge/entry/version/urls/__init__.py +3 -0
  410. django_spire/knowledge/entry/version/urls/json_urls.py +2 -1
  411. django_spire/knowledge/entry/version/urls/page_urls.py +2 -0
  412. django_spire/knowledge/entry/version/urls/redirect_urls.py +2 -0
  413. django_spire/knowledge/entry/version/views/json_views.py +5 -1
  414. django_spire/knowledge/entry/version/views/page_views.py +10 -3
  415. django_spire/knowledge/entry/version/views/redirect_views.py +5 -1
  416. django_spire/knowledge/entry/views/form_views.py +16 -8
  417. django_spire/knowledge/entry/views/json_views.py +3 -1
  418. django_spire/knowledge/entry/views/page_views.py +8 -2
  419. django_spire/knowledge/entry/views/template_views.py +7 -1
  420. django_spire/knowledge/exceptions.py +2 -1
  421. django_spire/knowledge/intelligence/intel/answer_intel.py +2 -1
  422. django_spire/knowledge/intelligence/intel/entry_intel.py +0 -1
  423. django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +4 -5
  424. django_spire/knowledge/models.py +1 -2
  425. django_spire/knowledge/tests/__init__.py +0 -0
  426. django_spire/knowledge/tests/test_templatetags.py +40 -0
  427. django_spire/knowledge/tests/test_urls/__init__.py +0 -0
  428. django_spire/knowledge/tests/test_urls/test_page_urls.py +24 -0
  429. django_spire/knowledge/urls/__init__.py +2 -0
  430. django_spire/knowledge/urls/page_urls.py +2 -0
  431. django_spire/knowledge/views/page_views.py +8 -3
  432. django_spire/notification/admin.py +3 -1
  433. django_spire/notification/app/admin.py +2 -0
  434. django_spire/notification/app/apps.py +3 -1
  435. django_spire/notification/app/exceptions.py +9 -2
  436. django_spire/notification/app/models.py +8 -4
  437. django_spire/notification/app/processor.py +22 -26
  438. django_spire/notification/app/querysets.py +2 -0
  439. django_spire/notification/app/tests/__init__.py +0 -0
  440. django_spire/notification/app/tests/factories.py +34 -0
  441. django_spire/notification/app/tests/test_apps.py +24 -0
  442. django_spire/notification/app/tests/test_models.py +72 -0
  443. django_spire/notification/app/tests/test_processor.py +111 -0
  444. django_spire/notification/app/tests/test_querysets.py +90 -0
  445. django_spire/notification/app/tests/test_views/__init__.py +0 -0
  446. django_spire/notification/app/tests/test_views/test_json_views.py +48 -0
  447. django_spire/notification/app/tests/test_views/test_page_views.py +19 -0
  448. django_spire/notification/app/urls/__init__.py +3 -1
  449. django_spire/notification/app/urls/json_urls.py +6 -4
  450. django_spire/notification/app/urls/page_urls.py +4 -3
  451. django_spire/notification/app/urls/template_urls.py +4 -2
  452. django_spire/notification/apps.py +4 -1
  453. django_spire/notification/email/admin.py +5 -1
  454. django_spire/notification/email/apps.py +3 -1
  455. django_spire/notification/email/exceptions.py +4 -2
  456. django_spire/notification/email/helper.py +5 -3
  457. django_spire/notification/email/models.py +4 -0
  458. django_spire/notification/email/processor.py +19 -15
  459. django_spire/notification/email/querysets.py +3 -0
  460. django_spire/notification/email/tests/__init__.py +0 -0
  461. django_spire/notification/email/tests/factories.py +35 -0
  462. django_spire/notification/email/tests/test_apps.py +24 -0
  463. django_spire/notification/email/tests/test_models.py +52 -0
  464. django_spire/notification/email/tests/test_processor.py +92 -0
  465. django_spire/notification/email/tests/test_querysets.py +43 -0
  466. django_spire/notification/exceptions.py +17 -2
  467. django_spire/notification/managers.py +7 -1
  468. django_spire/notification/maps.py +4 -1
  469. django_spire/notification/mixins.py +2 -0
  470. django_spire/notification/models.py +3 -1
  471. django_spire/notification/processors/notification.py +12 -5
  472. django_spire/notification/processors/processor.py +2 -0
  473. django_spire/notification/processors/tests/__init__.py +0 -0
  474. django_spire/notification/processors/tests/test_notification.py +106 -0
  475. django_spire/notification/push/admin.py +10 -1
  476. django_spire/notification/push/apps.py +3 -1
  477. django_spire/notification/push/models.py +2 -3
  478. django_spire/notification/push/tests/__init__.py +0 -0
  479. django_spire/notification/push/tests/test_apps.py +24 -0
  480. django_spire/notification/push/tests/test_models.py +28 -0
  481. django_spire/notification/querysets.py +7 -1
  482. django_spire/notification/sms/admin.py +2 -0
  483. django_spire/notification/sms/apps.py +4 -1
  484. django_spire/notification/sms/automations.py +2 -0
  485. django_spire/notification/sms/choices.py +2 -0
  486. django_spire/notification/sms/exceptions.py +19 -5
  487. django_spire/notification/sms/helper.py +33 -23
  488. django_spire/notification/sms/models.py +5 -1
  489. django_spire/notification/sms/processor.py +20 -20
  490. django_spire/notification/sms/querysets.py +2 -0
  491. django_spire/notification/sms/tests/factories.py +33 -0
  492. django_spire/notification/sms/tests/test_apps.py +24 -0
  493. django_spire/notification/sms/tests/test_automation.py +38 -0
  494. django_spire/notification/sms/tests/test_choices.py +15 -0
  495. django_spire/notification/sms/tests/test_consts.py +17 -0
  496. django_spire/notification/sms/tests/test_exceptions.py +27 -0
  497. django_spire/notification/sms/tests/test_helper.py +50 -0
  498. django_spire/notification/sms/tests/test_models.py +81 -0
  499. django_spire/notification/sms/tests/test_processor.py +107 -0
  500. django_spire/notification/sms/tests/test_tools.py +25 -11
  501. django_spire/notification/sms/tools.py +16 -5
  502. django_spire/notification/sms/urls/__init__.py +3 -1
  503. django_spire/notification/sms/urls/media_urls.py +2 -0
  504. django_spire/notification/sms/views/media_views.py +14 -4
  505. django_spire/notification/tests/__init__.py +0 -0
  506. django_spire/notification/tests/factories.py +26 -0
  507. django_spire/notification/tests/test_admin.py +55 -0
  508. django_spire/notification/tests/test_apps.py +30 -0
  509. django_spire/notification/tests/test_automation.py +18 -0
  510. django_spire/notification/tests/test_choices.py +59 -0
  511. django_spire/notification/tests/test_exceptions.py +58 -0
  512. django_spire/notification/tests/test_managers.py +100 -0
  513. django_spire/notification/tests/test_maps.py +31 -0
  514. django_spire/notification/tests/test_models.py +76 -0
  515. django_spire/notification/tests/test_querysets.py +184 -0
  516. django_spire/notification/tests/test_utils.py +23 -0
  517. django_spire/notification/urls.py +3 -1
  518. django_spire/notification/utils.py +3 -1
  519. django_spire/settings.py +3 -0
  520. django_spire/theme/tests/test_context_processor.py +15 -13
  521. django_spire/theme/tests/test_enums.py +2 -2
  522. django_spire/theme/tests/test_filesystem.py +2 -5
  523. django_spire/theme/tests/test_integration.py +12 -12
  524. django_spire/theme/tests/test_model.py +40 -38
  525. django_spire/theme/tests/test_views/test_json_views.py +33 -33
  526. django_spire/theme/urls/json_urls.py +3 -0
  527. django_spire/theme/urls/page_urls.py +3 -0
  528. django_spire/urls.py +19 -15
  529. django_spire/utils.py +13 -4
  530. {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/METADATA +2 -2
  531. {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/RECORD +534 -362
  532. {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/licenses/LICENSE.md +1 -1
  533. django_spire/contrib/options/tests/test_unit.py +0 -148
  534. django_spire/contrib/seeding/tests/test_seeding.py +0 -25
  535. django_spire/core/tests/test_templatetags.py +0 -117
  536. django_spire/core/tests/tests_shortcuts.py +0 -73
  537. django_spire/history/activity/tests.py +0 -3
  538. django_spire/history/activity/views.py +0 -3
  539. django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +0 -71
  540. django_spire/notification/app/tests.py +0 -3
  541. {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/WHEEL +0 -0
  542. {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ from django.test import override_settings
4
+
5
+ from django_spire.core.tests.test_cases import BaseTestCase
6
+ from django_spire.file.models import File
7
+ from django_spire.file.tests.factories import create_test_file
8
+
9
+
10
+ STORAGES_OVERRIDE = {
11
+ 'default': {
12
+ 'BACKEND': 'django.core.files.storage.FileSystemStorage',
13
+ },
14
+ 'staticfiles': {
15
+ 'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage',
16
+ },
17
+ }
18
+
19
+
20
+ @override_settings(STORAGES=STORAGES_OVERRIDE)
21
+ class FileQuerySetTests(BaseTestCase):
22
+ def setUp(self):
23
+ super().setUp()
24
+
25
+ self.file1 = create_test_file(name='File 1')
26
+ self.file2 = create_test_file(name='File 2')
27
+
28
+ def test_active_returns_non_deleted(self):
29
+ result = File.objects.active()
30
+
31
+ assert self.file1 in result
32
+ assert self.file2 in result
33
+
34
+ def test_active_excludes_deleted(self):
35
+ self.file1.is_deleted = True
36
+ self.file1.save()
37
+
38
+ result = File.objects.active()
39
+
40
+ assert self.file1 not in result
41
+ assert self.file2 in result
42
+
43
+ def test_active_excludes_inactive(self):
44
+ self.file1.is_active = False
45
+ self.file1.save()
46
+
47
+ result = File.objects.active()
48
+
49
+ assert self.file1 not in result
50
+ assert self.file2 in result
51
+
52
+ def test_active_excludes_deleted_and_inactive(self):
53
+ self.file1.is_deleted = True
54
+ self.file1.is_active = False
55
+ self.file1.save()
56
+
57
+ result = File.objects.active()
58
+
59
+ assert self.file1 not in result
60
+ assert self.file2 in result
61
+
62
+ def test_related_field_filters_correctly(self):
63
+ self.file1.related_field = 'abc'
64
+ self.file1.save()
65
+ self.file2.related_field = 'xyz'
66
+ self.file2.save()
67
+
68
+ result = File.objects.related_field('abc')
69
+
70
+ assert self.file1 in result
71
+ assert self.file2 not in result
72
+
73
+ def test_related_field_returns_empty_when_no_match(self):
74
+ self.file1.related_field = 'abc'
75
+ self.file1.save()
76
+
77
+ result = File.objects.related_field('xyz')
78
+
79
+ assert result.count() == 0
80
+
81
+ def test_related_field_filters_null_values(self):
82
+ self.file1.related_field = None
83
+ self.file1.save()
84
+ self.file2.related_field = 'abc'
85
+ self.file2.save()
86
+
87
+ result = File.objects.related_field(None)
88
+
89
+ assert self.file1 in result
90
+ assert self.file2 not in result
91
+
92
+ def test_chaining_active_and_related_field(self):
93
+ self.file1.related_field = 'abc'
94
+ self.file1.save()
95
+ self.file2.related_field = 'abc'
96
+ self.file2.is_deleted = True
97
+ self.file2.save()
98
+
99
+ result = File.objects.active().related_field('abc')
100
+
101
+ assert self.file1 in result
102
+ assert self.file2 not in result
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from django_spire.core.tests.test_cases import BaseTestCase
4
+ from django_spire.file.utils import random_64_char_token
5
+
6
+
7
+ class Random64CharTokenTests(BaseTestCase):
8
+ def test_returns_string(self):
9
+ result = random_64_char_token()
10
+
11
+ assert isinstance(result, str)
12
+
13
+ def test_returns_64_characters(self):
14
+ result = random_64_char_token()
15
+
16
+ assert len(result) == 64
17
+
18
+ def test_returns_hexadecimal(self):
19
+ result = random_64_char_token()
20
+
21
+ int(result, 16)
22
+
23
+ def test_returns_unique_values(self):
24
+ result1 = random_64_char_token()
25
+ result2 = random_64_char_token()
26
+
27
+ assert result1 != result2
28
+
29
+ def test_returns_lowercase(self):
30
+ result = random_64_char_token()
31
+
32
+ assert result == result.lower()
@@ -0,0 +1,145 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ from django.test import RequestFactory, override_settings
6
+
7
+ from django_spire.core.tests.test_cases import BaseTestCase
8
+ from django_spire.file.models import File
9
+ from django_spire.file.tests.factories import create_test_in_memory_uploaded_file
10
+ from django_spire.file.views import file_multiple_upload_ajax, file_single_upload_ajax
11
+
12
+
13
+ STORAGES_OVERRIDE = {
14
+ 'default': {
15
+ 'BACKEND': 'django.core.files.storage.FileSystemStorage',
16
+ },
17
+ 'staticfiles': {
18
+ 'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage',
19
+ },
20
+ }
21
+
22
+
23
+ @override_settings(STORAGES=STORAGES_OVERRIDE)
24
+ class FileMultipleUploadAjaxViewTests(BaseTestCase):
25
+ def setUp(self):
26
+ super().setUp()
27
+
28
+ self.factory = RequestFactory()
29
+
30
+ def test_post_creates_files(self):
31
+ initial_count = File.objects.count()
32
+ uploaded_file = create_test_in_memory_uploaded_file()
33
+
34
+ request = self.factory.post(
35
+ '/upload/multiple/ajax',
36
+ data={'related_field': ''},
37
+ )
38
+ request.FILES['file'] = uploaded_file
39
+ request.user = self.super_user
40
+
41
+ file_multiple_upload_ajax(request)
42
+
43
+ assert File.objects.count() == initial_count + 1
44
+
45
+ def test_post_returns_json_response(self):
46
+ uploaded_file = create_test_in_memory_uploaded_file()
47
+
48
+ request = self.factory.post(
49
+ '/upload/multiple/ajax',
50
+ data={'related_field': ''},
51
+ )
52
+ request.FILES['file'] = uploaded_file
53
+ request.user = self.super_user
54
+
55
+ response = file_multiple_upload_ajax(request)
56
+
57
+ assert response.status_code == 200
58
+ assert response['Content-Type'] == 'application/json'
59
+
60
+ def test_post_returns_files_in_response(self):
61
+ uploaded_file = create_test_in_memory_uploaded_file(name='test_upload')
62
+
63
+ request = self.factory.post(
64
+ '/upload/multiple/ajax',
65
+ data={'related_field': ''},
66
+ )
67
+ request.FILES['file'] = uploaded_file
68
+ request.user = self.super_user
69
+
70
+ response = file_multiple_upload_ajax(request)
71
+ data = json.loads(response.content)
72
+
73
+ assert 'files' in data
74
+ assert len(data['files']) == 1
75
+ assert data['files'][0]['name'] == 'test_upload'
76
+
77
+ def test_get_returns_none(self):
78
+ request = self.factory.get('/upload/multiple/ajax')
79
+ request.user = self.super_user
80
+
81
+ response = file_multiple_upload_ajax(request)
82
+
83
+ assert response is None
84
+
85
+
86
+ @override_settings(STORAGES=STORAGES_OVERRIDE)
87
+ class FileSingleUploadAjaxViewTests(BaseTestCase):
88
+ def setUp(self):
89
+ super().setUp()
90
+
91
+ self.factory = RequestFactory()
92
+
93
+ def test_post_creates_file(self):
94
+ initial_count = File.objects.count()
95
+ uploaded_file = create_test_in_memory_uploaded_file()
96
+
97
+ request = self.factory.post(
98
+ '/upload/single/ajax',
99
+ data={'related_field': ''},
100
+ )
101
+ request.FILES['file'] = uploaded_file
102
+ request.user = self.super_user
103
+
104
+ file_single_upload_ajax(request)
105
+
106
+ assert File.objects.count() == initial_count + 1
107
+
108
+ def test_post_returns_json_response(self):
109
+ uploaded_file = create_test_in_memory_uploaded_file()
110
+
111
+ request = self.factory.post(
112
+ '/upload/single/ajax',
113
+ data={'related_field': ''},
114
+ )
115
+ request.FILES['file'] = uploaded_file
116
+ request.user = self.super_user
117
+
118
+ response = file_single_upload_ajax(request)
119
+
120
+ assert response.status_code == 200
121
+ assert response['Content-Type'] == 'application/json'
122
+
123
+ def test_post_returns_file_in_response(self):
124
+ uploaded_file = create_test_in_memory_uploaded_file(name='single_upload')
125
+
126
+ request = self.factory.post(
127
+ '/upload/single/ajax',
128
+ data={'related_field': ''},
129
+ )
130
+ request.FILES['file'] = uploaded_file
131
+ request.user = self.super_user
132
+
133
+ response = file_single_upload_ajax(request)
134
+ data = json.loads(response.content)
135
+
136
+ assert 'file' in data
137
+ assert data['file']['name'] == 'single_upload'
138
+
139
+ def test_get_returns_none(self):
140
+ request = self.factory.get('/upload/single/ajax')
141
+ request.user = self.super_user
142
+
143
+ response = file_single_upload_ajax(request)
144
+
145
+ assert response is None
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ from django_spire.core.tests.test_cases import BaseTestCase
6
+ from django_spire.file.widgets import MultipleWidget, SingleFileWidget
7
+
8
+
9
+ class MultipleWidgetTests(BaseTestCase):
10
+ def setUp(self):
11
+ super().setUp()
12
+
13
+ self.widget = MultipleWidget()
14
+
15
+ def test_needs_multipart_form(self):
16
+ assert self.widget.needs_multipart_form is True
17
+
18
+ def test_template_name(self):
19
+ assert self.widget.template_name == 'django_spire/file/widget/multiple_file_widget.html'
20
+
21
+ def test_value_from_datadict(self):
22
+ data = {
23
+ 'files_data': json.dumps([{'id': 1, 'name': 'test'}])
24
+ }
25
+
26
+ result = self.widget.value_from_datadict(data, None, 'files')
27
+
28
+ assert result == [{'id': 1, 'name': 'test'}]
29
+
30
+ def test_value_from_datadict_empty(self):
31
+ data = {
32
+ 'files_data': json.dumps([])
33
+ }
34
+
35
+ result = self.widget.value_from_datadict(data, None, 'files')
36
+
37
+ assert result == []
38
+
39
+ def test_value_from_datadict_multiple_files(self):
40
+ data = {
41
+ 'files_data': json.dumps([
42
+ {'id': 1, 'name': 'file1'},
43
+ {'id': 2, 'name': 'file2'},
44
+ ])
45
+ }
46
+
47
+ result = self.widget.value_from_datadict(data, None, 'files')
48
+
49
+ assert len(result) == 2
50
+ assert result[0]['name'] == 'file1'
51
+ assert result[1]['name'] == 'file2'
52
+
53
+
54
+ class SingleFileWidgetTests(BaseTestCase):
55
+ def setUp(self):
56
+ super().setUp()
57
+
58
+ self.widget = SingleFileWidget()
59
+
60
+ def test_needs_multipart_form(self):
61
+ assert self.widget.needs_multipart_form is True
62
+
63
+ def test_template_name(self):
64
+ assert self.widget.template_name == 'django_spire/file/widget/single_file_widget.html'
65
+
66
+ def test_value_from_datadict(self):
67
+ data = {
68
+ 'file_data': json.dumps({'id': 1, 'name': 'test'})
69
+ }
70
+
71
+ result = self.widget.value_from_datadict(data, None, 'file')
72
+
73
+ assert result == {'id': 1, 'name': 'test'}
74
+
75
+ def test_value_from_datadict_null(self):
76
+ data = {
77
+ 'file_data': json.dumps(None)
78
+ }
79
+
80
+ result = self.widget.value_from_datadict(data, None, 'file')
81
+
82
+ assert result is None
@@ -1,11 +1,16 @@
1
- from typing import Any
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
2
4
 
3
5
  from django.contrib.contenttypes.models import ContentType
4
6
 
5
7
  from django_spire.file.models import File
6
8
 
9
+ if TYPE_CHECKING:
10
+ from django.db import models
11
+
7
12
 
8
- def copy_files_from_source_to_target_model_object(source: Any, target: Any) -> list[File]:
13
+ def copy_files_from_source_to_target_model_object(source: models.Model, target: models.Model) -> list[File]:
9
14
  target_class = target.__class__
10
15
  target_content_type = ContentType.objects.get_for_model(target_class)
11
16
 
@@ -15,6 +20,7 @@ def copy_files_from_source_to_target_model_object(source: Any, target: Any) -> l
15
20
  return []
16
21
 
17
22
  files_to_create = []
23
+
18
24
  for file in file_list:
19
25
  new_file = File(
20
26
  content_type=target_content_type,
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
12
12
 
13
13
 
14
14
  @login_required()
15
- def file_multiple_upload_ajax(request: WSGIRequest) -> JsonResponse:
15
+ def file_multiple_upload_ajax(request: WSGIRequest) -> JsonResponse | None:
16
16
  if request.method == 'POST':
17
17
  file_uploader = MultiFileUploader(request.POST.get('related_field', ''))
18
18
  files = file_uploader.upload(list(request.FILES.values()))
@@ -21,13 +21,17 @@ def file_multiple_upload_ajax(request: WSGIRequest) -> JsonResponse:
21
21
  'files': [file.to_dict() for file in files]
22
22
  })
23
23
 
24
+ return None
25
+
24
26
 
25
27
  @login_required()
26
- def file_single_upload_ajax(request: WSGIRequest) -> JsonResponse:
28
+ def file_single_upload_ajax(request: WSGIRequest) -> JsonResponse | None:
27
29
  if request.method == 'POST':
28
30
  file_uploader = SingleFileUploader(request.POST.get('related_field', ''))
29
- file = file_uploader.upload(list(request.FILES.values())[0])
31
+ file = file_uploader.upload(next(iter(request.FILES.values())))
30
32
 
31
33
  return JsonResponse({
32
34
  'file': file.to_dict()
33
35
  })
36
+
37
+ return None
@@ -2,38 +2,38 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
 
5
- from django.template import loader
6
- from django.utils.safestring import mark_safe
7
5
  from django.forms import Widget
6
+ from django.template import loader
7
+ from django.utils.safestring import SafeString, mark_safe
8
8
 
9
9
 
10
10
  class MultipleWidget(Widget):
11
11
  needs_multipart_form = True
12
12
  template_name = 'django_spire/file/widget/multiple_file_widget.html'
13
13
 
14
- def __init__(self, *args, **kwargs):
14
+ def __init__(self, *args, **kwargs) -> None:
15
15
  super().__init__(*args, **kwargs)
16
16
 
17
- def value_from_datadict(self, data, files, name):
18
- return json.loads(data.get(f'{name}_data'))
19
-
20
- def render(self, name, value, attrs=None, renderer=None):
17
+ def render(self, name: str, value, attrs: dict | None = None, renderer=None) -> SafeString:
21
18
  context = self.get_context(name, value, attrs)
22
19
  template = loader.get_template(self.template_name).render(context)
23
20
  return mark_safe(template)
24
21
 
22
+ def value_from_datadict(self, data: dict, files, name: str) -> list[dict]:
23
+ return json.loads(data.get(f'{name}_data'))
24
+
25
25
 
26
26
  class SingleFileWidget(Widget):
27
27
  needs_multipart_form = True
28
28
  template_name = 'django_spire/file/widget/single_file_widget.html'
29
29
 
30
- def __init__(self, *args, **kwargs):
30
+ def __init__(self, *args, **kwargs) -> None:
31
31
  super().__init__(*args, **kwargs)
32
32
 
33
- def value_from_datadict(self, data, files, name) -> dict:
34
- return json.loads(data.get(f'{name}_data'))
35
-
36
- def render(self, name, value, attrs=None, renderer=None):
33
+ def render(self, name: str, value, attrs: dict | None = None, renderer=None) -> SafeString:
37
34
  context = self.get_context(name, value, attrs)
38
35
  template = loader.get_template(self.template_name).render(context)
39
36
  return mark_safe(template)
37
+
38
+ def value_from_datadict(self, data: dict, files, name: str) -> dict:
39
+ return json.loads(data.get(f'{name}_data'))
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ from django.contrib import admin
4
+
5
+ from django_spire.help_desk.models import HelpDeskTicket
6
+
7
+
8
+ @admin.register(HelpDeskTicket)
9
+ class HelpDeskTicketAdmin(admin.ModelAdmin):
10
+ list_display = ('pk', 'purpose', 'priority', 'status', 'created_by', 'created_datetime')
11
+ list_filter = ('priority', 'purpose', 'status', 'is_active', 'is_deleted')
12
+ ordering = ('-created_datetime',)
13
+ raw_id_fields = ('created_by',)
14
+ readonly_fields = ('created_by', 'created_datetime', 'is_active', 'is_deleted')
15
+ search_fields = ('description', 'created_by__username', 'created_by__email')
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.apps import AppConfig
2
4
 
3
5
  from django_spire.utils import check_required_apps
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django_spire.auth.controller.controller import BaseAuthController
2
4
 
3
5
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.db import models
2
4
 
3
5
 
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from enum import Enum
2
4
 
3
5
 
@@ -1,5 +1,33 @@
1
- class TicketEventNotificationTypeNotSupportedError(TypeError):
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from django_spire.exceptions import DjangoSpireError
6
+
7
+ if TYPE_CHECKING:
8
+ from django_spire.help_desk.enums import TicketEventType
9
+ from django_spire.notification.choices import NotificationTypeChoices
10
+
11
+
12
+ class HelpDeskError(DjangoSpireError):
2
13
  pass
3
14
 
4
- class HelpDeskNotificationRecipientMissingEmailError(ValueError):
5
- pass
15
+
16
+ class TicketEventNotificationTypeNotSupportedError(HelpDeskError, TypeError):
17
+ def __init__(
18
+ self,
19
+ event_type: TicketEventType | None,
20
+ notification_type: NotificationTypeChoices
21
+ ) -> None:
22
+ if event_type is None:
23
+ super().__init__(f'Notification type not supported: {notification_type}')
24
+ else:
25
+ super().__init__(
26
+ f'Combination of event type and notification type not supported: '
27
+ f'Event type {event_type} - Notification type {notification_type}'
28
+ )
29
+
30
+
31
+ class HelpDeskNotificationRecipientMissingEmailError(HelpDeskError, ValueError):
32
+ def __init__(self, recipient: str) -> None:
33
+ super().__init__(f'Recipient is missing an email address: {recipient}')
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import ClassVar
2
4
 
3
5
  from django import forms
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.db import models
2
4
  from django.contrib.auth.models import User
3
5
 
@@ -1,4 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  from django_spire.history.querysets import HistoryQuerySet
2
4
 
5
+
3
6
  class HelpDeskTicketQuerySet(HistoryQuerySet):
4
- pass
7
+ pass