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.
- django_spire/ai/admin.py +11 -11
- django_spire/ai/chat/apps.py +1 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +1 -1
- django_spire/ai/chat/tests/factories.py +15 -0
- django_spire/ai/chat/tests/test_controller.py +45 -0
- django_spire/ai/chat/tests/test_models.py +301 -0
- django_spire/ai/chat/tests/test_prompts.py +48 -0
- django_spire/ai/chat/tests/test_responses.py +208 -0
- django_spire/ai/chat/tests/test_router/test_base_chat_router.py +66 -6
- django_spire/ai/chat/tests/test_router/test_chat_workflow.py +73 -3
- django_spire/ai/chat/tests/test_router/test_integration.py +86 -6
- django_spire/ai/chat/tests/test_router/test_intent_decoder.py +93 -1
- django_spire/ai/chat/tests/test_router/test_message_intel.py +60 -1
- django_spire/ai/chat/tests/test_router/test_spire_chat_router.py +110 -0
- django_spire/ai/chat/tests/test_urls/test_json_urls.py +202 -1
- django_spire/ai/context/tests/__init__.py +0 -0
- django_spire/ai/context/tests/test_context.py +188 -0
- django_spire/ai/decorators.py +7 -6
- django_spire/ai/prompt/tests/test_bots.py +100 -10
- django_spire/ai/prompt/tests/test_prompt_intel.py +83 -0
- django_spire/ai/prompt/tests/test_prompt_tuning.py +126 -0
- django_spire/ai/sms/decorators.py +8 -2
- django_spire/ai/sms/tests/test_sms.py +240 -16
- django_spire/ai/sms/tests/test_sms_intel.py +42 -0
- django_spire/ai/sms/tests/test_webhook.py +155 -7
- django_spire/ai/sms/views.py +23 -24
- django_spire/ai/tests/test_ai.py +131 -7
- django_spire/auth/apps.py +4 -2
- django_spire/auth/controller/controller.py +36 -23
- django_spire/auth/controller/exceptions.py +9 -0
- django_spire/auth/group/admin.py +1 -0
- django_spire/auth/group/apps.py +2 -0
- django_spire/auth/group/factories.py +17 -8
- django_spire/auth/group/forms.py +7 -0
- django_spire/auth/group/tests/test_factories.py +146 -0
- django_spire/auth/group/tests/test_forms.py +282 -0
- django_spire/auth/group/tests/test_models.py +192 -0
- django_spire/auth/group/tests/test_querysets.py +98 -0
- django_spire/auth/group/tests/test_utils.py +341 -0
- django_spire/auth/group/tests/test_views.py +377 -0
- django_spire/auth/group/urls/__init__.py +3 -1
- django_spire/auth/group/urls/form_urls.py +2 -0
- django_spire/auth/group/urls/json_urls.py +3 -0
- django_spire/auth/group/urls/page_urls.py +2 -0
- django_spire/auth/group/utils.py +6 -2
- django_spire/auth/group/views/form_views.py +6 -3
- django_spire/auth/group/views/json_views.py +6 -2
- django_spire/auth/mfa/admin.py +2 -0
- django_spire/auth/mfa/apps.py +2 -0
- django_spire/auth/mfa/forms.py +1 -0
- django_spire/auth/mfa/querysets.py +9 -2
- django_spire/auth/mfa/tests/test_models.py +233 -0
- django_spire/auth/mfa/tests/test_utils.py +106 -0
- django_spire/auth/mfa/urls/__init__.py +2 -0
- django_spire/auth/mfa/urls/page_urls.py +2 -0
- django_spire/auth/mfa/urls/redirect_urls.py +2 -0
- django_spire/auth/mfa/views/page_views.py +2 -1
- django_spire/auth/permissions/consts.py +2 -2
- django_spire/auth/permissions/decorators.py +8 -8
- django_spire/auth/permissions/permissions.py +28 -35
- django_spire/auth/permissions/tests/test_decorators.py +333 -0
- django_spire/auth/permissions/tests/test_permissions.py +337 -0
- django_spire/auth/permissions/tests/test_tools.py +305 -0
- django_spire/auth/permissions/tools.py +21 -15
- django_spire/auth/seeding/seed.py +3 -0
- django_spire/auth/seeding/seeder.py +2 -0
- django_spire/auth/tests/test_controller.py +323 -0
- django_spire/auth/tests/test_url_endpoints.py +9 -9
- django_spire/auth/tests/test_views.py +406 -0
- django_spire/auth/urls/admin_urls.py +2 -0
- django_spire/auth/urls/redirect_urls.py +2 -0
- django_spire/auth/user/apps.py +2 -0
- django_spire/auth/user/forms.py +9 -0
- django_spire/auth/user/models.py +1 -1
- django_spire/auth/user/services/services.py +1 -0
- django_spire/auth/user/tests/factories.py +14 -13
- django_spire/auth/user/tests/test_factories.py +166 -2
- django_spire/auth/user/tests/test_forms.py +573 -0
- django_spire/auth/user/tests/test_models.py +257 -0
- django_spire/auth/user/tests/test_services.py +200 -0
- django_spire/auth/user/tests/test_tools.py +153 -0
- django_spire/auth/user/tests/test_user_factories.py +139 -0
- django_spire/auth/user/tests/test_views.py +363 -0
- django_spire/auth/user/tools.py +7 -1
- django_spire/auth/user/urls/form_urls.py +3 -0
- django_spire/auth/user/urls/page_urls.py +3 -0
- django_spire/auth/user/views/form_views.py +19 -10
- django_spire/auth/user/views/page_views.py +8 -2
- django_spire/auth/views/redirect_views.py +14 -9
- django_spire/comment/admin.py +2 -0
- django_spire/comment/apps.py +2 -0
- django_spire/comment/templatetags/comment_tags.py +1 -0
- django_spire/comment/tests/test_forms.py +27 -0
- django_spire/comment/tests/test_models.py +215 -0
- django_spire/comment/tests/test_querysets.py +101 -0
- django_spire/comment/tests/test_utils.py +90 -0
- django_spire/comment/urls.py +2 -0
- django_spire/comment/utils.py +22 -13
- django_spire/comment/views.py +1 -1
- django_spire/conf.py +8 -6
- django_spire/consts.py +1 -1
- django_spire/contrib/breadcrumb/apps.py +2 -0
- django_spire/contrib/breadcrumb/breadcrumbs.py +18 -18
- django_spire/contrib/breadcrumb/tests/test_breadcrumbs.py +198 -0
- django_spire/contrib/constructor/__init__.py +3 -3
- django_spire/contrib/constructor/constructor.py +15 -15
- django_spire/contrib/constructor/django_model_constructor.py +5 -4
- django_spire/contrib/constructor/exceptions.py +5 -3
- django_spire/contrib/constructor/tests/__init__.py +0 -0
- django_spire/contrib/constructor/tests/test_constructor.py +193 -0
- django_spire/contrib/form/tests/__init__.py +0 -0
- django_spire/contrib/form/tests/test_forms.py +203 -0
- django_spire/contrib/generic_views/modal_views.py +2 -1
- django_spire/contrib/generic_views/portal_views.py +20 -19
- django_spire/contrib/generic_views/tests/__init__.py +0 -0
- django_spire/contrib/generic_views/tests/test_views.py +459 -0
- django_spire/contrib/help/apps.py +2 -0
- django_spire/contrib/help/templatetags/help.py +1 -0
- django_spire/contrib/help/tests/__init__.py +0 -0
- django_spire/contrib/help/tests/test_templatetags.py +100 -0
- django_spire/contrib/options/mixins.py +6 -5
- django_spire/contrib/options/tests/factories.py +5 -1
- django_spire/contrib/options/tests/test_options.py +234 -0
- django_spire/contrib/ordering/exceptions.py +7 -3
- django_spire/contrib/ordering/mixins.py +2 -0
- django_spire/contrib/ordering/querysets.py +3 -1
- django_spire/contrib/ordering/services/processor_service.py +8 -4
- django_spire/contrib/ordering/services/service.py +1 -2
- django_spire/contrib/ordering/tests/__init__.py +0 -0
- django_spire/contrib/ordering/tests/test_ordering.py +165 -0
- django_spire/contrib/ordering/validators.py +6 -6
- django_spire/contrib/pagination/templatetags/pagination_tags.py +12 -5
- django_spire/contrib/pagination/tests/__init__.py +0 -0
- django_spire/contrib/pagination/tests/test_pagination.py +179 -0
- django_spire/contrib/performance/decorators.py +16 -6
- django_spire/contrib/performance/tests/__init__.py +0 -0
- django_spire/contrib/performance/tests/test_performance.py +107 -0
- django_spire/contrib/progress/__init__.py +1 -3
- django_spire/contrib/progress/static/django_spire/js/contrib/progress/progress.js +38 -82
- django_spire/contrib/queryset/enums.py +3 -1
- django_spire/contrib/queryset/filter_tools.py +10 -5
- django_spire/contrib/queryset/mixins.py +16 -16
- django_spire/contrib/queryset/tests/__init__.py +0 -0
- django_spire/contrib/queryset/tests/test_queryset.py +137 -0
- django_spire/contrib/seeding/field/base.py +13 -7
- django_spire/contrib/seeding/field/callable.py +8 -1
- django_spire/contrib/seeding/field/cleaners.py +5 -5
- django_spire/contrib/seeding/field/custom.py +20 -10
- django_spire/contrib/seeding/field/django/seeder.py +8 -6
- django_spire/contrib/seeding/field/enums.py +7 -5
- django_spire/contrib/seeding/field/override.py +16 -6
- django_spire/contrib/seeding/field/static.py +9 -2
- django_spire/contrib/seeding/field/tests/test_base.py +18 -14
- django_spire/contrib/seeding/field/tests/test_callable.py +13 -9
- django_spire/contrib/seeding/field/tests/test_cleaners.py +51 -38
- django_spire/contrib/seeding/field/tests/test_static.py +13 -9
- django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +2 -0
- django_spire/contrib/seeding/intelligence/intel.py +5 -1
- django_spire/contrib/seeding/intelligence/prompts/factory.py +6 -1
- django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +6 -1
- django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +2 -0
- django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +7 -1
- django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +6 -2
- django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +8 -2
- django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +2 -0
- django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +6 -1
- django_spire/contrib/seeding/management/commands/seeding.py +9 -3
- django_spire/contrib/seeding/management/example.py +2 -0
- django_spire/contrib/seeding/model/base.py +16 -7
- django_spire/contrib/seeding/model/config.py +31 -15
- django_spire/contrib/seeding/model/django/config.py +13 -13
- django_spire/contrib/seeding/model/django/seeder.py +4 -4
- django_spire/contrib/seeding/model/django/tests/test_seeder.py +34 -23
- django_spire/contrib/seeding/model/enums.py +2 -0
- django_spire/contrib/seeding/tests/test_config.py +71 -0
- django_spire/contrib/seeding/tests/test_custom.py +35 -0
- django_spire/contrib/seeding/tests/test_enums.py +40 -0
- django_spire/contrib/seeding/tests/test_intel.py +32 -0
- django_spire/contrib/seeding/tests/test_override.py +63 -0
- django_spire/contrib/service/__init__.py +2 -2
- django_spire/contrib/service/django_model_service.py +16 -15
- django_spire/contrib/service/exceptions.py +5 -3
- django_spire/contrib/service/tests/__init__.py +0 -0
- django_spire/contrib/service/tests/test_service.py +153 -0
- django_spire/contrib/session/apps.py +2 -0
- django_spire/contrib/session/controller.py +48 -42
- django_spire/contrib/session/templatetags/session_tags.py +11 -2
- django_spire/contrib/session/tests/test_session_controller.py +117 -53
- django_spire/contrib/tests/__init__.py +0 -0
- django_spire/contrib/tests/test_utils.py +37 -0
- django_spire/contrib/utils.py +4 -1
- django_spire/core/apps.py +2 -0
- django_spire/core/converters/tests/test_to_data.py +353 -0
- django_spire/core/converters/tests/test_to_enums.py +61 -41
- django_spire/core/converters/tests/test_to_pydantic.py +138 -109
- django_spire/core/converters/to_data.py +29 -10
- django_spire/core/converters/to_enums.py +4 -2
- django_spire/core/converters/to_pydantic.py +22 -22
- django_spire/core/decorators.py +19 -6
- django_spire/core/forms/widgets.py +4 -0
- django_spire/core/maps.py +3 -1
- django_spire/core/middleware/maintenance.py +3 -3
- django_spire/core/middleware.py +8 -6
- django_spire/core/redirect/__init__.py +5 -0
- django_spire/core/redirect/generic_redirect.py +1 -2
- django_spire/core/redirect/tests/__init__.py +0 -0
- django_spire/core/redirect/tests/test_generic_redirect.py +34 -0
- django_spire/core/{tests/tests_redirect.py → redirect/tests/test_safe_redirect.py} +55 -81
- django_spire/core/shortcuts.py +3 -3
- django_spire/core/static/django_spire/css/app-layout.css +1 -1
- django_spire/core/static/django_spire/css/app-navigation.css +3 -3
- django_spire/core/static/django_spire/css/bootstrap-override.css +4 -0
- django_spire/core/tag/admin.py +12 -0
- django_spire/core/tag/intelligence/tag_set_bot.py +2 -0
- django_spire/core/tag/mixins.py +2 -0
- django_spire/core/tag/models.py +2 -0
- django_spire/core/tag/querysets.py +2 -0
- django_spire/core/tag/service/tag_service.py +6 -3
- django_spire/core/tag/tests/test_intelligence.py +9 -9
- django_spire/core/tag/tests/test_tags.py +44 -54
- django_spire/core/tag/tests/test_tools.py +191 -0
- django_spire/core/tag/tools.py +3 -0
- django_spire/core/templates/django_spire/card/card.html +5 -2
- django_spire/core/templates/django_spire/card/title_card.html +9 -4
- django_spire/core/templates/django_spire/infinite_scroll/base.html +1 -0
- django_spire/core/templates/django_spire/navigation/side_navigation.html +19 -24
- django_spire/core/templates/django_spire/page/full_page.html +46 -16
- django_spire/core/templates/django_spire/table/base.html +4 -2
- django_spire/core/templatetags/json.py +6 -2
- django_spire/core/templatetags/message.py +13 -8
- django_spire/core/templatetags/string_formating.py +8 -5
- django_spire/core/templatetags/tests/__init__.py +0 -0
- django_spire/core/templatetags/tests/test_templatetags.py +427 -0
- django_spire/core/templatetags/variable_types.py +17 -9
- django_spire/core/tests/test_cases.py +1 -1
- django_spire/core/tests/test_conf.py +43 -0
- django_spire/core/tests/test_consts.py +28 -0
- django_spire/core/tests/test_context_processors.py +93 -0
- django_spire/core/tests/test_decorators.py +95 -0
- django_spire/core/tests/test_django_spire_utils.py +56 -0
- django_spire/core/tests/test_exceptions.py +37 -0
- django_spire/core/tests/test_models.py +54 -0
- django_spire/core/tests/test_settings.py +45 -0
- django_spire/core/tests/test_shortcuts.py +74 -0
- django_spire/core/tests/test_urls.py +16 -0
- django_spire/core/tests/test_utils.py +58 -0
- django_spire/core/urls.py +4 -1
- django_spire/core/utils.py +12 -8
- django_spire/exceptions.py +16 -1
- django_spire/file/admin.py +4 -2
- django_spire/file/apps.py +8 -10
- django_spire/file/fields.py +7 -7
- django_spire/file/forms.py +1 -1
- django_spire/file/interfaces.py +15 -15
- django_spire/file/mixins.py +1 -4
- django_spire/file/models.py +3 -5
- django_spire/file/tests/factories.py +59 -0
- django_spire/file/tests/test_admin.py +69 -0
- django_spire/file/tests/test_apps.py +24 -0
- django_spire/file/tests/test_fields.py +114 -0
- django_spire/file/tests/test_forms.py +20 -0
- django_spire/file/tests/test_interfaces.py +183 -0
- django_spire/file/tests/test_models.py +82 -0
- django_spire/file/tests/test_querysets.py +102 -0
- django_spire/file/tests/test_utils.py +32 -0
- django_spire/file/tests/test_views.py +145 -0
- django_spire/file/tests/test_widgets.py +82 -0
- django_spire/file/tools.py +8 -2
- django_spire/file/views.py +7 -3
- django_spire/file/widgets.py +12 -12
- django_spire/help_desk/admin.py +15 -0
- django_spire/help_desk/apps.py +2 -0
- django_spire/help_desk/auth/controller.py +2 -0
- django_spire/help_desk/choices.py +2 -0
- django_spire/help_desk/enums.py +2 -0
- django_spire/help_desk/exceptions.py +31 -3
- django_spire/help_desk/forms.py +2 -0
- django_spire/help_desk/models.py +2 -0
- django_spire/help_desk/querysets.py +4 -1
- django_spire/help_desk/services/notification_service.py +26 -27
- django_spire/help_desk/services/service.py +2 -3
- django_spire/help_desk/tests/factories.py +8 -3
- django_spire/help_desk/tests/test_admin.py +41 -0
- django_spire/help_desk/tests/test_apps.py +41 -0
- django_spire/help_desk/tests/test_choices.py +50 -0
- django_spire/help_desk/tests/test_controller.py +87 -0
- django_spire/help_desk/tests/test_enums.py +18 -0
- django_spire/help_desk/tests/test_exceptions.py +37 -0
- django_spire/help_desk/tests/test_forms.py +89 -0
- django_spire/help_desk/tests/test_models.py +59 -0
- django_spire/help_desk/tests/test_querysets.py +38 -0
- django_spire/help_desk/tests/test_services/test_notification_service.py +15 -8
- django_spire/help_desk/tests/test_services/test_service.py +92 -0
- django_spire/help_desk/tests/test_urls/test_form_urls.py +6 -6
- django_spire/help_desk/tests/test_urls/test_page_urls.py +8 -9
- django_spire/help_desk/tests/test_views/test_form_views.py +46 -19
- django_spire/help_desk/tests/test_views/test_page_views.py +32 -9
- django_spire/help_desk/urls/__init__.py +4 -1
- django_spire/help_desk/urls/form_urls.py +3 -0
- django_spire/help_desk/urls/page_urls.py +3 -0
- django_spire/help_desk/views/form_views.py +13 -5
- django_spire/help_desk/views/page_views.py +11 -3
- django_spire/history/activity/admin.py +2 -0
- django_spire/history/activity/apps.py +3 -1
- django_spire/history/activity/mixins.py +13 -7
- django_spire/history/activity/models.py +6 -5
- django_spire/history/activity/querysets.py +2 -0
- django_spire/history/activity/tests/__init__.py +0 -0
- django_spire/history/activity/tests/test_activity.py +176 -0
- django_spire/history/admin.py +9 -2
- django_spire/history/choices.py +3 -0
- django_spire/history/models.py +5 -5
- django_spire/history/tests/test_admin.py +93 -0
- django_spire/history/tests/test_history.py +101 -0
- django_spire/history/tests/test_mixins.py +84 -0
- django_spire/history/viewed/admin.py +3 -1
- django_spire/history/viewed/apps.py +3 -1
- django_spire/history/viewed/models.py +2 -0
- django_spire/history/viewed/tests/__init__.py +0 -0
- django_spire/history/viewed/tests/test_viewed.py +46 -0
- django_spire/knowledge/auth/tests/__init__.py +0 -0
- django_spire/knowledge/auth/tests/test_controller.py +116 -0
- django_spire/knowledge/collection/admin.py +5 -1
- django_spire/knowledge/collection/models.py +3 -1
- django_spire/knowledge/collection/seeding/seed.py +1 -0
- django_spire/knowledge/collection/services/factory_service.py +10 -11
- django_spire/knowledge/collection/services/ordering_service.py +1 -2
- django_spire/knowledge/collection/services/service.py +5 -10
- django_spire/knowledge/collection/services/tag_service.py +5 -2
- django_spire/knowledge/collection/tests/factories.py +28 -1
- django_spire/knowledge/collection/tests/test_models.py +48 -0
- django_spire/knowledge/collection/tests/test_querysets.py +93 -0
- django_spire/knowledge/collection/tests/test_services/test_factory_service.py +100 -0
- django_spire/knowledge/collection/tests/test_services/test_services.py +160 -0
- django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +21 -3
- django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +39 -1
- django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +12 -4
- django_spire/knowledge/collection/urls/__init__.py +3 -0
- django_spire/knowledge/collection/urls/form_urls.py +2 -0
- django_spire/knowledge/collection/urls/json_urls.py +2 -0
- django_spire/knowledge/collection/urls/page_urls.py +2 -0
- django_spire/knowledge/collection/views/form_views.py +4 -4
- django_spire/knowledge/collection/views/json_views.py +5 -1
- django_spire/knowledge/collection/views/page_views.py +5 -2
- django_spire/knowledge/entry/admin.py +7 -1
- django_spire/knowledge/entry/forms.py +2 -0
- django_spire/knowledge/entry/models.py +2 -0
- django_spire/knowledge/entry/seeding/seed.py +3 -0
- django_spire/knowledge/entry/services/automation_service.py +5 -4
- django_spire/knowledge/entry/services/factory_service.py +7 -5
- django_spire/knowledge/entry/services/service.py +4 -7
- django_spire/knowledge/entry/services/tag_service.py +0 -1
- django_spire/knowledge/entry/services/tool_service.py +1 -0
- django_spire/knowledge/entry/services/transformation_services.py +1 -5
- django_spire/knowledge/entry/tests/factories.py +1 -2
- django_spire/knowledge/entry/tests/test_factory_service.py +20 -0
- django_spire/knowledge/entry/tests/test_models.py +41 -0
- django_spire/knowledge/entry/tests/test_querysets.py +71 -0
- django_spire/knowledge/entry/tests/test_services.py +94 -0
- django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +9 -14
- django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +48 -5
- django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +6 -8
- django_spire/knowledge/entry/tests/test_urls/test_template_urls.py +40 -0
- django_spire/knowledge/entry/urls/form_urls.py +2 -0
- django_spire/knowledge/entry/urls/json_urls.py +2 -0
- django_spire/knowledge/entry/urls/page_urls.py +2 -0
- django_spire/knowledge/entry/urls/template_urls.py +2 -0
- django_spire/knowledge/entry/version/block/choices.py +2 -0
- django_spire/knowledge/entry/version/block/data/data.py +1 -0
- django_spire/knowledge/entry/version/block/data/list/data.py +8 -13
- django_spire/knowledge/entry/version/block/data/list/maps.py +3 -0
- django_spire/knowledge/entry/version/block/data/list/meta.py +1 -2
- django_spire/knowledge/entry/version/block/data/list/tests/__init__.py +0 -0
- django_spire/knowledge/entry/version/block/data/list/tests/test_maps.py +32 -0
- django_spire/knowledge/entry/version/block/data/list/tests/test_meta.py +58 -0
- django_spire/knowledge/entry/version/block/data/maps.py +3 -6
- django_spire/knowledge/entry/version/block/models.py +7 -5
- django_spire/knowledge/entry/version/block/seeding/constants.py +5 -4
- django_spire/knowledge/entry/version/block/services/service.py +2 -3
- django_spire/knowledge/entry/version/block/tests/factories.py +4 -10
- django_spire/knowledge/entry/version/block/tests/test_choices.py +56 -0
- django_spire/knowledge/entry/version/block/tests/test_data.py +90 -0
- django_spire/knowledge/entry/version/block/tests/test_maps.py +37 -0
- django_spire/knowledge/entry/version/block/tests/test_models.py +55 -0
- django_spire/knowledge/entry/version/block/tests/test_querysets.py +35 -0
- django_spire/knowledge/entry/version/block/tests/test_services.py +65 -0
- django_spire/knowledge/entry/version/choices.py +2 -0
- django_spire/knowledge/entry/version/converters/converter.py +1 -1
- django_spire/knowledge/entry/version/converters/docx_converter.py +4 -7
- django_spire/knowledge/entry/version/converters/markdown_converter.py +20 -20
- django_spire/knowledge/entry/version/maps.py +4 -5
- django_spire/knowledge/entry/version/querysets.py +1 -1
- django_spire/knowledge/entry/version/seeding/seeder.py +1 -2
- django_spire/knowledge/entry/version/services/processor_service.py +5 -4
- django_spire/knowledge/entry/version/services/service.py +1 -2
- django_spire/knowledge/entry/version/tests/factories.py +2 -2
- django_spire/knowledge/entry/version/tests/test_choices.py +18 -0
- django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +56 -8
- django_spire/knowledge/entry/version/tests/test_converters/test_markdown_converter.py +78 -0
- django_spire/knowledge/entry/version/tests/test_maps.py +58 -0
- django_spire/knowledge/entry/version/tests/test_models.py +23 -0
- django_spire/knowledge/entry/version/tests/test_querysets.py +26 -0
- django_spire/knowledge/entry/version/tests/test_services.py +62 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +27 -8
- django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +15 -8
- django_spire/knowledge/entry/version/tests/test_urls/test_redirect_urls.py +38 -0
- django_spire/knowledge/entry/version/urls/__init__.py +3 -0
- django_spire/knowledge/entry/version/urls/json_urls.py +2 -1
- django_spire/knowledge/entry/version/urls/page_urls.py +2 -0
- django_spire/knowledge/entry/version/urls/redirect_urls.py +2 -0
- django_spire/knowledge/entry/version/views/json_views.py +5 -1
- django_spire/knowledge/entry/version/views/page_views.py +10 -3
- django_spire/knowledge/entry/version/views/redirect_views.py +5 -1
- django_spire/knowledge/entry/views/form_views.py +16 -8
- django_spire/knowledge/entry/views/json_views.py +3 -1
- django_spire/knowledge/entry/views/page_views.py +8 -2
- django_spire/knowledge/entry/views/template_views.py +7 -1
- django_spire/knowledge/exceptions.py +2 -1
- django_spire/knowledge/intelligence/intel/answer_intel.py +2 -1
- django_spire/knowledge/intelligence/intel/entry_intel.py +0 -1
- django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +4 -5
- django_spire/knowledge/models.py +1 -2
- django_spire/knowledge/tests/__init__.py +0 -0
- django_spire/knowledge/tests/test_templatetags.py +40 -0
- django_spire/knowledge/tests/test_urls/__init__.py +0 -0
- django_spire/knowledge/tests/test_urls/test_page_urls.py +24 -0
- django_spire/knowledge/urls/__init__.py +2 -0
- django_spire/knowledge/urls/page_urls.py +2 -0
- django_spire/knowledge/views/page_views.py +8 -3
- django_spire/notification/admin.py +3 -1
- django_spire/notification/app/admin.py +2 -0
- django_spire/notification/app/apps.py +3 -1
- django_spire/notification/app/exceptions.py +9 -2
- django_spire/notification/app/models.py +8 -4
- django_spire/notification/app/processor.py +22 -26
- django_spire/notification/app/querysets.py +2 -0
- django_spire/notification/app/tests/__init__.py +0 -0
- django_spire/notification/app/tests/factories.py +34 -0
- django_spire/notification/app/tests/test_apps.py +24 -0
- django_spire/notification/app/tests/test_models.py +72 -0
- django_spire/notification/app/tests/test_processor.py +111 -0
- django_spire/notification/app/tests/test_querysets.py +90 -0
- django_spire/notification/app/tests/test_views/__init__.py +0 -0
- django_spire/notification/app/tests/test_views/test_json_views.py +48 -0
- django_spire/notification/app/tests/test_views/test_page_views.py +19 -0
- django_spire/notification/app/urls/__init__.py +3 -1
- django_spire/notification/app/urls/json_urls.py +6 -4
- django_spire/notification/app/urls/page_urls.py +4 -3
- django_spire/notification/app/urls/template_urls.py +4 -2
- django_spire/notification/apps.py +4 -1
- django_spire/notification/email/admin.py +5 -1
- django_spire/notification/email/apps.py +3 -1
- django_spire/notification/email/exceptions.py +4 -2
- django_spire/notification/email/helper.py +5 -3
- django_spire/notification/email/models.py +4 -0
- django_spire/notification/email/processor.py +19 -15
- django_spire/notification/email/querysets.py +3 -0
- django_spire/notification/email/tests/__init__.py +0 -0
- django_spire/notification/email/tests/factories.py +35 -0
- django_spire/notification/email/tests/test_apps.py +24 -0
- django_spire/notification/email/tests/test_models.py +52 -0
- django_spire/notification/email/tests/test_processor.py +92 -0
- django_spire/notification/email/tests/test_querysets.py +43 -0
- django_spire/notification/exceptions.py +17 -2
- django_spire/notification/managers.py +7 -1
- django_spire/notification/maps.py +4 -1
- django_spire/notification/mixins.py +2 -0
- django_spire/notification/models.py +3 -1
- django_spire/notification/processors/notification.py +12 -5
- django_spire/notification/processors/processor.py +2 -0
- django_spire/notification/processors/tests/__init__.py +0 -0
- django_spire/notification/processors/tests/test_notification.py +106 -0
- django_spire/notification/push/admin.py +10 -1
- django_spire/notification/push/apps.py +3 -1
- django_spire/notification/push/models.py +2 -3
- django_spire/notification/push/tests/__init__.py +0 -0
- django_spire/notification/push/tests/test_apps.py +24 -0
- django_spire/notification/push/tests/test_models.py +28 -0
- django_spire/notification/querysets.py +7 -1
- django_spire/notification/sms/admin.py +2 -0
- django_spire/notification/sms/apps.py +4 -1
- django_spire/notification/sms/automations.py +2 -0
- django_spire/notification/sms/choices.py +2 -0
- django_spire/notification/sms/exceptions.py +19 -5
- django_spire/notification/sms/helper.py +33 -23
- django_spire/notification/sms/models.py +5 -1
- django_spire/notification/sms/processor.py +20 -20
- django_spire/notification/sms/querysets.py +2 -0
- django_spire/notification/sms/tests/factories.py +33 -0
- django_spire/notification/sms/tests/test_apps.py +24 -0
- django_spire/notification/sms/tests/test_automation.py +38 -0
- django_spire/notification/sms/tests/test_choices.py +15 -0
- django_spire/notification/sms/tests/test_consts.py +17 -0
- django_spire/notification/sms/tests/test_exceptions.py +27 -0
- django_spire/notification/sms/tests/test_helper.py +50 -0
- django_spire/notification/sms/tests/test_models.py +81 -0
- django_spire/notification/sms/tests/test_processor.py +107 -0
- django_spire/notification/sms/tests/test_tools.py +25 -11
- django_spire/notification/sms/tools.py +16 -5
- django_spire/notification/sms/urls/__init__.py +3 -1
- django_spire/notification/sms/urls/media_urls.py +2 -0
- django_spire/notification/sms/views/media_views.py +14 -4
- django_spire/notification/tests/__init__.py +0 -0
- django_spire/notification/tests/factories.py +26 -0
- django_spire/notification/tests/test_admin.py +55 -0
- django_spire/notification/tests/test_apps.py +30 -0
- django_spire/notification/tests/test_automation.py +18 -0
- django_spire/notification/tests/test_choices.py +59 -0
- django_spire/notification/tests/test_exceptions.py +58 -0
- django_spire/notification/tests/test_managers.py +100 -0
- django_spire/notification/tests/test_maps.py +31 -0
- django_spire/notification/tests/test_models.py +76 -0
- django_spire/notification/tests/test_querysets.py +184 -0
- django_spire/notification/tests/test_utils.py +23 -0
- django_spire/notification/urls.py +3 -1
- django_spire/notification/utils.py +3 -1
- django_spire/settings.py +3 -0
- django_spire/theme/tests/test_context_processor.py +15 -13
- django_spire/theme/tests/test_enums.py +2 -2
- django_spire/theme/tests/test_filesystem.py +2 -5
- django_spire/theme/tests/test_integration.py +12 -12
- django_spire/theme/tests/test_model.py +40 -38
- django_spire/theme/tests/test_views/test_json_views.py +33 -33
- django_spire/theme/urls/json_urls.py +3 -0
- django_spire/theme/urls/page_urls.py +3 -0
- django_spire/urls.py +19 -15
- django_spire/utils.py +13 -4
- {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/METADATA +1 -1
- {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/RECORD +532 -361
- django_spire/contrib/options/tests/test_unit.py +0 -148
- django_spire/contrib/progress/views.py +0 -64
- django_spire/contrib/seeding/tests/test_seeding.py +0 -25
- django_spire/core/tests/test_templatetags.py +0 -117
- django_spire/core/tests/tests_shortcuts.py +0 -73
- django_spire/history/activity/tests.py +0 -3
- django_spire/history/activity/views.py +0 -3
- django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +0 -71
- django_spire/notification/app/tests.py +0 -3
- {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/WHEEL +0 -0
- {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.23.6.dist-info → django_spire-0.23.8.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.conf import settings
|
|
4
6
|
from django.contrib.auth.models import User
|
|
5
7
|
from django.contrib.contenttypes.models import ContentType
|
|
6
8
|
from django.contrib.sites.models import Site
|
|
7
9
|
from django.db.models import Q
|
|
8
|
-
from typing import TYPE_CHECKING
|
|
9
|
-
|
|
10
10
|
from django.urls import reverse
|
|
11
11
|
|
|
12
12
|
from django_spire.contrib.service import BaseDjangoModelService
|
|
13
|
-
from django_spire.help_desk.exceptions import
|
|
14
|
-
TicketEventNotificationTypeNotSupportedError,
|
|
15
|
-
)
|
|
13
|
+
from django_spire.help_desk.exceptions import TicketEventNotificationTypeNotSupportedError
|
|
16
14
|
from django_spire.help_desk.enums import TicketEventType
|
|
17
15
|
from django_spire.notification.app.models import AppNotification
|
|
18
16
|
from django_spire.notification.choices import NotificationTypeChoices
|
|
@@ -63,9 +61,9 @@ class HelpDeskTicketNotificationService(BaseDjangoModelService['HelpDeskTicket']
|
|
|
63
61
|
return content_map[event_type]
|
|
64
62
|
|
|
65
63
|
def _get_ticket_event_notification_body(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
self,
|
|
65
|
+
event_type: TicketEventType,
|
|
66
|
+
notification_type: NotificationTypeChoices,
|
|
69
67
|
) -> str:
|
|
70
68
|
content_map = {
|
|
71
69
|
TicketEventType.NEW: {
|
|
@@ -86,14 +84,11 @@ class HelpDeskTicketNotificationService(BaseDjangoModelService['HelpDeskTicket']
|
|
|
86
84
|
try:
|
|
87
85
|
return content_map[event_type][notification_type]
|
|
88
86
|
except KeyError:
|
|
89
|
-
raise TicketEventNotificationTypeNotSupportedError(
|
|
90
|
-
f'Combination of event type and notification type not supported: '
|
|
91
|
-
f'Event type {event_type} - Notification type {notification_type}'
|
|
92
|
-
)
|
|
87
|
+
raise TicketEventNotificationTypeNotSupportedError(event_type, notification_type) from None
|
|
93
88
|
|
|
94
89
|
def _get_ticket_notification_url(
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
self,
|
|
91
|
+
notification_type: NotificationTypeChoices
|
|
97
92
|
) -> str:
|
|
98
93
|
path = reverse('django_spire:help_desk:page:detail', kwargs={'pk': self.obj.pk})
|
|
99
94
|
|
|
@@ -104,12 +99,12 @@ class HelpDeskTicketNotificationService(BaseDjangoModelService['HelpDeskTicket']
|
|
|
104
99
|
return path
|
|
105
100
|
|
|
106
101
|
def _create_ticket_event_notification_for_user(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
self,
|
|
103
|
+
notification_type: NotificationTypeChoices,
|
|
104
|
+
body: str,
|
|
105
|
+
title: str,
|
|
106
|
+
url: str,
|
|
107
|
+
user: User,
|
|
113
108
|
) -> EmailNotification | AppNotification:
|
|
114
109
|
base_notification = Notification(
|
|
115
110
|
content_type=ContentType.objects.get_for_model(self.obj),
|
|
@@ -128,22 +123,25 @@ class HelpDeskTicketNotificationService(BaseDjangoModelService['HelpDeskTicket']
|
|
|
128
123
|
settings.DEVELOPMENT_EMAIL if settings.DEBUG else user.email
|
|
129
124
|
)
|
|
130
125
|
)
|
|
131
|
-
|
|
126
|
+
|
|
127
|
+
if notification_type == NotificationTypeChoices.APP:
|
|
132
128
|
return AppNotification(notification=base_notification)
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
|
|
130
|
+
raise TicketEventNotificationTypeNotSupportedError(None, notification_type)
|
|
135
131
|
|
|
136
132
|
def _create_ticket_event_notifications(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
self,
|
|
134
|
+
users: list[User],
|
|
135
|
+
notification_type: NotificationTypeChoices,
|
|
136
|
+
event_type: TicketEventType
|
|
141
137
|
):
|
|
142
138
|
title = self._get_ticket_event_notification_title(event_type)
|
|
139
|
+
|
|
143
140
|
body = self._get_ticket_event_notification_body(
|
|
144
141
|
event_type=event_type,
|
|
145
142
|
notification_type=notification_type,
|
|
146
143
|
)
|
|
144
|
+
|
|
147
145
|
url = self._get_ticket_notification_url(notification_type)
|
|
148
146
|
|
|
149
147
|
notifications = [
|
|
@@ -164,6 +162,7 @@ class HelpDeskTicketNotificationService(BaseDjangoModelService['HelpDeskTicket']
|
|
|
164
162
|
if notification
|
|
165
163
|
]
|
|
166
164
|
)
|
|
165
|
+
|
|
167
166
|
NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP[notification_type].objects.bulk_create(
|
|
168
167
|
notifications
|
|
169
168
|
)
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import TYPE_CHECKING
|
|
3
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from django_spire.contrib.service import BaseDjangoModelService
|
|
6
|
-
from django_spire.help_desk.services.notification_service import
|
|
7
|
-
HelpDeskTicketNotificationService
|
|
6
|
+
from django_spire.help_desk.services.notification_service import HelpDeskTicketNotificationService
|
|
8
7
|
|
|
9
8
|
if TYPE_CHECKING:
|
|
10
9
|
from django.contrib.auth.models import User
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.contrib.auth.models import User
|
|
2
4
|
|
|
3
|
-
from django_spire.help_desk.choices import
|
|
4
|
-
|
|
5
|
+
from django_spire.help_desk.choices import (
|
|
6
|
+
HelpDeskTicketPriorityChoices,
|
|
7
|
+
HelpDeskTicketPurposeChoices,
|
|
8
|
+
HelpDeskTicketStatusChoices,
|
|
9
|
+
)
|
|
5
10
|
from django_spire.help_desk.models import HelpDeskTicket
|
|
6
11
|
|
|
7
12
|
|
|
8
|
-
def create_test_helpdesk_ticket(**kwargs):
|
|
13
|
+
def create_test_helpdesk_ticket(**kwargs) -> HelpDeskTicket:
|
|
9
14
|
if 'created_by' not in kwargs:
|
|
10
15
|
kwargs['created_by'] = User.objects.first()
|
|
11
16
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.admin.sites import AdminSite
|
|
4
|
+
from django.test import RequestFactory
|
|
5
|
+
|
|
6
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
7
|
+
from django_spire.help_desk.admin import HelpDeskTicketAdmin
|
|
8
|
+
from django_spire.help_desk.models import HelpDeskTicket
|
|
9
|
+
from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HelpDeskTicketAdminTests(BaseTestCase):
|
|
13
|
+
def setUp(self):
|
|
14
|
+
super().setUp()
|
|
15
|
+
|
|
16
|
+
self.site = AdminSite()
|
|
17
|
+
self.admin = HelpDeskTicketAdmin(HelpDeskTicket, self.site)
|
|
18
|
+
self.factory = RequestFactory()
|
|
19
|
+
self.ticket = create_test_helpdesk_ticket()
|
|
20
|
+
|
|
21
|
+
def test_list_display(self):
|
|
22
|
+
expected = ('pk', 'purpose', 'priority', 'status', 'created_by', 'created_datetime')
|
|
23
|
+
assert self.admin.list_display == expected
|
|
24
|
+
|
|
25
|
+
def test_list_filter(self):
|
|
26
|
+
expected = ('priority', 'purpose', 'status', 'is_active', 'is_deleted')
|
|
27
|
+
assert self.admin.list_filter == expected
|
|
28
|
+
|
|
29
|
+
def test_ordering(self):
|
|
30
|
+
assert self.admin.ordering == ('-created_datetime',)
|
|
31
|
+
|
|
32
|
+
def test_raw_id_fields(self):
|
|
33
|
+
assert self.admin.raw_id_fields == ('created_by',)
|
|
34
|
+
|
|
35
|
+
def test_readonly_fields(self):
|
|
36
|
+
expected = ('created_by', 'created_datetime', 'is_active', 'is_deleted')
|
|
37
|
+
assert self.admin.readonly_fields == expected
|
|
38
|
+
|
|
39
|
+
def test_search_fields(self):
|
|
40
|
+
expected = ('description', 'created_by__username', 'created_by__email')
|
|
41
|
+
assert self.admin.search_fields == expected
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.apps import apps
|
|
4
|
+
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.help_desk.apps import HelpDeskConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HelpDeskConfigTests(BaseTestCase):
|
|
10
|
+
def test_app_name(self):
|
|
11
|
+
assert HelpDeskConfig.name == 'django_spire.help_desk'
|
|
12
|
+
|
|
13
|
+
def test_app_label(self):
|
|
14
|
+
assert HelpDeskConfig.label == 'django_spire_help_desk'
|
|
15
|
+
|
|
16
|
+
def test_default_auto_field(self):
|
|
17
|
+
assert HelpDeskConfig.default_auto_field == 'django.db.models.BigAutoField'
|
|
18
|
+
|
|
19
|
+
def test_required_apps(self):
|
|
20
|
+
assert HelpDeskConfig.REQUIRED_APPS == ('django_spire_core',)
|
|
21
|
+
|
|
22
|
+
def test_urlpatterns_include(self):
|
|
23
|
+
assert HelpDeskConfig.URLPATTERNS_INCLUDE == 'django_spire.help_desk.urls'
|
|
24
|
+
|
|
25
|
+
def test_urlpatterns_namespace(self):
|
|
26
|
+
assert HelpDeskConfig.URLPATTERNS_NAMESPACE == 'help_desk'
|
|
27
|
+
|
|
28
|
+
def test_model_permissions(self):
|
|
29
|
+
expected = (
|
|
30
|
+
{
|
|
31
|
+
'name': 'help_desk',
|
|
32
|
+
'verbose_name': 'Help Desk',
|
|
33
|
+
'model_class_path': 'django_spire.help_desk.models.HelpDeskTicket',
|
|
34
|
+
'is_proxy_model': False,
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
assert expected == HelpDeskConfig.MODEL_PERMISSIONS
|
|
39
|
+
|
|
40
|
+
def test_app_is_installed(self):
|
|
41
|
+
assert apps.is_installed('django_spire.help_desk')
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.help_desk.choices import (
|
|
5
|
+
HelpDeskTicketPriorityChoices,
|
|
6
|
+
HelpDeskTicketPurposeChoices,
|
|
7
|
+
HelpDeskTicketStatusChoices,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class HelpDeskTicketPurposeChoicesTests(BaseTestCase):
|
|
12
|
+
def test_app_value(self):
|
|
13
|
+
assert HelpDeskTicketPurposeChoices.APP == 'app'
|
|
14
|
+
|
|
15
|
+
def test_company_value(self):
|
|
16
|
+
assert HelpDeskTicketPurposeChoices.COMPANY == 'comp'
|
|
17
|
+
|
|
18
|
+
def test_choices_count(self):
|
|
19
|
+
assert len(HelpDeskTicketPurposeChoices.choices) == 2
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HelpDeskTicketStatusChoicesTests(BaseTestCase):
|
|
23
|
+
def test_ready_value(self):
|
|
24
|
+
assert HelpDeskTicketStatusChoices.READY == 'read'
|
|
25
|
+
|
|
26
|
+
def test_inprogress_value(self):
|
|
27
|
+
assert HelpDeskTicketStatusChoices.INPROGRESS == 'prog'
|
|
28
|
+
|
|
29
|
+
def test_done_value(self):
|
|
30
|
+
assert HelpDeskTicketStatusChoices.DONE == 'done'
|
|
31
|
+
|
|
32
|
+
def test_choices_count(self):
|
|
33
|
+
assert len(HelpDeskTicketStatusChoices.choices) == 3
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class HelpDeskTicketPriorityChoicesTests(BaseTestCase):
|
|
37
|
+
def test_low_value(self):
|
|
38
|
+
assert HelpDeskTicketPriorityChoices.LOW == 'low'
|
|
39
|
+
|
|
40
|
+
def test_medium_value(self):
|
|
41
|
+
assert HelpDeskTicketPriorityChoices.MEDIUM == 'med'
|
|
42
|
+
|
|
43
|
+
def test_high_value(self):
|
|
44
|
+
assert HelpDeskTicketPriorityChoices.HIGH == 'high'
|
|
45
|
+
|
|
46
|
+
def test_urgent_value(self):
|
|
47
|
+
assert HelpDeskTicketPriorityChoices.URGENT == 'urge'
|
|
48
|
+
|
|
49
|
+
def test_choices_count(self):
|
|
50
|
+
assert len(HelpDeskTicketPriorityChoices.choices) == 4
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth.models import Permission
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.test import RequestFactory
|
|
6
|
+
|
|
7
|
+
from django_spire.auth.user.tests.factories import create_user
|
|
8
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
9
|
+
from django_spire.help_desk.auth.controller import BaseHelpDeskAuthController
|
|
10
|
+
from django_spire.help_desk.models import HelpDeskTicket
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseHelpDeskAuthControllerTests(BaseTestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
super().setUp()
|
|
16
|
+
|
|
17
|
+
self.factory = RequestFactory()
|
|
18
|
+
self.user = create_user(username='test_helpdesk_auth_user')
|
|
19
|
+
self.request = self.factory.get('/')
|
|
20
|
+
self.request.user = self.user
|
|
21
|
+
|
|
22
|
+
content_type = ContentType.objects.get_for_model(HelpDeskTicket)
|
|
23
|
+
|
|
24
|
+
self.view_permission = Permission.objects.get(
|
|
25
|
+
codename='view_helpdeskticket',
|
|
26
|
+
content_type=content_type
|
|
27
|
+
)
|
|
28
|
+
self.add_permission = Permission.objects.get(
|
|
29
|
+
codename='add_helpdeskticket',
|
|
30
|
+
content_type=content_type
|
|
31
|
+
)
|
|
32
|
+
self.change_permission = Permission.objects.get(
|
|
33
|
+
codename='change_helpdeskticket',
|
|
34
|
+
content_type=content_type
|
|
35
|
+
)
|
|
36
|
+
self.delete_permission = Permission.objects.get(
|
|
37
|
+
codename='delete_helpdeskticket',
|
|
38
|
+
content_type=content_type
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def _refresh_user_and_request(self):
|
|
42
|
+
self.user = type(self.user).objects.get(pk=self.user.pk)
|
|
43
|
+
self.request.user = self.user
|
|
44
|
+
|
|
45
|
+
def test_can_view_without_permission(self):
|
|
46
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
47
|
+
assert controller.can_view() is False
|
|
48
|
+
|
|
49
|
+
def test_can_view_with_permission(self):
|
|
50
|
+
self.user.user_permissions.add(self.view_permission)
|
|
51
|
+
self._refresh_user_and_request()
|
|
52
|
+
|
|
53
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
54
|
+
assert controller.can_view() is True
|
|
55
|
+
|
|
56
|
+
def test_can_add_without_permission(self):
|
|
57
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
58
|
+
assert controller.can_add() is False
|
|
59
|
+
|
|
60
|
+
def test_can_add_with_permission(self):
|
|
61
|
+
self.user.user_permissions.add(self.add_permission)
|
|
62
|
+
self._refresh_user_and_request()
|
|
63
|
+
|
|
64
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
65
|
+
assert controller.can_add() is True
|
|
66
|
+
|
|
67
|
+
def test_can_change_without_permission(self):
|
|
68
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
69
|
+
assert controller.can_change() is False
|
|
70
|
+
|
|
71
|
+
def test_can_change_with_permission(self):
|
|
72
|
+
self.user.user_permissions.add(self.change_permission)
|
|
73
|
+
self._refresh_user_and_request()
|
|
74
|
+
|
|
75
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
76
|
+
assert controller.can_change() is True
|
|
77
|
+
|
|
78
|
+
def test_can_delete_without_permission(self):
|
|
79
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
80
|
+
assert controller.can_delete() is False
|
|
81
|
+
|
|
82
|
+
def test_can_delete_with_permission(self):
|
|
83
|
+
self.user.user_permissions.add(self.delete_permission)
|
|
84
|
+
self._refresh_user_and_request()
|
|
85
|
+
|
|
86
|
+
controller = BaseHelpDeskAuthController(self.request)
|
|
87
|
+
assert controller.can_delete() is True
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.help_desk.enums import TicketEventType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TicketEventTypeTests(BaseTestCase):
|
|
8
|
+
def test_new_value(self):
|
|
9
|
+
assert TicketEventType.NEW.value == 'new'
|
|
10
|
+
|
|
11
|
+
def test_update_value(self):
|
|
12
|
+
assert TicketEventType.UPDATE.value == 'update'
|
|
13
|
+
|
|
14
|
+
def test_comment_value(self):
|
|
15
|
+
assert TicketEventType.COMMENT.value == 'comment'
|
|
16
|
+
|
|
17
|
+
def test_enum_members_count(self):
|
|
18
|
+
assert len(TicketEventType) == 3
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.help_desk.enums import TicketEventType
|
|
5
|
+
from django_spire.help_desk.exceptions import (
|
|
6
|
+
HelpDeskNotificationRecipientMissingEmailError,
|
|
7
|
+
TicketEventNotificationTypeNotSupportedError,
|
|
8
|
+
)
|
|
9
|
+
from django_spire.notification.choices import NotificationTypeChoices
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HelpDeskExceptionsTests(BaseTestCase):
|
|
13
|
+
def test_ticket_event_notification_type_not_supported_error_is_type_error(self):
|
|
14
|
+
assert issubclass(TicketEventNotificationTypeNotSupportedError, TypeError)
|
|
15
|
+
|
|
16
|
+
def test_ticket_event_notification_type_not_supported_error_message(self):
|
|
17
|
+
error = TicketEventNotificationTypeNotSupportedError(
|
|
18
|
+
TicketEventType.NEW,
|
|
19
|
+
NotificationTypeChoices.SMS
|
|
20
|
+
)
|
|
21
|
+
assert 'Event type' in str(error)
|
|
22
|
+
assert 'Notification type' in str(error)
|
|
23
|
+
|
|
24
|
+
def test_ticket_event_notification_type_not_supported_error_message_without_event_type(self):
|
|
25
|
+
error = TicketEventNotificationTypeNotSupportedError(
|
|
26
|
+
None,
|
|
27
|
+
NotificationTypeChoices.SMS
|
|
28
|
+
)
|
|
29
|
+
assert 'Notification type not supported' in str(error)
|
|
30
|
+
|
|
31
|
+
def test_helpdesk_notification_recipient_missing_email_error_is_value_error(self):
|
|
32
|
+
assert issubclass(HelpDeskNotificationRecipientMissingEmailError, ValueError)
|
|
33
|
+
|
|
34
|
+
def test_helpdesk_notification_recipient_missing_email_error_message(self):
|
|
35
|
+
error = HelpDeskNotificationRecipientMissingEmailError('John Doe')
|
|
36
|
+
assert 'John Doe' in str(error)
|
|
37
|
+
assert 'missing an email address' in str(error)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.help_desk.choices import (
|
|
5
|
+
HelpDeskTicketPriorityChoices,
|
|
6
|
+
HelpDeskTicketPurposeChoices,
|
|
7
|
+
HelpDeskTicketStatusChoices,
|
|
8
|
+
)
|
|
9
|
+
from django_spire.help_desk.forms import (
|
|
10
|
+
HelpDeskTicketCreateForm,
|
|
11
|
+
HelpDeskTicketUpdateForm,
|
|
12
|
+
)
|
|
13
|
+
from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HelpDeskTicketCreateFormTests(BaseTestCase):
|
|
17
|
+
def test_valid_form(self):
|
|
18
|
+
form_data = {
|
|
19
|
+
'purpose': HelpDeskTicketPurposeChoices.APP,
|
|
20
|
+
'priority': HelpDeskTicketPriorityChoices.LOW,
|
|
21
|
+
'description': 'Test description',
|
|
22
|
+
}
|
|
23
|
+
form = HelpDeskTicketCreateForm(data=form_data)
|
|
24
|
+
assert form.is_valid()
|
|
25
|
+
|
|
26
|
+
def test_invalid_form_missing_purpose(self):
|
|
27
|
+
form_data = {
|
|
28
|
+
'priority': HelpDeskTicketPriorityChoices.LOW,
|
|
29
|
+
'description': 'Test description',
|
|
30
|
+
}
|
|
31
|
+
form = HelpDeskTicketCreateForm(data=form_data)
|
|
32
|
+
assert not form.is_valid()
|
|
33
|
+
assert 'purpose' in form.errors
|
|
34
|
+
|
|
35
|
+
def test_invalid_form_missing_priority(self):
|
|
36
|
+
form_data = {
|
|
37
|
+
'purpose': HelpDeskTicketPurposeChoices.APP,
|
|
38
|
+
'description': 'Test description',
|
|
39
|
+
}
|
|
40
|
+
form = HelpDeskTicketCreateForm(data=form_data)
|
|
41
|
+
assert not form.is_valid()
|
|
42
|
+
assert 'priority' in form.errors
|
|
43
|
+
|
|
44
|
+
def test_invalid_form_missing_description(self):
|
|
45
|
+
form_data = {
|
|
46
|
+
'purpose': HelpDeskTicketPurposeChoices.APP,
|
|
47
|
+
'priority': HelpDeskTicketPriorityChoices.LOW,
|
|
48
|
+
}
|
|
49
|
+
form = HelpDeskTicketCreateForm(data=form_data)
|
|
50
|
+
assert not form.is_valid()
|
|
51
|
+
assert 'description' in form.errors
|
|
52
|
+
|
|
53
|
+
def test_excludes_created_by(self):
|
|
54
|
+
assert 'created_by' not in HelpDeskTicketCreateForm().fields
|
|
55
|
+
|
|
56
|
+
def test_excludes_status(self):
|
|
57
|
+
assert 'status' not in HelpDeskTicketCreateForm().fields
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class HelpDeskTicketUpdateFormTests(BaseTestCase):
|
|
61
|
+
def setUp(self):
|
|
62
|
+
super().setUp()
|
|
63
|
+
self.ticket = create_test_helpdesk_ticket()
|
|
64
|
+
|
|
65
|
+
def test_valid_form(self):
|
|
66
|
+
form_data = {
|
|
67
|
+
'purpose': HelpDeskTicketPurposeChoices.COMPANY,
|
|
68
|
+
'priority': HelpDeskTicketPriorityChoices.HIGH,
|
|
69
|
+
'status': HelpDeskTicketStatusChoices.INPROGRESS,
|
|
70
|
+
'description': 'Updated description',
|
|
71
|
+
}
|
|
72
|
+
form = HelpDeskTicketUpdateForm(data=form_data, instance=self.ticket)
|
|
73
|
+
assert form.is_valid()
|
|
74
|
+
|
|
75
|
+
def test_includes_status(self):
|
|
76
|
+
assert 'status' in HelpDeskTicketUpdateForm().fields
|
|
77
|
+
|
|
78
|
+
def test_excludes_created_by(self):
|
|
79
|
+
assert 'created_by' not in HelpDeskTicketUpdateForm().fields
|
|
80
|
+
|
|
81
|
+
def test_invalid_form_missing_status(self):
|
|
82
|
+
form_data = {
|
|
83
|
+
'purpose': HelpDeskTicketPurposeChoices.APP,
|
|
84
|
+
'priority': HelpDeskTicketPriorityChoices.LOW,
|
|
85
|
+
'description': 'Test description',
|
|
86
|
+
}
|
|
87
|
+
form = HelpDeskTicketUpdateForm(data=form_data, instance=self.ticket)
|
|
88
|
+
assert not form.is_valid()
|
|
89
|
+
assert 'status' in form.errors
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.help_desk.choices import (
|
|
5
|
+
HelpDeskTicketPriorityChoices,
|
|
6
|
+
HelpDeskTicketPurposeChoices,
|
|
7
|
+
HelpDeskTicketStatusChoices,
|
|
8
|
+
)
|
|
9
|
+
from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class HelpDeskTicketModelTests(BaseTestCase):
|
|
13
|
+
def setUp(self):
|
|
14
|
+
super().setUp()
|
|
15
|
+
|
|
16
|
+
self.ticket = create_test_helpdesk_ticket()
|
|
17
|
+
|
|
18
|
+
def test_str(self):
|
|
19
|
+
assert str(self.ticket) == f'Ticket #{self.ticket.pk}'
|
|
20
|
+
|
|
21
|
+
def test_default_status_is_ready(self):
|
|
22
|
+
assert self.ticket.status == HelpDeskTicketStatusChoices.READY
|
|
23
|
+
|
|
24
|
+
def test_get_priority_display(self):
|
|
25
|
+
self.ticket.priority = HelpDeskTicketPriorityChoices.LOW
|
|
26
|
+
assert self.ticket.get_priority_display() == 'Low'
|
|
27
|
+
|
|
28
|
+
self.ticket.priority = HelpDeskTicketPriorityChoices.MEDIUM
|
|
29
|
+
assert self.ticket.get_priority_display() == 'Medium'
|
|
30
|
+
|
|
31
|
+
self.ticket.priority = HelpDeskTicketPriorityChoices.HIGH
|
|
32
|
+
assert self.ticket.get_priority_display() == 'High'
|
|
33
|
+
|
|
34
|
+
self.ticket.priority = HelpDeskTicketPriorityChoices.URGENT
|
|
35
|
+
assert self.ticket.get_priority_display() == 'Urgent'
|
|
36
|
+
|
|
37
|
+
def test_get_purpose_display(self):
|
|
38
|
+
self.ticket.purpose = HelpDeskTicketPurposeChoices.APP
|
|
39
|
+
assert self.ticket.get_purpose_display() == 'App'
|
|
40
|
+
|
|
41
|
+
self.ticket.purpose = HelpDeskTicketPurposeChoices.COMPANY
|
|
42
|
+
assert self.ticket.get_purpose_display() == 'Company'
|
|
43
|
+
|
|
44
|
+
def test_get_status_display(self):
|
|
45
|
+
self.ticket.status = HelpDeskTicketStatusChoices.READY
|
|
46
|
+
assert self.ticket.get_status_display() == 'Ready'
|
|
47
|
+
|
|
48
|
+
self.ticket.status = HelpDeskTicketStatusChoices.INPROGRESS
|
|
49
|
+
assert self.ticket.get_status_display() == 'In Progress'
|
|
50
|
+
|
|
51
|
+
self.ticket.status = HelpDeskTicketStatusChoices.DONE
|
|
52
|
+
assert self.ticket.get_status_display() == 'Done'
|
|
53
|
+
|
|
54
|
+
def test_created_by_relationship(self):
|
|
55
|
+
assert self.ticket.created_by is not None
|
|
56
|
+
assert self.ticket.created_by.pk is not None
|
|
57
|
+
|
|
58
|
+
def test_created_datetime_auto_set(self):
|
|
59
|
+
assert self.ticket.created_datetime is not None
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.help_desk.models import HelpDeskTicket
|
|
5
|
+
from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HelpDeskTicketQuerySetTests(BaseTestCase):
|
|
9
|
+
def setUp(self):
|
|
10
|
+
super().setUp()
|
|
11
|
+
|
|
12
|
+
self.ticket1 = create_test_helpdesk_ticket(description='Ticket 1')
|
|
13
|
+
self.ticket2 = create_test_helpdesk_ticket(description='Ticket 2')
|
|
14
|
+
|
|
15
|
+
def test_active_returns_non_deleted(self):
|
|
16
|
+
result = HelpDeskTicket.objects.active()
|
|
17
|
+
assert self.ticket1 in result
|
|
18
|
+
assert self.ticket2 in result
|
|
19
|
+
|
|
20
|
+
def test_active_excludes_deleted(self):
|
|
21
|
+
self.ticket1.is_deleted = True
|
|
22
|
+
self.ticket1.save()
|
|
23
|
+
|
|
24
|
+
result = HelpDeskTicket.objects.active()
|
|
25
|
+
assert self.ticket1 not in result
|
|
26
|
+
assert self.ticket2 in result
|
|
27
|
+
|
|
28
|
+
def test_active_excludes_inactive(self):
|
|
29
|
+
self.ticket1.is_active = False
|
|
30
|
+
self.ticket1.save()
|
|
31
|
+
|
|
32
|
+
result = HelpDeskTicket.objects.active()
|
|
33
|
+
assert self.ticket1 not in result
|
|
34
|
+
assert self.ticket2 in result
|
|
35
|
+
|
|
36
|
+
def test_order_by_created_datetime(self):
|
|
37
|
+
result = list(HelpDeskTicket.objects.order_by('-created_datetime'))
|
|
38
|
+
assert result[0].created_datetime >= result[1].created_datetime
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
from django.conf import settings
|
|
2
6
|
from django.contrib.auth.models import User
|
|
3
7
|
from django.db.models import Q, QuerySet
|
|
4
8
|
from django.test import override_settings
|
|
5
9
|
|
|
6
10
|
from django_spire.core.tests.test_cases import BaseTestCase
|
|
7
|
-
from django_spire.help_desk.models import HelpDeskTicket
|
|
8
11
|
from django_spire.help_desk.tests.factories import create_test_helpdesk_ticket
|
|
9
12
|
from django_spire.notification.app.models import AppNotification
|
|
10
13
|
from django_spire.notification.email.models import EmailNotification
|
|
11
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from django_spire.help_desk.models import HelpDeskTicket
|
|
17
|
+
|
|
18
|
+
|
|
12
19
|
TEST_ADMINS = [
|
|
13
20
|
('developer1', 'developer1@stratus.com'),
|
|
14
21
|
('developer2', 'developer2@stratus.com'),
|
|
@@ -17,17 +24,17 @@ TEST_ADMINS = [
|
|
|
17
24
|
|
|
18
25
|
class HelpDeskTicketNotificationServiceTestCase(BaseTestCase):
|
|
19
26
|
def _assert_user_notification_ticket_integrity(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
self,
|
|
28
|
+
ticket: HelpDeskTicket,
|
|
29
|
+
notification_type: type,
|
|
30
|
+
users: QuerySet[User]
|
|
24
31
|
):
|
|
25
32
|
notifications = notification_type.objects.by_users(users)
|
|
26
|
-
|
|
33
|
+
assert len(notifications) == len(users)
|
|
27
34
|
|
|
28
35
|
for notification in notifications:
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
assert notification.notification.content_object == ticket
|
|
37
|
+
assert users.filter(pk=notification.notification.user.pk).exists()
|
|
31
38
|
|
|
32
39
|
@override_settings(ADMINS=TEST_ADMINS)
|
|
33
40
|
def test_create_new_ticket_notifications(self):
|