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
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth.models import User
|
|
4
|
+
|
|
5
|
+
from django_spire.notification.choices import (
|
|
6
|
+
NotificationPriorityChoices,
|
|
7
|
+
NotificationStatusChoices,
|
|
8
|
+
NotificationTypeChoices,
|
|
9
|
+
)
|
|
10
|
+
from django_spire.notification.models import Notification
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def create_test_notification(**kwargs) -> Notification:
|
|
14
|
+
if 'user' not in kwargs:
|
|
15
|
+
kwargs['user'] = User.objects.first()
|
|
16
|
+
|
|
17
|
+
data = {
|
|
18
|
+
'type': NotificationTypeChoices.APP,
|
|
19
|
+
'title': 'Test Notification',
|
|
20
|
+
'body': 'This is a test notification body.',
|
|
21
|
+
'url': 'https://example.com',
|
|
22
|
+
'status': NotificationStatusChoices.PENDING,
|
|
23
|
+
'priority': NotificationPriorityChoices.LOW,
|
|
24
|
+
}
|
|
25
|
+
data.update(kwargs)
|
|
26
|
+
return Notification.objects.create(**data)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.admin.sites import AdminSite
|
|
4
|
+
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.notification.admin import NotificationAdmin
|
|
7
|
+
from django_spire.notification.models import Notification
|
|
8
|
+
from django_spire.notification.tests.factories import create_test_notification
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NotificationAdminTests(BaseTestCase):
|
|
12
|
+
def setUp(self):
|
|
13
|
+
super().setUp()
|
|
14
|
+
|
|
15
|
+
self.site = AdminSite()
|
|
16
|
+
self.admin = NotificationAdmin(Notification, self.site)
|
|
17
|
+
self.notification = create_test_notification()
|
|
18
|
+
|
|
19
|
+
def test_list_display(self):
|
|
20
|
+
expected = (
|
|
21
|
+
'id', 'title', 'type', 'user', 'view_body_snippet', 'url_link', 'status',
|
|
22
|
+
'status_message', 'priority', 'sent_datetime', 'publish_datetime',
|
|
23
|
+
'content_type', 'object_id', 'is_deleted'
|
|
24
|
+
)
|
|
25
|
+
assert self.admin.list_display == expected
|
|
26
|
+
|
|
27
|
+
def test_list_filter(self):
|
|
28
|
+
expected = ('type',)
|
|
29
|
+
assert self.admin.list_filter == expected
|
|
30
|
+
|
|
31
|
+
def test_search_fields(self):
|
|
32
|
+
expected = ('id', 'title', 'type')
|
|
33
|
+
assert self.admin.search_fields == expected
|
|
34
|
+
|
|
35
|
+
def test_view_body_snippet_truncates_long_body(self):
|
|
36
|
+
self.notification.body = 'A' * 50
|
|
37
|
+
result = self.admin.view_body_snippet(self.notification)
|
|
38
|
+
assert len(result) <= 23
|
|
39
|
+
assert result.endswith('...')
|
|
40
|
+
|
|
41
|
+
def test_view_body_snippet_short_body(self):
|
|
42
|
+
self.notification.body = 'Short'
|
|
43
|
+
result = self.admin.view_body_snippet(self.notification)
|
|
44
|
+
assert result == 'Short'
|
|
45
|
+
|
|
46
|
+
def test_url_link_with_url(self):
|
|
47
|
+
self.notification.url = 'https://example.com'
|
|
48
|
+
result = self.admin.url_link(self.notification)
|
|
49
|
+
assert 'href="https://example.com"' in result
|
|
50
|
+
assert 'Link' in result
|
|
51
|
+
|
|
52
|
+
def test_url_link_without_url(self):
|
|
53
|
+
self.notification.url = ''
|
|
54
|
+
result = self.admin.url_link(self.notification)
|
|
55
|
+
assert result == 'No URL'
|
|
@@ -0,0 +1,30 @@
|
|
|
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.notification.apps import NotificationConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NotificationConfigTests(BaseTestCase):
|
|
10
|
+
def test_app_name(self):
|
|
11
|
+
assert NotificationConfig.name == 'django_spire.notification'
|
|
12
|
+
|
|
13
|
+
def test_app_label(self):
|
|
14
|
+
assert NotificationConfig.label == 'django_spire_notification'
|
|
15
|
+
|
|
16
|
+
def test_default_auto_field(self):
|
|
17
|
+
assert NotificationConfig.default_auto_field == 'django.db.models.BigAutoField'
|
|
18
|
+
|
|
19
|
+
def test_required_apps(self):
|
|
20
|
+
expected = ('django_spire_core', 'django_spire_history', 'django_spire_history_viewed')
|
|
21
|
+
assert expected == NotificationConfig.REQUIRED_APPS
|
|
22
|
+
|
|
23
|
+
def test_urlpatterns_include(self):
|
|
24
|
+
assert NotificationConfig.URLPATTERNS_INCLUDE == 'django_spire.notification.urls'
|
|
25
|
+
|
|
26
|
+
def test_urlpatterns_namespace(self):
|
|
27
|
+
assert NotificationConfig.URLPATTERNS_NAMESPACE == 'notification'
|
|
28
|
+
|
|
29
|
+
def test_app_is_installed(self):
|
|
30
|
+
assert apps.is_installed('django_spire.notification')
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.notification.automations import process_notifications
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NotificationAutomationsTests(BaseTestCase):
|
|
10
|
+
@patch('django_spire.notification.automations.NotificationManager')
|
|
11
|
+
def test_process_notifications_calls_manager(self, mock_manager_class: MagicMock):
|
|
12
|
+
mock_manager = MagicMock()
|
|
13
|
+
mock_manager_class.return_value = mock_manager
|
|
14
|
+
|
|
15
|
+
result = process_notifications()
|
|
16
|
+
|
|
17
|
+
mock_manager.process_ready_notifications.assert_called_once()
|
|
18
|
+
assert result == 'Successfully Completed'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.notification.choices import (
|
|
5
|
+
NotificationPriorityChoices,
|
|
6
|
+
NotificationStatusChoices,
|
|
7
|
+
NotificationTypeChoices,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NotificationTypeChoicesTests(BaseTestCase):
|
|
12
|
+
def test_app_value(self):
|
|
13
|
+
assert NotificationTypeChoices.APP == 'app'
|
|
14
|
+
|
|
15
|
+
def test_email_value(self):
|
|
16
|
+
assert NotificationTypeChoices.EMAIL == 'email'
|
|
17
|
+
|
|
18
|
+
def test_push_value(self):
|
|
19
|
+
assert NotificationTypeChoices.PUSH == 'push'
|
|
20
|
+
|
|
21
|
+
def test_sms_value(self):
|
|
22
|
+
assert NotificationTypeChoices.SMS == 'sms'
|
|
23
|
+
|
|
24
|
+
def test_choices_count(self):
|
|
25
|
+
assert len(NotificationTypeChoices.choices) == 4
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class NotificationStatusChoicesTests(BaseTestCase):
|
|
29
|
+
def test_pending_value(self):
|
|
30
|
+
assert NotificationStatusChoices.PENDING == 'pending'
|
|
31
|
+
|
|
32
|
+
def test_processing_value(self):
|
|
33
|
+
assert NotificationStatusChoices.PROCESSING == 'processing'
|
|
34
|
+
|
|
35
|
+
def test_sent_value(self):
|
|
36
|
+
assert NotificationStatusChoices.SENT == 'sent'
|
|
37
|
+
|
|
38
|
+
def test_errored_value(self):
|
|
39
|
+
assert NotificationStatusChoices.ERRORED == 'errored'
|
|
40
|
+
|
|
41
|
+
def test_failed_value(self):
|
|
42
|
+
assert NotificationStatusChoices.FAILED == 'failed'
|
|
43
|
+
|
|
44
|
+
def test_choices_count(self):
|
|
45
|
+
assert len(NotificationStatusChoices.choices) == 5
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class NotificationPriorityChoicesTests(BaseTestCase):
|
|
49
|
+
def test_low_value(self):
|
|
50
|
+
assert NotificationPriorityChoices.LOW == 'low'
|
|
51
|
+
|
|
52
|
+
def test_medium_value(self):
|
|
53
|
+
assert NotificationPriorityChoices.MEDIUM == 'medium'
|
|
54
|
+
|
|
55
|
+
def test_high_value(self):
|
|
56
|
+
assert NotificationPriorityChoices.HIGH == 'high'
|
|
57
|
+
|
|
58
|
+
def test_choices_count(self):
|
|
59
|
+
assert len(NotificationPriorityChoices.choices) == 3
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.exceptions import DjangoSpireError
|
|
5
|
+
from django_spire.notification.app.exceptions import AppNotificationError
|
|
6
|
+
from django_spire.notification.email.exceptions import EmailNotificationError
|
|
7
|
+
from django_spire.notification.exceptions import NotificationError
|
|
8
|
+
from django_spire.notification.sms.exceptions import (
|
|
9
|
+
SmsNotificationError,
|
|
10
|
+
SmsTemporaryMediaError,
|
|
11
|
+
TwilioAPIConcurrentError,
|
|
12
|
+
TwilioError,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NotificationExceptionsTests(BaseTestCase):
|
|
17
|
+
def test_notification_error_is_django_spire_error(self):
|
|
18
|
+
assert issubclass(NotificationError, DjangoSpireError)
|
|
19
|
+
|
|
20
|
+
def test_notification_error_message(self):
|
|
21
|
+
error = NotificationError('Test error')
|
|
22
|
+
assert str(error) == 'Test error'
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AppNotificationExceptionsTests(BaseTestCase):
|
|
26
|
+
def test_app_notification_error_is_django_spire_error(self):
|
|
27
|
+
assert issubclass(AppNotificationError, DjangoSpireError)
|
|
28
|
+
|
|
29
|
+
def test_app_notification_error_message(self):
|
|
30
|
+
error = AppNotificationError('Test error')
|
|
31
|
+
assert str(error) == 'Test error'
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class EmailNotificationExceptionsTests(BaseTestCase):
|
|
35
|
+
def test_email_notification_error_is_django_spire_error(self):
|
|
36
|
+
assert issubclass(EmailNotificationError, DjangoSpireError)
|
|
37
|
+
|
|
38
|
+
def test_email_notification_error_message(self):
|
|
39
|
+
error = EmailNotificationError('Test error')
|
|
40
|
+
assert str(error) == 'Test error'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class SmsNotificationExceptionsTests(BaseTestCase):
|
|
44
|
+
def test_sms_notification_error_is_django_spire_error(self):
|
|
45
|
+
assert issubclass(SmsNotificationError, DjangoSpireError)
|
|
46
|
+
|
|
47
|
+
def test_sms_notification_error_message(self):
|
|
48
|
+
error = SmsNotificationError('Test error')
|
|
49
|
+
assert str(error) == 'Test error'
|
|
50
|
+
|
|
51
|
+
def test_sms_temporary_media_error_is_sms_notification_error(self):
|
|
52
|
+
assert issubclass(SmsTemporaryMediaError, SmsNotificationError)
|
|
53
|
+
|
|
54
|
+
def test_twilio_error_is_exception(self):
|
|
55
|
+
assert issubclass(TwilioError, Exception)
|
|
56
|
+
|
|
57
|
+
def test_twilio_api_concurrent_error_is_twilio_error(self):
|
|
58
|
+
assert issubclass(TwilioAPIConcurrentError, TwilioError)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.notification.managers import NotificationManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NotificationManagerTests(BaseTestCase):
|
|
10
|
+
@patch('django_spire.notification.managers.NotificationProcessor')
|
|
11
|
+
def test_process_ready_notifications(self, mock_processor_class: MagicMock):
|
|
12
|
+
mock_processor = MagicMock()
|
|
13
|
+
mock_processor_class.return_value = mock_processor
|
|
14
|
+
|
|
15
|
+
NotificationManager.process_ready_notifications()
|
|
16
|
+
|
|
17
|
+
mock_processor.process_ready.assert_called_once()
|
|
18
|
+
|
|
19
|
+
@patch('django_spire.notification.managers.NotificationProcessor')
|
|
20
|
+
def test_process_errored_notifications(self, mock_processor_class: MagicMock):
|
|
21
|
+
mock_processor = MagicMock()
|
|
22
|
+
mock_processor_class.return_value = mock_processor
|
|
23
|
+
|
|
24
|
+
NotificationManager.process_errored_notifications()
|
|
25
|
+
|
|
26
|
+
mock_processor.process_errored.assert_called_once()
|
|
27
|
+
|
|
28
|
+
@patch('django_spire.notification.managers.AppNotificationProcessor')
|
|
29
|
+
def test_process_ready_app_notifications(self, mock_processor_class: MagicMock):
|
|
30
|
+
mock_processor = MagicMock()
|
|
31
|
+
mock_processor_class.return_value = mock_processor
|
|
32
|
+
|
|
33
|
+
NotificationManager.process_ready_app_notifications()
|
|
34
|
+
|
|
35
|
+
mock_processor.process_ready.assert_called_once()
|
|
36
|
+
|
|
37
|
+
@patch('django_spire.notification.managers.AppNotificationProcessor')
|
|
38
|
+
def test_process_errored_app_notifications(self, mock_processor_class: MagicMock):
|
|
39
|
+
mock_processor = MagicMock()
|
|
40
|
+
mock_processor_class.return_value = mock_processor
|
|
41
|
+
|
|
42
|
+
NotificationManager.process_errored_app_notifications()
|
|
43
|
+
|
|
44
|
+
mock_processor.process_errored.assert_called_once()
|
|
45
|
+
|
|
46
|
+
@patch('django_spire.notification.managers.EmailNotificationProcessor')
|
|
47
|
+
def test_process_ready_email_notifications(self, mock_processor_class: MagicMock):
|
|
48
|
+
mock_processor = MagicMock()
|
|
49
|
+
mock_processor_class.return_value = mock_processor
|
|
50
|
+
|
|
51
|
+
NotificationManager.process_ready_email_notifications()
|
|
52
|
+
|
|
53
|
+
mock_processor.process_ready.assert_called_once()
|
|
54
|
+
|
|
55
|
+
@patch('django_spire.notification.managers.EmailNotificationProcessor')
|
|
56
|
+
def test_process_errored_email_notifications(self, mock_processor_class: MagicMock):
|
|
57
|
+
mock_processor = MagicMock()
|
|
58
|
+
mock_processor_class.return_value = mock_processor
|
|
59
|
+
|
|
60
|
+
NotificationManager.process_errored_email_notifications()
|
|
61
|
+
|
|
62
|
+
mock_processor.process_errored.assert_called_once()
|
|
63
|
+
|
|
64
|
+
@patch('django_spire.notification.managers.SMSNotificationProcessor')
|
|
65
|
+
def test_process_ready_sms_notifications(self, mock_processor_class: MagicMock):
|
|
66
|
+
mock_processor = MagicMock()
|
|
67
|
+
mock_processor_class.return_value = mock_processor
|
|
68
|
+
|
|
69
|
+
NotificationManager.process_ready_sms_notifications()
|
|
70
|
+
|
|
71
|
+
mock_processor.process_ready.assert_called_once()
|
|
72
|
+
|
|
73
|
+
@patch('django_spire.notification.managers.SMSNotificationProcessor')
|
|
74
|
+
def test_process_errored_sms_notifications(self, mock_processor_class: MagicMock):
|
|
75
|
+
mock_processor = MagicMock()
|
|
76
|
+
mock_processor_class.return_value = mock_processor
|
|
77
|
+
|
|
78
|
+
NotificationManager.process_errored_sms_notifications()
|
|
79
|
+
|
|
80
|
+
mock_processor.process_errored.assert_called_once()
|
|
81
|
+
|
|
82
|
+
@patch('django_spire.notification.managers.NotificationProcessor')
|
|
83
|
+
def test_process_notification(self, mock_processor_class: MagicMock):
|
|
84
|
+
mock_processor = MagicMock()
|
|
85
|
+
mock_processor_class.return_value = mock_processor
|
|
86
|
+
mock_notification = MagicMock()
|
|
87
|
+
|
|
88
|
+
NotificationManager.process_notification(mock_notification)
|
|
89
|
+
|
|
90
|
+
mock_processor.process.assert_called_once_with(mock_notification)
|
|
91
|
+
|
|
92
|
+
@patch('django_spire.notification.managers.NotificationProcessor')
|
|
93
|
+
def test_process_notifications(self, mock_processor_class: MagicMock):
|
|
94
|
+
mock_processor = MagicMock()
|
|
95
|
+
mock_processor_class.return_value = mock_processor
|
|
96
|
+
mock_notifications = [MagicMock(), MagicMock()]
|
|
97
|
+
|
|
98
|
+
NotificationManager.process_notifications(mock_notifications)
|
|
99
|
+
|
|
100
|
+
mock_processor.process_list.assert_called_once_with(mock_notifications)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.notification.app.models import AppNotification
|
|
5
|
+
from django_spire.notification.choices import NotificationTypeChoices
|
|
6
|
+
from django_spire.notification.email.models import EmailNotification
|
|
7
|
+
from django_spire.notification.maps import NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP
|
|
8
|
+
from django_spire.notification.push.models import PushNotification
|
|
9
|
+
from django_spire.notification.sms.models import SmsNotification
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NotificationMapsTests(BaseTestCase):
|
|
13
|
+
def test_map_contains_app(self):
|
|
14
|
+
assert NotificationTypeChoices.APP in NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP
|
|
15
|
+
assert NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP[NotificationTypeChoices.APP] == AppNotification
|
|
16
|
+
|
|
17
|
+
def test_map_contains_email(self):
|
|
18
|
+
assert NotificationTypeChoices.EMAIL in NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP
|
|
19
|
+
assert NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP[NotificationTypeChoices.EMAIL] == EmailNotification
|
|
20
|
+
|
|
21
|
+
def test_map_contains_push(self):
|
|
22
|
+
assert NotificationTypeChoices.PUSH in NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP
|
|
23
|
+
assert NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP[NotificationTypeChoices.PUSH] == PushNotification
|
|
24
|
+
|
|
25
|
+
def test_map_contains_sms(self):
|
|
26
|
+
assert NotificationTypeChoices.SMS in NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP
|
|
27
|
+
assert NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP[NotificationTypeChoices.SMS] == SmsNotification
|
|
28
|
+
|
|
29
|
+
def test_map_has_all_notification_types(self):
|
|
30
|
+
for choice in NotificationTypeChoices:
|
|
31
|
+
assert choice in NOTIFICATION_TYPE_CHOICE_TO_MODEL_MAP
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.notification.choices import (
|
|
5
|
+
NotificationPriorityChoices,
|
|
6
|
+
NotificationStatusChoices,
|
|
7
|
+
NotificationTypeChoices,
|
|
8
|
+
)
|
|
9
|
+
from django_spire.notification.tests.factories import create_test_notification
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NotificationModelTests(BaseTestCase):
|
|
13
|
+
def setUp(self):
|
|
14
|
+
super().setUp()
|
|
15
|
+
|
|
16
|
+
self.notification = create_test_notification()
|
|
17
|
+
|
|
18
|
+
def test_default_status_is_pending(self):
|
|
19
|
+
assert self.notification.status == NotificationStatusChoices.PENDING
|
|
20
|
+
|
|
21
|
+
def test_default_priority_is_low(self):
|
|
22
|
+
assert self.notification.priority == NotificationPriorityChoices.LOW
|
|
23
|
+
|
|
24
|
+
def test_get_type_display(self):
|
|
25
|
+
self.notification.type = NotificationTypeChoices.APP
|
|
26
|
+
assert self.notification.get_type_display() == 'App'
|
|
27
|
+
|
|
28
|
+
self.notification.type = NotificationTypeChoices.EMAIL
|
|
29
|
+
assert self.notification.get_type_display() == 'Email'
|
|
30
|
+
|
|
31
|
+
self.notification.type = NotificationTypeChoices.SMS
|
|
32
|
+
assert self.notification.get_type_display() == 'Sms'
|
|
33
|
+
|
|
34
|
+
self.notification.type = NotificationTypeChoices.PUSH
|
|
35
|
+
assert self.notification.get_type_display() == 'Push'
|
|
36
|
+
|
|
37
|
+
def test_get_status_display(self):
|
|
38
|
+
self.notification.status = NotificationStatusChoices.PENDING
|
|
39
|
+
assert self.notification.get_status_display() == 'Pending'
|
|
40
|
+
|
|
41
|
+
self.notification.status = NotificationStatusChoices.PROCESSING
|
|
42
|
+
assert self.notification.get_status_display() == 'Processing'
|
|
43
|
+
|
|
44
|
+
self.notification.status = NotificationStatusChoices.SENT
|
|
45
|
+
assert self.notification.get_status_display() == 'Sent'
|
|
46
|
+
|
|
47
|
+
self.notification.status = NotificationStatusChoices.ERRORED
|
|
48
|
+
assert self.notification.get_status_display() == 'Errored'
|
|
49
|
+
|
|
50
|
+
self.notification.status = NotificationStatusChoices.FAILED
|
|
51
|
+
assert self.notification.get_status_display() == 'Failed'
|
|
52
|
+
|
|
53
|
+
def test_get_priority_display(self):
|
|
54
|
+
self.notification.priority = NotificationPriorityChoices.LOW
|
|
55
|
+
assert self.notification.get_priority_display() == 'Low'
|
|
56
|
+
|
|
57
|
+
self.notification.priority = NotificationPriorityChoices.MEDIUM
|
|
58
|
+
assert self.notification.get_priority_display() == 'Medium'
|
|
59
|
+
|
|
60
|
+
self.notification.priority = NotificationPriorityChoices.HIGH
|
|
61
|
+
assert self.notification.get_priority_display() == 'High'
|
|
62
|
+
|
|
63
|
+
def test_user_relationship(self):
|
|
64
|
+
assert self.notification.user is not None
|
|
65
|
+
assert self.notification.user.pk is not None
|
|
66
|
+
|
|
67
|
+
def test_created_datetime_auto_set(self):
|
|
68
|
+
assert self.notification.created_datetime is not None
|
|
69
|
+
|
|
70
|
+
def test_sent_datetime_null_by_default(self):
|
|
71
|
+
assert self.notification.sent_datetime is None
|
|
72
|
+
|
|
73
|
+
def test_content_object_null_by_default(self):
|
|
74
|
+
assert self.notification.content_object is None
|
|
75
|
+
assert self.notification.content_type is None
|
|
76
|
+
assert self.notification.object_id is None
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.utils.timezone import now, timedelta
|
|
4
|
+
|
|
5
|
+
from django_spire.auth.user.tests.factories import create_user
|
|
6
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
7
|
+
from django_spire.notification.choices import (
|
|
8
|
+
NotificationPriorityChoices,
|
|
9
|
+
NotificationStatusChoices,
|
|
10
|
+
NotificationTypeChoices,
|
|
11
|
+
)
|
|
12
|
+
from django_spire.notification.models import Notification
|
|
13
|
+
from django_spire.notification.tests.factories import create_test_notification
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NotificationQuerySetTests(BaseTestCase):
|
|
17
|
+
def setUp(self):
|
|
18
|
+
super().setUp()
|
|
19
|
+
|
|
20
|
+
self.user = create_user(username='test_notification_user')
|
|
21
|
+
self.notification = create_test_notification(user=self.user)
|
|
22
|
+
|
|
23
|
+
def test_active_returns_non_deleted(self):
|
|
24
|
+
result = Notification.objects.active()
|
|
25
|
+
assert self.notification in result
|
|
26
|
+
|
|
27
|
+
def test_active_excludes_deleted(self):
|
|
28
|
+
self.notification.is_deleted = True
|
|
29
|
+
self.notification.save()
|
|
30
|
+
|
|
31
|
+
result = Notification.objects.active()
|
|
32
|
+
assert self.notification not in result
|
|
33
|
+
|
|
34
|
+
def test_by_user(self):
|
|
35
|
+
other_user = create_user(username='other_user')
|
|
36
|
+
other_notification = create_test_notification(user=other_user)
|
|
37
|
+
|
|
38
|
+
result = Notification.objects.by_user(self.user)
|
|
39
|
+
assert self.notification in result
|
|
40
|
+
assert other_notification not in result
|
|
41
|
+
|
|
42
|
+
def test_by_users(self):
|
|
43
|
+
other_user = create_user(username='other_user')
|
|
44
|
+
other_notification = create_test_notification(user=other_user)
|
|
45
|
+
|
|
46
|
+
result = Notification.objects.by_users([self.user, other_user])
|
|
47
|
+
assert self.notification in result
|
|
48
|
+
assert other_notification in result
|
|
49
|
+
|
|
50
|
+
def test_app_notifications(self):
|
|
51
|
+
self.notification.type = NotificationTypeChoices.APP
|
|
52
|
+
self.notification.save()
|
|
53
|
+
|
|
54
|
+
email_notification = create_test_notification(
|
|
55
|
+
user=self.user,
|
|
56
|
+
type=NotificationTypeChoices.EMAIL
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
result = Notification.objects.app_notifications()
|
|
60
|
+
assert self.notification in result
|
|
61
|
+
assert email_notification not in result
|
|
62
|
+
|
|
63
|
+
def test_email_notifications(self):
|
|
64
|
+
self.notification.type = NotificationTypeChoices.EMAIL
|
|
65
|
+
self.notification.save()
|
|
66
|
+
|
|
67
|
+
app_notification = create_test_notification(
|
|
68
|
+
user=self.user,
|
|
69
|
+
type=NotificationTypeChoices.APP
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
result = Notification.objects.email_notifications()
|
|
73
|
+
assert self.notification in result
|
|
74
|
+
assert app_notification not in result
|
|
75
|
+
|
|
76
|
+
def test_sms_notifications(self):
|
|
77
|
+
self.notification.type = NotificationTypeChoices.SMS
|
|
78
|
+
self.notification.save()
|
|
79
|
+
|
|
80
|
+
result = Notification.objects.sms_notifications()
|
|
81
|
+
assert self.notification in result
|
|
82
|
+
|
|
83
|
+
def test_pending(self):
|
|
84
|
+
self.notification.status = NotificationStatusChoices.PENDING
|
|
85
|
+
self.notification.save()
|
|
86
|
+
|
|
87
|
+
sent_notification = create_test_notification(
|
|
88
|
+
user=self.user,
|
|
89
|
+
status=NotificationStatusChoices.SENT
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
result = Notification.objects.pending()
|
|
93
|
+
assert self.notification in result
|
|
94
|
+
assert sent_notification not in result
|
|
95
|
+
|
|
96
|
+
def test_processing(self):
|
|
97
|
+
self.notification.status = NotificationStatusChoices.PROCESSING
|
|
98
|
+
self.notification.save()
|
|
99
|
+
|
|
100
|
+
result = Notification.objects.processing()
|
|
101
|
+
assert self.notification in result
|
|
102
|
+
|
|
103
|
+
def test_sent(self):
|
|
104
|
+
self.notification.status = NotificationStatusChoices.SENT
|
|
105
|
+
self.notification.save()
|
|
106
|
+
|
|
107
|
+
result = Notification.objects.sent()
|
|
108
|
+
assert self.notification in result
|
|
109
|
+
|
|
110
|
+
def test_errored(self):
|
|
111
|
+
self.notification.status = NotificationStatusChoices.ERRORED
|
|
112
|
+
self.notification.save()
|
|
113
|
+
|
|
114
|
+
result = Notification.objects.errored()
|
|
115
|
+
assert self.notification in result
|
|
116
|
+
|
|
117
|
+
def test_failed(self):
|
|
118
|
+
self.notification.status = NotificationStatusChoices.FAILED
|
|
119
|
+
self.notification.save()
|
|
120
|
+
|
|
121
|
+
result = Notification.objects.failed()
|
|
122
|
+
assert self.notification in result
|
|
123
|
+
|
|
124
|
+
def test_unsent(self):
|
|
125
|
+
pending = create_test_notification(
|
|
126
|
+
user=self.user,
|
|
127
|
+
status=NotificationStatusChoices.PENDING
|
|
128
|
+
)
|
|
129
|
+
processing = create_test_notification(
|
|
130
|
+
user=self.user,
|
|
131
|
+
status=NotificationStatusChoices.PROCESSING
|
|
132
|
+
)
|
|
133
|
+
sent = create_test_notification(
|
|
134
|
+
user=self.user,
|
|
135
|
+
status=NotificationStatusChoices.SENT
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
result = Notification.objects.unsent()
|
|
139
|
+
assert pending in result
|
|
140
|
+
assert processing in result
|
|
141
|
+
assert sent not in result
|
|
142
|
+
|
|
143
|
+
def test_ready_to_send(self):
|
|
144
|
+
past_notification = create_test_notification(
|
|
145
|
+
user=self.user,
|
|
146
|
+
status=NotificationStatusChoices.PENDING,
|
|
147
|
+
publish_datetime=now() - timedelta(hours=1)
|
|
148
|
+
)
|
|
149
|
+
future_notification = create_test_notification(
|
|
150
|
+
user=self.user,
|
|
151
|
+
status=NotificationStatusChoices.PENDING,
|
|
152
|
+
publish_datetime=now() + timedelta(hours=1)
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
result = Notification.objects.ready_to_send()
|
|
156
|
+
assert past_notification in result
|
|
157
|
+
assert future_notification not in result
|
|
158
|
+
|
|
159
|
+
def test_high_priority(self):
|
|
160
|
+
self.notification.priority = NotificationPriorityChoices.HIGH
|
|
161
|
+
self.notification.save()
|
|
162
|
+
|
|
163
|
+
low_priority = create_test_notification(
|
|
164
|
+
user=self.user,
|
|
165
|
+
priority=NotificationPriorityChoices.LOW
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
result = Notification.objects.high_priority()
|
|
169
|
+
assert self.notification in result
|
|
170
|
+
assert low_priority not in result
|
|
171
|
+
|
|
172
|
+
def test_medium_priority(self):
|
|
173
|
+
self.notification.priority = NotificationPriorityChoices.MEDIUM
|
|
174
|
+
self.notification.save()
|
|
175
|
+
|
|
176
|
+
result = Notification.objects.medium_priority()
|
|
177
|
+
assert self.notification in result
|
|
178
|
+
|
|
179
|
+
def test_low_priority(self):
|
|
180
|
+
self.notification.priority = NotificationPriorityChoices.LOW
|
|
181
|
+
self.notification.save()
|
|
182
|
+
|
|
183
|
+
result = Notification.objects.low_priority()
|
|
184
|
+
assert self.notification in result
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.test import override_settings
|
|
4
|
+
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.notification.utils import get_throttle_sleep_time
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NotificationUtilsTests(BaseTestCase):
|
|
10
|
+
@override_settings(NOTIFICATION_THROTTLE_RATE_PER_MINUTE=60)
|
|
11
|
+
def test_get_throttle_sleep_time_60_per_minute(self):
|
|
12
|
+
result = get_throttle_sleep_time()
|
|
13
|
+
assert result == 1.0
|
|
14
|
+
|
|
15
|
+
@override_settings(NOTIFICATION_THROTTLE_RATE_PER_MINUTE=120)
|
|
16
|
+
def test_get_throttle_sleep_time_120_per_minute(self):
|
|
17
|
+
result = get_throttle_sleep_time()
|
|
18
|
+
assert result == 2.0
|
|
19
|
+
|
|
20
|
+
@override_settings(NOTIFICATION_THROTTLE_RATE_PER_MINUTE=30)
|
|
21
|
+
def test_get_throttle_sleep_time_30_per_minute(self):
|
|
22
|
+
result = get_throttle_sleep_time()
|
|
23
|
+
assert result == 0.5
|