django-spire 0.23.7__py3-none-any.whl → 0.23.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (538) 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/maps.py +3 -1
  200. django_spire/core/middleware/maintenance.py +3 -3
  201. django_spire/core/middleware.py +8 -6
  202. django_spire/core/redirect/__init__.py +5 -0
  203. django_spire/core/redirect/generic_redirect.py +1 -2
  204. django_spire/core/redirect/tests/__init__.py +0 -0
  205. django_spire/core/redirect/tests/test_generic_redirect.py +34 -0
  206. django_spire/core/{tests/tests_redirect.py → redirect/tests/test_safe_redirect.py} +55 -81
  207. django_spire/core/shortcuts.py +3 -3
  208. django_spire/core/static/django_spire/css/app-layout.css +1 -1
  209. django_spire/core/static/django_spire/css/app-navigation.css +3 -3
  210. django_spire/core/static/django_spire/css/bootstrap-override.css +4 -0
  211. django_spire/core/tag/admin.py +12 -0
  212. django_spire/core/tag/intelligence/tag_set_bot.py +2 -0
  213. django_spire/core/tag/mixins.py +2 -0
  214. django_spire/core/tag/models.py +2 -0
  215. django_spire/core/tag/querysets.py +2 -0
  216. django_spire/core/tag/service/tag_service.py +6 -3
  217. django_spire/core/tag/tests/test_intelligence.py +9 -9
  218. django_spire/core/tag/tests/test_tags.py +44 -54
  219. django_spire/core/tag/tests/test_tools.py +191 -0
  220. django_spire/core/tag/tools.py +3 -0
  221. django_spire/core/templates/django_spire/card/card.html +5 -2
  222. django_spire/core/templates/django_spire/card/title_card.html +9 -4
  223. django_spire/core/templates/django_spire/infinite_scroll/base.html +1 -0
  224. django_spire/core/templates/django_spire/navigation/side_navigation.html +19 -24
  225. django_spire/core/templates/django_spire/page/full_page.html +46 -16
  226. django_spire/core/templates/django_spire/table/base.html +4 -2
  227. django_spire/core/templatetags/json.py +6 -2
  228. django_spire/core/templatetags/message.py +13 -8
  229. django_spire/core/templatetags/string_formating.py +8 -5
  230. django_spire/core/templatetags/tests/__init__.py +0 -0
  231. django_spire/core/templatetags/tests/test_templatetags.py +427 -0
  232. django_spire/core/templatetags/variable_types.py +17 -9
  233. django_spire/core/tests/test_cases.py +1 -1
  234. django_spire/core/tests/test_conf.py +43 -0
  235. django_spire/core/tests/test_consts.py +28 -0
  236. django_spire/core/tests/test_context_processors.py +93 -0
  237. django_spire/core/tests/test_decorators.py +95 -0
  238. django_spire/core/tests/test_django_spire_utils.py +56 -0
  239. django_spire/core/tests/test_exceptions.py +37 -0
  240. django_spire/core/tests/test_models.py +54 -0
  241. django_spire/core/tests/test_settings.py +45 -0
  242. django_spire/core/tests/test_shortcuts.py +74 -0
  243. django_spire/core/tests/test_urls.py +16 -0
  244. django_spire/core/tests/test_utils.py +58 -0
  245. django_spire/core/urls.py +4 -1
  246. django_spire/core/utils.py +12 -8
  247. django_spire/exceptions.py +16 -1
  248. django_spire/file/admin.py +4 -2
  249. django_spire/file/apps.py +8 -10
  250. django_spire/file/fields.py +7 -7
  251. django_spire/file/forms.py +1 -1
  252. django_spire/file/interfaces.py +15 -15
  253. django_spire/file/mixins.py +1 -4
  254. django_spire/file/models.py +3 -5
  255. django_spire/file/tests/factories.py +59 -0
  256. django_spire/file/tests/test_admin.py +69 -0
  257. django_spire/file/tests/test_apps.py +24 -0
  258. django_spire/file/tests/test_fields.py +114 -0
  259. django_spire/file/tests/test_forms.py +20 -0
  260. django_spire/file/tests/test_interfaces.py +183 -0
  261. django_spire/file/tests/test_models.py +82 -0
  262. django_spire/file/tests/test_querysets.py +102 -0
  263. django_spire/file/tests/test_utils.py +32 -0
  264. django_spire/file/tests/test_views.py +145 -0
  265. django_spire/file/tests/test_widgets.py +82 -0
  266. django_spire/file/tools.py +8 -2
  267. django_spire/file/views.py +7 -3
  268. django_spire/file/widgets.py +12 -12
  269. django_spire/help_desk/admin.py +15 -0
  270. django_spire/help_desk/apps.py +2 -0
  271. django_spire/help_desk/auth/controller.py +2 -0
  272. django_spire/help_desk/choices.py +2 -0
  273. django_spire/help_desk/enums.py +2 -0
  274. django_spire/help_desk/exceptions.py +31 -3
  275. django_spire/help_desk/forms.py +2 -0
  276. django_spire/help_desk/models.py +2 -0
  277. django_spire/help_desk/querysets.py +4 -1
  278. django_spire/help_desk/services/notification_service.py +26 -27
  279. django_spire/help_desk/services/service.py +2 -3
  280. django_spire/help_desk/tests/factories.py +8 -3
  281. django_spire/help_desk/tests/test_admin.py +41 -0
  282. django_spire/help_desk/tests/test_apps.py +41 -0
  283. django_spire/help_desk/tests/test_choices.py +50 -0
  284. django_spire/help_desk/tests/test_controller.py +87 -0
  285. django_spire/help_desk/tests/test_enums.py +18 -0
  286. django_spire/help_desk/tests/test_exceptions.py +37 -0
  287. django_spire/help_desk/tests/test_forms.py +89 -0
  288. django_spire/help_desk/tests/test_models.py +59 -0
  289. django_spire/help_desk/tests/test_querysets.py +38 -0
  290. django_spire/help_desk/tests/test_services/test_notification_service.py +15 -8
  291. django_spire/help_desk/tests/test_services/test_service.py +92 -0
  292. django_spire/help_desk/tests/test_urls/test_form_urls.py +6 -6
  293. django_spire/help_desk/tests/test_urls/test_page_urls.py +8 -9
  294. django_spire/help_desk/tests/test_views/test_form_views.py +46 -19
  295. django_spire/help_desk/tests/test_views/test_page_views.py +32 -9
  296. django_spire/help_desk/urls/__init__.py +4 -1
  297. django_spire/help_desk/urls/form_urls.py +3 -0
  298. django_spire/help_desk/urls/page_urls.py +3 -0
  299. django_spire/help_desk/views/form_views.py +13 -5
  300. django_spire/help_desk/views/page_views.py +11 -3
  301. django_spire/history/activity/admin.py +2 -0
  302. django_spire/history/activity/apps.py +3 -1
  303. django_spire/history/activity/mixins.py +13 -7
  304. django_spire/history/activity/models.py +6 -5
  305. django_spire/history/activity/querysets.py +2 -0
  306. django_spire/history/activity/tests/__init__.py +0 -0
  307. django_spire/history/activity/tests/test_activity.py +176 -0
  308. django_spire/history/admin.py +9 -2
  309. django_spire/history/choices.py +3 -0
  310. django_spire/history/models.py +5 -5
  311. django_spire/history/tests/test_admin.py +93 -0
  312. django_spire/history/tests/test_history.py +101 -0
  313. django_spire/history/tests/test_mixins.py +84 -0
  314. django_spire/history/viewed/admin.py +3 -1
  315. django_spire/history/viewed/apps.py +3 -1
  316. django_spire/history/viewed/models.py +2 -0
  317. django_spire/history/viewed/tests/__init__.py +0 -0
  318. django_spire/history/viewed/tests/test_viewed.py +46 -0
  319. django_spire/knowledge/auth/tests/__init__.py +0 -0
  320. django_spire/knowledge/auth/tests/test_controller.py +116 -0
  321. django_spire/knowledge/collection/admin.py +5 -1
  322. django_spire/knowledge/collection/models.py +3 -1
  323. django_spire/knowledge/collection/seeding/seed.py +1 -0
  324. django_spire/knowledge/collection/services/factory_service.py +10 -11
  325. django_spire/knowledge/collection/services/ordering_service.py +1 -2
  326. django_spire/knowledge/collection/services/service.py +5 -10
  327. django_spire/knowledge/collection/services/tag_service.py +5 -2
  328. django_spire/knowledge/collection/tests/factories.py +28 -1
  329. django_spire/knowledge/collection/tests/test_models.py +48 -0
  330. django_spire/knowledge/collection/tests/test_querysets.py +93 -0
  331. django_spire/knowledge/collection/tests/test_services/test_factory_service.py +100 -0
  332. django_spire/knowledge/collection/tests/test_services/test_services.py +160 -0
  333. django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +21 -3
  334. django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +39 -1
  335. django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +12 -4
  336. django_spire/knowledge/collection/urls/__init__.py +3 -0
  337. django_spire/knowledge/collection/urls/form_urls.py +2 -0
  338. django_spire/knowledge/collection/urls/json_urls.py +2 -0
  339. django_spire/knowledge/collection/urls/page_urls.py +2 -0
  340. django_spire/knowledge/collection/views/form_views.py +4 -4
  341. django_spire/knowledge/collection/views/json_views.py +5 -1
  342. django_spire/knowledge/collection/views/page_views.py +5 -2
  343. django_spire/knowledge/entry/admin.py +7 -1
  344. django_spire/knowledge/entry/forms.py +2 -0
  345. django_spire/knowledge/entry/models.py +2 -0
  346. django_spire/knowledge/entry/seeding/seed.py +3 -0
  347. django_spire/knowledge/entry/services/automation_service.py +5 -4
  348. django_spire/knowledge/entry/services/factory_service.py +7 -5
  349. django_spire/knowledge/entry/services/service.py +4 -7
  350. django_spire/knowledge/entry/services/tag_service.py +0 -1
  351. django_spire/knowledge/entry/services/tool_service.py +1 -0
  352. django_spire/knowledge/entry/services/transformation_services.py +1 -5
  353. django_spire/knowledge/entry/tests/factories.py +1 -2
  354. django_spire/knowledge/entry/tests/test_factory_service.py +20 -0
  355. django_spire/knowledge/entry/tests/test_models.py +41 -0
  356. django_spire/knowledge/entry/tests/test_querysets.py +71 -0
  357. django_spire/knowledge/entry/tests/test_services.py +94 -0
  358. django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +9 -14
  359. django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +48 -5
  360. django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +6 -8
  361. django_spire/knowledge/entry/tests/test_urls/test_template_urls.py +40 -0
  362. django_spire/knowledge/entry/urls/form_urls.py +2 -0
  363. django_spire/knowledge/entry/urls/json_urls.py +2 -0
  364. django_spire/knowledge/entry/urls/page_urls.py +2 -0
  365. django_spire/knowledge/entry/urls/template_urls.py +2 -0
  366. django_spire/knowledge/entry/version/block/choices.py +2 -0
  367. django_spire/knowledge/entry/version/block/data/data.py +1 -0
  368. django_spire/knowledge/entry/version/block/data/list/data.py +8 -13
  369. django_spire/knowledge/entry/version/block/data/list/maps.py +3 -0
  370. django_spire/knowledge/entry/version/block/data/list/meta.py +1 -2
  371. django_spire/knowledge/entry/version/block/data/list/tests/__init__.py +0 -0
  372. django_spire/knowledge/entry/version/block/data/list/tests/test_maps.py +32 -0
  373. django_spire/knowledge/entry/version/block/data/list/tests/test_meta.py +58 -0
  374. django_spire/knowledge/entry/version/block/data/maps.py +3 -6
  375. django_spire/knowledge/entry/version/block/models.py +7 -5
  376. django_spire/knowledge/entry/version/block/seeding/constants.py +5 -4
  377. django_spire/knowledge/entry/version/block/services/service.py +2 -3
  378. django_spire/knowledge/entry/version/block/tests/factories.py +4 -10
  379. django_spire/knowledge/entry/version/block/tests/test_choices.py +56 -0
  380. django_spire/knowledge/entry/version/block/tests/test_data.py +90 -0
  381. django_spire/knowledge/entry/version/block/tests/test_maps.py +37 -0
  382. django_spire/knowledge/entry/version/block/tests/test_models.py +55 -0
  383. django_spire/knowledge/entry/version/block/tests/test_querysets.py +35 -0
  384. django_spire/knowledge/entry/version/block/tests/test_services.py +65 -0
  385. django_spire/knowledge/entry/version/choices.py +2 -0
  386. django_spire/knowledge/entry/version/converters/converter.py +1 -1
  387. django_spire/knowledge/entry/version/converters/docx_converter.py +4 -7
  388. django_spire/knowledge/entry/version/converters/markdown_converter.py +20 -20
  389. django_spire/knowledge/entry/version/maps.py +4 -5
  390. django_spire/knowledge/entry/version/querysets.py +1 -1
  391. django_spire/knowledge/entry/version/seeding/seeder.py +1 -2
  392. django_spire/knowledge/entry/version/services/processor_service.py +5 -4
  393. django_spire/knowledge/entry/version/services/service.py +1 -2
  394. django_spire/knowledge/entry/version/tests/factories.py +2 -2
  395. django_spire/knowledge/entry/version/tests/test_choices.py +18 -0
  396. django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +56 -8
  397. django_spire/knowledge/entry/version/tests/test_converters/test_markdown_converter.py +78 -0
  398. django_spire/knowledge/entry/version/tests/test_maps.py +58 -0
  399. django_spire/knowledge/entry/version/tests/test_models.py +23 -0
  400. django_spire/knowledge/entry/version/tests/test_querysets.py +26 -0
  401. django_spire/knowledge/entry/version/tests/test_services.py +62 -0
  402. django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +27 -8
  403. django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +15 -8
  404. django_spire/knowledge/entry/version/tests/test_urls/test_redirect_urls.py +38 -0
  405. django_spire/knowledge/entry/version/urls/__init__.py +3 -0
  406. django_spire/knowledge/entry/version/urls/json_urls.py +2 -1
  407. django_spire/knowledge/entry/version/urls/page_urls.py +2 -0
  408. django_spire/knowledge/entry/version/urls/redirect_urls.py +2 -0
  409. django_spire/knowledge/entry/version/views/json_views.py +5 -1
  410. django_spire/knowledge/entry/version/views/page_views.py +10 -3
  411. django_spire/knowledge/entry/version/views/redirect_views.py +5 -1
  412. django_spire/knowledge/entry/views/form_views.py +16 -8
  413. django_spire/knowledge/entry/views/json_views.py +3 -1
  414. django_spire/knowledge/entry/views/page_views.py +8 -2
  415. django_spire/knowledge/entry/views/template_views.py +7 -1
  416. django_spire/knowledge/exceptions.py +2 -1
  417. django_spire/knowledge/intelligence/intel/answer_intel.py +2 -1
  418. django_spire/knowledge/intelligence/intel/entry_intel.py +0 -1
  419. django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +4 -5
  420. django_spire/knowledge/models.py +1 -2
  421. django_spire/knowledge/tests/__init__.py +0 -0
  422. django_spire/knowledge/tests/test_templatetags.py +40 -0
  423. django_spire/knowledge/tests/test_urls/__init__.py +0 -0
  424. django_spire/knowledge/tests/test_urls/test_page_urls.py +24 -0
  425. django_spire/knowledge/urls/__init__.py +2 -0
  426. django_spire/knowledge/urls/page_urls.py +2 -0
  427. django_spire/knowledge/views/page_views.py +8 -3
  428. django_spire/notification/admin.py +3 -1
  429. django_spire/notification/app/admin.py +2 -0
  430. django_spire/notification/app/apps.py +3 -1
  431. django_spire/notification/app/exceptions.py +9 -2
  432. django_spire/notification/app/models.py +8 -4
  433. django_spire/notification/app/processor.py +22 -26
  434. django_spire/notification/app/querysets.py +2 -0
  435. django_spire/notification/app/tests/__init__.py +0 -0
  436. django_spire/notification/app/tests/factories.py +34 -0
  437. django_spire/notification/app/tests/test_apps.py +24 -0
  438. django_spire/notification/app/tests/test_models.py +72 -0
  439. django_spire/notification/app/tests/test_processor.py +111 -0
  440. django_spire/notification/app/tests/test_querysets.py +90 -0
  441. django_spire/notification/app/tests/test_views/__init__.py +0 -0
  442. django_spire/notification/app/tests/test_views/test_json_views.py +48 -0
  443. django_spire/notification/app/tests/test_views/test_page_views.py +19 -0
  444. django_spire/notification/app/urls/__init__.py +3 -1
  445. django_spire/notification/app/urls/json_urls.py +6 -4
  446. django_spire/notification/app/urls/page_urls.py +4 -3
  447. django_spire/notification/app/urls/template_urls.py +4 -2
  448. django_spire/notification/apps.py +4 -1
  449. django_spire/notification/email/admin.py +5 -1
  450. django_spire/notification/email/apps.py +3 -1
  451. django_spire/notification/email/exceptions.py +4 -2
  452. django_spire/notification/email/helper.py +5 -3
  453. django_spire/notification/email/models.py +4 -0
  454. django_spire/notification/email/processor.py +19 -15
  455. django_spire/notification/email/querysets.py +3 -0
  456. django_spire/notification/email/tests/__init__.py +0 -0
  457. django_spire/notification/email/tests/factories.py +35 -0
  458. django_spire/notification/email/tests/test_apps.py +24 -0
  459. django_spire/notification/email/tests/test_models.py +52 -0
  460. django_spire/notification/email/tests/test_processor.py +92 -0
  461. django_spire/notification/email/tests/test_querysets.py +43 -0
  462. django_spire/notification/exceptions.py +17 -2
  463. django_spire/notification/managers.py +7 -1
  464. django_spire/notification/maps.py +4 -1
  465. django_spire/notification/mixins.py +2 -0
  466. django_spire/notification/models.py +3 -1
  467. django_spire/notification/processors/notification.py +12 -5
  468. django_spire/notification/processors/processor.py +2 -0
  469. django_spire/notification/processors/tests/__init__.py +0 -0
  470. django_spire/notification/processors/tests/test_notification.py +106 -0
  471. django_spire/notification/push/admin.py +10 -1
  472. django_spire/notification/push/apps.py +3 -1
  473. django_spire/notification/push/models.py +2 -3
  474. django_spire/notification/push/tests/__init__.py +0 -0
  475. django_spire/notification/push/tests/test_apps.py +24 -0
  476. django_spire/notification/push/tests/test_models.py +28 -0
  477. django_spire/notification/querysets.py +7 -1
  478. django_spire/notification/sms/admin.py +2 -0
  479. django_spire/notification/sms/apps.py +4 -1
  480. django_spire/notification/sms/automations.py +2 -0
  481. django_spire/notification/sms/choices.py +2 -0
  482. django_spire/notification/sms/exceptions.py +19 -5
  483. django_spire/notification/sms/helper.py +33 -23
  484. django_spire/notification/sms/models.py +5 -1
  485. django_spire/notification/sms/processor.py +20 -20
  486. django_spire/notification/sms/querysets.py +2 -0
  487. django_spire/notification/sms/tests/factories.py +33 -0
  488. django_spire/notification/sms/tests/test_apps.py +24 -0
  489. django_spire/notification/sms/tests/test_automation.py +38 -0
  490. django_spire/notification/sms/tests/test_choices.py +15 -0
  491. django_spire/notification/sms/tests/test_consts.py +17 -0
  492. django_spire/notification/sms/tests/test_exceptions.py +27 -0
  493. django_spire/notification/sms/tests/test_helper.py +50 -0
  494. django_spire/notification/sms/tests/test_models.py +81 -0
  495. django_spire/notification/sms/tests/test_processor.py +107 -0
  496. django_spire/notification/sms/tests/test_tools.py +25 -11
  497. django_spire/notification/sms/tools.py +16 -5
  498. django_spire/notification/sms/urls/__init__.py +3 -1
  499. django_spire/notification/sms/urls/media_urls.py +2 -0
  500. django_spire/notification/sms/views/media_views.py +14 -4
  501. django_spire/notification/tests/__init__.py +0 -0
  502. django_spire/notification/tests/factories.py +26 -0
  503. django_spire/notification/tests/test_admin.py +55 -0
  504. django_spire/notification/tests/test_apps.py +30 -0
  505. django_spire/notification/tests/test_automation.py +18 -0
  506. django_spire/notification/tests/test_choices.py +59 -0
  507. django_spire/notification/tests/test_exceptions.py +58 -0
  508. django_spire/notification/tests/test_managers.py +100 -0
  509. django_spire/notification/tests/test_maps.py +31 -0
  510. django_spire/notification/tests/test_models.py +76 -0
  511. django_spire/notification/tests/test_querysets.py +184 -0
  512. django_spire/notification/tests/test_utils.py +23 -0
  513. django_spire/notification/urls.py +3 -1
  514. django_spire/notification/utils.py +3 -1
  515. django_spire/settings.py +3 -0
  516. django_spire/theme/tests/test_context_processor.py +15 -13
  517. django_spire/theme/tests/test_enums.py +2 -2
  518. django_spire/theme/tests/test_filesystem.py +2 -5
  519. django_spire/theme/tests/test_integration.py +12 -12
  520. django_spire/theme/tests/test_model.py +40 -38
  521. django_spire/theme/tests/test_views/test_json_views.py +33 -33
  522. django_spire/theme/urls/json_urls.py +3 -0
  523. django_spire/theme/urls/page_urls.py +3 -0
  524. django_spire/urls.py +19 -15
  525. django_spire/utils.py +13 -4
  526. {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/METADATA +1 -1
  527. {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/RECORD +530 -358
  528. django_spire/contrib/options/tests/test_unit.py +0 -148
  529. django_spire/contrib/seeding/tests/test_seeding.py +0 -25
  530. django_spire/core/tests/test_templatetags.py +0 -117
  531. django_spire/core/tests/tests_shortcuts.py +0 -73
  532. django_spire/history/activity/tests.py +0 -3
  533. django_spire/history/activity/views.py +0 -3
  534. django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +0 -71
  535. django_spire/notification/app/tests.py +0 -3
  536. {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/WHEEL +0 -0
  537. {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/licenses/LICENSE.md +0 -0
  538. {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock, patch
4
+
5
+ from django_spire.core.tests.test_cases import BaseTestCase
6
+ from django_spire.help_desk.choices import (
7
+ HelpDeskTicketPriorityChoices,
8
+ HelpDeskTicketPurposeChoices,
9
+ HelpDeskTicketStatusChoices,
10
+ )
11
+ from django_spire.help_desk.models import HelpDeskTicket
12
+ from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
13
+
14
+
15
+ class HelpDeskTicketServiceTests(BaseTestCase):
16
+ def setUp(self):
17
+ super().setUp()
18
+ self.ticket = create_test_helpdesk_ticket()
19
+
20
+ def test_save_model_obj_updates_existing(self):
21
+ self.ticket.services.obj = self.ticket
22
+ updated_ticket, created = self.ticket.services.save_model_obj(
23
+ description='Updated description'
24
+ )
25
+
26
+ assert created is False
27
+ assert updated_ticket.description == 'Updated description'
28
+
29
+ def test_save_model_obj_updates_priority(self):
30
+ self.ticket.services.obj = self.ticket
31
+ updated_ticket, _ = self.ticket.services.save_model_obj(
32
+ priority=HelpDeskTicketPriorityChoices.URGENT
33
+ )
34
+
35
+ assert updated_ticket.priority == HelpDeskTicketPriorityChoices.URGENT
36
+
37
+ def test_save_model_obj_updates_status(self):
38
+ self.ticket.services.obj = self.ticket
39
+ updated_ticket, _ = self.ticket.services.save_model_obj(
40
+ status=HelpDeskTicketStatusChoices.DONE
41
+ )
42
+
43
+ assert updated_ticket.status == HelpDeskTicketStatusChoices.DONE
44
+
45
+ @patch(
46
+ 'django_spire.help_desk.services.notification_service.'
47
+ 'HelpDeskTicketNotificationService.create_new_ticket_notifications'
48
+ )
49
+ def test_create_sets_created_by(self, _mock_notifications: MagicMock):
50
+ ticket = HelpDeskTicket()
51
+
52
+ created_ticket = ticket.services.create(
53
+ created_by=self.super_user,
54
+ purpose=HelpDeskTicketPurposeChoices.APP,
55
+ priority=HelpDeskTicketPriorityChoices.LOW,
56
+ description='Test ticket',
57
+ )
58
+
59
+ assert created_ticket.created_by == self.super_user
60
+
61
+ @patch(
62
+ 'django_spire.help_desk.services.notification_service.'
63
+ 'HelpDeskTicketNotificationService.create_new_ticket_notifications'
64
+ )
65
+ def test_create_calls_notification_service(self, mock_notifications: MagicMock):
66
+ ticket = HelpDeskTicket()
67
+
68
+ ticket.services.create(
69
+ created_by=self.super_user,
70
+ purpose=HelpDeskTicketPurposeChoices.APP,
71
+ priority=HelpDeskTicketPriorityChoices.LOW,
72
+ description='Test ticket',
73
+ )
74
+
75
+ mock_notifications.assert_called_once()
76
+
77
+ @patch(
78
+ 'django_spire.help_desk.services.notification_service.'
79
+ 'HelpDeskTicketNotificationService.create_new_ticket_notifications'
80
+ )
81
+ def test_create_returns_ticket(self, _mock_notifications: MagicMock):
82
+ ticket = HelpDeskTicket()
83
+
84
+ created_ticket = ticket.services.create(
85
+ created_by=self.super_user,
86
+ purpose=HelpDeskTicketPurposeChoices.APP,
87
+ priority=HelpDeskTicketPriorityChoices.LOW,
88
+ description='Test ticket',
89
+ )
90
+
91
+ assert created_ticket.pk is not None
92
+ assert created_ticket.purpose == HelpDeskTicketPurposeChoices.APP
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import reverse
2
4
 
3
5
  from django_spire.core.tests.test_cases import BaseTestCase
@@ -5,17 +7,15 @@ from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
5
7
 
6
8
 
7
9
  class HelpDeskFormUrlsTestCase(BaseTestCase):
8
- def test_helpdesk_ticket_form_create_url_path(self) -> None:
10
+ def test_helpdesk_ticket_form_create_url_path(self):
9
11
  response = self.client.get(path=reverse('django_spire:help_desk:form:create'))
12
+ assert response.status_code == 200
10
13
 
11
- self.assertEqual(response.status_code, 200)
12
-
13
- def test_helpdesk_ticket_form_update_url_path(self) -> None:
14
+ def test_helpdesk_ticket_form_update_url_path(self):
14
15
  response = self.client.get(
15
16
  path=reverse(
16
17
  'django_spire:help_desk:form:update',
17
18
  kwargs={'pk': create_test_helpdesk_ticket().pk}
18
19
  ),
19
20
  )
20
-
21
- self.assertEqual(response.status_code, 200)
21
+ assert response.status_code == 200
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import reverse
2
4
 
3
5
  from django_spire.core.tests.test_cases import BaseTestCase
@@ -10,27 +12,24 @@ class HelpDeskPageUrlsTestCase(BaseTestCase):
10
12
 
11
13
  self.test_ticket = create_test_helpdesk_ticket()
12
14
 
13
- def test_ticket_delete_view_url_path(self) -> None:
15
+ def test_ticket_delete_view_url_path(self):
14
16
  response = self.client.get(
15
17
  path=reverse(
16
18
  'django_spire:help_desk:page:delete',
17
19
  kwargs={'pk': self.test_ticket.pk}
18
20
  ),
19
21
  )
22
+ assert response.status_code == 200
20
23
 
21
- self.assertEqual(response.status_code, 200)
22
-
23
- def test_ticket_detail_view_url_path(self) -> None:
24
+ def test_ticket_detail_view_url_path(self):
24
25
  response = self.client.get(
25
26
  path=reverse(
26
27
  'django_spire:help_desk:page:detail',
27
28
  kwargs={'pk': self.test_ticket.pk}
28
29
  ),
29
30
  )
31
+ assert response.status_code == 200
30
32
 
31
- self.assertEqual(response.status_code, 200)
32
-
33
- def test_ticket_list_view_url_path(self) -> None:
33
+ def test_ticket_list_view_url_path(self):
34
34
  response = self.client.get(path=reverse('django_spire:help_desk:page:list'))
35
-
36
- self.assertEqual(response.status_code, 200)
35
+ assert response.status_code == 200
@@ -1,16 +1,17 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import reverse
2
4
 
3
5
  from django_spire.core.tests.test_cases import BaseTestCase
4
6
  from django_spire.help_desk.models import HelpDeskTicket
5
- from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket, \
6
- create_test_helpdesk_ticket_data
7
+ from django_spire.help_desk.tests.factories import (
8
+ create_test_helpdesk_ticket,
9
+ create_test_helpdesk_ticket_data,
10
+ )
7
11
 
8
12
 
9
13
  class HelpDeskFormViewsTestCase(BaseTestCase):
10
- def setUp(self) -> None:
11
- super().setUp()
12
-
13
- def test_helpdesk_ticket_form_create_view(self) -> None:
14
+ def test_helpdesk_ticket_form_create_view(self):
14
15
  ticket_data = create_test_helpdesk_ticket_data()
15
16
 
16
17
  response = self.client.post(
@@ -20,15 +21,15 @@ class HelpDeskFormViewsTestCase(BaseTestCase):
20
21
 
21
22
  ticket = HelpDeskTicket.objects.first()
22
23
 
23
- self.assertEqual(response.status_code, 302)
24
- self.assertEqual(response.url, reverse('django_spire:help_desk:page:list'))
24
+ assert response.status_code == 302
25
+ assert response.url == reverse('django_spire:help_desk:page:list')
25
26
 
26
- self.assertEqual(ticket.priority, ticket_data['priority'])
27
- self.assertEqual(ticket.purpose, ticket_data['purpose'])
28
- self.assertEqual(ticket.status, ticket_data['status'])
29
- self.assertEqual(ticket.description, ticket_data['description'])
27
+ assert ticket.priority == ticket_data['priority']
28
+ assert ticket.purpose == ticket_data['purpose']
29
+ assert ticket.status == ticket_data['status']
30
+ assert ticket.description == ticket_data['description']
30
31
 
31
- def test_helpdesk_ticket_form_update_view(self) -> None:
32
+ def test_helpdesk_ticket_form_update_view(self):
32
33
  test_ticket = create_test_helpdesk_ticket()
33
34
 
34
35
  updated_ticket_data = create_test_helpdesk_ticket_data(
@@ -46,11 +47,37 @@ class HelpDeskFormViewsTestCase(BaseTestCase):
46
47
  ),
47
48
  )
48
49
 
49
- self.assertEqual(response.status_code, 302)
50
- self.assertEqual(response.url, reverse('django_spire:help_desk:page:list'))
50
+ assert response.status_code == 302
51
+ assert response.url == reverse('django_spire:help_desk:page:list')
51
52
 
52
53
  test_ticket.refresh_from_db()
53
- self.assertEqual(test_ticket.priority, updated_ticket_data['priority'])
54
- self.assertEqual(test_ticket.purpose, updated_ticket_data['purpose'])
55
- self.assertEqual(test_ticket.status, updated_ticket_data['status'])
56
- self.assertEqual(test_ticket.description, updated_ticket_data['description'])
54
+ assert test_ticket.priority == updated_ticket_data['priority']
55
+ assert test_ticket.purpose == updated_ticket_data['purpose']
56
+ assert test_ticket.status == updated_ticket_data['status']
57
+ assert test_ticket.description == updated_ticket_data['description']
58
+
59
+ def test_helpdesk_ticket_form_create_view_invalid_data(self):
60
+ invalid_data = {
61
+ 'priority': '',
62
+ 'purpose': '',
63
+ 'description': '',
64
+ }
65
+
66
+ response = self.client.post(
67
+ reverse('django_spire:help_desk:form:create'),
68
+ data=invalid_data,
69
+ )
70
+
71
+ assert response.status_code == 200
72
+ assert HelpDeskTicket.objects.count() == 0
73
+
74
+ def test_helpdesk_ticket_form_create_view_sets_created_by(self):
75
+ ticket_data = create_test_helpdesk_ticket_data()
76
+
77
+ self.client.post(
78
+ reverse('django_spire:help_desk:form:create'),
79
+ data=ticket_data,
80
+ )
81
+
82
+ ticket = HelpDeskTicket.objects.first()
83
+ assert ticket.created_by == self.super_user
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import reverse
2
4
 
3
5
  from django_spire.core.tests.test_cases import BaseTestCase
@@ -10,7 +12,7 @@ class HelpDeskPageViewsTestCase(BaseTestCase):
10
12
 
11
13
  self.test_ticket = create_test_helpdesk_ticket()
12
14
 
13
- def test_ticket_delete_view(self) -> None:
15
+ def test_ticket_delete_view(self):
14
16
  response = self.client.post(
15
17
  reverse(
16
18
  'django_spire:help_desk:page:delete',
@@ -19,11 +21,11 @@ class HelpDeskPageViewsTestCase(BaseTestCase):
19
21
  data={'should_delete': 'on'},
20
22
  )
21
23
 
22
- self.assertEqual(response.status_code, 302)
24
+ assert response.status_code == 302
23
25
  self.test_ticket.refresh_from_db()
24
- self.assertTrue(self.test_ticket.is_deleted)
26
+ assert self.test_ticket.is_deleted is True
25
27
 
26
- def test_ticket_detail_view(self) -> None:
28
+ def test_ticket_detail_view(self):
27
29
  response = self.client.get(
28
30
  reverse(
29
31
  'django_spire:help_desk:page:detail',
@@ -31,16 +33,37 @@ class HelpDeskPageViewsTestCase(BaseTestCase):
31
33
  ),
32
34
  )
33
35
 
34
- self.assertEqual(response.status_code, 200)
35
- self.assertEqual(response.context_data['ticket'], self.test_ticket)
36
+ assert response.status_code == 200
37
+ assert response.context_data['ticket'] == self.test_ticket
36
38
 
37
- def test_ticket_list_view(self) -> None:
39
+ def test_ticket_list_view(self):
38
40
  tickets = [create_test_helpdesk_ticket() for _ in range(5)]
39
41
 
40
42
  response = self.client.get(
41
43
  reverse('django_spire:help_desk:page:list'),
42
44
  )
43
45
 
44
- self.assertEqual(response.status_code, 200)
46
+ assert response.status_code == 200
45
47
  for ticket in tickets:
46
- self.assertIn(ticket, response.context_data['tickets'])
48
+ assert ticket in response.context_data['tickets']
49
+
50
+ def test_ticket_list_view_excludes_deleted(self):
51
+ deleted_ticket = create_test_helpdesk_ticket()
52
+ deleted_ticket.is_deleted = True
53
+ deleted_ticket.save()
54
+
55
+ response = self.client.get(
56
+ reverse('django_spire:help_desk:page:list'),
57
+ )
58
+
59
+ assert deleted_ticket not in response.context_data['tickets']
60
+
61
+ def test_ticket_list_view_ordered_by_created_datetime_desc(self):
62
+ response = self.client.get(
63
+ reverse('django_spire:help_desk:page:list'),
64
+ )
65
+
66
+ tickets = list(response.context_data['tickets'])
67
+ if len(tickets) > 1:
68
+ for i in range(len(tickets) - 1):
69
+ assert tickets[i].created_datetime >= tickets[i + 1].created_datetime
@@ -1,8 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import include, path
2
4
 
5
+
3
6
  app_name = 'help_desk'
4
7
 
5
8
  urlpatterns = [
6
9
  path('page/', include('django_spire.help_desk.urls.page_urls', namespace='page')),
7
10
  path('form/', include('django_spire.help_desk.urls.form_urls', namespace='form')),
8
- ]
11
+ ]
@@ -1,7 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import path
2
4
 
3
5
  from django_spire.help_desk.views import form_views
4
6
 
7
+
5
8
  app_name = 'form'
6
9
 
7
10
  urlpatterns = [
@@ -1,7 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.urls import path
2
4
 
3
5
  from django_spire.help_desk.views import page_views
4
6
 
7
+
5
8
  app_name = 'page'
6
9
 
7
10
  urlpatterns = [
@@ -1,4 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import django_glue as dg
4
+
5
+ from typing import TYPE_CHECKING
6
+
2
7
  from django.shortcuts import redirect, get_object_or_404
3
8
  from django.urls import reverse
4
9
 
@@ -8,9 +13,13 @@ from django_spire.contrib.generic_views import portal_views
8
13
  from django_spire.help_desk import forms
9
14
  from django_spire.help_desk.models import HelpDeskTicket
10
15
 
16
+ if TYPE_CHECKING:
17
+ from django.core.handlers.wsgi import WSGIRequest
18
+ from django.template.response import TemplateResponse
19
+
11
20
 
12
21
  @AppAuthController('help_desk').permission_required('can_add')
13
- def ticket_create_form_view(request):
22
+ def ticket_create_form_view(request: WSGIRequest) -> TemplateResponse:
14
23
  ticket = HelpDeskTicket()
15
24
 
16
25
  dg.glue_model_object(request, 'ticket', ticket)
@@ -34,7 +43,7 @@ def ticket_create_form_view(request):
34
43
  request,
35
44
  form=form,
36
45
  template='django_spire/help_desk/page/ticket_form_page.html',
37
- verb=f'Create',
46
+ verb='Create',
38
47
  obj=ticket,
39
48
  context_data={
40
49
  'form_action_url': reverse('django_spire:help_desk:form:create'),
@@ -43,7 +52,7 @@ def ticket_create_form_view(request):
43
52
 
44
53
 
45
54
  @AppAuthController('help_desk').permission_required('can_change')
46
- def ticket_update_form_view(request, pk: int):
55
+ def ticket_update_form_view(request: WSGIRequest, pk: int) -> TemplateResponse:
47
56
  ticket = get_object_or_404(HelpDeskTicket, pk=pk)
48
57
 
49
58
  dg.glue_model_object(request, 'ticket', ticket)
@@ -52,8 +61,7 @@ def ticket_update_form_view(request, pk: int):
52
61
  form = forms.HelpDeskTicketUpdateForm(request.POST, instance=ticket)
53
62
 
54
63
  if form.is_valid():
55
- _, _ = ticket.services.save_model_obj(**form.cleaned_data)
56
-
64
+ ticket.services.save_model_obj(**form.cleaned_data)
57
65
  return redirect(reverse('django_spire:help_desk:page:list'))
58
66
 
59
67
  show_form_errors(request, form)
@@ -1,3 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
1
5
  from django.shortcuts import get_object_or_404
2
6
  from django.urls import reverse
3
7
 
@@ -5,9 +9,13 @@ from django_spire.auth.controller.controller import AppAuthController
5
9
  from django_spire.contrib.generic_views import portal_views
6
10
  from django_spire.help_desk.models import HelpDeskTicket
7
11
 
12
+ if TYPE_CHECKING:
13
+ from django.core.handlers.wsgi import WSGIRequest
14
+ from django.template.response import TemplateResponse
15
+
8
16
 
9
17
  @AppAuthController('help_desk').permission_required('can_delete')
10
- def ticket_delete_view(request, pk):
18
+ def ticket_delete_view(request: WSGIRequest, pk: int):
11
19
  ticket = get_object_or_404(HelpDeskTicket, pk=pk)
12
20
 
13
21
  return portal_views.delete_form_view(
@@ -21,7 +29,7 @@ def ticket_delete_view(request, pk):
21
29
 
22
30
 
23
31
  @AppAuthController('help_desk').permission_required('can_view')
24
- def ticket_detail_view(request, pk):
32
+ def ticket_detail_view(request: WSGIRequest, pk: int):
25
33
  ticket = get_object_or_404(HelpDeskTicket, pk=pk)
26
34
 
27
35
  return portal_views.detail_view(
@@ -35,7 +43,7 @@ def ticket_detail_view(request, pk):
35
43
 
36
44
 
37
45
  @AppAuthController('help_desk').permission_required('can_view')
38
- def ticket_list_view(request):
46
+ def ticket_list_view(request: WSGIRequest) -> TemplateResponse:
39
47
  tickets = HelpDeskTicket.objects.order_by('-created_datetime').active()
40
48
 
41
49
  return portal_views.list_view(
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.contrib import admin
2
4
  from django.urls.base import reverse
3
5
  from django.utils.html import format_html
@@ -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
@@ -11,4 +13,4 @@ class HistoryActivityConfig(AppConfig):
11
13
  REQUIRED_APPS = ('django_spire_core', 'django_spire_history')
12
14
 
13
15
  def ready(self) -> None:
14
- check_required_apps(self.label)
16
+ check_required_apps(self.label)
@@ -1,9 +1,15 @@
1
- from django.contrib.auth.models import User
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
2
5
  from django.contrib.contenttypes.fields import GenericRelation
3
6
  from django.db import models
4
7
 
5
8
  from django_spire.history.activity.models import Activity, ActivitySubscriber
6
9
 
10
+ if TYPE_CHECKING:
11
+ from django.contrib.auth.models import User
12
+
7
13
 
8
14
  class ActivityMixin(models.Model):
9
15
  activities = GenericRelation(
@@ -13,12 +19,12 @@ class ActivityMixin(models.Model):
13
19
  )
14
20
 
15
21
  def add_activity(
16
- self,
17
- user: User,
18
- verb: str,
19
- information: str,
20
- recipient: User = None,
21
- subscribers: list[User] | None = None
22
+ self,
23
+ user: User,
24
+ verb: str,
25
+ information: str,
26
+ recipient: User = None,
27
+ subscribers: list[User] | None = None
22
28
  ) -> Activity:
23
29
 
24
30
  activity = self.activities.create(
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.contrib.auth.models import User
2
4
  from django.contrib.contenttypes.fields import GenericForeignKey
3
5
  from django.contrib.contenttypes.models import ContentType
@@ -12,9 +14,10 @@ class Activity(models.Model):
12
14
  ContentType,
13
15
  related_name='activities',
14
16
  related_query_name='activity',
15
- on_delete=models.CASCADE,
17
+ on_delete=models.CASCADE,
16
18
  editable=False
17
19
  )
20
+
18
21
  object_id = models.PositiveIntegerField(editable=False)
19
22
  content_object = GenericForeignKey('content_type', 'object_id')
20
23
 
@@ -47,9 +50,7 @@ class Activity(models.Model):
47
50
  return f'{self.user} - {self.verb}'
48
51
 
49
52
  def add_subscriber(self, subscriber: User) -> None:
50
- self.subscribers.create(
51
- user=subscriber
52
- )
53
+ self.subscribers.create(subscriber=subscriber)
53
54
 
54
55
  class Meta:
55
56
  db_table = 'django_spire_history_activity'
@@ -78,7 +79,7 @@ class ActivitySubscriber(models.Model):
78
79
 
79
80
  def __str__(self):
80
81
  return f'{self.activity} - {self.subscriber}'
81
-
82
+
82
83
  class Meta:
83
84
  db_table = 'django_spire_history_activity_subscriber'
84
85
  verbose_name = 'Activity Subscriber'
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.db.models import QuerySet
2
4
 
3
5
 
File without changes