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,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
|
|
5
|
+
from django.contrib.auth.models import User
|
|
6
|
+
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.test import TestCase
|
|
8
|
+
|
|
9
|
+
from django_spire.history.activity.models import Activity, ActivitySubscriber
|
|
10
|
+
from django_spire.history.activity.utils import add_form_activity
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestActivity(TestCase):
|
|
14
|
+
def setUp(self) -> None:
|
|
15
|
+
self.user = User.objects.create_user(
|
|
16
|
+
username='testuser',
|
|
17
|
+
password='testpass' # noqa: S106
|
|
18
|
+
)
|
|
19
|
+
self.recipient = User.objects.create_user(
|
|
20
|
+
username='recipient',
|
|
21
|
+
password='testpass' # noqa: S106
|
|
22
|
+
)
|
|
23
|
+
self.content_type = ContentType.objects.get_for_model(User)
|
|
24
|
+
|
|
25
|
+
def test_add_subscriber(self) -> None:
|
|
26
|
+
activity = Activity.objects.create(
|
|
27
|
+
content_type=self.content_type,
|
|
28
|
+
object_id=self.user.pk,
|
|
29
|
+
user=self.user,
|
|
30
|
+
verb='created',
|
|
31
|
+
information='Test information'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
subscriber = User.objects.create_user(
|
|
35
|
+
username='subscriber',
|
|
36
|
+
password='testpass' # noqa: S106
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
activity.add_subscriber(subscriber)
|
|
40
|
+
|
|
41
|
+
assert activity.subscribers.count() == 1
|
|
42
|
+
|
|
43
|
+
def test_create_activity(self) -> None:
|
|
44
|
+
activity = Activity.objects.create(
|
|
45
|
+
content_type=self.content_type,
|
|
46
|
+
object_id=self.user.pk,
|
|
47
|
+
user=self.user,
|
|
48
|
+
verb='created',
|
|
49
|
+
information='Test information'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
assert activity.pk is not None
|
|
53
|
+
assert activity.verb == 'created'
|
|
54
|
+
assert activity.information == 'Test information'
|
|
55
|
+
assert activity.content_object == self.user
|
|
56
|
+
|
|
57
|
+
def test_create_activity_with_recipient(self) -> None:
|
|
58
|
+
activity = Activity.objects.create(
|
|
59
|
+
content_type=self.content_type,
|
|
60
|
+
object_id=self.user.pk,
|
|
61
|
+
user=self.user,
|
|
62
|
+
recipient=self.recipient,
|
|
63
|
+
verb='assigned',
|
|
64
|
+
information='Assigned to recipient'
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
assert activity.recipient == self.recipient
|
|
68
|
+
|
|
69
|
+
def test_str_representation(self) -> None:
|
|
70
|
+
activity = Activity.objects.create(
|
|
71
|
+
content_type=self.content_type,
|
|
72
|
+
object_id=self.user.pk,
|
|
73
|
+
user=self.user,
|
|
74
|
+
verb='updated'
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
assert 'testuser' in str(activity)
|
|
78
|
+
assert 'updated' in str(activity)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestActivitySubscriber(TestCase):
|
|
82
|
+
def setUp(self) -> None:
|
|
83
|
+
self.user = User.objects.create_user(
|
|
84
|
+
username='testuser',
|
|
85
|
+
password='testpass' # noqa: S106
|
|
86
|
+
)
|
|
87
|
+
self.subscriber = User.objects.create_user(
|
|
88
|
+
username='subscriber',
|
|
89
|
+
password='testpass' # noqa: S106
|
|
90
|
+
)
|
|
91
|
+
self.content_type = ContentType.objects.get_for_model(User)
|
|
92
|
+
self.activity = Activity.objects.create(
|
|
93
|
+
content_type=self.content_type,
|
|
94
|
+
object_id=self.user.pk,
|
|
95
|
+
user=self.user,
|
|
96
|
+
verb='created'
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def test_create_activity_subscriber(self) -> None:
|
|
100
|
+
activity_subscriber = ActivitySubscriber.objects.create(
|
|
101
|
+
activity=self.activity,
|
|
102
|
+
subscriber=self.subscriber
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
assert activity_subscriber.pk is not None
|
|
106
|
+
assert activity_subscriber.activity == self.activity
|
|
107
|
+
assert activity_subscriber.subscriber == self.subscriber
|
|
108
|
+
|
|
109
|
+
def test_str_representation(self) -> None:
|
|
110
|
+
activity_subscriber = ActivitySubscriber.objects.create(
|
|
111
|
+
activity=self.activity,
|
|
112
|
+
subscriber=self.subscriber
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
assert str(self.activity) in str(activity_subscriber)
|
|
116
|
+
assert 'subscriber' in str(activity_subscriber)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class TestActivityQuerySet(TestCase):
|
|
120
|
+
def setUp(self) -> None:
|
|
121
|
+
self.user = User.objects.create_user(
|
|
122
|
+
username='testuser',
|
|
123
|
+
password='testpass' # noqa: S106
|
|
124
|
+
)
|
|
125
|
+
self.content_type = ContentType.objects.get_for_model(User)
|
|
126
|
+
|
|
127
|
+
def test_prefetch_user(self) -> None:
|
|
128
|
+
Activity.objects.create(
|
|
129
|
+
content_type=self.content_type,
|
|
130
|
+
object_id=self.user.pk,
|
|
131
|
+
user=self.user,
|
|
132
|
+
verb='created'
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
activities = Activity.objects.prefetch_user()
|
|
136
|
+
|
|
137
|
+
assert activities.count() == 1
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestAddFormActivity(TestCase):
|
|
141
|
+
def setUp(self) -> None:
|
|
142
|
+
self.user = User.objects.create_user(
|
|
143
|
+
username='testuser',
|
|
144
|
+
password='testpass', # noqa: S106
|
|
145
|
+
first_name='Test',
|
|
146
|
+
last_name='User'
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def test_add_form_activity_created(self) -> None:
|
|
150
|
+
model_object = MagicMock()
|
|
151
|
+
model_object._meta.verbose_name = 'Test Model'
|
|
152
|
+
model_object.__str__ = MagicMock(return_value='Test Object')
|
|
153
|
+
model_object.add_activity = MagicMock()
|
|
154
|
+
|
|
155
|
+
add_form_activity(model_object, pk=None, user=self.user)
|
|
156
|
+
|
|
157
|
+
model_object.add_activity.assert_called_once()
|
|
158
|
+
call_kwargs = model_object.add_activity.call_args[1]
|
|
159
|
+
|
|
160
|
+
assert call_kwargs['user'] == self.user
|
|
161
|
+
assert call_kwargs['verb'] == 'updated'
|
|
162
|
+
assert 'updated' in call_kwargs['information']
|
|
163
|
+
|
|
164
|
+
def test_add_form_activity_updated(self) -> None:
|
|
165
|
+
model_object = MagicMock()
|
|
166
|
+
model_object._meta.verbose_name = 'Test Model'
|
|
167
|
+
model_object.__str__ = MagicMock(return_value='Test Object')
|
|
168
|
+
model_object.add_activity = MagicMock()
|
|
169
|
+
|
|
170
|
+
add_form_activity(model_object, pk=1, user=self.user)
|
|
171
|
+
|
|
172
|
+
model_object.add_activity.assert_called_once()
|
|
173
|
+
call_kwargs = model_object.add_activity.call_args[1]
|
|
174
|
+
|
|
175
|
+
assert call_kwargs['verb'] == 'created'
|
|
176
|
+
assert 'created' in call_kwargs['information']
|
django_spire/history/admin.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.contrib import admin
|
|
2
4
|
from django.urls import reverse
|
|
3
5
|
from django.utils.html import format_html
|
|
@@ -8,10 +10,15 @@ from django_spire.history import models
|
|
|
8
10
|
@admin.register(models.HistoryEvent)
|
|
9
11
|
class HistoryEventAdmin(admin.ModelAdmin):
|
|
10
12
|
list_display = (
|
|
11
|
-
'id',
|
|
13
|
+
'id',
|
|
14
|
+
'content_object_link',
|
|
15
|
+
'content_type',
|
|
16
|
+
'created_datetime',
|
|
17
|
+
'event_verbose'
|
|
12
18
|
)
|
|
19
|
+
|
|
13
20
|
list_filter = ('event', 'created_datetime')
|
|
14
|
-
search_fields = ('id', 'content_type__model'
|
|
21
|
+
search_fields = ('id', 'content_type__model')
|
|
15
22
|
ordering = ('-created_datetime',)
|
|
16
23
|
|
|
17
24
|
def content_object_link(self, history_event: models.HistoryEvent) -> str:
|
django_spire/history/choices.py
CHANGED
django_spire/history/models.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from django.contrib.auth.models import User
|
|
4
3
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
5
4
|
from django.contrib.contenttypes.models import ContentType
|
|
6
5
|
from django.db import models
|
|
@@ -11,12 +10,13 @@ from django_spire.history.choices import HistoryEventChoices
|
|
|
11
10
|
|
|
12
11
|
class HistoryEvent(models.Model):
|
|
13
12
|
content_type = models.ForeignKey(
|
|
14
|
-
ContentType,
|
|
15
|
-
related_name='history_events',
|
|
16
|
-
related_query_name='history_event',
|
|
17
|
-
on_delete=models.CASCADE,
|
|
13
|
+
ContentType,
|
|
14
|
+
related_name='history_events',
|
|
15
|
+
related_query_name='history_event',
|
|
16
|
+
on_delete=models.CASCADE,
|
|
18
17
|
editable=False
|
|
19
18
|
)
|
|
19
|
+
|
|
20
20
|
object_id = models.PositiveIntegerField(editable=False)
|
|
21
21
|
content_object = GenericForeignKey('content_type', 'object_id')
|
|
22
22
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock
|
|
4
|
+
|
|
5
|
+
from django.contrib.admin.sites import AdminSite
|
|
6
|
+
from django.test import TestCase
|
|
7
|
+
|
|
8
|
+
from django_spire.history.activity.admin import ActivityAdmin
|
|
9
|
+
from django_spire.history.activity.models import Activity
|
|
10
|
+
from django_spire.history.admin import HistoryEventAdmin
|
|
11
|
+
from django_spire.history.models import HistoryEvent
|
|
12
|
+
from django_spire.history.viewed.admin import ViewAdmin
|
|
13
|
+
from django_spire.history.viewed.models import Viewed
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestActivityAdmin(TestCase):
|
|
17
|
+
def setUp(self) -> None:
|
|
18
|
+
self.site = AdminSite()
|
|
19
|
+
self.admin = ActivityAdmin(Activity, self.site)
|
|
20
|
+
|
|
21
|
+
def test_information_snippet_long(self) -> None:
|
|
22
|
+
activity = MagicMock()
|
|
23
|
+
activity.information = 'This is a very long information string that exceeds twenty characters'
|
|
24
|
+
|
|
25
|
+
result = self.admin.information_snippet(activity)
|
|
26
|
+
|
|
27
|
+
assert result.endswith('...')
|
|
28
|
+
assert len(result) == 23
|
|
29
|
+
|
|
30
|
+
def test_information_snippet_none(self) -> None:
|
|
31
|
+
activity = MagicMock()
|
|
32
|
+
activity.information = None
|
|
33
|
+
|
|
34
|
+
result = self.admin.information_snippet(activity)
|
|
35
|
+
|
|
36
|
+
assert result == 'No Information'
|
|
37
|
+
|
|
38
|
+
def test_information_snippet_short(self) -> None:
|
|
39
|
+
activity = MagicMock()
|
|
40
|
+
activity.information = 'Short info'
|
|
41
|
+
|
|
42
|
+
result = self.admin.information_snippet(activity)
|
|
43
|
+
|
|
44
|
+
assert result == 'Short info'
|
|
45
|
+
|
|
46
|
+
def test_list_display(self) -> None:
|
|
47
|
+
assert 'id' in self.admin.list_display
|
|
48
|
+
assert 'content_object_link' in self.admin.list_display
|
|
49
|
+
assert 'verb' in self.admin.list_display
|
|
50
|
+
assert 'user_link' in self.admin.list_display
|
|
51
|
+
|
|
52
|
+
def test_list_filter(self) -> None:
|
|
53
|
+
assert 'verb' in self.admin.list_filter
|
|
54
|
+
assert 'created_datetime' in self.admin.list_filter
|
|
55
|
+
|
|
56
|
+
def test_recipient_link_no_recipient(self) -> None:
|
|
57
|
+
activity = MagicMock()
|
|
58
|
+
activity.recipient = None
|
|
59
|
+
|
|
60
|
+
result = self.admin.recipient_link(activity)
|
|
61
|
+
|
|
62
|
+
assert result == 'No Recipient'
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TestHistoryEventAdmin(TestCase):
|
|
66
|
+
def setUp(self) -> None:
|
|
67
|
+
self.site = AdminSite()
|
|
68
|
+
self.admin = HistoryEventAdmin(HistoryEvent, self.site)
|
|
69
|
+
|
|
70
|
+
def test_list_display(self) -> None:
|
|
71
|
+
assert 'id' in self.admin.list_display
|
|
72
|
+
assert 'content_object_link' in self.admin.list_display
|
|
73
|
+
assert 'content_type' in self.admin.list_display
|
|
74
|
+
assert 'event_verbose' in self.admin.list_display
|
|
75
|
+
|
|
76
|
+
def test_list_filter(self) -> None:
|
|
77
|
+
assert 'event' in self.admin.list_filter
|
|
78
|
+
assert 'created_datetime' in self.admin.list_filter
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestViewedAdmin(TestCase):
|
|
82
|
+
def setUp(self) -> None:
|
|
83
|
+
self.site = AdminSite()
|
|
84
|
+
self.admin = ViewAdmin(Viewed, self.site)
|
|
85
|
+
|
|
86
|
+
def test_list_display(self) -> None:
|
|
87
|
+
assert 'id' in self.admin.list_display
|
|
88
|
+
assert 'content_object_link' in self.admin.list_display
|
|
89
|
+
assert 'user_link' in self.admin.list_display
|
|
90
|
+
assert 'created_datetime' in self.admin.list_display
|
|
91
|
+
|
|
92
|
+
def test_list_filter(self) -> None:
|
|
93
|
+
assert 'created_datetime' in self.admin.list_filter
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth.models import User
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.test import TestCase
|
|
6
|
+
|
|
7
|
+
from django_spire.history.choices import HistoryEventChoices
|
|
8
|
+
from django_spire.history.models import HistoryEvent
|
|
9
|
+
from django_spire.history.querysets import HistoryQuerySet
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestHistoryEventChoices(TestCase):
|
|
13
|
+
def test_active_choice(self) -> None:
|
|
14
|
+
assert HistoryEventChoices.ACTIVE.value == 'acti'
|
|
15
|
+
assert HistoryEventChoices.ACTIVE.label == 'Active'
|
|
16
|
+
|
|
17
|
+
def test_all_choices_exist(self) -> None:
|
|
18
|
+
choices = [c[0] for c in HistoryEventChoices.choices]
|
|
19
|
+
|
|
20
|
+
assert 'crea' in choices
|
|
21
|
+
assert 'upda' in choices
|
|
22
|
+
assert 'acti' in choices
|
|
23
|
+
assert 'inac' in choices
|
|
24
|
+
assert 'dele' in choices
|
|
25
|
+
assert 'unde' in choices
|
|
26
|
+
|
|
27
|
+
def test_created_choice(self) -> None:
|
|
28
|
+
assert HistoryEventChoices.CREATED.value == 'crea'
|
|
29
|
+
assert HistoryEventChoices.CREATED.label == 'Created'
|
|
30
|
+
|
|
31
|
+
def test_deleted_choice(self) -> None:
|
|
32
|
+
assert HistoryEventChoices.DELETED.value == 'dele'
|
|
33
|
+
assert HistoryEventChoices.DELETED.label == 'Deleted'
|
|
34
|
+
|
|
35
|
+
def test_inactive_choice(self) -> None:
|
|
36
|
+
assert HistoryEventChoices.INACTIVE.value == 'inac'
|
|
37
|
+
assert HistoryEventChoices.INACTIVE.label == 'Inactive'
|
|
38
|
+
|
|
39
|
+
def test_undeleted_choice(self) -> None:
|
|
40
|
+
assert HistoryEventChoices.UNDELETED.value == 'unde'
|
|
41
|
+
assert HistoryEventChoices.UNDELETED.label == 'Un-Deleted'
|
|
42
|
+
|
|
43
|
+
def test_updated_choice(self) -> None:
|
|
44
|
+
assert HistoryEventChoices.UPDATED.value == 'upda'
|
|
45
|
+
assert HistoryEventChoices.UPDATED.label == 'Updated'
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestHistoryEvent(TestCase):
|
|
49
|
+
def setUp(self) -> None:
|
|
50
|
+
self.user = User.objects.create_user(
|
|
51
|
+
username='testuser',
|
|
52
|
+
password='testpass' # noqa: S106
|
|
53
|
+
)
|
|
54
|
+
self.content_type = ContentType.objects.get_for_model(User)
|
|
55
|
+
|
|
56
|
+
def test_create_history_event(self) -> None:
|
|
57
|
+
event = HistoryEvent.objects.create(
|
|
58
|
+
content_type=self.content_type,
|
|
59
|
+
object_id=self.user.pk,
|
|
60
|
+
event=HistoryEventChoices.CREATED
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
assert event.pk is not None
|
|
64
|
+
assert event.event == 'crea'
|
|
65
|
+
assert event.content_object == self.user
|
|
66
|
+
|
|
67
|
+
def test_event_verbose_property(self) -> None:
|
|
68
|
+
event = HistoryEvent.objects.create(
|
|
69
|
+
content_type=self.content_type,
|
|
70
|
+
object_id=self.user.pk,
|
|
71
|
+
event=HistoryEventChoices.UPDATED
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
assert event.event_verbose == 'Updated'
|
|
75
|
+
|
|
76
|
+
def test_str_representation(self) -> None:
|
|
77
|
+
event = HistoryEvent.objects.create(
|
|
78
|
+
content_type=self.content_type,
|
|
79
|
+
object_id=self.user.pk,
|
|
80
|
+
event=HistoryEventChoices.CREATED
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
assert 'Created' in str(event)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TestHistoryQuerySet(TestCase):
|
|
87
|
+
def test_active_filter(self) -> None:
|
|
88
|
+
# HistoryQuerySet is meant to be used with models that have
|
|
89
|
+
# is_active and is_deleted fields (via HistoryModelMixin)
|
|
90
|
+
qs = HistoryQuerySet(model=HistoryEvent)
|
|
91
|
+
|
|
92
|
+
# Verify the method exists and returns a QuerySet
|
|
93
|
+
assert hasattr(qs, 'active')
|
|
94
|
+
|
|
95
|
+
def test_deleted_filter(self) -> None:
|
|
96
|
+
qs = HistoryQuerySet(model=HistoryEvent)
|
|
97
|
+
assert hasattr(qs, 'deleted')
|
|
98
|
+
|
|
99
|
+
def test_inactive_filter(self) -> None:
|
|
100
|
+
qs = HistoryQuerySet(model=HistoryEvent)
|
|
101
|
+
assert hasattr(qs, 'inactive')
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.contenttypes.fields import GenericRelation
|
|
4
|
+
from django.test import TestCase
|
|
5
|
+
|
|
6
|
+
from django_spire.history.activity.mixins import ActivityMixin
|
|
7
|
+
from django_spire.history.mixins import HistoryModelMixin
|
|
8
|
+
from django_spire.history.viewed.mixins import ViewedModelMixin
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestActivityMixin(TestCase):
|
|
12
|
+
def test_activities_field_exists(self) -> None:
|
|
13
|
+
assert hasattr(ActivityMixin, 'activities')
|
|
14
|
+
|
|
15
|
+
def test_activities_is_generic_relation(self) -> None:
|
|
16
|
+
field = ActivityMixin._meta.get_field('activities')
|
|
17
|
+
assert isinstance(field, GenericRelation)
|
|
18
|
+
|
|
19
|
+
def test_add_activity_method_exists(self) -> None:
|
|
20
|
+
assert hasattr(ActivityMixin, 'add_activity')
|
|
21
|
+
assert callable(ActivityMixin.add_activity)
|
|
22
|
+
|
|
23
|
+
def test_creator_property_exists(self) -> None:
|
|
24
|
+
assert hasattr(ActivityMixin, 'creator')
|
|
25
|
+
|
|
26
|
+
def test_is_abstract(self) -> None:
|
|
27
|
+
assert ActivityMixin._meta.abstract is True
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestHistoryModelMixin(TestCase):
|
|
31
|
+
def test_created_datetime_field_exists(self) -> None:
|
|
32
|
+
assert hasattr(HistoryModelMixin, 'created_datetime')
|
|
33
|
+
|
|
34
|
+
def test_history_events_field_exists(self) -> None:
|
|
35
|
+
assert hasattr(HistoryModelMixin, 'history_events')
|
|
36
|
+
|
|
37
|
+
def test_history_events_is_generic_relation(self) -> None:
|
|
38
|
+
field = HistoryModelMixin._meta.get_field('history_events')
|
|
39
|
+
assert isinstance(field, GenericRelation)
|
|
40
|
+
|
|
41
|
+
def test_is_abstract(self) -> None:
|
|
42
|
+
assert HistoryModelMixin._meta.abstract is True
|
|
43
|
+
|
|
44
|
+
def test_is_active_field_exists(self) -> None:
|
|
45
|
+
assert hasattr(HistoryModelMixin, 'is_active')
|
|
46
|
+
|
|
47
|
+
def test_is_deleted_field_exists(self) -> None:
|
|
48
|
+
assert hasattr(HistoryModelMixin, 'is_deleted')
|
|
49
|
+
|
|
50
|
+
def test_set_active_method_exists(self) -> None:
|
|
51
|
+
assert hasattr(HistoryModelMixin, 'set_active')
|
|
52
|
+
assert callable(HistoryModelMixin.set_active)
|
|
53
|
+
|
|
54
|
+
def test_set_deleted_method_exists(self) -> None:
|
|
55
|
+
assert hasattr(HistoryModelMixin, 'set_deleted')
|
|
56
|
+
assert callable(HistoryModelMixin.set_deleted)
|
|
57
|
+
|
|
58
|
+
def test_set_inactive_method_exists(self) -> None:
|
|
59
|
+
assert hasattr(HistoryModelMixin, 'set_inactive')
|
|
60
|
+
assert callable(HistoryModelMixin.set_inactive)
|
|
61
|
+
|
|
62
|
+
def test_un_set_deleted_method_exists(self) -> None:
|
|
63
|
+
assert hasattr(HistoryModelMixin, 'un_set_deleted')
|
|
64
|
+
assert callable(HistoryModelMixin.un_set_deleted)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TestViewedModelMixin(TestCase):
|
|
68
|
+
def test_add_view_method_exists(self) -> None:
|
|
69
|
+
assert hasattr(ViewedModelMixin, 'add_view')
|
|
70
|
+
assert callable(ViewedModelMixin.add_view)
|
|
71
|
+
|
|
72
|
+
def test_is_abstract(self) -> None:
|
|
73
|
+
assert ViewedModelMixin._meta.abstract is True
|
|
74
|
+
|
|
75
|
+
def test_is_viewed_method_exists(self) -> None:
|
|
76
|
+
assert hasattr(ViewedModelMixin, 'is_viewed')
|
|
77
|
+
assert callable(ViewedModelMixin.is_viewed)
|
|
78
|
+
|
|
79
|
+
def test_views_field_exists(self) -> None:
|
|
80
|
+
assert hasattr(ViewedModelMixin, 'views')
|
|
81
|
+
|
|
82
|
+
def test_views_is_generic_relation(self) -> None:
|
|
83
|
+
field = ViewedModelMixin._meta.get_field('views')
|
|
84
|
+
assert isinstance(field, GenericRelation)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.contrib import admin
|
|
2
4
|
from django.urls.base import reverse
|
|
3
5
|
from django.utils.html import format_html
|
|
@@ -11,7 +13,7 @@ class ViewAdmin(admin.ModelAdmin):
|
|
|
11
13
|
'id', 'content_object_link', 'user_link', 'created_datetime'
|
|
12
14
|
)
|
|
13
15
|
list_filter = ('created_datetime',)
|
|
14
|
-
search_fields = ('id', 'user__first_name', 'user__last_name', 'content_type__model'
|
|
16
|
+
search_fields = ('id', 'user__first_name', 'user__last_name', 'content_type__model')
|
|
15
17
|
ordering = ('-created_datetime',)
|
|
16
18
|
|
|
17
19
|
def content_object_link(self, view: Viewed) -> str:
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.apps import AppConfig
|
|
2
4
|
|
|
3
5
|
from django_spire.utils import check_required_apps
|
|
@@ -11,4 +13,4 @@ class HistoryViewedConfig(AppConfig):
|
|
|
11
13
|
REQUIRED_APPS = ('django_spire_core', 'django_spire_history')
|
|
12
14
|
|
|
13
15
|
def ready(self) -> None:
|
|
14
|
-
check_required_apps(self.label)
|
|
16
|
+
check_required_apps(self.label)
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth.models import User
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.test import TestCase
|
|
6
|
+
|
|
7
|
+
from django_spire.history.viewed.models import Viewed
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestViewed(TestCase):
|
|
11
|
+
def setUp(self) -> None:
|
|
12
|
+
self.user = User.objects.create_user(
|
|
13
|
+
username='testuser',
|
|
14
|
+
password='testpass' # noqa: S106
|
|
15
|
+
)
|
|
16
|
+
self.content_type = ContentType.objects.get_for_model(User)
|
|
17
|
+
|
|
18
|
+
def test_create_viewed(self) -> None:
|
|
19
|
+
viewed = Viewed.objects.create(
|
|
20
|
+
content_type=self.content_type,
|
|
21
|
+
object_id=self.user.pk,
|
|
22
|
+
user=self.user
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
assert viewed.pk is not None
|
|
26
|
+
assert viewed.user == self.user
|
|
27
|
+
assert viewed.content_object == self.user
|
|
28
|
+
|
|
29
|
+
def test_created_datetime_auto_set(self) -> None:
|
|
30
|
+
viewed = Viewed.objects.create(
|
|
31
|
+
content_type=self.content_type,
|
|
32
|
+
object_id=self.user.pk,
|
|
33
|
+
user=self.user
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
assert viewed.created_datetime is not None
|
|
37
|
+
|
|
38
|
+
def test_str_representation(self) -> None:
|
|
39
|
+
viewed = Viewed.objects.create(
|
|
40
|
+
content_type=self.content_type,
|
|
41
|
+
object_id=self.user.pk,
|
|
42
|
+
user=self.user
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
assert 'testuser' in str(viewed)
|
|
46
|
+
assert 'viewed' in str(viewed)
|
|
File without changes
|