django-spire 0.23.6__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 (541) 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/progress/__init__.py +1 -3
  139. django_spire/contrib/progress/static/django_spire/js/contrib/progress/progress.js +38 -82
  140. django_spire/contrib/queryset/enums.py +3 -1
  141. django_spire/contrib/queryset/filter_tools.py +10 -5
  142. django_spire/contrib/queryset/mixins.py +16 -16
  143. django_spire/contrib/queryset/tests/__init__.py +0 -0
  144. django_spire/contrib/queryset/tests/test_queryset.py +137 -0
  145. django_spire/contrib/seeding/field/base.py +13 -7
  146. django_spire/contrib/seeding/field/callable.py +8 -1
  147. django_spire/contrib/seeding/field/cleaners.py +5 -5
  148. django_spire/contrib/seeding/field/custom.py +20 -10
  149. django_spire/contrib/seeding/field/django/seeder.py +8 -6
  150. django_spire/contrib/seeding/field/enums.py +7 -5
  151. django_spire/contrib/seeding/field/override.py +16 -6
  152. django_spire/contrib/seeding/field/static.py +9 -2
  153. django_spire/contrib/seeding/field/tests/test_base.py +18 -14
  154. django_spire/contrib/seeding/field/tests/test_callable.py +13 -9
  155. django_spire/contrib/seeding/field/tests/test_cleaners.py +51 -38
  156. django_spire/contrib/seeding/field/tests/test_static.py +13 -9
  157. django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +2 -0
  158. django_spire/contrib/seeding/intelligence/intel.py +5 -1
  159. django_spire/contrib/seeding/intelligence/prompts/factory.py +6 -1
  160. django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +6 -1
  161. django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +2 -0
  162. django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +7 -1
  163. django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +6 -2
  164. django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +8 -2
  165. django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +2 -0
  166. django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +6 -1
  167. django_spire/contrib/seeding/management/commands/seeding.py +9 -3
  168. django_spire/contrib/seeding/management/example.py +2 -0
  169. django_spire/contrib/seeding/model/base.py +16 -7
  170. django_spire/contrib/seeding/model/config.py +31 -15
  171. django_spire/contrib/seeding/model/django/config.py +13 -13
  172. django_spire/contrib/seeding/model/django/seeder.py +4 -4
  173. django_spire/contrib/seeding/model/django/tests/test_seeder.py +34 -23
  174. django_spire/contrib/seeding/model/enums.py +2 -0
  175. django_spire/contrib/seeding/tests/test_config.py +71 -0
  176. django_spire/contrib/seeding/tests/test_custom.py +35 -0
  177. django_spire/contrib/seeding/tests/test_enums.py +40 -0
  178. django_spire/contrib/seeding/tests/test_intel.py +32 -0
  179. django_spire/contrib/seeding/tests/test_override.py +63 -0
  180. django_spire/contrib/service/__init__.py +2 -2
  181. django_spire/contrib/service/django_model_service.py +16 -15
  182. django_spire/contrib/service/exceptions.py +5 -3
  183. django_spire/contrib/service/tests/__init__.py +0 -0
  184. django_spire/contrib/service/tests/test_service.py +153 -0
  185. django_spire/contrib/session/apps.py +2 -0
  186. django_spire/contrib/session/controller.py +48 -42
  187. django_spire/contrib/session/templatetags/session_tags.py +11 -2
  188. django_spire/contrib/session/tests/test_session_controller.py +117 -53
  189. django_spire/contrib/tests/__init__.py +0 -0
  190. django_spire/contrib/tests/test_utils.py +37 -0
  191. django_spire/contrib/utils.py +4 -1
  192. django_spire/core/apps.py +2 -0
  193. django_spire/core/converters/tests/test_to_data.py +353 -0
  194. django_spire/core/converters/tests/test_to_enums.py +61 -41
  195. django_spire/core/converters/tests/test_to_pydantic.py +138 -109
  196. django_spire/core/converters/to_data.py +29 -10
  197. django_spire/core/converters/to_enums.py +4 -2
  198. django_spire/core/converters/to_pydantic.py +22 -22
  199. django_spire/core/decorators.py +19 -6
  200. django_spire/core/forms/widgets.py +4 -0
  201. django_spire/core/maps.py +3 -1
  202. django_spire/core/middleware/maintenance.py +3 -3
  203. django_spire/core/middleware.py +8 -6
  204. django_spire/core/redirect/__init__.py +5 -0
  205. django_spire/core/redirect/generic_redirect.py +1 -2
  206. django_spire/core/redirect/tests/__init__.py +0 -0
  207. django_spire/core/redirect/tests/test_generic_redirect.py +34 -0
  208. django_spire/core/{tests/tests_redirect.py → redirect/tests/test_safe_redirect.py} +55 -81
  209. django_spire/core/shortcuts.py +3 -3
  210. django_spire/core/static/django_spire/css/app-layout.css +1 -1
  211. django_spire/core/static/django_spire/css/app-navigation.css +3 -3
  212. django_spire/core/static/django_spire/css/bootstrap-override.css +4 -0
  213. django_spire/core/tag/admin.py +12 -0
  214. django_spire/core/tag/intelligence/tag_set_bot.py +2 -0
  215. django_spire/core/tag/mixins.py +2 -0
  216. django_spire/core/tag/models.py +2 -0
  217. django_spire/core/tag/querysets.py +2 -0
  218. django_spire/core/tag/service/tag_service.py +6 -3
  219. django_spire/core/tag/tests/test_intelligence.py +9 -9
  220. django_spire/core/tag/tests/test_tags.py +44 -54
  221. django_spire/core/tag/tests/test_tools.py +191 -0
  222. django_spire/core/tag/tools.py +3 -0
  223. django_spire/core/templates/django_spire/card/card.html +5 -2
  224. django_spire/core/templates/django_spire/card/title_card.html +9 -4
  225. django_spire/core/templates/django_spire/infinite_scroll/base.html +1 -0
  226. django_spire/core/templates/django_spire/navigation/side_navigation.html +19 -24
  227. django_spire/core/templates/django_spire/page/full_page.html +46 -16
  228. django_spire/core/templates/django_spire/table/base.html +4 -2
  229. django_spire/core/templatetags/json.py +6 -2
  230. django_spire/core/templatetags/message.py +13 -8
  231. django_spire/core/templatetags/string_formating.py +8 -5
  232. django_spire/core/templatetags/tests/__init__.py +0 -0
  233. django_spire/core/templatetags/tests/test_templatetags.py +427 -0
  234. django_spire/core/templatetags/variable_types.py +17 -9
  235. django_spire/core/tests/test_cases.py +1 -1
  236. django_spire/core/tests/test_conf.py +43 -0
  237. django_spire/core/tests/test_consts.py +28 -0
  238. django_spire/core/tests/test_context_processors.py +93 -0
  239. django_spire/core/tests/test_decorators.py +95 -0
  240. django_spire/core/tests/test_django_spire_utils.py +56 -0
  241. django_spire/core/tests/test_exceptions.py +37 -0
  242. django_spire/core/tests/test_models.py +54 -0
  243. django_spire/core/tests/test_settings.py +45 -0
  244. django_spire/core/tests/test_shortcuts.py +74 -0
  245. django_spire/core/tests/test_urls.py +16 -0
  246. django_spire/core/tests/test_utils.py +58 -0
  247. django_spire/core/urls.py +4 -1
  248. django_spire/core/utils.py +12 -8
  249. django_spire/exceptions.py +16 -1
  250. django_spire/file/admin.py +4 -2
  251. django_spire/file/apps.py +8 -10
  252. django_spire/file/fields.py +7 -7
  253. django_spire/file/forms.py +1 -1
  254. django_spire/file/interfaces.py +15 -15
  255. django_spire/file/mixins.py +1 -4
  256. django_spire/file/models.py +3 -5
  257. django_spire/file/tests/factories.py +59 -0
  258. django_spire/file/tests/test_admin.py +69 -0
  259. django_spire/file/tests/test_apps.py +24 -0
  260. django_spire/file/tests/test_fields.py +114 -0
  261. django_spire/file/tests/test_forms.py +20 -0
  262. django_spire/file/tests/test_interfaces.py +183 -0
  263. django_spire/file/tests/test_models.py +82 -0
  264. django_spire/file/tests/test_querysets.py +102 -0
  265. django_spire/file/tests/test_utils.py +32 -0
  266. django_spire/file/tests/test_views.py +145 -0
  267. django_spire/file/tests/test_widgets.py +82 -0
  268. django_spire/file/tools.py +8 -2
  269. django_spire/file/views.py +7 -3
  270. django_spire/file/widgets.py +12 -12
  271. django_spire/help_desk/admin.py +15 -0
  272. django_spire/help_desk/apps.py +2 -0
  273. django_spire/help_desk/auth/controller.py +2 -0
  274. django_spire/help_desk/choices.py +2 -0
  275. django_spire/help_desk/enums.py +2 -0
  276. django_spire/help_desk/exceptions.py +31 -3
  277. django_spire/help_desk/forms.py +2 -0
  278. django_spire/help_desk/models.py +2 -0
  279. django_spire/help_desk/querysets.py +4 -1
  280. django_spire/help_desk/services/notification_service.py +26 -27
  281. django_spire/help_desk/services/service.py +2 -3
  282. django_spire/help_desk/tests/factories.py +8 -3
  283. django_spire/help_desk/tests/test_admin.py +41 -0
  284. django_spire/help_desk/tests/test_apps.py +41 -0
  285. django_spire/help_desk/tests/test_choices.py +50 -0
  286. django_spire/help_desk/tests/test_controller.py +87 -0
  287. django_spire/help_desk/tests/test_enums.py +18 -0
  288. django_spire/help_desk/tests/test_exceptions.py +37 -0
  289. django_spire/help_desk/tests/test_forms.py +89 -0
  290. django_spire/help_desk/tests/test_models.py +59 -0
  291. django_spire/help_desk/tests/test_querysets.py +38 -0
  292. django_spire/help_desk/tests/test_services/test_notification_service.py +15 -8
  293. django_spire/help_desk/tests/test_services/test_service.py +92 -0
  294. django_spire/help_desk/tests/test_urls/test_form_urls.py +6 -6
  295. django_spire/help_desk/tests/test_urls/test_page_urls.py +8 -9
  296. django_spire/help_desk/tests/test_views/test_form_views.py +46 -19
  297. django_spire/help_desk/tests/test_views/test_page_views.py +32 -9
  298. django_spire/help_desk/urls/__init__.py +4 -1
  299. django_spire/help_desk/urls/form_urls.py +3 -0
  300. django_spire/help_desk/urls/page_urls.py +3 -0
  301. django_spire/help_desk/views/form_views.py +13 -5
  302. django_spire/help_desk/views/page_views.py +11 -3
  303. django_spire/history/activity/admin.py +2 -0
  304. django_spire/history/activity/apps.py +3 -1
  305. django_spire/history/activity/mixins.py +13 -7
  306. django_spire/history/activity/models.py +6 -5
  307. django_spire/history/activity/querysets.py +2 -0
  308. django_spire/history/activity/tests/__init__.py +0 -0
  309. django_spire/history/activity/tests/test_activity.py +176 -0
  310. django_spire/history/admin.py +9 -2
  311. django_spire/history/choices.py +3 -0
  312. django_spire/history/models.py +5 -5
  313. django_spire/history/tests/test_admin.py +93 -0
  314. django_spire/history/tests/test_history.py +101 -0
  315. django_spire/history/tests/test_mixins.py +84 -0
  316. django_spire/history/viewed/admin.py +3 -1
  317. django_spire/history/viewed/apps.py +3 -1
  318. django_spire/history/viewed/models.py +2 -0
  319. django_spire/history/viewed/tests/__init__.py +0 -0
  320. django_spire/history/viewed/tests/test_viewed.py +46 -0
  321. django_spire/knowledge/auth/tests/__init__.py +0 -0
  322. django_spire/knowledge/auth/tests/test_controller.py +116 -0
  323. django_spire/knowledge/collection/admin.py +5 -1
  324. django_spire/knowledge/collection/models.py +3 -1
  325. django_spire/knowledge/collection/seeding/seed.py +1 -0
  326. django_spire/knowledge/collection/services/factory_service.py +10 -11
  327. django_spire/knowledge/collection/services/ordering_service.py +1 -2
  328. django_spire/knowledge/collection/services/service.py +5 -10
  329. django_spire/knowledge/collection/services/tag_service.py +5 -2
  330. django_spire/knowledge/collection/tests/factories.py +28 -1
  331. django_spire/knowledge/collection/tests/test_models.py +48 -0
  332. django_spire/knowledge/collection/tests/test_querysets.py +93 -0
  333. django_spire/knowledge/collection/tests/test_services/test_factory_service.py +100 -0
  334. django_spire/knowledge/collection/tests/test_services/test_services.py +160 -0
  335. django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +21 -3
  336. django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +39 -1
  337. django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +12 -4
  338. django_spire/knowledge/collection/urls/__init__.py +3 -0
  339. django_spire/knowledge/collection/urls/form_urls.py +2 -0
  340. django_spire/knowledge/collection/urls/json_urls.py +2 -0
  341. django_spire/knowledge/collection/urls/page_urls.py +2 -0
  342. django_spire/knowledge/collection/views/form_views.py +4 -4
  343. django_spire/knowledge/collection/views/json_views.py +5 -1
  344. django_spire/knowledge/collection/views/page_views.py +5 -2
  345. django_spire/knowledge/entry/admin.py +7 -1
  346. django_spire/knowledge/entry/forms.py +2 -0
  347. django_spire/knowledge/entry/models.py +2 -0
  348. django_spire/knowledge/entry/seeding/seed.py +3 -0
  349. django_spire/knowledge/entry/services/automation_service.py +5 -4
  350. django_spire/knowledge/entry/services/factory_service.py +7 -5
  351. django_spire/knowledge/entry/services/service.py +4 -7
  352. django_spire/knowledge/entry/services/tag_service.py +0 -1
  353. django_spire/knowledge/entry/services/tool_service.py +1 -0
  354. django_spire/knowledge/entry/services/transformation_services.py +1 -5
  355. django_spire/knowledge/entry/tests/factories.py +1 -2
  356. django_spire/knowledge/entry/tests/test_factory_service.py +20 -0
  357. django_spire/knowledge/entry/tests/test_models.py +41 -0
  358. django_spire/knowledge/entry/tests/test_querysets.py +71 -0
  359. django_spire/knowledge/entry/tests/test_services.py +94 -0
  360. django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +9 -14
  361. django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +48 -5
  362. django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +6 -8
  363. django_spire/knowledge/entry/tests/test_urls/test_template_urls.py +40 -0
  364. django_spire/knowledge/entry/urls/form_urls.py +2 -0
  365. django_spire/knowledge/entry/urls/json_urls.py +2 -0
  366. django_spire/knowledge/entry/urls/page_urls.py +2 -0
  367. django_spire/knowledge/entry/urls/template_urls.py +2 -0
  368. django_spire/knowledge/entry/version/block/choices.py +2 -0
  369. django_spire/knowledge/entry/version/block/data/data.py +1 -0
  370. django_spire/knowledge/entry/version/block/data/list/data.py +8 -13
  371. django_spire/knowledge/entry/version/block/data/list/maps.py +3 -0
  372. django_spire/knowledge/entry/version/block/data/list/meta.py +1 -2
  373. django_spire/knowledge/entry/version/block/data/list/tests/__init__.py +0 -0
  374. django_spire/knowledge/entry/version/block/data/list/tests/test_maps.py +32 -0
  375. django_spire/knowledge/entry/version/block/data/list/tests/test_meta.py +58 -0
  376. django_spire/knowledge/entry/version/block/data/maps.py +3 -6
  377. django_spire/knowledge/entry/version/block/models.py +7 -5
  378. django_spire/knowledge/entry/version/block/seeding/constants.py +5 -4
  379. django_spire/knowledge/entry/version/block/services/service.py +2 -3
  380. django_spire/knowledge/entry/version/block/tests/factories.py +4 -10
  381. django_spire/knowledge/entry/version/block/tests/test_choices.py +56 -0
  382. django_spire/knowledge/entry/version/block/tests/test_data.py +90 -0
  383. django_spire/knowledge/entry/version/block/tests/test_maps.py +37 -0
  384. django_spire/knowledge/entry/version/block/tests/test_models.py +55 -0
  385. django_spire/knowledge/entry/version/block/tests/test_querysets.py +35 -0
  386. django_spire/knowledge/entry/version/block/tests/test_services.py +65 -0
  387. django_spire/knowledge/entry/version/choices.py +2 -0
  388. django_spire/knowledge/entry/version/converters/converter.py +1 -1
  389. django_spire/knowledge/entry/version/converters/docx_converter.py +4 -7
  390. django_spire/knowledge/entry/version/converters/markdown_converter.py +20 -20
  391. django_spire/knowledge/entry/version/maps.py +4 -5
  392. django_spire/knowledge/entry/version/querysets.py +1 -1
  393. django_spire/knowledge/entry/version/seeding/seeder.py +1 -2
  394. django_spire/knowledge/entry/version/services/processor_service.py +5 -4
  395. django_spire/knowledge/entry/version/services/service.py +1 -2
  396. django_spire/knowledge/entry/version/tests/factories.py +2 -2
  397. django_spire/knowledge/entry/version/tests/test_choices.py +18 -0
  398. django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +56 -8
  399. django_spire/knowledge/entry/version/tests/test_converters/test_markdown_converter.py +78 -0
  400. django_spire/knowledge/entry/version/tests/test_maps.py +58 -0
  401. django_spire/knowledge/entry/version/tests/test_models.py +23 -0
  402. django_spire/knowledge/entry/version/tests/test_querysets.py +26 -0
  403. django_spire/knowledge/entry/version/tests/test_services.py +62 -0
  404. django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +27 -8
  405. django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +15 -8
  406. django_spire/knowledge/entry/version/tests/test_urls/test_redirect_urls.py +38 -0
  407. django_spire/knowledge/entry/version/urls/__init__.py +3 -0
  408. django_spire/knowledge/entry/version/urls/json_urls.py +2 -1
  409. django_spire/knowledge/entry/version/urls/page_urls.py +2 -0
  410. django_spire/knowledge/entry/version/urls/redirect_urls.py +2 -0
  411. django_spire/knowledge/entry/version/views/json_views.py +5 -1
  412. django_spire/knowledge/entry/version/views/page_views.py +10 -3
  413. django_spire/knowledge/entry/version/views/redirect_views.py +5 -1
  414. django_spire/knowledge/entry/views/form_views.py +16 -8
  415. django_spire/knowledge/entry/views/json_views.py +3 -1
  416. django_spire/knowledge/entry/views/page_views.py +8 -2
  417. django_spire/knowledge/entry/views/template_views.py +7 -1
  418. django_spire/knowledge/exceptions.py +2 -1
  419. django_spire/knowledge/intelligence/intel/answer_intel.py +2 -1
  420. django_spire/knowledge/intelligence/intel/entry_intel.py +0 -1
  421. django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +4 -5
  422. django_spire/knowledge/models.py +1 -2
  423. django_spire/knowledge/tests/__init__.py +0 -0
  424. django_spire/knowledge/tests/test_templatetags.py +40 -0
  425. django_spire/knowledge/tests/test_urls/__init__.py +0 -0
  426. django_spire/knowledge/tests/test_urls/test_page_urls.py +24 -0
  427. django_spire/knowledge/urls/__init__.py +2 -0
  428. django_spire/knowledge/urls/page_urls.py +2 -0
  429. django_spire/knowledge/views/page_views.py +8 -3
  430. django_spire/notification/admin.py +3 -1
  431. django_spire/notification/app/admin.py +2 -0
  432. django_spire/notification/app/apps.py +3 -1
  433. django_spire/notification/app/exceptions.py +9 -2
  434. django_spire/notification/app/models.py +8 -4
  435. django_spire/notification/app/processor.py +22 -26
  436. django_spire/notification/app/querysets.py +2 -0
  437. django_spire/notification/app/tests/__init__.py +0 -0
  438. django_spire/notification/app/tests/factories.py +34 -0
  439. django_spire/notification/app/tests/test_apps.py +24 -0
  440. django_spire/notification/app/tests/test_models.py +72 -0
  441. django_spire/notification/app/tests/test_processor.py +111 -0
  442. django_spire/notification/app/tests/test_querysets.py +90 -0
  443. django_spire/notification/app/tests/test_views/__init__.py +0 -0
  444. django_spire/notification/app/tests/test_views/test_json_views.py +48 -0
  445. django_spire/notification/app/tests/test_views/test_page_views.py +19 -0
  446. django_spire/notification/app/urls/__init__.py +3 -1
  447. django_spire/notification/app/urls/json_urls.py +6 -4
  448. django_spire/notification/app/urls/page_urls.py +4 -3
  449. django_spire/notification/app/urls/template_urls.py +4 -2
  450. django_spire/notification/apps.py +4 -1
  451. django_spire/notification/email/admin.py +5 -1
  452. django_spire/notification/email/apps.py +3 -1
  453. django_spire/notification/email/exceptions.py +4 -2
  454. django_spire/notification/email/helper.py +5 -3
  455. django_spire/notification/email/models.py +4 -0
  456. django_spire/notification/email/processor.py +19 -15
  457. django_spire/notification/email/querysets.py +3 -0
  458. django_spire/notification/email/tests/__init__.py +0 -0
  459. django_spire/notification/email/tests/factories.py +35 -0
  460. django_spire/notification/email/tests/test_apps.py +24 -0
  461. django_spire/notification/email/tests/test_models.py +52 -0
  462. django_spire/notification/email/tests/test_processor.py +92 -0
  463. django_spire/notification/email/tests/test_querysets.py +43 -0
  464. django_spire/notification/exceptions.py +17 -2
  465. django_spire/notification/managers.py +7 -1
  466. django_spire/notification/maps.py +4 -1
  467. django_spire/notification/mixins.py +2 -0
  468. django_spire/notification/models.py +3 -1
  469. django_spire/notification/processors/notification.py +12 -5
  470. django_spire/notification/processors/processor.py +2 -0
  471. django_spire/notification/processors/tests/__init__.py +0 -0
  472. django_spire/notification/processors/tests/test_notification.py +106 -0
  473. django_spire/notification/push/admin.py +10 -1
  474. django_spire/notification/push/apps.py +3 -1
  475. django_spire/notification/push/models.py +2 -3
  476. django_spire/notification/push/tests/__init__.py +0 -0
  477. django_spire/notification/push/tests/test_apps.py +24 -0
  478. django_spire/notification/push/tests/test_models.py +28 -0
  479. django_spire/notification/querysets.py +7 -1
  480. django_spire/notification/sms/admin.py +2 -0
  481. django_spire/notification/sms/apps.py +4 -1
  482. django_spire/notification/sms/automations.py +2 -0
  483. django_spire/notification/sms/choices.py +2 -0
  484. django_spire/notification/sms/exceptions.py +19 -5
  485. django_spire/notification/sms/helper.py +33 -23
  486. django_spire/notification/sms/models.py +5 -1
  487. django_spire/notification/sms/processor.py +20 -20
  488. django_spire/notification/sms/querysets.py +2 -0
  489. django_spire/notification/sms/tests/factories.py +33 -0
  490. django_spire/notification/sms/tests/test_apps.py +24 -0
  491. django_spire/notification/sms/tests/test_automation.py +38 -0
  492. django_spire/notification/sms/tests/test_choices.py +15 -0
  493. django_spire/notification/sms/tests/test_consts.py +17 -0
  494. django_spire/notification/sms/tests/test_exceptions.py +27 -0
  495. django_spire/notification/sms/tests/test_helper.py +50 -0
  496. django_spire/notification/sms/tests/test_models.py +81 -0
  497. django_spire/notification/sms/tests/test_processor.py +107 -0
  498. django_spire/notification/sms/tests/test_tools.py +25 -11
  499. django_spire/notification/sms/tools.py +16 -5
  500. django_spire/notification/sms/urls/__init__.py +3 -1
  501. django_spire/notification/sms/urls/media_urls.py +2 -0
  502. django_spire/notification/sms/views/media_views.py +14 -4
  503. django_spire/notification/tests/__init__.py +0 -0
  504. django_spire/notification/tests/factories.py +26 -0
  505. django_spire/notification/tests/test_admin.py +55 -0
  506. django_spire/notification/tests/test_apps.py +30 -0
  507. django_spire/notification/tests/test_automation.py +18 -0
  508. django_spire/notification/tests/test_choices.py +59 -0
  509. django_spire/notification/tests/test_exceptions.py +58 -0
  510. django_spire/notification/tests/test_managers.py +100 -0
  511. django_spire/notification/tests/test_maps.py +31 -0
  512. django_spire/notification/tests/test_models.py +76 -0
  513. django_spire/notification/tests/test_querysets.py +184 -0
  514. django_spire/notification/tests/test_utils.py +23 -0
  515. django_spire/notification/urls.py +3 -1
  516. django_spire/notification/utils.py +3 -1
  517. django_spire/settings.py +3 -0
  518. django_spire/theme/tests/test_context_processor.py +15 -13
  519. django_spire/theme/tests/test_enums.py +2 -2
  520. django_spire/theme/tests/test_filesystem.py +2 -5
  521. django_spire/theme/tests/test_integration.py +12 -12
  522. django_spire/theme/tests/test_model.py +40 -38
  523. django_spire/theme/tests/test_views/test_json_views.py +33 -33
  524. django_spire/theme/urls/json_urls.py +3 -0
  525. django_spire/theme/urls/page_urls.py +3 -0
  526. django_spire/urls.py +19 -15
  527. django_spire/utils.py +13 -4
  528. {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/METADATA +1 -1
  529. {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/RECORD +532 -361
  530. django_spire/contrib/options/tests/test_unit.py +0 -148
  531. django_spire/contrib/progress/views.py +0 -64
  532. django_spire/contrib/seeding/tests/test_seeding.py +0 -25
  533. django_spire/core/tests/test_templatetags.py +0 -117
  534. django_spire/core/tests/tests_shortcuts.py +0 -73
  535. django_spire/history/activity/tests.py +0 -3
  536. django_spire/history/activity/views.py +0 -3
  537. django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +0 -71
  538. django_spire/notification/app/tests.py +0 -3
  539. {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/WHEEL +0 -0
  540. {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/licenses/LICENSE.md +0 -0
  541. {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,176 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock
4
+
5
+ from django.contrib.auth.models import User
6
+ from django.contrib.contenttypes.models import ContentType
7
+ from django.test import TestCase
8
+
9
+ from django_spire.history.activity.models import Activity, ActivitySubscriber
10
+ from django_spire.history.activity.utils import add_form_activity
11
+
12
+
13
+ class TestActivity(TestCase):
14
+ def setUp(self) -> None:
15
+ self.user = User.objects.create_user(
16
+ username='testuser',
17
+ password='testpass' # noqa: S106
18
+ )
19
+ self.recipient = User.objects.create_user(
20
+ username='recipient',
21
+ password='testpass' # noqa: S106
22
+ )
23
+ self.content_type = ContentType.objects.get_for_model(User)
24
+
25
+ def test_add_subscriber(self) -> None:
26
+ activity = Activity.objects.create(
27
+ content_type=self.content_type,
28
+ object_id=self.user.pk,
29
+ user=self.user,
30
+ verb='created',
31
+ information='Test information'
32
+ )
33
+
34
+ subscriber = User.objects.create_user(
35
+ username='subscriber',
36
+ password='testpass' # noqa: S106
37
+ )
38
+
39
+ activity.add_subscriber(subscriber)
40
+
41
+ assert activity.subscribers.count() == 1
42
+
43
+ def test_create_activity(self) -> None:
44
+ activity = Activity.objects.create(
45
+ content_type=self.content_type,
46
+ object_id=self.user.pk,
47
+ user=self.user,
48
+ verb='created',
49
+ information='Test information'
50
+ )
51
+
52
+ assert activity.pk is not None
53
+ assert activity.verb == 'created'
54
+ assert activity.information == 'Test information'
55
+ assert activity.content_object == self.user
56
+
57
+ def test_create_activity_with_recipient(self) -> None:
58
+ activity = Activity.objects.create(
59
+ content_type=self.content_type,
60
+ object_id=self.user.pk,
61
+ user=self.user,
62
+ recipient=self.recipient,
63
+ verb='assigned',
64
+ information='Assigned to recipient'
65
+ )
66
+
67
+ assert activity.recipient == self.recipient
68
+
69
+ def test_str_representation(self) -> None:
70
+ activity = Activity.objects.create(
71
+ content_type=self.content_type,
72
+ object_id=self.user.pk,
73
+ user=self.user,
74
+ verb='updated'
75
+ )
76
+
77
+ assert 'testuser' in str(activity)
78
+ assert 'updated' in str(activity)
79
+
80
+
81
+ class TestActivitySubscriber(TestCase):
82
+ def setUp(self) -> None:
83
+ self.user = User.objects.create_user(
84
+ username='testuser',
85
+ password='testpass' # noqa: S106
86
+ )
87
+ self.subscriber = User.objects.create_user(
88
+ username='subscriber',
89
+ password='testpass' # noqa: S106
90
+ )
91
+ self.content_type = ContentType.objects.get_for_model(User)
92
+ self.activity = Activity.objects.create(
93
+ content_type=self.content_type,
94
+ object_id=self.user.pk,
95
+ user=self.user,
96
+ verb='created'
97
+ )
98
+
99
+ def test_create_activity_subscriber(self) -> None:
100
+ activity_subscriber = ActivitySubscriber.objects.create(
101
+ activity=self.activity,
102
+ subscriber=self.subscriber
103
+ )
104
+
105
+ assert activity_subscriber.pk is not None
106
+ assert activity_subscriber.activity == self.activity
107
+ assert activity_subscriber.subscriber == self.subscriber
108
+
109
+ def test_str_representation(self) -> None:
110
+ activity_subscriber = ActivitySubscriber.objects.create(
111
+ activity=self.activity,
112
+ subscriber=self.subscriber
113
+ )
114
+
115
+ assert str(self.activity) in str(activity_subscriber)
116
+ assert 'subscriber' in str(activity_subscriber)
117
+
118
+
119
+ class TestActivityQuerySet(TestCase):
120
+ def setUp(self) -> None:
121
+ self.user = User.objects.create_user(
122
+ username='testuser',
123
+ password='testpass' # noqa: S106
124
+ )
125
+ self.content_type = ContentType.objects.get_for_model(User)
126
+
127
+ def test_prefetch_user(self) -> None:
128
+ Activity.objects.create(
129
+ content_type=self.content_type,
130
+ object_id=self.user.pk,
131
+ user=self.user,
132
+ verb='created'
133
+ )
134
+
135
+ activities = Activity.objects.prefetch_user()
136
+
137
+ assert activities.count() == 1
138
+
139
+
140
+ class TestAddFormActivity(TestCase):
141
+ def setUp(self) -> None:
142
+ self.user = User.objects.create_user(
143
+ username='testuser',
144
+ password='testpass', # noqa: S106
145
+ first_name='Test',
146
+ last_name='User'
147
+ )
148
+
149
+ def test_add_form_activity_created(self) -> None:
150
+ model_object = MagicMock()
151
+ model_object._meta.verbose_name = 'Test Model'
152
+ model_object.__str__ = MagicMock(return_value='Test Object')
153
+ model_object.add_activity = MagicMock()
154
+
155
+ add_form_activity(model_object, pk=None, user=self.user)
156
+
157
+ model_object.add_activity.assert_called_once()
158
+ call_kwargs = model_object.add_activity.call_args[1]
159
+
160
+ assert call_kwargs['user'] == self.user
161
+ assert call_kwargs['verb'] == 'updated'
162
+ assert 'updated' in call_kwargs['information']
163
+
164
+ def test_add_form_activity_updated(self) -> None:
165
+ model_object = MagicMock()
166
+ model_object._meta.verbose_name = 'Test Model'
167
+ model_object.__str__ = MagicMock(return_value='Test Object')
168
+ model_object.add_activity = MagicMock()
169
+
170
+ add_form_activity(model_object, pk=1, user=self.user)
171
+
172
+ model_object.add_activity.assert_called_once()
173
+ call_kwargs = model_object.add_activity.call_args[1]
174
+
175
+ assert call_kwargs['verb'] == 'created'
176
+ assert 'created' in call_kwargs['information']
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.contrib import admin
2
4
  from django.urls import reverse
3
5
  from django.utils.html import format_html
@@ -8,10 +10,15 @@ from django_spire.history import models
8
10
  @admin.register(models.HistoryEvent)
9
11
  class HistoryEventAdmin(admin.ModelAdmin):
10
12
  list_display = (
11
- 'id', 'content_object_link', 'content_type', 'created_datetime', 'event_verbose'
13
+ 'id',
14
+ 'content_object_link',
15
+ 'content_type',
16
+ 'created_datetime',
17
+ 'event_verbose'
12
18
  )
19
+
13
20
  list_filter = ('event', 'created_datetime')
14
- search_fields = ('id', 'content_type__model',)
21
+ search_fields = ('id', 'content_type__model')
15
22
  ordering = ('-created_datetime',)
16
23
 
17
24
  def content_object_link(self, history_event: models.HistoryEvent) -> str:
@@ -1,5 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  from django.db import models
2
4
 
5
+
3
6
  class HistoryEventChoices(models.TextChoices):
4
7
  CREATED = 'crea', 'Created'
5
8
  UPDATED = 'upda', 'Updated'
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from django.contrib.auth.models import User
4
3
  from django.contrib.contenttypes.fields import GenericForeignKey
5
4
  from django.contrib.contenttypes.models import ContentType
6
5
  from django.db import models
@@ -11,12 +10,13 @@ from django_spire.history.choices import HistoryEventChoices
11
10
 
12
11
  class HistoryEvent(models.Model):
13
12
  content_type = models.ForeignKey(
14
- ContentType,
15
- related_name='history_events',
16
- related_query_name='history_event',
17
- on_delete=models.CASCADE,
13
+ ContentType,
14
+ related_name='history_events',
15
+ related_query_name='history_event',
16
+ on_delete=models.CASCADE,
18
17
  editable=False
19
18
  )
19
+
20
20
  object_id = models.PositiveIntegerField(editable=False)
21
21
  content_object = GenericForeignKey('content_type', 'object_id')
22
22
 
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ from unittest.mock import MagicMock
4
+
5
+ from django.contrib.admin.sites import AdminSite
6
+ from django.test import TestCase
7
+
8
+ from django_spire.history.activity.admin import ActivityAdmin
9
+ from django_spire.history.activity.models import Activity
10
+ from django_spire.history.admin import HistoryEventAdmin
11
+ from django_spire.history.models import HistoryEvent
12
+ from django_spire.history.viewed.admin import ViewAdmin
13
+ from django_spire.history.viewed.models import Viewed
14
+
15
+
16
+ class TestActivityAdmin(TestCase):
17
+ def setUp(self) -> None:
18
+ self.site = AdminSite()
19
+ self.admin = ActivityAdmin(Activity, self.site)
20
+
21
+ def test_information_snippet_long(self) -> None:
22
+ activity = MagicMock()
23
+ activity.information = 'This is a very long information string that exceeds twenty characters'
24
+
25
+ result = self.admin.information_snippet(activity)
26
+
27
+ assert result.endswith('...')
28
+ assert len(result) == 23
29
+
30
+ def test_information_snippet_none(self) -> None:
31
+ activity = MagicMock()
32
+ activity.information = None
33
+
34
+ result = self.admin.information_snippet(activity)
35
+
36
+ assert result == 'No Information'
37
+
38
+ def test_information_snippet_short(self) -> None:
39
+ activity = MagicMock()
40
+ activity.information = 'Short info'
41
+
42
+ result = self.admin.information_snippet(activity)
43
+
44
+ assert result == 'Short info'
45
+
46
+ def test_list_display(self) -> None:
47
+ assert 'id' in self.admin.list_display
48
+ assert 'content_object_link' in self.admin.list_display
49
+ assert 'verb' in self.admin.list_display
50
+ assert 'user_link' in self.admin.list_display
51
+
52
+ def test_list_filter(self) -> None:
53
+ assert 'verb' in self.admin.list_filter
54
+ assert 'created_datetime' in self.admin.list_filter
55
+
56
+ def test_recipient_link_no_recipient(self) -> None:
57
+ activity = MagicMock()
58
+ activity.recipient = None
59
+
60
+ result = self.admin.recipient_link(activity)
61
+
62
+ assert result == 'No Recipient'
63
+
64
+
65
+ class TestHistoryEventAdmin(TestCase):
66
+ def setUp(self) -> None:
67
+ self.site = AdminSite()
68
+ self.admin = HistoryEventAdmin(HistoryEvent, self.site)
69
+
70
+ def test_list_display(self) -> None:
71
+ assert 'id' in self.admin.list_display
72
+ assert 'content_object_link' in self.admin.list_display
73
+ assert 'content_type' in self.admin.list_display
74
+ assert 'event_verbose' in self.admin.list_display
75
+
76
+ def test_list_filter(self) -> None:
77
+ assert 'event' in self.admin.list_filter
78
+ assert 'created_datetime' in self.admin.list_filter
79
+
80
+
81
+ class TestViewedAdmin(TestCase):
82
+ def setUp(self) -> None:
83
+ self.site = AdminSite()
84
+ self.admin = ViewAdmin(Viewed, self.site)
85
+
86
+ def test_list_display(self) -> None:
87
+ assert 'id' in self.admin.list_display
88
+ assert 'content_object_link' in self.admin.list_display
89
+ assert 'user_link' in self.admin.list_display
90
+ assert 'created_datetime' in self.admin.list_display
91
+
92
+ def test_list_filter(self) -> None:
93
+ assert 'created_datetime' in self.admin.list_filter
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ from django.contrib.auth.models import User
4
+ from django.contrib.contenttypes.models import ContentType
5
+ from django.test import TestCase
6
+
7
+ from django_spire.history.choices import HistoryEventChoices
8
+ from django_spire.history.models import HistoryEvent
9
+ from django_spire.history.querysets import HistoryQuerySet
10
+
11
+
12
+ class TestHistoryEventChoices(TestCase):
13
+ def test_active_choice(self) -> None:
14
+ assert HistoryEventChoices.ACTIVE.value == 'acti'
15
+ assert HistoryEventChoices.ACTIVE.label == 'Active'
16
+
17
+ def test_all_choices_exist(self) -> None:
18
+ choices = [c[0] for c in HistoryEventChoices.choices]
19
+
20
+ assert 'crea' in choices
21
+ assert 'upda' in choices
22
+ assert 'acti' in choices
23
+ assert 'inac' in choices
24
+ assert 'dele' in choices
25
+ assert 'unde' in choices
26
+
27
+ def test_created_choice(self) -> None:
28
+ assert HistoryEventChoices.CREATED.value == 'crea'
29
+ assert HistoryEventChoices.CREATED.label == 'Created'
30
+
31
+ def test_deleted_choice(self) -> None:
32
+ assert HistoryEventChoices.DELETED.value == 'dele'
33
+ assert HistoryEventChoices.DELETED.label == 'Deleted'
34
+
35
+ def test_inactive_choice(self) -> None:
36
+ assert HistoryEventChoices.INACTIVE.value == 'inac'
37
+ assert HistoryEventChoices.INACTIVE.label == 'Inactive'
38
+
39
+ def test_undeleted_choice(self) -> None:
40
+ assert HistoryEventChoices.UNDELETED.value == 'unde'
41
+ assert HistoryEventChoices.UNDELETED.label == 'Un-Deleted'
42
+
43
+ def test_updated_choice(self) -> None:
44
+ assert HistoryEventChoices.UPDATED.value == 'upda'
45
+ assert HistoryEventChoices.UPDATED.label == 'Updated'
46
+
47
+
48
+ class TestHistoryEvent(TestCase):
49
+ def setUp(self) -> None:
50
+ self.user = User.objects.create_user(
51
+ username='testuser',
52
+ password='testpass' # noqa: S106
53
+ )
54
+ self.content_type = ContentType.objects.get_for_model(User)
55
+
56
+ def test_create_history_event(self) -> None:
57
+ event = HistoryEvent.objects.create(
58
+ content_type=self.content_type,
59
+ object_id=self.user.pk,
60
+ event=HistoryEventChoices.CREATED
61
+ )
62
+
63
+ assert event.pk is not None
64
+ assert event.event == 'crea'
65
+ assert event.content_object == self.user
66
+
67
+ def test_event_verbose_property(self) -> None:
68
+ event = HistoryEvent.objects.create(
69
+ content_type=self.content_type,
70
+ object_id=self.user.pk,
71
+ event=HistoryEventChoices.UPDATED
72
+ )
73
+
74
+ assert event.event_verbose == 'Updated'
75
+
76
+ def test_str_representation(self) -> None:
77
+ event = HistoryEvent.objects.create(
78
+ content_type=self.content_type,
79
+ object_id=self.user.pk,
80
+ event=HistoryEventChoices.CREATED
81
+ )
82
+
83
+ assert 'Created' in str(event)
84
+
85
+
86
+ class TestHistoryQuerySet(TestCase):
87
+ def test_active_filter(self) -> None:
88
+ # HistoryQuerySet is meant to be used with models that have
89
+ # is_active and is_deleted fields (via HistoryModelMixin)
90
+ qs = HistoryQuerySet(model=HistoryEvent)
91
+
92
+ # Verify the method exists and returns a QuerySet
93
+ assert hasattr(qs, 'active')
94
+
95
+ def test_deleted_filter(self) -> None:
96
+ qs = HistoryQuerySet(model=HistoryEvent)
97
+ assert hasattr(qs, 'deleted')
98
+
99
+ def test_inactive_filter(self) -> None:
100
+ qs = HistoryQuerySet(model=HistoryEvent)
101
+ assert hasattr(qs, 'inactive')
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ from django.contrib.contenttypes.fields import GenericRelation
4
+ from django.test import TestCase
5
+
6
+ from django_spire.history.activity.mixins import ActivityMixin
7
+ from django_spire.history.mixins import HistoryModelMixin
8
+ from django_spire.history.viewed.mixins import ViewedModelMixin
9
+
10
+
11
+ class TestActivityMixin(TestCase):
12
+ def test_activities_field_exists(self) -> None:
13
+ assert hasattr(ActivityMixin, 'activities')
14
+
15
+ def test_activities_is_generic_relation(self) -> None:
16
+ field = ActivityMixin._meta.get_field('activities')
17
+ assert isinstance(field, GenericRelation)
18
+
19
+ def test_add_activity_method_exists(self) -> None:
20
+ assert hasattr(ActivityMixin, 'add_activity')
21
+ assert callable(ActivityMixin.add_activity)
22
+
23
+ def test_creator_property_exists(self) -> None:
24
+ assert hasattr(ActivityMixin, 'creator')
25
+
26
+ def test_is_abstract(self) -> None:
27
+ assert ActivityMixin._meta.abstract is True
28
+
29
+
30
+ class TestHistoryModelMixin(TestCase):
31
+ def test_created_datetime_field_exists(self) -> None:
32
+ assert hasattr(HistoryModelMixin, 'created_datetime')
33
+
34
+ def test_history_events_field_exists(self) -> None:
35
+ assert hasattr(HistoryModelMixin, 'history_events')
36
+
37
+ def test_history_events_is_generic_relation(self) -> None:
38
+ field = HistoryModelMixin._meta.get_field('history_events')
39
+ assert isinstance(field, GenericRelation)
40
+
41
+ def test_is_abstract(self) -> None:
42
+ assert HistoryModelMixin._meta.abstract is True
43
+
44
+ def test_is_active_field_exists(self) -> None:
45
+ assert hasattr(HistoryModelMixin, 'is_active')
46
+
47
+ def test_is_deleted_field_exists(self) -> None:
48
+ assert hasattr(HistoryModelMixin, 'is_deleted')
49
+
50
+ def test_set_active_method_exists(self) -> None:
51
+ assert hasattr(HistoryModelMixin, 'set_active')
52
+ assert callable(HistoryModelMixin.set_active)
53
+
54
+ def test_set_deleted_method_exists(self) -> None:
55
+ assert hasattr(HistoryModelMixin, 'set_deleted')
56
+ assert callable(HistoryModelMixin.set_deleted)
57
+
58
+ def test_set_inactive_method_exists(self) -> None:
59
+ assert hasattr(HistoryModelMixin, 'set_inactive')
60
+ assert callable(HistoryModelMixin.set_inactive)
61
+
62
+ def test_un_set_deleted_method_exists(self) -> None:
63
+ assert hasattr(HistoryModelMixin, 'un_set_deleted')
64
+ assert callable(HistoryModelMixin.un_set_deleted)
65
+
66
+
67
+ class TestViewedModelMixin(TestCase):
68
+ def test_add_view_method_exists(self) -> None:
69
+ assert hasattr(ViewedModelMixin, 'add_view')
70
+ assert callable(ViewedModelMixin.add_view)
71
+
72
+ def test_is_abstract(self) -> None:
73
+ assert ViewedModelMixin._meta.abstract is True
74
+
75
+ def test_is_viewed_method_exists(self) -> None:
76
+ assert hasattr(ViewedModelMixin, 'is_viewed')
77
+ assert callable(ViewedModelMixin.is_viewed)
78
+
79
+ def test_views_field_exists(self) -> None:
80
+ assert hasattr(ViewedModelMixin, 'views')
81
+
82
+ def test_views_is_generic_relation(self) -> None:
83
+ field = ViewedModelMixin._meta.get_field('views')
84
+ assert isinstance(field, GenericRelation)
@@ -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
@@ -11,7 +13,7 @@ class ViewAdmin(admin.ModelAdmin):
11
13
  'id', 'content_object_link', 'user_link', 'created_datetime'
12
14
  )
13
15
  list_filter = ('created_datetime',)
14
- search_fields = ('id', 'user__first_name', 'user__last_name', 'content_type__model',)
16
+ search_fields = ('id', 'user__first_name', 'user__last_name', 'content_type__model')
15
17
  ordering = ('-created_datetime',)
16
18
 
17
19
  def content_object_link(self, view: Viewed) -> str:
@@ -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 HistoryViewedConfig(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,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
File without changes
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from django.contrib.auth.models import User
4
+ from django.contrib.contenttypes.models import ContentType
5
+ from django.test import TestCase
6
+
7
+ from django_spire.history.viewed.models import Viewed
8
+
9
+
10
+ class TestViewed(TestCase):
11
+ def setUp(self) -> None:
12
+ self.user = User.objects.create_user(
13
+ username='testuser',
14
+ password='testpass' # noqa: S106
15
+ )
16
+ self.content_type = ContentType.objects.get_for_model(User)
17
+
18
+ def test_create_viewed(self) -> None:
19
+ viewed = Viewed.objects.create(
20
+ content_type=self.content_type,
21
+ object_id=self.user.pk,
22
+ user=self.user
23
+ )
24
+
25
+ assert viewed.pk is not None
26
+ assert viewed.user == self.user
27
+ assert viewed.content_object == self.user
28
+
29
+ def test_created_datetime_auto_set(self) -> None:
30
+ viewed = Viewed.objects.create(
31
+ content_type=self.content_type,
32
+ object_id=self.user.pk,
33
+ user=self.user
34
+ )
35
+
36
+ assert viewed.created_datetime is not None
37
+
38
+ def test_str_representation(self) -> None:
39
+ viewed = Viewed.objects.create(
40
+ content_type=self.content_type,
41
+ object_id=self.user.pk,
42
+ user=self.user
43
+ )
44
+
45
+ assert 'testuser' in str(viewed)
46
+ assert 'viewed' in str(viewed)
File without changes