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
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.conf import settings
|
|
2
4
|
|
|
3
5
|
from django_spire.consts import NOTIFICATION_THROTTLE_RATE_PER_MINUTE_SETTINGS_NAME
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def get_throttle_sleep_time() -> float:
|
|
7
|
-
return float(getattr(settings, NOTIFICATION_THROTTLE_RATE_PER_MINUTE_SETTINGS_NAME) / 60)
|
|
9
|
+
return float(getattr(settings, NOTIFICATION_THROTTLE_RATE_PER_MINUTE_SETTINGS_NAME) / 60)
|
django_spire/settings.py
CHANGED
|
@@ -20,12 +20,12 @@ class ThemeContextProcessorTests(TestCase):
|
|
|
20
20
|
with patch('django_spire.conf.settings.DJANGO_SPIRE_DEFAULT_THEME', 'gruvbox-dark'):
|
|
21
21
|
context = theme_context(request)
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
assert context['DJANGO_SPIRE_DEFAULT_THEME'] == 'gruvbox-dark'
|
|
24
|
+
assert 'theme' in context
|
|
25
25
|
|
|
26
26
|
data = context['theme']
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
assert data['family'] == 'gruvbox'
|
|
28
|
+
assert data['mode'] == 'dark'
|
|
29
29
|
|
|
30
30
|
def test_theme_context_with_cookie(self) -> None:
|
|
31
31
|
request = self.factory.get('/')
|
|
@@ -34,10 +34,10 @@ class ThemeContextProcessorTests(TestCase):
|
|
|
34
34
|
|
|
35
35
|
context = theme_context(request)
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
assert 'theme' in context
|
|
38
38
|
data = context['theme']
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
assert data['family'] == 'one-dark'
|
|
40
|
+
assert data['mode'] == 'light'
|
|
41
41
|
|
|
42
42
|
def test_theme_context_invalid_cookie(self) -> None:
|
|
43
43
|
request = self.factory.get('/')
|
|
@@ -48,30 +48,32 @@ class ThemeContextProcessorTests(TestCase):
|
|
|
48
48
|
context = theme_context(request)
|
|
49
49
|
|
|
50
50
|
data = context['theme']
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
assert data['family'] == 'gruvbox'
|
|
52
|
+
assert data['mode'] == 'dark'
|
|
53
53
|
|
|
54
54
|
def test_theme_context_path_setting(self) -> None:
|
|
55
55
|
request = self.factory.get('/')
|
|
56
56
|
request.COOKIES = {}
|
|
57
57
|
|
|
58
58
|
path = '/custom/path/{family}/{mode}.css'
|
|
59
|
+
|
|
59
60
|
with patch('django_spire.conf.settings.DJANGO_SPIRE_THEME_PATH', path):
|
|
60
61
|
context = theme_context(request)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
assert context['DJANGO_SPIRE_THEME_PATH'] == path
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
class ThemeContextProcessorIntegrationTests(BaseTestCase):
|
|
66
67
|
def setUp(self) -> None:
|
|
67
68
|
super().setUp()
|
|
69
|
+
|
|
68
70
|
self.factory = RequestFactory()
|
|
69
71
|
|
|
70
72
|
def test_theme_context_with_authenticated_client(self) -> None:
|
|
71
73
|
cookie_name = get_theme_cookie_name()
|
|
72
74
|
self.client.cookies[cookie_name] = 'gruvbox-dark'
|
|
73
75
|
response = self.client.get('/')
|
|
74
|
-
|
|
76
|
+
assert response.status_code == 200
|
|
75
77
|
|
|
76
78
|
def test_theme_context_with_media_root_override(self) -> None:
|
|
77
79
|
request = self.factory.get('/')
|
|
@@ -80,5 +82,5 @@ class ThemeContextProcessorIntegrationTests(BaseTestCase):
|
|
|
80
82
|
|
|
81
83
|
context = theme_context(request)
|
|
82
84
|
data = context['theme']
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
assert data['family'] == 'material'
|
|
86
|
+
assert data['mode'] == 'light'
|
|
@@ -21,9 +21,9 @@ class ThemeEnumTests(TestCase):
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
actual = {family.value for family in ThemeFamily}
|
|
24
|
-
|
|
24
|
+
assert actual == expected
|
|
25
25
|
|
|
26
26
|
def test_theme_modes(self) -> None:
|
|
27
27
|
expected = {'dark', 'light'}
|
|
28
28
|
actual = {mode.value for mode in ThemeMode}
|
|
29
|
-
|
|
29
|
+
assert actual == expected
|
|
@@ -21,7 +21,7 @@ class ThemeFilesystemValidationTests(TestCase):
|
|
|
21
21
|
if not self.base_path.exists():
|
|
22
22
|
self.skipTest(f'Themes directory does not exist: {self.base_path}. Create it to enable filesystem validation tests.')
|
|
23
23
|
|
|
24
|
-
self.
|
|
24
|
+
assert self.base_path.is_dir(), f'Themes path is not a directory: {self.base_path}'
|
|
25
25
|
|
|
26
26
|
def test_all_theme_families_have_directories(self) -> None:
|
|
27
27
|
if not self.base_path.exists():
|
|
@@ -175,7 +175,4 @@ class ThemeFilesystemValidationTests(TestCase):
|
|
|
175
175
|
file = directory / filename
|
|
176
176
|
|
|
177
177
|
if file.exists():
|
|
178
|
-
|
|
179
|
-
file.name, filename,
|
|
180
|
-
f'Filename {file.name} does not match expected {filename}'
|
|
181
|
-
)
|
|
178
|
+
assert file.name == filename, f'Filename {file.name} does not match expected {filename}'
|
|
@@ -10,39 +10,39 @@ class ThemeIntegrationTests(BaseTestCase):
|
|
|
10
10
|
for theme in Theme.get_available():
|
|
11
11
|
stylesheet = theme.stylesheet
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
assert f'{theme.family.value}/' in stylesheet
|
|
14
|
+
assert f'app-{theme.mode.value}.css' in stylesheet
|
|
15
15
|
|
|
16
16
|
def test_roundtrip_conversion(self) -> None:
|
|
17
17
|
for theme in Theme.get_available():
|
|
18
18
|
string = theme.value
|
|
19
19
|
recovered = Theme.from_string(string)
|
|
20
|
-
|
|
20
|
+
assert theme == recovered
|
|
21
21
|
|
|
22
22
|
dictionary = theme.to_dict()
|
|
23
23
|
recovered2 = Theme.from_string(dictionary['full'])
|
|
24
|
-
|
|
24
|
+
assert theme == recovered2
|
|
25
25
|
|
|
26
26
|
def test_display_names_complete(self) -> None:
|
|
27
27
|
for family in ThemeFamily:
|
|
28
|
-
|
|
28
|
+
assert family in Theme.FAMILY_DISPLAY_NAMES
|
|
29
29
|
display_name = Theme.FAMILY_DISPLAY_NAMES[family]
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
assert isinstance(display_name, str)
|
|
31
|
+
assert len(display_name) > 0
|
|
32
32
|
|
|
33
33
|
def test_theme_integration_with_authenticated_user(self) -> None:
|
|
34
34
|
available = Theme.get_available()
|
|
35
|
-
|
|
35
|
+
assert len(available) > 0
|
|
36
36
|
|
|
37
37
|
for theme in available[:3]:
|
|
38
38
|
self.client.cookies['app-theme'] = theme.value
|
|
39
39
|
response = self.client.get('/')
|
|
40
|
-
|
|
40
|
+
assert response.status_code == 200
|
|
41
41
|
|
|
42
42
|
def test_theme_media_integration(self) -> None:
|
|
43
43
|
theme = Theme(family=ThemeFamily.GRUVBOX, mode='dark')
|
|
44
44
|
stylesheet = theme.stylesheet
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
assert 'django_spire/css/themes/' in stylesheet
|
|
47
|
+
assert 'gruvbox' in stylesheet
|
|
48
|
+
assert 'app-dark.css' in stylesheet
|
|
@@ -14,37 +14,39 @@ class ThemeModelTests(TestCase):
|
|
|
14
14
|
def test_dataclass_fields(self) -> None:
|
|
15
15
|
theme_fields = {f.name: f.type for f in fields(Theme)}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
assert set(theme_fields.keys()) == {'family', 'mode'}
|
|
18
|
+
assert theme_fields['family'] == 'ThemeFamily'
|
|
19
|
+
assert theme_fields['mode'] == 'ThemeMode'
|
|
20
20
|
|
|
21
21
|
def test_class_variables(self) -> None:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
assert Theme.DEFAULT_FAMILY == ThemeFamily.DEFAULT
|
|
23
|
+
assert Theme.DEFAULT_MODE == ThemeMode.LIGHT
|
|
24
|
+
assert Theme.SEPARATOR == '-'
|
|
25
25
|
|
|
26
26
|
for family in ThemeFamily:
|
|
27
|
-
|
|
27
|
+
assert family in Theme.FAMILY_DISPLAY_NAMES
|
|
28
28
|
|
|
29
29
|
def test_theme_initialization_with_enums(self) -> None:
|
|
30
30
|
theme = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
assert theme.family == ThemeFamily.GRUVBOX
|
|
32
|
+
assert theme.mode == ThemeMode.DARK
|
|
33
33
|
|
|
34
34
|
def test_theme_initialization_with_strings(self) -> None:
|
|
35
35
|
theme = Theme(family='gruvbox', mode='dark')
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
assert theme.family == ThemeFamily.GRUVBOX
|
|
37
|
+
assert theme.mode == ThemeMode.DARK
|
|
38
38
|
|
|
39
39
|
def test_theme_initialization_invalid_family(self) -> None:
|
|
40
40
|
with pytest.raises(ValueError) as ctx:
|
|
41
41
|
Theme(family='invalid-family', mode='dark')
|
|
42
|
-
|
|
42
|
+
|
|
43
|
+
assert 'Invalid theme family' in str(ctx.value)
|
|
43
44
|
|
|
44
45
|
def test_theme_initialization_invalid_mode(self) -> None:
|
|
45
46
|
with pytest.raises(ValueError) as ctx:
|
|
46
47
|
Theme(family='gruvbox', mode='invalid-mode')
|
|
47
|
-
|
|
48
|
+
|
|
49
|
+
assert 'Invalid theme mode' in str(ctx.value)
|
|
48
50
|
|
|
49
51
|
def test_from_string_valid(self) -> None:
|
|
50
52
|
cases = [
|
|
@@ -57,23 +59,23 @@ class ThemeModelTests(TestCase):
|
|
|
57
59
|
for string, family, mode in cases:
|
|
58
60
|
with self.subTest(string=string):
|
|
59
61
|
theme = Theme.from_string(string)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
assert theme.family == family
|
|
63
|
+
assert theme.mode == mode
|
|
62
64
|
|
|
63
65
|
def test_from_string_empty_with_default(self) -> None:
|
|
64
66
|
default = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
65
67
|
theme = Theme.from_string('', default=default)
|
|
66
|
-
|
|
68
|
+
assert theme == default
|
|
67
69
|
|
|
68
70
|
def test_from_string_empty_without_default(self) -> None:
|
|
69
71
|
theme = Theme.from_string('')
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
assert theme.family == Theme.DEFAULT_FAMILY
|
|
73
|
+
assert theme.mode == Theme.DEFAULT_MODE
|
|
72
74
|
|
|
73
75
|
def test_from_string_invalid_with_default(self) -> None:
|
|
74
76
|
default = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
75
77
|
theme = Theme.from_string('invalid', default=default)
|
|
76
|
-
|
|
78
|
+
assert theme == default
|
|
77
79
|
|
|
78
80
|
def test_from_string_invalid_without_default(self) -> None:
|
|
79
81
|
with pytest.raises(ValueError):
|
|
@@ -83,35 +85,35 @@ class ThemeModelTests(TestCase):
|
|
|
83
85
|
available = Theme.get_available()
|
|
84
86
|
|
|
85
87
|
count = len(ThemeFamily) * len(ThemeMode)
|
|
86
|
-
|
|
88
|
+
assert len(available) == count
|
|
87
89
|
|
|
88
90
|
for family in ThemeFamily:
|
|
89
91
|
for mode in ThemeMode:
|
|
90
92
|
theme = Theme(family=family, mode=mode)
|
|
91
|
-
|
|
93
|
+
assert theme in available
|
|
92
94
|
|
|
93
95
|
def test_get_default(self) -> None:
|
|
94
96
|
default = Theme.get_default()
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
assert default.family == Theme.DEFAULT_FAMILY
|
|
98
|
+
assert default.mode == Theme.DEFAULT_MODE
|
|
97
99
|
|
|
98
100
|
def test_display_property(self) -> None:
|
|
99
101
|
theme = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
100
|
-
|
|
102
|
+
assert theme.display == 'Gruvbox - Dark'
|
|
101
103
|
|
|
102
104
|
theme = Theme(family=ThemeFamily.ONE_DARK, mode=ThemeMode.LIGHT)
|
|
103
|
-
|
|
105
|
+
assert theme.display == 'One Dark Pro - Light'
|
|
104
106
|
|
|
105
107
|
def test_family_display_property(self) -> None:
|
|
106
108
|
theme = Theme(family=ThemeFamily.ONE_DARK, mode=ThemeMode.DARK)
|
|
107
|
-
|
|
109
|
+
assert theme.family_display == 'One Dark Pro'
|
|
108
110
|
|
|
109
111
|
def test_is_dark_property(self) -> None:
|
|
110
112
|
dark = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
111
|
-
|
|
113
|
+
assert dark.is_dark
|
|
112
114
|
|
|
113
115
|
light = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.LIGHT)
|
|
114
|
-
|
|
116
|
+
assert not light.is_dark
|
|
115
117
|
|
|
116
118
|
def test_stylesheet_property(self) -> None:
|
|
117
119
|
cases = [
|
|
@@ -124,7 +126,7 @@ class ThemeModelTests(TestCase):
|
|
|
124
126
|
for family, mode, path in cases:
|
|
125
127
|
with self.subTest(family=family, mode=mode):
|
|
126
128
|
theme = Theme(family=family, mode=mode)
|
|
127
|
-
|
|
129
|
+
assert theme.stylesheet == path
|
|
128
130
|
|
|
129
131
|
def test_value_property(self) -> None:
|
|
130
132
|
cases = [
|
|
@@ -137,7 +139,7 @@ class ThemeModelTests(TestCase):
|
|
|
137
139
|
for family, mode, value in cases:
|
|
138
140
|
with self.subTest(family=family, mode=mode):
|
|
139
141
|
theme = Theme(family=family, mode=mode)
|
|
140
|
-
|
|
142
|
+
assert theme.value == value
|
|
141
143
|
|
|
142
144
|
def test_to_dict(self) -> None:
|
|
143
145
|
theme = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
@@ -153,15 +155,15 @@ class ThemeModelTests(TestCase):
|
|
|
153
155
|
'stylesheet'
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
assert set(result.keys()) == keys
|
|
157
159
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
assert result['display'] == 'Gruvbox - Dark'
|
|
161
|
+
assert result['family'] == 'gruvbox'
|
|
162
|
+
assert result['family_display'] == 'Gruvbox'
|
|
163
|
+
assert result['full'] == 'gruvbox-dark'
|
|
164
|
+
assert result['is_dark']
|
|
165
|
+
assert result['mode'] == 'dark'
|
|
166
|
+
assert result['stylesheet'] == 'django_spire/css/themes/gruvbox/app-dark.css'
|
|
165
167
|
|
|
166
168
|
def test_theme_immutability(self) -> None:
|
|
167
169
|
theme = Theme(family=ThemeFamily.GRUVBOX, mode=ThemeMode.DARK)
|
|
@@ -22,30 +22,30 @@ class ThemeViewTests(TestCase):
|
|
|
22
22
|
request = self.factory.get('/django_spire/theme/json/get_config/')
|
|
23
23
|
response = json_views.get_config(request)
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
assert isinstance(response, JsonResponse)
|
|
26
|
+
assert response.status_code == HTTPStatus.OK
|
|
27
27
|
|
|
28
28
|
data = json.loads(response.content)
|
|
29
|
-
|
|
29
|
+
assert data['success']
|
|
30
30
|
|
|
31
31
|
config = data['data']
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
assert 'families' in config
|
|
33
|
+
assert 'default_family' in config
|
|
34
|
+
assert 'default_mode' in config
|
|
35
|
+
assert 'separator' in config
|
|
36
36
|
|
|
37
37
|
for family in ThemeFamily:
|
|
38
|
-
|
|
38
|
+
assert family.value in config['families']
|
|
39
39
|
family_config = config['families'][family.value]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
assert 'name' in family_config
|
|
41
|
+
assert 'modes' in family_config
|
|
42
|
+
assert set(family_config['modes']) == {'dark', 'light'}
|
|
43
43
|
|
|
44
44
|
def test_get_config_cache_header(self) -> None:
|
|
45
45
|
_ = self.factory.get('/django_spire/theme/json/get_config/')
|
|
46
46
|
|
|
47
47
|
with patch('django_spire.theme.views.json_views.cache_page') as _:
|
|
48
|
-
|
|
48
|
+
assert hasattr(json_views.get_config, '__wrapped__')
|
|
49
49
|
|
|
50
50
|
def test_set_theme_success(self) -> None:
|
|
51
51
|
request = self.factory.post(
|
|
@@ -55,20 +55,20 @@ class ThemeViewTests(TestCase):
|
|
|
55
55
|
)
|
|
56
56
|
response = json_views.set_theme(request)
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
assert isinstance(response, JsonResponse)
|
|
59
|
+
assert response.status_code == HTTPStatus.OK
|
|
60
60
|
|
|
61
61
|
data = json.loads(response.content)
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
assert data['success']
|
|
63
|
+
assert 'theme' in data
|
|
64
64
|
|
|
65
65
|
data = data['theme']
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
assert data['family'] == 'gruvbox'
|
|
67
|
+
assert data['mode'] == 'dark'
|
|
68
68
|
|
|
69
69
|
cookie_name = get_theme_cookie_name()
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
assert cookie_name in response.cookies
|
|
71
|
+
assert response.cookies[cookie_name].value == 'gruvbox-dark'
|
|
72
72
|
|
|
73
73
|
def test_set_theme_missing_theme(self) -> None:
|
|
74
74
|
request = self.factory.post(
|
|
@@ -78,11 +78,11 @@ class ThemeViewTests(TestCase):
|
|
|
78
78
|
)
|
|
79
79
|
response = json_views.set_theme(request)
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
assert response.status_code == HTTPStatus.BAD_REQUEST
|
|
82
82
|
data = json.loads(response.content)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
assert not data['success']
|
|
84
|
+
assert 'error' in data
|
|
85
|
+
assert data['error'] == 'Theme is required'
|
|
86
86
|
|
|
87
87
|
def test_set_theme_invalid_theme(self) -> None:
|
|
88
88
|
request = self.factory.post(
|
|
@@ -92,11 +92,11 @@ class ThemeViewTests(TestCase):
|
|
|
92
92
|
)
|
|
93
93
|
response = json_views.set_theme(request)
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
assert response.status_code == HTTPStatus.BAD_REQUEST
|
|
96
96
|
data = json.loads(response.content)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
assert not data['success']
|
|
98
|
+
assert 'error' in data
|
|
99
|
+
assert 'Invalid theme' in data['error']
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
class ThemeViewIntegrationTests(BaseTestCase):
|
|
@@ -107,14 +107,14 @@ class ThemeViewIntegrationTests(BaseTestCase):
|
|
|
107
107
|
content_type='application/json'
|
|
108
108
|
)
|
|
109
109
|
|
|
110
|
-
|
|
110
|
+
assert response.status_code == HTTPStatus.OK
|
|
111
111
|
data = json.loads(response.content)
|
|
112
|
-
|
|
112
|
+
assert data['success']
|
|
113
113
|
|
|
114
114
|
def test_get_config_with_authenticated_client(self) -> None:
|
|
115
115
|
response = self.client.get('/django_spire/theme/json/get_config/')
|
|
116
|
-
|
|
116
|
+
assert response.status_code == HTTPStatus.OK
|
|
117
117
|
|
|
118
118
|
data = json.loads(response.content)
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
assert data['success']
|
|
120
|
+
assert 'families' in data['data']
|
django_spire/urls.py
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.apps import apps
|
|
2
|
-
from django.urls import
|
|
4
|
+
from django.urls import include, path
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
from django_spire.exceptions import DjangoSpireConfigurationError
|
|
5
7
|
|
|
6
|
-
urlpatterns = []
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
)
|
|
17
|
-
),
|
|
9
|
+
app_name = 'django_spire'
|
|
10
|
+
|
|
11
|
+
urlpatterns = [
|
|
12
|
+
path(
|
|
13
|
+
f'{app_config.URLPATTERNS_NAMESPACE}/',
|
|
14
|
+
include(
|
|
15
|
+
app_config.URLPATTERNS_INCLUDE,
|
|
16
|
+
namespace=app_config.URLPATTERNS_NAMESPACE,
|
|
18
17
|
)
|
|
18
|
+
)
|
|
19
|
+
for app_config in apps.get_app_configs()
|
|
20
|
+
if hasattr(app_config, 'URLPATTERNS_INCLUDE') and hasattr(app_config, 'URLPATTERNS_NAMESPACE')
|
|
21
|
+
]
|
|
19
22
|
|
|
20
|
-
if
|
|
21
|
-
|
|
23
|
+
if not urlpatterns:
|
|
24
|
+
message = 'You need to have at least one app installed to use Django Spire.'
|
|
25
|
+
raise DjangoSpireConfigurationError(message)
|
django_spire/utils.py
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.apps import apps
|
|
2
4
|
|
|
5
|
+
from django_spire.exceptions import (
|
|
6
|
+
DjangoSpireInvalidClassStringError,
|
|
7
|
+
DjangoSpireMissingRequiredAppError
|
|
8
|
+
)
|
|
9
|
+
|
|
3
10
|
|
|
4
11
|
def app_is_installed(app_label: str) -> bool:
|
|
5
12
|
return app_label in list(apps.app_configs.keys())
|
|
@@ -7,17 +14,19 @@ def app_is_installed(app_label: str) -> bool:
|
|
|
7
14
|
|
|
8
15
|
def check_required_apps(app_label: str) -> None:
|
|
9
16
|
app_config = apps.get_app_config(app_label)
|
|
17
|
+
|
|
10
18
|
for required_app_name in app_config.REQUIRED_APPS:
|
|
11
19
|
if not app_is_installed(required_app_name):
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
message = f'{app_label} requires {required_app_name} is be in the "INSTALLED_APPS" list before {app_label} in the django settings module.'
|
|
21
|
+
raise DjangoSpireMissingRequiredAppError(message)
|
|
14
22
|
|
|
15
23
|
|
|
16
24
|
def get_class_from_string(class_string: str) -> type:
|
|
17
25
|
class_parts = class_string.split('.')
|
|
18
26
|
|
|
19
27
|
if len(class_parts) < 2:
|
|
20
|
-
|
|
28
|
+
message = f'Class string {class_string} is not a valid class string.'
|
|
29
|
+
raise DjangoSpireInvalidClassStringError(message)
|
|
21
30
|
|
|
22
31
|
module_path = '.'.join(class_parts[:-1])
|
|
23
32
|
class_name = class_parts[-1]
|
|
@@ -28,4 +37,4 @@ def get_class_from_string(class_string: str) -> type:
|
|
|
28
37
|
|
|
29
38
|
|
|
30
39
|
def get_class_name_from_class(cls: type) -> str:
|
|
31
|
-
return cls.__module__ + '.' + cls.__qualname__
|
|
40
|
+
return cls.__module__ + '.' + cls.__qualname__
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-spire
|
|
3
|
-
Version: 0.23.
|
|
3
|
+
Version: 0.23.8
|
|
4
4
|
Summary: A project for Django Spire
|
|
5
5
|
Author-email: Brayden Carlson <braydenc@stratusadv.com>, Nathan Johnson <nathanj@stratusadv.com>
|
|
6
6
|
License: Copyright (c) 2024 Stratus Advanced Technologies and Contributors.
|