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,27 +1,32 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
3
5
|
from django.conf import settings
|
|
4
6
|
from django.contrib.auth import logout
|
|
5
7
|
from django.contrib.auth.decorators import login_required
|
|
6
8
|
from django.http import HttpResponseRedirect
|
|
7
9
|
from django.urls import reverse
|
|
8
10
|
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
@login_required()
|
|
11
|
-
def login_redirect_view(request, **kwargs):
|
|
16
|
+
def login_redirect_view(request: WSGIRequest, **kwargs):
|
|
12
17
|
if not request.user.is_authenticated:
|
|
13
18
|
return HttpResponseRedirect(reverse('django_spire:auth:admin:login'))
|
|
14
|
-
else:
|
|
15
|
-
# MFA implementation if required
|
|
16
|
-
# profile = request.user.profile
|
|
17
|
-
#
|
|
18
|
-
# if profile.requires_mfa():
|
|
19
|
-
# return HttpResponseRedirect(reverse('django_spire:auth:mfa:redirect:notification'))
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
# MFA implementation if required
|
|
21
|
+
# profile = request.user.profile
|
|
22
|
+
#
|
|
23
|
+
# if profile.requires_mfa():
|
|
24
|
+
# return HttpResponseRedirect(reverse('django_spire:auth:mfa:redirect:notification'))
|
|
25
|
+
|
|
26
|
+
return HttpResponseRedirect(reverse(settings.LOGIN_REDIRECT_SUCCESS_URL))
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
@login_required()
|
|
25
|
-
def logout_redirect_view(request, **kwargs):
|
|
30
|
+
def logout_redirect_view(request: WSGIRequest, **kwargs):
|
|
26
31
|
logout(request)
|
|
27
32
|
return HttpResponseRedirect(reverse('django_spire:auth:admin:login'))
|
django_spire/comment/admin.py
CHANGED
django_spire/comment/apps.py
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.contenttypes.models import ContentType
|
|
4
|
+
|
|
5
|
+
from django_spire.comment.forms import CommentForm
|
|
6
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestCommentForm(BaseTestCase):
|
|
10
|
+
def setUp(self) -> None:
|
|
11
|
+
super().setUp()
|
|
12
|
+
|
|
13
|
+
self.content_type = ContentType.objects.get_for_model(self.super_user)
|
|
14
|
+
|
|
15
|
+
def test_valid_form(self) -> None:
|
|
16
|
+
form = CommentForm(data={'information': 'Test comment'})
|
|
17
|
+
assert form.is_valid() is True
|
|
18
|
+
|
|
19
|
+
def test_form_save(self) -> None:
|
|
20
|
+
form = CommentForm(data={'information': 'New comment'})
|
|
21
|
+
assert form.is_valid() is True
|
|
22
|
+
comment = form.save(commit=False)
|
|
23
|
+
comment.content_type = self.content_type
|
|
24
|
+
comment.object_id = self.super_user.pk
|
|
25
|
+
comment.user = self.super_user
|
|
26
|
+
comment.save()
|
|
27
|
+
assert comment.information == 'New comment'
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.contenttypes.models import ContentType
|
|
4
|
+
|
|
5
|
+
from django_spire.comment.models import Comment
|
|
6
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestCommentModel(BaseTestCase):
|
|
10
|
+
def setUp(self) -> None:
|
|
11
|
+
super().setUp()
|
|
12
|
+
|
|
13
|
+
self.content_type = ContentType.objects.get_for_model(self.super_user)
|
|
14
|
+
self.comment = Comment.objects.create(
|
|
15
|
+
content_type=self.content_type,
|
|
16
|
+
object_id=self.super_user.pk,
|
|
17
|
+
user=self.super_user,
|
|
18
|
+
information='This is a test comment.'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def test_str(self) -> None:
|
|
22
|
+
assert str(self.comment) == 'This is a ...'
|
|
23
|
+
|
|
24
|
+
def test_str_short_information(self) -> None:
|
|
25
|
+
comment = Comment.objects.create(
|
|
26
|
+
content_type=self.content_type,
|
|
27
|
+
object_id=self.super_user.pk,
|
|
28
|
+
user=self.super_user,
|
|
29
|
+
information='Short'
|
|
30
|
+
)
|
|
31
|
+
assert str(comment) == 'Short...'
|
|
32
|
+
|
|
33
|
+
def test_scrape_username_list(self) -> None:
|
|
34
|
+
comment = Comment.objects.create(
|
|
35
|
+
content_type=self.content_type,
|
|
36
|
+
object_id=self.super_user.pk,
|
|
37
|
+
user=self.super_user,
|
|
38
|
+
information='Hello @john_doe and @jane_doe'
|
|
39
|
+
)
|
|
40
|
+
usernames = comment.scrape_username_list()
|
|
41
|
+
assert usernames == ['john_doe', 'jane_doe']
|
|
42
|
+
|
|
43
|
+
def test_scrape_username_list_empty(self) -> None:
|
|
44
|
+
usernames = self.comment.scrape_username_list()
|
|
45
|
+
assert usernames == []
|
|
46
|
+
|
|
47
|
+
def test_scrape_username_list_single(self) -> None:
|
|
48
|
+
comment = Comment.objects.create(
|
|
49
|
+
content_type=self.content_type,
|
|
50
|
+
object_id=self.super_user.pk,
|
|
51
|
+
user=self.super_user,
|
|
52
|
+
information='Hello @stratus'
|
|
53
|
+
)
|
|
54
|
+
usernames = comment.scrape_username_list()
|
|
55
|
+
assert usernames == ['stratus']
|
|
56
|
+
|
|
57
|
+
def test_find_user_list(self) -> None:
|
|
58
|
+
comment = Comment.objects.create(
|
|
59
|
+
content_type=self.content_type,
|
|
60
|
+
object_id=self.super_user.pk,
|
|
61
|
+
user=self.super_user,
|
|
62
|
+
information='Hello @stratus'
|
|
63
|
+
)
|
|
64
|
+
users = comment.find_user_list()
|
|
65
|
+
assert self.super_user in users
|
|
66
|
+
|
|
67
|
+
def test_find_user_list_empty(self) -> None:
|
|
68
|
+
users = self.comment.find_user_list()
|
|
69
|
+
assert users.count() == 0
|
|
70
|
+
|
|
71
|
+
def test_update(self) -> None:
|
|
72
|
+
self.comment.update('Updated information')
|
|
73
|
+
self.comment.refresh_from_db()
|
|
74
|
+
assert self.comment.information == 'Updated information'
|
|
75
|
+
assert self.comment.is_edited is True
|
|
76
|
+
|
|
77
|
+
def test_update_sets_is_edited(self) -> None:
|
|
78
|
+
assert self.comment.is_edited is False
|
|
79
|
+
self.comment.update('New content')
|
|
80
|
+
assert self.comment.is_edited is True
|
|
81
|
+
|
|
82
|
+
def test_parent_relationship(self) -> None:
|
|
83
|
+
parent_comment = Comment.objects.create(
|
|
84
|
+
content_type=self.content_type,
|
|
85
|
+
object_id=self.super_user.pk,
|
|
86
|
+
user=self.super_user,
|
|
87
|
+
information='Parent comment'
|
|
88
|
+
)
|
|
89
|
+
child_comment = Comment.objects.create(
|
|
90
|
+
content_type=self.content_type,
|
|
91
|
+
object_id=self.super_user.pk,
|
|
92
|
+
user=self.super_user,
|
|
93
|
+
parent=parent_comment,
|
|
94
|
+
information='Child comment'
|
|
95
|
+
)
|
|
96
|
+
assert child_comment.parent == parent_comment
|
|
97
|
+
assert child_comment in parent_comment.children.all()
|
|
98
|
+
|
|
99
|
+
def test_default_ordering(self) -> None:
|
|
100
|
+
comment1 = Comment.objects.create(
|
|
101
|
+
content_type=self.content_type,
|
|
102
|
+
object_id=self.super_user.pk,
|
|
103
|
+
user=self.super_user,
|
|
104
|
+
information='First'
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
comment2 = Comment.objects.create(
|
|
108
|
+
content_type=self.content_type,
|
|
109
|
+
object_id=self.super_user.pk,
|
|
110
|
+
user=self.super_user,
|
|
111
|
+
information='Second'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
comments = list(Comment.objects.order_by('-pk')[:2])
|
|
115
|
+
assert comments == [comment2, comment1]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class TestCommentRelationships(BaseTestCase):
|
|
119
|
+
def setUp(self) -> None:
|
|
120
|
+
super().setUp()
|
|
121
|
+
|
|
122
|
+
self.content_type = ContentType.objects.get_for_model(self.super_user)
|
|
123
|
+
self.comment = Comment.objects.create(
|
|
124
|
+
content_type=self.content_type,
|
|
125
|
+
object_id=self.super_user.pk,
|
|
126
|
+
user=self.super_user,
|
|
127
|
+
information='Test comment'
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def test_comment_user_relationship(self) -> None:
|
|
131
|
+
assert self.comment.user == self.super_user
|
|
132
|
+
assert self.comment in self.super_user.comment_list.all()
|
|
133
|
+
|
|
134
|
+
def test_comment_content_object(self) -> None:
|
|
135
|
+
assert self.comment.content_object == self.super_user
|
|
136
|
+
assert self.comment.object_id == self.super_user.pk
|
|
137
|
+
|
|
138
|
+
def test_nested_replies(self) -> None:
|
|
139
|
+
parent_comment = Comment.objects.create(
|
|
140
|
+
content_type=self.content_type,
|
|
141
|
+
object_id=self.super_user.pk,
|
|
142
|
+
user=self.super_user,
|
|
143
|
+
information='Parent'
|
|
144
|
+
)
|
|
145
|
+
child1 = Comment.objects.create(
|
|
146
|
+
content_type=self.content_type,
|
|
147
|
+
object_id=self.super_user.pk,
|
|
148
|
+
user=self.super_user,
|
|
149
|
+
parent=parent_comment,
|
|
150
|
+
information='Child 1'
|
|
151
|
+
)
|
|
152
|
+
child2 = Comment.objects.create(
|
|
153
|
+
content_type=self.content_type,
|
|
154
|
+
object_id=self.super_user.pk,
|
|
155
|
+
user=self.super_user,
|
|
156
|
+
parent=parent_comment,
|
|
157
|
+
information='Child 2'
|
|
158
|
+
)
|
|
159
|
+
assert parent_comment.children.count() == 2
|
|
160
|
+
assert child1 in parent_comment.children.all()
|
|
161
|
+
assert child2 in parent_comment.children.all()
|
|
162
|
+
|
|
163
|
+
def test_cascade_delete_parent(self) -> None:
|
|
164
|
+
parent_comment = Comment.objects.create(
|
|
165
|
+
content_type=self.content_type,
|
|
166
|
+
object_id=self.super_user.pk,
|
|
167
|
+
user=self.super_user,
|
|
168
|
+
information='Parent'
|
|
169
|
+
)
|
|
170
|
+
child_comment = Comment.objects.create(
|
|
171
|
+
content_type=self.content_type,
|
|
172
|
+
object_id=self.super_user.pk,
|
|
173
|
+
user=self.super_user,
|
|
174
|
+
parent=parent_comment,
|
|
175
|
+
information='Child'
|
|
176
|
+
)
|
|
177
|
+
child_pk = child_comment.pk
|
|
178
|
+
parent_comment.delete()
|
|
179
|
+
assert Comment.objects.filter(pk=child_pk).exists() is False
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class TestCommentDefaults(BaseTestCase):
|
|
183
|
+
def setUp(self) -> None:
|
|
184
|
+
super().setUp()
|
|
185
|
+
|
|
186
|
+
self.content_type = ContentType.objects.get_for_model(self.super_user)
|
|
187
|
+
self.comment = Comment.objects.create(
|
|
188
|
+
content_type=self.content_type,
|
|
189
|
+
object_id=self.super_user.pk,
|
|
190
|
+
user=self.super_user,
|
|
191
|
+
information='Test comment'
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def test_default_is_active(self) -> None:
|
|
195
|
+
assert self.comment.is_active is True
|
|
196
|
+
|
|
197
|
+
def test_default_is_deleted(self) -> None:
|
|
198
|
+
assert self.comment.is_deleted is False
|
|
199
|
+
|
|
200
|
+
def test_default_is_edited(self) -> None:
|
|
201
|
+
assert self.comment.is_edited is False
|
|
202
|
+
|
|
203
|
+
def test_default_information(self) -> None:
|
|
204
|
+
comment = Comment.objects.create(
|
|
205
|
+
content_type=self.content_type,
|
|
206
|
+
object_id=self.super_user.pk,
|
|
207
|
+
user=self.super_user
|
|
208
|
+
)
|
|
209
|
+
assert comment.information == ''
|
|
210
|
+
|
|
211
|
+
def test_created_datetime_auto_set(self) -> None:
|
|
212
|
+
assert self.comment.created_datetime is not None
|
|
213
|
+
|
|
214
|
+
def test_parent_default_null(self) -> None:
|
|
215
|
+
assert self.comment.parent is None
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.contenttypes.models import ContentType
|
|
4
|
+
|
|
5
|
+
from django_spire.comment.models import Comment
|
|
6
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestCommentQuerySet(BaseTestCase):
|
|
10
|
+
def setUp(self) -> None:
|
|
11
|
+
super().setUp()
|
|
12
|
+
|
|
13
|
+
self.content_type = ContentType.objects.get_for_model(self.super_user)
|
|
14
|
+
self.comment = Comment.objects.create(
|
|
15
|
+
content_type=self.content_type,
|
|
16
|
+
object_id=self.super_user.pk,
|
|
17
|
+
user=self.super_user,
|
|
18
|
+
information='Test comment'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def test_active(self) -> None:
|
|
22
|
+
comments = Comment.objects.active()
|
|
23
|
+
assert self.comment in comments
|
|
24
|
+
|
|
25
|
+
def test_active_excludes_deleted(self) -> None:
|
|
26
|
+
comment = Comment.objects.create(
|
|
27
|
+
content_type=self.content_type,
|
|
28
|
+
object_id=self.super_user.pk,
|
|
29
|
+
user=self.super_user,
|
|
30
|
+
information='Deleted comment',
|
|
31
|
+
is_deleted=True
|
|
32
|
+
)
|
|
33
|
+
comments = Comment.objects.active()
|
|
34
|
+
assert comment not in comments
|
|
35
|
+
|
|
36
|
+
def test_top_level(self) -> None:
|
|
37
|
+
parent_comment = Comment.objects.create(
|
|
38
|
+
content_type=self.content_type,
|
|
39
|
+
object_id=self.super_user.pk,
|
|
40
|
+
user=self.super_user,
|
|
41
|
+
information='Parent'
|
|
42
|
+
)
|
|
43
|
+
child_comment = Comment.objects.create(
|
|
44
|
+
content_type=self.content_type,
|
|
45
|
+
object_id=self.super_user.pk,
|
|
46
|
+
user=self.super_user,
|
|
47
|
+
parent=parent_comment,
|
|
48
|
+
information='Child'
|
|
49
|
+
)
|
|
50
|
+
top_level = Comment.objects.top_level()
|
|
51
|
+
assert parent_comment in top_level
|
|
52
|
+
assert child_comment not in top_level
|
|
53
|
+
|
|
54
|
+
def test_prefetch_user(self) -> None:
|
|
55
|
+
comments = Comment.objects.prefetch_user()
|
|
56
|
+
assert self.comment in comments
|
|
57
|
+
|
|
58
|
+
def test_prefetch_parent(self) -> None:
|
|
59
|
+
parent_comment = Comment.objects.create(
|
|
60
|
+
content_type=self.content_type,
|
|
61
|
+
object_id=self.super_user.pk,
|
|
62
|
+
user=self.super_user,
|
|
63
|
+
information='Parent'
|
|
64
|
+
)
|
|
65
|
+
child_comment = Comment.objects.create(
|
|
66
|
+
content_type=self.content_type,
|
|
67
|
+
object_id=self.super_user.pk,
|
|
68
|
+
user=self.super_user,
|
|
69
|
+
parent=parent_comment,
|
|
70
|
+
information='Child'
|
|
71
|
+
)
|
|
72
|
+
comments = Comment.objects.prefetch_parent()
|
|
73
|
+
assert child_comment in comments
|
|
74
|
+
|
|
75
|
+
def test_prefetch_replies(self) -> None:
|
|
76
|
+
parent_comment = Comment.objects.create(
|
|
77
|
+
content_type=self.content_type,
|
|
78
|
+
object_id=self.super_user.pk,
|
|
79
|
+
user=self.super_user,
|
|
80
|
+
information='Parent'
|
|
81
|
+
)
|
|
82
|
+
comments = Comment.objects.prefetch_replies()
|
|
83
|
+
assert parent_comment in comments
|
|
84
|
+
|
|
85
|
+
def test_chained_querysets(self) -> None:
|
|
86
|
+
parent_comment = Comment.objects.create(
|
|
87
|
+
content_type=self.content_type,
|
|
88
|
+
object_id=self.super_user.pk,
|
|
89
|
+
user=self.super_user,
|
|
90
|
+
information='Parent'
|
|
91
|
+
)
|
|
92
|
+
child_comment = Comment.objects.create(
|
|
93
|
+
content_type=self.content_type,
|
|
94
|
+
object_id=self.super_user.pk,
|
|
95
|
+
user=self.super_user,
|
|
96
|
+
parent=parent_comment,
|
|
97
|
+
information='Child'
|
|
98
|
+
)
|
|
99
|
+
comments = Comment.objects.active().top_level().prefetch_user()
|
|
100
|
+
assert parent_comment in comments
|
|
101
|
+
assert child_comment not in comments
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.contrib.auth.models import Group, Permission
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
|
|
6
|
+
from django_spire.auth.user.models import AuthUser
|
|
7
|
+
from django_spire.comment.models import Comment
|
|
8
|
+
from django_spire.comment.utils import (
|
|
9
|
+
find_user_list_from_content_type,
|
|
10
|
+
generate_comment_user_list_data,
|
|
11
|
+
parse_user_id_to_int_list,
|
|
12
|
+
)
|
|
13
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestGenerateCommentUserListData(BaseTestCase):
|
|
17
|
+
def test_single_user(self) -> None:
|
|
18
|
+
self.super_user.first_name = 'Test'
|
|
19
|
+
self.super_user.last_name = 'User'
|
|
20
|
+
self.super_user.save()
|
|
21
|
+
user_list = [self.super_user]
|
|
22
|
+
data = generate_comment_user_list_data(user_list)
|
|
23
|
+
assert len(data) == 1
|
|
24
|
+
assert data[0]['full_name'] == 'Test_User'
|
|
25
|
+
assert data[0]['id'] == self.super_user.pk
|
|
26
|
+
|
|
27
|
+
def test_empty_list(self) -> None:
|
|
28
|
+
data = generate_comment_user_list_data([])
|
|
29
|
+
assert data == []
|
|
30
|
+
|
|
31
|
+
def test_multiple_users(self) -> None:
|
|
32
|
+
self.super_user.first_name = 'Test'
|
|
33
|
+
self.super_user.last_name = 'User'
|
|
34
|
+
self.super_user.save()
|
|
35
|
+
other_user = AuthUser.objects.create_user(
|
|
36
|
+
username='other',
|
|
37
|
+
first_name='Other',
|
|
38
|
+
last_name='User'
|
|
39
|
+
)
|
|
40
|
+
user_list = [self.super_user, other_user]
|
|
41
|
+
data = generate_comment_user_list_data(user_list)
|
|
42
|
+
assert len(data) == 2
|
|
43
|
+
full_names = [d['full_name'] for d in data]
|
|
44
|
+
assert 'Test_User' in full_names
|
|
45
|
+
assert 'Other_User' in full_names
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestParseUserIdToIntList(BaseTestCase):
|
|
49
|
+
def test_multiple_ids(self) -> None:
|
|
50
|
+
other_user = AuthUser.objects.create_user(username='other')
|
|
51
|
+
user_id_str = f'{self.super_user.pk},{other_user.pk}'
|
|
52
|
+
users = parse_user_id_to_int_list(user_id_str)
|
|
53
|
+
assert self.super_user in users
|
|
54
|
+
assert other_user in users
|
|
55
|
+
|
|
56
|
+
def test_single_id(self) -> None:
|
|
57
|
+
user_id_str = str(self.super_user.pk)
|
|
58
|
+
users = parse_user_id_to_int_list(user_id_str)
|
|
59
|
+
assert self.super_user in users
|
|
60
|
+
assert users.count() == 1
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TestFindUserListFromContentType(BaseTestCase):
|
|
64
|
+
def test_find_users_with_permission(self) -> None:
|
|
65
|
+
content_type = ContentType.objects.get_for_model(Comment)
|
|
66
|
+
permission = Permission.objects.get(
|
|
67
|
+
content_type=content_type,
|
|
68
|
+
codename='view_comment'
|
|
69
|
+
)
|
|
70
|
+
group = Group.objects.create(name='test_group')
|
|
71
|
+
group.permissions.add(permission)
|
|
72
|
+
self.super_user.groups.add(group)
|
|
73
|
+
|
|
74
|
+
users = find_user_list_from_content_type('django_spire_comment', 'comment')
|
|
75
|
+
assert self.super_user in users
|
|
76
|
+
|
|
77
|
+
def test_excludes_users_without_permission(self) -> None:
|
|
78
|
+
other_user = AuthUser.objects.create_user(username='other')
|
|
79
|
+
content_type = ContentType.objects.get_for_model(Comment)
|
|
80
|
+
permission = Permission.objects.get(
|
|
81
|
+
content_type=content_type,
|
|
82
|
+
codename='view_comment'
|
|
83
|
+
)
|
|
84
|
+
group = Group.objects.create(name='test_group')
|
|
85
|
+
group.permissions.add(permission)
|
|
86
|
+
self.super_user.groups.add(group)
|
|
87
|
+
|
|
88
|
+
users = find_user_list_from_content_type('django_spire_comment', 'comment')
|
|
89
|
+
assert self.super_user in users
|
|
90
|
+
assert other_user not in users
|
django_spire/comment/urls.py
CHANGED
django_spire/comment/utils.py
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
from django.contrib.
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from django.contrib.auth.models import User
|
|
7
|
+
from django.db.models import Model, QuerySet
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_user_list_from_content_type(app_label: str, model_name: str) -> QuerySet[User]:
|
|
6
11
|
from django.contrib.auth.models import Permission, Group, User
|
|
12
|
+
from django.contrib.contenttypes.models import ContentType
|
|
7
13
|
|
|
8
14
|
content_type = ContentType.objects.get(app_label=app_label, model=model_name)
|
|
9
15
|
permission = Permission.objects.get(content_type=content_type, codename=f'view_{model_name}')
|
|
@@ -12,28 +18,31 @@ def find_user_list_from_content_type(app_label, model_name):
|
|
|
12
18
|
return User.objects.filter(groups__in=groups).distinct()
|
|
13
19
|
|
|
14
20
|
|
|
15
|
-
def generate_comment_user_list_data(user_list):
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
for user in user_list:
|
|
19
|
-
user_data.append({
|
|
21
|
+
def generate_comment_user_list_data(user_list: QuerySet[User] | list[User]) -> list[dict]:
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
20
24
|
'full_name': user.get_full_name().replace(' ', '_'),
|
|
21
25
|
'id': user.pk,
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
}
|
|
27
|
+
for user in user_list
|
|
28
|
+
]
|
|
25
29
|
|
|
26
30
|
|
|
27
|
-
def parse_user_id_to_int_list(user_id_str):
|
|
31
|
+
def parse_user_id_to_int_list(user_id_str: str) -> QuerySet[User]:
|
|
28
32
|
from django.contrib.auth.models import User
|
|
29
33
|
|
|
30
34
|
user_id_list = [int(user_id) for user_id in user_id_str.split(',')]
|
|
31
35
|
return User.objects.filter(id__in=user_id_list)
|
|
32
36
|
|
|
33
37
|
|
|
34
|
-
def process_comment_notifications(
|
|
35
|
-
|
|
38
|
+
def process_comment_notifications(
|
|
39
|
+
user_list: QuerySet[User] | list[User],
|
|
40
|
+
comment_information: str,
|
|
41
|
+
related_obj: Model,
|
|
42
|
+
user_commenting: User
|
|
43
|
+
) -> None:
|
|
36
44
|
from django.contrib.sites.models import Site
|
|
45
|
+
from django_spire.notification.models import Notification
|
|
37
46
|
|
|
38
47
|
for user in user_list:
|
|
39
48
|
if user != user_commenting:
|
django_spire/comment/views.py
CHANGED
|
@@ -78,7 +78,7 @@ def comment_form_view(
|
|
|
78
78
|
if request.method == 'POST':
|
|
79
79
|
form = CommentForm(request.POST, instance=comment)
|
|
80
80
|
if form.is_valid():
|
|
81
|
-
#
|
|
81
|
+
# TODO: Create comment factory.
|
|
82
82
|
if comment_pk == 0:
|
|
83
83
|
obj.add_comment(
|
|
84
84
|
user=request.user,
|
django_spire/conf.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.conf import settings as django_settings
|
|
2
4
|
|
|
3
5
|
from django_spire import settings as django_spire_default_settings
|
|
@@ -14,12 +16,11 @@ class Settings:
|
|
|
14
16
|
if hasattr(django_spire_default_settings, name):
|
|
15
17
|
django_spire_value = getattr(django_spire_default_settings, name)
|
|
16
18
|
|
|
17
|
-
if name == 'DJANGO_SPIRE_AUTH_CONTROLLERS':
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
19
|
+
if name == 'DJANGO_SPIRE_AUTH_CONTROLLERS' and isinstance(django_value, dict) and isinstance(django_spire_value, dict):
|
|
20
|
+
return {
|
|
21
|
+
**django_spire_value,
|
|
22
|
+
**django_value
|
|
23
|
+
}
|
|
23
24
|
|
|
24
25
|
if django_value is not None:
|
|
25
26
|
return django_value
|
|
@@ -29,4 +30,5 @@ class Settings:
|
|
|
29
30
|
|
|
30
31
|
return None
|
|
31
32
|
|
|
33
|
+
|
|
32
34
|
settings = Settings()
|
django_spire/consts.py
CHANGED