django-spire 0.23.7__py3-none-any.whl → 0.23.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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/management/commands/spire_startapp_pkg/template/app/apps.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/__init__.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/form_urls.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/page_urls.py.template +2 -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 +12 -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 +5 -3
- 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.7.dist-info → django_spire-0.23.9.dist-info}/METADATA +2 -2
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/RECORD +534 -362
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/licenses/LICENSE.md +1 -1
- django_spire/contrib/options/tests/test_unit.py +0 -148
- 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.7.dist-info → django_spire-0.23.9.dist-info}/WHEEL +0 -0
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.auth.user.factories import register_new_user
|
|
4
|
+
from django_spire.auth.user.models import AuthUser
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RegisterNewUserTestCase(BaseTestCase):
|
|
9
|
+
def test_creates_user(self) -> None:
|
|
10
|
+
user = register_new_user(
|
|
11
|
+
first_name='Test',
|
|
12
|
+
last_name='User',
|
|
13
|
+
email='test@example.com',
|
|
14
|
+
password='securepassword123' # noqa: S106
|
|
15
|
+
)
|
|
16
|
+
assert isinstance(user, AuthUser) is True
|
|
17
|
+
assert AuthUser.objects.filter(pk=user.pk).exists() is True
|
|
18
|
+
|
|
19
|
+
def test_sets_correct_attributes(self) -> None:
|
|
20
|
+
user = register_new_user(
|
|
21
|
+
first_name='John',
|
|
22
|
+
last_name='Doe',
|
|
23
|
+
email='john.doe@example.com',
|
|
24
|
+
password='securepassword123' # noqa: S106
|
|
25
|
+
)
|
|
26
|
+
assert user.first_name == 'John'
|
|
27
|
+
assert user.last_name == 'Doe'
|
|
28
|
+
assert user.email == 'john.doe@example.com'
|
|
29
|
+
|
|
30
|
+
def test_username_is_lowercase_email(self) -> None:
|
|
31
|
+
user = register_new_user(
|
|
32
|
+
first_name='Test',
|
|
33
|
+
last_name='User',
|
|
34
|
+
email='Test.User@Example.COM',
|
|
35
|
+
password='securepassword123' # noqa: S106
|
|
36
|
+
)
|
|
37
|
+
assert user.username == 'test.user@example.com'
|
|
38
|
+
assert user.email == 'test.user@example.com'
|
|
39
|
+
|
|
40
|
+
def test_password_is_set(self) -> None:
|
|
41
|
+
user = register_new_user(
|
|
42
|
+
first_name='Test',
|
|
43
|
+
last_name='User',
|
|
44
|
+
email='test@example.com',
|
|
45
|
+
password='securepassword123' # noqa: S106
|
|
46
|
+
)
|
|
47
|
+
assert user.check_password('securepassword123') is True
|
|
48
|
+
|
|
49
|
+
def test_password_is_hashed(self) -> None:
|
|
50
|
+
user = register_new_user(
|
|
51
|
+
first_name='Test',
|
|
52
|
+
last_name='User',
|
|
53
|
+
email='test@example.com',
|
|
54
|
+
password='securepassword123' # noqa: S106
|
|
55
|
+
)
|
|
56
|
+
assert user.password != 'securepassword123' # noqa: S106
|
|
57
|
+
|
|
58
|
+
def test_user_is_active_by_default(self) -> None:
|
|
59
|
+
user = register_new_user(
|
|
60
|
+
first_name='Test',
|
|
61
|
+
last_name='User',
|
|
62
|
+
email='test@example.com',
|
|
63
|
+
password='securepassword123' # noqa: S106
|
|
64
|
+
)
|
|
65
|
+
assert user.is_active is True
|
|
66
|
+
|
|
67
|
+
def test_user_is_not_staff_by_default(self) -> None:
|
|
68
|
+
user = register_new_user(
|
|
69
|
+
first_name='Test',
|
|
70
|
+
last_name='User',
|
|
71
|
+
email='test@example.com',
|
|
72
|
+
password='securepassword123' # noqa: S106
|
|
73
|
+
)
|
|
74
|
+
assert user.is_staff is False
|
|
75
|
+
|
|
76
|
+
def test_user_is_not_superuser_by_default(self) -> None:
|
|
77
|
+
user = register_new_user(
|
|
78
|
+
first_name='Test',
|
|
79
|
+
last_name='User',
|
|
80
|
+
email='test@example.com',
|
|
81
|
+
password='securepassword123' # noqa: S106
|
|
82
|
+
)
|
|
83
|
+
assert user.is_superuser is False
|
|
84
|
+
|
|
85
|
+
def test_empty_first_name(self) -> None:
|
|
86
|
+
user = register_new_user(
|
|
87
|
+
first_name='',
|
|
88
|
+
last_name='User',
|
|
89
|
+
email='test@example.com',
|
|
90
|
+
password='securepassword123' # noqa: S106
|
|
91
|
+
)
|
|
92
|
+
assert user.first_name == ''
|
|
93
|
+
|
|
94
|
+
def test_empty_last_name(self) -> None:
|
|
95
|
+
user = register_new_user(
|
|
96
|
+
first_name='Test',
|
|
97
|
+
last_name='',
|
|
98
|
+
email='test@example.com',
|
|
99
|
+
password='securepassword123' # noqa: S106
|
|
100
|
+
)
|
|
101
|
+
assert user.last_name == ''
|
|
102
|
+
|
|
103
|
+
def test_unicode_names(self) -> None:
|
|
104
|
+
user = register_new_user(
|
|
105
|
+
first_name='Tëst',
|
|
106
|
+
last_name='Üser',
|
|
107
|
+
email='test@example.com',
|
|
108
|
+
password='securepassword123' # noqa: S106
|
|
109
|
+
)
|
|
110
|
+
assert user.first_name == 'Tëst'
|
|
111
|
+
assert user.last_name == 'Üser'
|
|
112
|
+
|
|
113
|
+
def test_long_password(self) -> None:
|
|
114
|
+
long_password = 'a' * 100
|
|
115
|
+
user = register_new_user(
|
|
116
|
+
first_name='Test',
|
|
117
|
+
last_name='User',
|
|
118
|
+
email='test@example.com',
|
|
119
|
+
password=long_password
|
|
120
|
+
)
|
|
121
|
+
assert user.check_password(long_password) is True
|
|
122
|
+
|
|
123
|
+
def test_special_characters_in_email(self) -> None:
|
|
124
|
+
user = register_new_user(
|
|
125
|
+
first_name='Test',
|
|
126
|
+
last_name='User',
|
|
127
|
+
email='test+special@example.com',
|
|
128
|
+
password='securepassword123' # noqa: S106
|
|
129
|
+
)
|
|
130
|
+
assert user.email == 'test+special@example.com'
|
|
131
|
+
|
|
132
|
+
def test_wrong_password_fails(self) -> None:
|
|
133
|
+
user = register_new_user(
|
|
134
|
+
first_name='Test',
|
|
135
|
+
last_name='User',
|
|
136
|
+
email='test@example.com',
|
|
137
|
+
password='securepassword123' # noqa: S106
|
|
138
|
+
)
|
|
139
|
+
assert user.check_password('wrongpassword') is False
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.urls import reverse
|
|
4
|
+
|
|
5
|
+
from django_spire.auth.group.models import AuthGroup
|
|
6
|
+
from django_spire.auth.user.models import AuthUser
|
|
7
|
+
from django_spire.auth.user.tests.factories import create_user
|
|
8
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class UserPageViewsTestCase(BaseTestCase):
|
|
12
|
+
def setUp(self) -> None:
|
|
13
|
+
super().setUp()
|
|
14
|
+
|
|
15
|
+
self.user = create_user(
|
|
16
|
+
username='testuser',
|
|
17
|
+
first_name='Test',
|
|
18
|
+
last_name='User',
|
|
19
|
+
email='test@example.com'
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def test_list_view_requires_permission(self) -> None:
|
|
23
|
+
normal_user = create_user(username='normaluser')
|
|
24
|
+
self.client.force_login(normal_user)
|
|
25
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
26
|
+
assert response.status_code == 403
|
|
27
|
+
|
|
28
|
+
def test_list_view_with_permission(self) -> None:
|
|
29
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
30
|
+
assert response.status_code == 200
|
|
31
|
+
|
|
32
|
+
def test_list_view_context_contains_users(self) -> None:
|
|
33
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
34
|
+
assert 'active_user_list' in response.context
|
|
35
|
+
assert 'inactive_user_list' in response.context
|
|
36
|
+
|
|
37
|
+
def test_list_view_separates_active_inactive(self) -> None:
|
|
38
|
+
inactive_user = create_user(username='inactiveuser', is_active=False)
|
|
39
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
40
|
+
active_ids = [u.pk for u in response.context['active_user_list']]
|
|
41
|
+
inactive_ids = [u.pk for u in response.context['inactive_user_list']]
|
|
42
|
+
assert inactive_user.pk in inactive_ids
|
|
43
|
+
assert inactive_user.pk not in active_ids
|
|
44
|
+
|
|
45
|
+
def test_detail_view_requires_permission(self) -> None:
|
|
46
|
+
normal_user = create_user(username='normaluser')
|
|
47
|
+
self.client.force_login(normal_user)
|
|
48
|
+
response = self.client.get(
|
|
49
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
50
|
+
)
|
|
51
|
+
assert response.status_code == 403
|
|
52
|
+
|
|
53
|
+
def test_detail_view_with_permission(self) -> None:
|
|
54
|
+
response = self.client.get(
|
|
55
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
56
|
+
)
|
|
57
|
+
assert response.status_code == 200
|
|
58
|
+
|
|
59
|
+
def test_detail_view_context_contains_user(self) -> None:
|
|
60
|
+
response = self.client.get(
|
|
61
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
62
|
+
)
|
|
63
|
+
assert 'user' in response.context
|
|
64
|
+
|
|
65
|
+
def test_detail_view_context_contains_groups(self) -> None:
|
|
66
|
+
response = self.client.get(
|
|
67
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
68
|
+
)
|
|
69
|
+
assert 'group_list' in response.context
|
|
70
|
+
|
|
71
|
+
def test_detail_view_context_contains_permissions(self) -> None:
|
|
72
|
+
response = self.client.get(
|
|
73
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
74
|
+
)
|
|
75
|
+
assert 'user_perm_data' in response.context
|
|
76
|
+
|
|
77
|
+
def test_detail_view_404_for_nonexistent_user(self) -> None:
|
|
78
|
+
response = self.client.get(
|
|
79
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': 99999})
|
|
80
|
+
)
|
|
81
|
+
assert response.status_code == 404
|
|
82
|
+
|
|
83
|
+
def test_list_view_contains_active_user(self) -> None:
|
|
84
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
85
|
+
active_ids = [u.pk for u in response.context['active_user_list']]
|
|
86
|
+
assert self.user.pk in active_ids
|
|
87
|
+
|
|
88
|
+
def test_list_view_pagination(self) -> None:
|
|
89
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
90
|
+
assert 'active_user_list' in response.context
|
|
91
|
+
assert hasattr(response.context['active_user_list'], 'paginator')
|
|
92
|
+
|
|
93
|
+
def test_detail_view_with_groups(self) -> None:
|
|
94
|
+
group = AuthGroup.objects.create(name='Test Group')
|
|
95
|
+
self.user.groups.add(group)
|
|
96
|
+
response = self.client.get(
|
|
97
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
98
|
+
)
|
|
99
|
+
assert response.status_code == 200
|
|
100
|
+
assert group in response.context['group_list']
|
|
101
|
+
|
|
102
|
+
def test_detail_view_context_contains_group_permission_data(self) -> None:
|
|
103
|
+
group = AuthGroup.objects.create(name='Test Group')
|
|
104
|
+
self.user.groups.add(group)
|
|
105
|
+
response = self.client.get(
|
|
106
|
+
reverse('django_spire:auth:user:page:detail', kwargs={'pk': self.user.pk})
|
|
107
|
+
)
|
|
108
|
+
assert 'group_list_permission_data' in response.context
|
|
109
|
+
|
|
110
|
+
def test_list_view_multiple_users(self) -> None:
|
|
111
|
+
create_user(username='user1', first_name='User', last_name='One')
|
|
112
|
+
create_user(username='user2', first_name='User', last_name='Two')
|
|
113
|
+
response = self.client.get(reverse('django_spire:auth:user:page:list'))
|
|
114
|
+
assert len(response.context['active_user_list']) >= 2
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class UserFormViewsTestCase(BaseTestCase):
|
|
118
|
+
def setUp(self) -> None:
|
|
119
|
+
super().setUp()
|
|
120
|
+
|
|
121
|
+
self.user = create_user(
|
|
122
|
+
username='testuser',
|
|
123
|
+
first_name='Test',
|
|
124
|
+
last_name='User',
|
|
125
|
+
email='test@example.com'
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def test_register_form_view_get(self) -> None:
|
|
129
|
+
response = self.client.get(reverse('django_spire:auth:user:form:register'))
|
|
130
|
+
assert response.status_code == 200
|
|
131
|
+
|
|
132
|
+
def test_register_form_view_post_creates_user(self) -> None:
|
|
133
|
+
response = self.client.post(
|
|
134
|
+
reverse('django_spire:auth:user:form:register'),
|
|
135
|
+
data={
|
|
136
|
+
'first_name': 'New',
|
|
137
|
+
'last_name': 'User',
|
|
138
|
+
'email': 'newuser@example.com',
|
|
139
|
+
'password': 'securepassword123'
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
assert response.status_code == 302
|
|
143
|
+
assert AuthUser.objects.filter(email='newuser@example.com').exists()
|
|
144
|
+
|
|
145
|
+
def test_register_form_view_post_invalid_password(self) -> None:
|
|
146
|
+
response = self.client.post(
|
|
147
|
+
reverse('django_spire:auth:user:form:register'),
|
|
148
|
+
data={
|
|
149
|
+
'first_name': 'New',
|
|
150
|
+
'last_name': 'User',
|
|
151
|
+
'email': 'newuser@example.com',
|
|
152
|
+
'password': 'short'
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
assert response.status_code == 200
|
|
156
|
+
assert not AuthUser.objects.filter(email='newuser@example.com').exists()
|
|
157
|
+
|
|
158
|
+
def test_register_form_view_post_invalid_email(self) -> None:
|
|
159
|
+
response = self.client.post(
|
|
160
|
+
reverse('django_spire:auth:user:form:register'),
|
|
161
|
+
data={
|
|
162
|
+
'first_name': 'New',
|
|
163
|
+
'last_name': 'User',
|
|
164
|
+
'email': 'invalid-email',
|
|
165
|
+
'password': 'securepassword123'
|
|
166
|
+
}
|
|
167
|
+
)
|
|
168
|
+
assert response.status_code == 200
|
|
169
|
+
|
|
170
|
+
def test_register_form_requires_permission(self) -> None:
|
|
171
|
+
normal_user = create_user(username='normaluser')
|
|
172
|
+
self.client.force_login(normal_user)
|
|
173
|
+
response = self.client.get(reverse('django_spire:auth:user:form:register'))
|
|
174
|
+
assert response.status_code == 403
|
|
175
|
+
|
|
176
|
+
def test_update_form_view_get(self) -> None:
|
|
177
|
+
response = self.client.get(
|
|
178
|
+
reverse('django_spire:auth:user:form:update', kwargs={'pk': self.user.pk})
|
|
179
|
+
)
|
|
180
|
+
assert response.status_code == 200
|
|
181
|
+
|
|
182
|
+
def test_update_form_view_404(self) -> None:
|
|
183
|
+
response = self.client.get(
|
|
184
|
+
reverse('django_spire:auth:user:form:update', kwargs={'pk': 99999})
|
|
185
|
+
)
|
|
186
|
+
assert response.status_code == 404
|
|
187
|
+
|
|
188
|
+
def test_update_form_requires_permission(self) -> None:
|
|
189
|
+
normal_user = create_user(username='normaluser')
|
|
190
|
+
self.client.force_login(normal_user)
|
|
191
|
+
response = self.client.get(
|
|
192
|
+
reverse('django_spire:auth:user:form:update', kwargs={'pk': self.user.pk})
|
|
193
|
+
)
|
|
194
|
+
assert response.status_code == 403
|
|
195
|
+
|
|
196
|
+
def test_group_form_view_get(self) -> None:
|
|
197
|
+
response = self.client.get(
|
|
198
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk})
|
|
199
|
+
)
|
|
200
|
+
assert response.status_code == 200
|
|
201
|
+
|
|
202
|
+
def test_group_form_view_post_updates_groups(self) -> None:
|
|
203
|
+
group = AuthGroup.objects.create(name='Test Group')
|
|
204
|
+
response = self.client.post(
|
|
205
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk}),
|
|
206
|
+
data={'group_list': [group.pk]}
|
|
207
|
+
)
|
|
208
|
+
assert response.status_code == 302
|
|
209
|
+
self.user.refresh_from_db()
|
|
210
|
+
assert group in self.user.groups.all()
|
|
211
|
+
|
|
212
|
+
def test_group_form_view_post_multiple_groups(self) -> None:
|
|
213
|
+
group1 = AuthGroup.objects.create(name='Group 1')
|
|
214
|
+
group2 = AuthGroup.objects.create(name='Group 2')
|
|
215
|
+
response = self.client.post(
|
|
216
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk}),
|
|
217
|
+
data={'group_list': [group1.pk, group2.pk]}
|
|
218
|
+
)
|
|
219
|
+
assert response.status_code == 302
|
|
220
|
+
self.user.refresh_from_db()
|
|
221
|
+
assert group1 in self.user.groups.all()
|
|
222
|
+
assert group2 in self.user.groups.all()
|
|
223
|
+
|
|
224
|
+
def test_group_form_view_404(self) -> None:
|
|
225
|
+
response = self.client.get(
|
|
226
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': 99999})
|
|
227
|
+
)
|
|
228
|
+
assert response.status_code == 404
|
|
229
|
+
|
|
230
|
+
def test_group_form_requires_permission(self) -> None:
|
|
231
|
+
normal_user = create_user(username='normaluser')
|
|
232
|
+
self.client.force_login(normal_user)
|
|
233
|
+
response = self.client.get(
|
|
234
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk})
|
|
235
|
+
)
|
|
236
|
+
assert response.status_code == 403
|
|
237
|
+
|
|
238
|
+
def test_register_form_view_post_empty_password(self) -> None:
|
|
239
|
+
response = self.client.post(
|
|
240
|
+
reverse('django_spire:auth:user:form:register'),
|
|
241
|
+
data={
|
|
242
|
+
'first_name': 'New',
|
|
243
|
+
'last_name': 'User',
|
|
244
|
+
'email': 'newuser@example.com',
|
|
245
|
+
'password': ''
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
assert response.status_code == 200
|
|
249
|
+
|
|
250
|
+
def test_register_form_view_post_empty_email(self) -> None:
|
|
251
|
+
response = self.client.post(
|
|
252
|
+
reverse('django_spire:auth:user:form:register'),
|
|
253
|
+
data={
|
|
254
|
+
'first_name': 'New',
|
|
255
|
+
'last_name': 'User',
|
|
256
|
+
'email': '',
|
|
257
|
+
'password': 'securepassword123'
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
assert response.status_code == 200
|
|
261
|
+
|
|
262
|
+
def test_register_form_view_post_unicode_names(self) -> None:
|
|
263
|
+
response = self.client.post(
|
|
264
|
+
reverse('django_spire:auth:user:form:register'),
|
|
265
|
+
data={
|
|
266
|
+
'first_name': 'Tëst',
|
|
267
|
+
'last_name': 'Üsér',
|
|
268
|
+
'email': 'unicode@example.com',
|
|
269
|
+
'password': 'securepassword123'
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
assert response.status_code == 302
|
|
273
|
+
user = AuthUser.objects.get(email='unicode@example.com')
|
|
274
|
+
assert user.first_name == 'Tëst'
|
|
275
|
+
assert user.last_name == 'Üsér'
|
|
276
|
+
|
|
277
|
+
def test_group_form_view_post_removes_existing_groups(self) -> None:
|
|
278
|
+
group1 = AuthGroup.objects.create(name='Group 1')
|
|
279
|
+
group2 = AuthGroup.objects.create(name='Group 2')
|
|
280
|
+
self.user.groups.add(group1)
|
|
281
|
+
|
|
282
|
+
response = self.client.post(
|
|
283
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk}),
|
|
284
|
+
data={'group_list': [group2.pk]}
|
|
285
|
+
)
|
|
286
|
+
assert response.status_code == 302
|
|
287
|
+
self.user.refresh_from_db()
|
|
288
|
+
assert group1 not in self.user.groups.all()
|
|
289
|
+
assert group2 in self.user.groups.all()
|
|
290
|
+
|
|
291
|
+
def test_group_form_view_post_invalid_group_id(self) -> None:
|
|
292
|
+
response = self.client.post(
|
|
293
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk}),
|
|
294
|
+
data={'group_list': [99999]}
|
|
295
|
+
)
|
|
296
|
+
assert response.status_code == 200
|
|
297
|
+
|
|
298
|
+
def test_register_redirects_after_success(self) -> None:
|
|
299
|
+
response = self.client.post(
|
|
300
|
+
reverse('django_spire:auth:user:form:register'),
|
|
301
|
+
data={
|
|
302
|
+
'first_name': 'New',
|
|
303
|
+
'last_name': 'User',
|
|
304
|
+
'email': 'newuser@example.com',
|
|
305
|
+
'password': 'securepassword123'
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
assert response.status_code == 302
|
|
309
|
+
assert 'list' in response.url or 'user' in response.url
|
|
310
|
+
|
|
311
|
+
def test_group_form_redirects_after_success(self) -> None:
|
|
312
|
+
group = AuthGroup.objects.create(name='Test Group')
|
|
313
|
+
response = self.client.post(
|
|
314
|
+
reverse('django_spire:auth:user:form:group_form', kwargs={'pk': self.user.pk}),
|
|
315
|
+
data={'group_list': [group.pk]}
|
|
316
|
+
)
|
|
317
|
+
assert response.status_code == 302
|
|
318
|
+
assert str(self.user.pk) in response.url
|
|
319
|
+
|
|
320
|
+
def test_update_form_view_post_updates_user(self) -> None:
|
|
321
|
+
response = self.client.post(
|
|
322
|
+
reverse('django_spire:auth:user:form:update', kwargs={'pk': self.user.pk}),
|
|
323
|
+
data={
|
|
324
|
+
'first_name': 'Updated',
|
|
325
|
+
'last_name': 'Name',
|
|
326
|
+
'email': 'updated@example.com',
|
|
327
|
+
'is_active': True
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
assert response.status_code == 302
|
|
331
|
+
self.user.refresh_from_db()
|
|
332
|
+
assert self.user.first_name == 'Updated'
|
|
333
|
+
assert self.user.last_name == 'Name'
|
|
334
|
+
assert self.user.email == 'updated@example.com'
|
|
335
|
+
|
|
336
|
+
def test_update_form_view_post_deactivates_user(self) -> None:
|
|
337
|
+
response = self.client.post(
|
|
338
|
+
reverse('django_spire:auth:user:form:update', kwargs={'pk': self.user.pk}),
|
|
339
|
+
data={
|
|
340
|
+
'first_name': 'Test',
|
|
341
|
+
'last_name': 'User',
|
|
342
|
+
'email': 'test@example.com',
|
|
343
|
+
'is_active': False
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
assert response.status_code == 302
|
|
347
|
+
self.user.refresh_from_db()
|
|
348
|
+
assert not self.user.is_active
|
|
349
|
+
|
|
350
|
+
def test_register_form_adds_to_all_users_group(self) -> None:
|
|
351
|
+
response = self.client.post(
|
|
352
|
+
reverse('django_spire:auth:user:form:register'),
|
|
353
|
+
data={
|
|
354
|
+
'first_name': 'New',
|
|
355
|
+
'last_name': 'User',
|
|
356
|
+
'email': 'newuser@example.com',
|
|
357
|
+
'password': 'securepassword123'
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
assert response.status_code == 302
|
|
361
|
+
user = AuthUser.objects.get(email='newuser@example.com')
|
|
362
|
+
group_names = [g.name for g in user.groups.all()]
|
|
363
|
+
assert 'All Users' in group_names
|
django_spire/auth/user/tools.py
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
1
5
|
from django.contrib.auth.models import Group
|
|
2
|
-
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from django_spire.auth.user.models import AuthUser
|
|
3
9
|
|
|
4
10
|
|
|
5
11
|
def add_user_to_all_user_group(user: AuthUser):
|
|
@@ -3,11 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
|
|
5
5
|
import django_glue as dg
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
8
8
|
from django.http import HttpResponseRedirect
|
|
9
9
|
from django.shortcuts import get_object_or_404
|
|
10
10
|
from django.urls import reverse
|
|
11
|
+
|
|
11
12
|
from django_glue.utils import serialize_to_json
|
|
12
13
|
|
|
13
14
|
from django_spire.auth.group.models import AuthGroup
|
|
@@ -16,15 +17,18 @@ from django_spire.auth.user import forms
|
|
|
16
17
|
from django_spire.auth.user.models import AuthUser
|
|
17
18
|
from django_spire.auth.user.tools import add_user_to_all_user_group
|
|
18
19
|
from django_spire.contrib import Breadcrumbs
|
|
19
|
-
from django_spire.contrib.form.confirmation_forms import DeleteConfirmationForm, ConfirmationForm
|
|
20
20
|
from django_spire.contrib.form.utils import show_form_errors
|
|
21
21
|
from django_spire.contrib.generic_views import portal_views
|
|
22
|
-
from django_spire.core.redirect import safe_redirect_url
|
|
23
22
|
from django_spire.history.activity.utils import add_form_activity
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
27
|
+
from django.template.response import TemplateResponse
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
@permission_required('django_spire_auth_user.add_authuser')
|
|
27
|
-
def register_form_view(request):
|
|
31
|
+
def register_form_view(request: WSGIRequest) -> TemplateResponse:
|
|
28
32
|
portal_user = AuthUser()
|
|
29
33
|
dg.glue_model_object(request, 'portal_user', portal_user, 'view')
|
|
30
34
|
|
|
@@ -44,7 +48,7 @@ def register_form_view(request):
|
|
|
44
48
|
user_form = forms.RegisterUserForm(instance=portal_user)
|
|
45
49
|
|
|
46
50
|
context_data = {
|
|
47
|
-
#
|
|
51
|
+
# TODO: Function that takes in all of the forms and dumps the data here?
|
|
48
52
|
'user_form_data': json.dumps(user_form.data, cls=DjangoJSONEncoder),
|
|
49
53
|
}
|
|
50
54
|
|
|
@@ -63,7 +67,7 @@ def register_form_view(request):
|
|
|
63
67
|
|
|
64
68
|
|
|
65
69
|
@permission_required('django_spire_auth_user.change_authuser')
|
|
66
|
-
def form_view(request, pk):
|
|
70
|
+
def form_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
67
71
|
portal_user = get_object_or_404(AuthUser, pk=pk)
|
|
68
72
|
dg.glue_model_object(request, 'portal_user', portal_user, 'view')
|
|
69
73
|
|
|
@@ -74,7 +78,12 @@ def form_view(request, pk):
|
|
|
74
78
|
portal_user = form.save()
|
|
75
79
|
add_form_activity(portal_user, pk, request.user)
|
|
76
80
|
|
|
77
|
-
return HttpResponseRedirect(
|
|
81
|
+
return HttpResponseRedirect(
|
|
82
|
+
reverse(
|
|
83
|
+
'django_spire:auth:user:page:detail',
|
|
84
|
+
kwargs={'pk': pk}
|
|
85
|
+
)
|
|
86
|
+
)
|
|
78
87
|
else:
|
|
79
88
|
form = forms.UserForm(instance=portal_user)
|
|
80
89
|
|
|
@@ -93,7 +102,7 @@ def form_view(request, pk):
|
|
|
93
102
|
|
|
94
103
|
|
|
95
104
|
@permission_required('django_spire_auth_group.change_authgroup')
|
|
96
|
-
def group_form_view(request, pk):
|
|
105
|
+
def group_form_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
97
106
|
user = get_object_or_404(AuthUser, pk=pk)
|
|
98
107
|
dg.glue_query_set(request, 'group_choices', AuthGroup.objects.all())
|
|
99
108
|
selected_group_ids = [group.pk for group in user.groups.all()]
|
|
@@ -104,8 +113,8 @@ def group_form_view(request, pk):
|
|
|
104
113
|
if form.is_valid():
|
|
105
114
|
user.groups.set(form.cleaned_data['group_list'])
|
|
106
115
|
return HttpResponseRedirect(reverse('django_spire:auth:user:page:detail', kwargs={'pk': pk}))
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
|
|
117
|
+
show_form_errors(request, form)
|
|
109
118
|
|
|
110
119
|
form = forms.UserGroupForm()
|
|
111
120
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.shortcuts import get_object_or_404
|
|
4
6
|
|
|
5
7
|
from django_spire.auth.permissions.decorators import permission_required
|
|
@@ -8,9 +10,13 @@ from django_spire.contrib.pagination.pagination import paginate_list
|
|
|
8
10
|
from django_spire.auth.user.models import AuthUser
|
|
9
11
|
from django_spire.contrib.generic_views import portal_views
|
|
10
12
|
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
15
|
+
from django.template.response import TemplateResponse
|
|
16
|
+
|
|
11
17
|
|
|
12
18
|
@permission_required('django_spire_auth_user.view_authuser')
|
|
13
|
-
def detail_view(request, pk):
|
|
19
|
+
def detail_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
14
20
|
user = get_object_or_404(AuthUser, pk=pk)
|
|
15
21
|
group_list = user.groups.all()
|
|
16
22
|
|
|
@@ -30,7 +36,7 @@ def detail_view(request, pk):
|
|
|
30
36
|
|
|
31
37
|
|
|
32
38
|
@permission_required('django_spire_auth_user.view_authuser')
|
|
33
|
-
def list_view(request):
|
|
39
|
+
def list_view(request: WSGIRequest) -> TemplateResponse:
|
|
34
40
|
active_user_list = AuthUser.objects.filter(is_active=True).prefetch_related('groups').order_by('first_name', 'last_name')
|
|
35
41
|
inactive_user_list = AuthUser.objects.filter(is_active=False).prefetch_related('groups').order_by('first_name', 'last_name')
|
|
36
42
|
|