django-spire 0.23.7__py3-none-any.whl → 0.23.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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.7.dist-info → django_spire-0.23.8.dist-info}/METADATA +1 -1
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/RECORD +530 -358
- 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.8.dist-info}/WHEEL +0 -0
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.23.7.dist-info → django_spire-0.23.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,377 @@
|
|
|
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.tests.factories import create_user
|
|
7
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GroupPageViewsTestCase(BaseTestCase):
|
|
11
|
+
def setUp(self) -> None:
|
|
12
|
+
super().setUp()
|
|
13
|
+
|
|
14
|
+
self.group = AuthGroup.objects.create(name='Test Group')
|
|
15
|
+
|
|
16
|
+
def test_list_view_requires_permission(self) -> None:
|
|
17
|
+
user = create_user(username='normaluser')
|
|
18
|
+
self.client.force_login(user)
|
|
19
|
+
response = self.client.get(reverse('django_spire:auth:group:page:list'))
|
|
20
|
+
assert response.status_code == 403
|
|
21
|
+
|
|
22
|
+
def test_list_view_with_permission(self) -> None:
|
|
23
|
+
response = self.client.get(reverse('django_spire:auth:group:page:list'))
|
|
24
|
+
assert response.status_code == 200
|
|
25
|
+
|
|
26
|
+
def test_list_view_context_contains_groups(self) -> None:
|
|
27
|
+
response = self.client.get(reverse('django_spire:auth:group:page:list'))
|
|
28
|
+
assert 'group_list' in response.context
|
|
29
|
+
|
|
30
|
+
def test_list_view_contains_created_group(self) -> None:
|
|
31
|
+
response = self.client.get(reverse('django_spire:auth:group:page:list'))
|
|
32
|
+
group_names = [g.name for g in response.context['group_list']]
|
|
33
|
+
assert 'Test Group' in group_names
|
|
34
|
+
|
|
35
|
+
def test_detail_view_requires_permission(self) -> None:
|
|
36
|
+
user = create_user(username='normaluser')
|
|
37
|
+
self.client.force_login(user)
|
|
38
|
+
response = self.client.get(
|
|
39
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
40
|
+
)
|
|
41
|
+
assert response.status_code == 403
|
|
42
|
+
|
|
43
|
+
def test_detail_view_with_permission(self) -> None:
|
|
44
|
+
response = self.client.get(
|
|
45
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
46
|
+
)
|
|
47
|
+
assert response.status_code == 200
|
|
48
|
+
|
|
49
|
+
def test_detail_view_context_contains_group(self) -> None:
|
|
50
|
+
response = self.client.get(
|
|
51
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
52
|
+
)
|
|
53
|
+
assert 'group' in response.context
|
|
54
|
+
assert response.context['group'] == self.group
|
|
55
|
+
|
|
56
|
+
def test_detail_view_404_for_nonexistent_group(self) -> None:
|
|
57
|
+
response = self.client.get(
|
|
58
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': 99999})
|
|
59
|
+
)
|
|
60
|
+
assert response.status_code == 404
|
|
61
|
+
|
|
62
|
+
def test_detail_view_contains_permission_data(self) -> None:
|
|
63
|
+
response = self.client.get(
|
|
64
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
65
|
+
)
|
|
66
|
+
assert 'permission_data' in response.context
|
|
67
|
+
|
|
68
|
+
def test_detail_view_contains_user_lists(self) -> None:
|
|
69
|
+
response = self.client.get(
|
|
70
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
71
|
+
)
|
|
72
|
+
assert 'active_user_list' in response.context
|
|
73
|
+
assert 'inactive_user_list' in response.context
|
|
74
|
+
|
|
75
|
+
def test_list_view_contains_permission_data(self) -> None:
|
|
76
|
+
response = self.client.get(reverse('django_spire:auth:group:page:list'))
|
|
77
|
+
assert 'group_list_permission_data' in response.context
|
|
78
|
+
|
|
79
|
+
def test_list_view_multiple_groups(self) -> None:
|
|
80
|
+
AuthGroup.objects.create(name='Group 2')
|
|
81
|
+
AuthGroup.objects.create(name='Group 3')
|
|
82
|
+
response = self.client.get(reverse('django_spire:auth:group:page:list'))
|
|
83
|
+
assert len(response.context['group_list']) >= 3
|
|
84
|
+
|
|
85
|
+
def test_detail_view_with_users_in_group(self) -> None:
|
|
86
|
+
user = create_user(username='groupmember')
|
|
87
|
+
self.group.user_set.add(user)
|
|
88
|
+
response = self.client.get(
|
|
89
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
90
|
+
)
|
|
91
|
+
assert response.status_code == 200
|
|
92
|
+
|
|
93
|
+
def test_detail_view_separates_active_inactive_users(self) -> None:
|
|
94
|
+
active_user = create_user(username='activeuser', is_active=True)
|
|
95
|
+
inactive_user = create_user(username='inactiveuser', is_active=False)
|
|
96
|
+
self.group.user_set.add(active_user, inactive_user)
|
|
97
|
+
response = self.client.get(
|
|
98
|
+
reverse('django_spire:auth:group:page:detail', kwargs={'pk': self.group.pk})
|
|
99
|
+
)
|
|
100
|
+
active_ids = [u.pk for u in response.context['active_user_list']]
|
|
101
|
+
inactive_ids = [u.pk for u in response.context['inactive_user_list']]
|
|
102
|
+
assert active_user.pk in active_ids
|
|
103
|
+
assert inactive_user.pk in inactive_ids
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class GroupFormViewsTestCase(BaseTestCase):
|
|
107
|
+
def setUp(self) -> None:
|
|
108
|
+
super().setUp()
|
|
109
|
+
|
|
110
|
+
self.group = AuthGroup.objects.create(name='Test Group')
|
|
111
|
+
|
|
112
|
+
def test_add_form_view_get(self) -> None:
|
|
113
|
+
response = self.client.get(reverse('django_spire:auth:group:form:add'))
|
|
114
|
+
assert response.status_code == 200
|
|
115
|
+
|
|
116
|
+
def test_add_form_view_post_creates_group(self) -> None:
|
|
117
|
+
response = self.client.post(
|
|
118
|
+
reverse('django_spire:auth:group:form:add'),
|
|
119
|
+
data={'name': 'New Group'}
|
|
120
|
+
)
|
|
121
|
+
assert response.status_code == 302
|
|
122
|
+
assert AuthGroup.objects.filter(name='New Group').exists()
|
|
123
|
+
|
|
124
|
+
def test_add_form_view_post_invalid_name(self) -> None:
|
|
125
|
+
response = self.client.post(
|
|
126
|
+
reverse('django_spire:auth:group:form:add'),
|
|
127
|
+
data={'name': 'All Users'}
|
|
128
|
+
)
|
|
129
|
+
assert response.status_code == 200
|
|
130
|
+
assert not AuthGroup.objects.filter(name='All Users').exists()
|
|
131
|
+
|
|
132
|
+
def test_add_form_view_post_empty_name(self) -> None:
|
|
133
|
+
response = self.client.post(
|
|
134
|
+
reverse('django_spire:auth:group:form:add'),
|
|
135
|
+
data={'name': ''}
|
|
136
|
+
)
|
|
137
|
+
assert response.status_code == 200
|
|
138
|
+
|
|
139
|
+
def test_update_form_view_get(self) -> None:
|
|
140
|
+
response = self.client.get(
|
|
141
|
+
reverse('django_spire:auth:group:form:update', kwargs={'pk': self.group.pk})
|
|
142
|
+
)
|
|
143
|
+
assert response.status_code == 200
|
|
144
|
+
|
|
145
|
+
def test_update_form_view_post_updates_group(self) -> None:
|
|
146
|
+
response = self.client.post(
|
|
147
|
+
reverse('django_spire:auth:group:form:update', kwargs={'pk': self.group.pk}),
|
|
148
|
+
data={'name': 'Updated Group'}
|
|
149
|
+
)
|
|
150
|
+
assert response.status_code == 302
|
|
151
|
+
self.group.refresh_from_db()
|
|
152
|
+
assert self.group.name == 'Updated Group'
|
|
153
|
+
|
|
154
|
+
def test_update_form_view_nonexistent_group_returns_200(self) -> None:
|
|
155
|
+
response = self.client.get(
|
|
156
|
+
reverse('django_spire:auth:group:form:update', kwargs={'pk': 99999})
|
|
157
|
+
)
|
|
158
|
+
assert response.status_code == 200
|
|
159
|
+
|
|
160
|
+
def test_delete_form_view_get(self) -> None:
|
|
161
|
+
response = self.client.get(
|
|
162
|
+
reverse('django_spire:auth:group:form:delete', kwargs={'pk': self.group.pk})
|
|
163
|
+
)
|
|
164
|
+
assert response.status_code == 200
|
|
165
|
+
|
|
166
|
+
def test_delete_form_view_404(self) -> None:
|
|
167
|
+
response = self.client.get(
|
|
168
|
+
reverse('django_spire:auth:group:form:delete', kwargs={'pk': 99999})
|
|
169
|
+
)
|
|
170
|
+
assert response.status_code == 404
|
|
171
|
+
|
|
172
|
+
def test_user_form_view_get(self) -> None:
|
|
173
|
+
response = self.client.get(
|
|
174
|
+
reverse('django_spire:auth:group:form:user', kwargs={'pk': self.group.pk})
|
|
175
|
+
)
|
|
176
|
+
assert response.status_code == 200
|
|
177
|
+
|
|
178
|
+
def test_user_form_view_404(self) -> None:
|
|
179
|
+
response = self.client.get(
|
|
180
|
+
reverse('django_spire:auth:group:form:user', kwargs={'pk': 99999})
|
|
181
|
+
)
|
|
182
|
+
assert response.status_code == 404
|
|
183
|
+
|
|
184
|
+
def test_add_form_requires_permission(self) -> None:
|
|
185
|
+
user = create_user(username='normaluser')
|
|
186
|
+
self.client.force_login(user)
|
|
187
|
+
response = self.client.get(reverse('django_spire:auth:group:form:add'))
|
|
188
|
+
assert response.status_code == 403
|
|
189
|
+
|
|
190
|
+
def test_update_form_requires_permission(self) -> None:
|
|
191
|
+
user = create_user(username='normaluser')
|
|
192
|
+
self.client.force_login(user)
|
|
193
|
+
response = self.client.get(
|
|
194
|
+
reverse('django_spire:auth:group:form:update', kwargs={'pk': self.group.pk})
|
|
195
|
+
)
|
|
196
|
+
assert response.status_code == 403
|
|
197
|
+
|
|
198
|
+
def test_delete_form_requires_permission(self) -> None:
|
|
199
|
+
user = create_user(username='normaluser')
|
|
200
|
+
self.client.force_login(user)
|
|
201
|
+
response = self.client.get(
|
|
202
|
+
reverse('django_spire:auth:group:form:delete', kwargs={'pk': self.group.pk})
|
|
203
|
+
)
|
|
204
|
+
assert response.status_code == 403
|
|
205
|
+
|
|
206
|
+
def test_user_form_requires_permission(self) -> None:
|
|
207
|
+
user = create_user(username='normaluser')
|
|
208
|
+
self.client.force_login(user)
|
|
209
|
+
response = self.client.get(
|
|
210
|
+
reverse('django_spire:auth:group:form:user', kwargs={'pk': self.group.pk})
|
|
211
|
+
)
|
|
212
|
+
assert response.status_code == 403
|
|
213
|
+
|
|
214
|
+
def test_add_form_view_post_duplicate_name(self) -> None:
|
|
215
|
+
response = self.client.post(
|
|
216
|
+
reverse('django_spire:auth:group:form:add'),
|
|
217
|
+
data={'name': 'Test Group'}
|
|
218
|
+
)
|
|
219
|
+
assert response.status_code == 200
|
|
220
|
+
|
|
221
|
+
def test_update_form_view_post_to_reserved_name(self) -> None:
|
|
222
|
+
response = self.client.post(
|
|
223
|
+
reverse('django_spire:auth:group:form:update', kwargs={'pk': self.group.pk}),
|
|
224
|
+
data={'name': 'All Users'}
|
|
225
|
+
)
|
|
226
|
+
assert response.status_code == 200
|
|
227
|
+
self.group.refresh_from_db()
|
|
228
|
+
assert self.group.name != 'All Users'
|
|
229
|
+
|
|
230
|
+
def test_add_form_view_post_unicode_name(self) -> None:
|
|
231
|
+
response = self.client.post(
|
|
232
|
+
reverse('django_spire:auth:group:form:add'),
|
|
233
|
+
data={'name': 'Tëst Grøup 日本語'}
|
|
234
|
+
)
|
|
235
|
+
assert response.status_code == 302
|
|
236
|
+
assert AuthGroup.objects.filter(name='Tëst Grøup 日本語').exists()
|
|
237
|
+
|
|
238
|
+
def test_add_form_view_post_special_characters(self) -> None:
|
|
239
|
+
response = self.client.post(
|
|
240
|
+
reverse('django_spire:auth:group:form:add'),
|
|
241
|
+
data={'name': 'Group & Co <Test>'}
|
|
242
|
+
)
|
|
243
|
+
assert response.status_code == 302
|
|
244
|
+
|
|
245
|
+
def test_user_form_view_post_adds_users(self) -> None:
|
|
246
|
+
user = create_user(username='newgroupuser')
|
|
247
|
+
response = self.client.post(
|
|
248
|
+
reverse('django_spire:auth:group:form:user', kwargs={'pk': self.group.pk}),
|
|
249
|
+
data={'users': [user.pk]}
|
|
250
|
+
)
|
|
251
|
+
assert response.status_code == 302
|
|
252
|
+
assert user in self.group.user_set.all()
|
|
253
|
+
|
|
254
|
+
def test_user_form_view_post_removes_users(self) -> None:
|
|
255
|
+
user = create_user(username='removeuser')
|
|
256
|
+
self.group.user_set.add(user)
|
|
257
|
+
response = self.client.post(
|
|
258
|
+
reverse('django_spire:auth:group:form:user', kwargs={'pk': self.group.pk}),
|
|
259
|
+
data={'users': []}
|
|
260
|
+
)
|
|
261
|
+
assert response.status_code == 302
|
|
262
|
+
assert user not in self.group.user_set.all()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class GroupJsonViewsTestCase(BaseTestCase):
|
|
266
|
+
def setUp(self) -> None:
|
|
267
|
+
super().setUp()
|
|
268
|
+
|
|
269
|
+
self.group = AuthGroup.objects.create(name='Test Group')
|
|
270
|
+
|
|
271
|
+
def test_permission_form_ajax_invalid_app(self) -> None:
|
|
272
|
+
response = self.client.post(
|
|
273
|
+
reverse(
|
|
274
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
275
|
+
kwargs={'pk': self.group.pk, 'app_name': 'invalid_app'}
|
|
276
|
+
),
|
|
277
|
+
data={'perm_level': 'View'},
|
|
278
|
+
content_type='application/json'
|
|
279
|
+
)
|
|
280
|
+
assert response.status_code == 200
|
|
281
|
+
data = response.json()
|
|
282
|
+
assert data['status'] == 400
|
|
283
|
+
|
|
284
|
+
def test_permission_form_ajax_valid_app(self) -> None:
|
|
285
|
+
response = self.client.post(
|
|
286
|
+
reverse(
|
|
287
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
288
|
+
kwargs={'pk': self.group.pk, 'app_name': 'group'}
|
|
289
|
+
),
|
|
290
|
+
data='{"perm_level": "View"}',
|
|
291
|
+
content_type='application/json'
|
|
292
|
+
)
|
|
293
|
+
assert response.status_code == 200
|
|
294
|
+
|
|
295
|
+
def test_permission_form_ajax_requires_post(self) -> None:
|
|
296
|
+
response = self.client.get(
|
|
297
|
+
reverse(
|
|
298
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
299
|
+
kwargs={'pk': self.group.pk, 'app_name': 'group'}
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
assert response.status_code == 405
|
|
303
|
+
|
|
304
|
+
def test_permission_form_ajax_404_group(self) -> None:
|
|
305
|
+
response = self.client.post(
|
|
306
|
+
reverse(
|
|
307
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
308
|
+
kwargs={'pk': 99999, 'app_name': 'group'}
|
|
309
|
+
),
|
|
310
|
+
data='{"perm_level": "View"}',
|
|
311
|
+
content_type='application/json'
|
|
312
|
+
)
|
|
313
|
+
assert response.status_code == 404
|
|
314
|
+
|
|
315
|
+
def test_special_role_ajax_invalid_app(self) -> None:
|
|
316
|
+
response = self.client.post(
|
|
317
|
+
reverse(
|
|
318
|
+
'django_spire:auth:group:json:group_special_role_ajax',
|
|
319
|
+
kwargs={'pk': self.group.pk, 'app_name': 'invalid_app'}
|
|
320
|
+
),
|
|
321
|
+
data='{"codename": "test", "grant_special_role_access": true}',
|
|
322
|
+
content_type='application/json'
|
|
323
|
+
)
|
|
324
|
+
assert response.status_code == 200
|
|
325
|
+
data = response.json()
|
|
326
|
+
assert data['status'] == 400
|
|
327
|
+
|
|
328
|
+
def test_permission_form_ajax_requires_permission(self) -> None:
|
|
329
|
+
user = create_user(username='normaluser')
|
|
330
|
+
self.client.force_login(user)
|
|
331
|
+
response = self.client.post(
|
|
332
|
+
reverse(
|
|
333
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
334
|
+
kwargs={'pk': self.group.pk, 'app_name': 'group'}
|
|
335
|
+
),
|
|
336
|
+
data='{"perm_level": "View"}',
|
|
337
|
+
content_type='application/json'
|
|
338
|
+
)
|
|
339
|
+
assert response.status_code == 403
|
|
340
|
+
|
|
341
|
+
def test_special_role_ajax_requires_permission(self) -> None:
|
|
342
|
+
user = create_user(username='normaluser')
|
|
343
|
+
self.client.force_login(user)
|
|
344
|
+
response = self.client.post(
|
|
345
|
+
reverse(
|
|
346
|
+
'django_spire:auth:group:json:group_special_role_ajax',
|
|
347
|
+
kwargs={'pk': self.group.pk, 'app_name': 'group'}
|
|
348
|
+
),
|
|
349
|
+
data='{"codename": "test", "grant_special_role_access": true}',
|
|
350
|
+
content_type='application/json'
|
|
351
|
+
)
|
|
352
|
+
assert response.status_code == 403
|
|
353
|
+
|
|
354
|
+
def test_permission_form_ajax_all_perm_levels(self) -> None:
|
|
355
|
+
for perm_level in ['None', 'View', 'Add', 'Change', 'Delete']:
|
|
356
|
+
response = self.client.post(
|
|
357
|
+
reverse(
|
|
358
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
359
|
+
kwargs={'pk': self.group.pk, 'app_name': 'group'}
|
|
360
|
+
),
|
|
361
|
+
data=f'{{"perm_level": "{perm_level}"}}',
|
|
362
|
+
content_type='application/json'
|
|
363
|
+
)
|
|
364
|
+
assert response.status_code == 200
|
|
365
|
+
|
|
366
|
+
def test_permission_form_ajax_invalid_perm_level_defaults_to_none(self) -> None:
|
|
367
|
+
response = self.client.post(
|
|
368
|
+
reverse(
|
|
369
|
+
'django_spire:auth:group:json:group_permission_ajax',
|
|
370
|
+
kwargs={'pk': self.group.pk, 'app_name': 'group'}
|
|
371
|
+
),
|
|
372
|
+
data='{"perm_level": "InvalidLevel"}',
|
|
373
|
+
content_type='application/json'
|
|
374
|
+
)
|
|
375
|
+
assert response.status_code == 200
|
|
376
|
+
data = response.json()
|
|
377
|
+
assert data['status'] == 200
|
django_spire/auth/group/utils.py
CHANGED
|
@@ -2,7 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
|
-
from django_spire.auth.permissions.consts import
|
|
5
|
+
from django_spire.auth.permissions.consts import (
|
|
6
|
+
PERMISSIONS_LEVEL_CHOICES,
|
|
7
|
+
VALID_PERMISSION_LEVELS
|
|
8
|
+
)
|
|
6
9
|
|
|
7
10
|
if TYPE_CHECKING:
|
|
8
11
|
from django.contrib.auth.models import Group, User
|
|
@@ -31,7 +34,8 @@ def codename_list_to_perm_level(codenames_list: list[str]) -> int:
|
|
|
31
34
|
def perm_level_to_int(perm_level: VALID_PERMISSION_LEVELS | str) -> int:
|
|
32
35
|
if isinstance(perm_level, int) and 4 >= perm_level >= 0:
|
|
33
36
|
return perm_level
|
|
34
|
-
|
|
37
|
+
|
|
38
|
+
if isinstance(perm_level, str):
|
|
35
39
|
for perm in PERMISSIONS_LEVEL_CHOICES:
|
|
36
40
|
if perm_level.lower() == perm[1].lower():
|
|
37
41
|
return perm[0]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.http import HttpResponseRedirect
|
|
4
6
|
from django.shortcuts import get_object_or_404
|
|
5
7
|
from django.urls import reverse
|
|
@@ -15,12 +17,13 @@ from django_spire.contrib.form.utils import show_form_errors
|
|
|
15
17
|
from django_spire.contrib.generic_views import portal_views
|
|
16
18
|
from django_spire.core.shortcuts import get_object_or_null_obj
|
|
17
19
|
from django_spire.history.activity.utils import add_form_activity
|
|
18
|
-
from typing import TYPE_CHECKING
|
|
19
20
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
21
22
|
from django.core.handlers.wsgi import WSGIRequest
|
|
22
23
|
from django.template.response import TemplateResponse
|
|
23
24
|
|
|
25
|
+
from django_spire.contrib.breadcrumb.models import Breadcrumbs
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
@permission_required('django_spire_auth_group.change_authgroup')
|
|
26
29
|
def form_view(
|
|
@@ -94,7 +97,7 @@ def user_form_view(
|
|
|
94
97
|
|
|
95
98
|
form = forms.GroupUserForm()
|
|
96
99
|
|
|
97
|
-
def crumbs(breadcrumbs) -> None:
|
|
100
|
+
def crumbs(breadcrumbs: Breadcrumbs) -> None:
|
|
98
101
|
breadcrumbs.add_breadcrumb(name='Edit Users')
|
|
99
102
|
|
|
100
103
|
context_data = {
|
|
@@ -160,7 +163,7 @@ def group_remove_user_form_view(
|
|
|
160
163
|
|
|
161
164
|
form = DeleteConfirmationForm(request.GET, obj=user)
|
|
162
165
|
|
|
163
|
-
def get_breadcrumbs(breadcrumbs) -> None:
|
|
166
|
+
def get_breadcrumbs(breadcrumbs: Breadcrumbs) -> None:
|
|
164
167
|
breadcrumbs = group.breadcrumbs()
|
|
165
168
|
breadcrumbs.add_breadcrumb(name=user.get_full_name())
|
|
166
169
|
breadcrumbs.add_breadcrumb(name='Remove')
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
4
5
|
from django.http import JsonResponse
|
|
5
6
|
from django.shortcuts import get_object_or_404
|
|
6
7
|
from django.views.decorators.http import require_POST
|
|
@@ -9,9 +10,12 @@ from django_spire.auth.group import models
|
|
|
9
10
|
from django_spire.auth.group.utils import perm_level_to_int, perm_level_to_django_permission
|
|
10
11
|
from django_spire.auth.permissions.decorators import permission_required
|
|
11
12
|
from django_spire.auth.permissions.permissions import GroupPermissions
|
|
12
|
-
from django_spire.auth.permissions.tools import
|
|
13
|
+
from django_spire.auth.permissions.tools import generate_model_key_permission_map
|
|
13
14
|
from django_spire.core.shortcuts import process_request_body
|
|
14
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
@require_POST
|
|
17
21
|
@permission_required('django_spire_auth_group.change_authgroup')
|
django_spire/auth/mfa/admin.py
CHANGED
django_spire/auth/mfa/apps.py
CHANGED
django_spire/auth/mfa/forms.py
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.db.models import QuerySet
|
|
4
6
|
from django.utils.timezone import localtime
|
|
5
7
|
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from django.contrib.auth.models import User
|
|
10
|
+
|
|
11
|
+
from django_spire.auth.mfa.models import MfaCode
|
|
12
|
+
|
|
6
13
|
|
|
7
14
|
class MfaCodeQuerySet(QuerySet):
|
|
8
|
-
def valid_code(self, user):
|
|
9
|
-
return self.filter(user=user,
|
|
15
|
+
def valid_code(self, user: User) -> MfaCode | None:
|
|
16
|
+
return self.filter(user=user, expiration_datetime__gt=localtime()).first()
|