django-spire 0.23.7__py3-none-any.whl → 0.23.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_spire/ai/admin.py +11 -11
- django_spire/ai/chat/apps.py +1 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +1 -1
- django_spire/ai/chat/tests/factories.py +15 -0
- django_spire/ai/chat/tests/test_controller.py +45 -0
- django_spire/ai/chat/tests/test_models.py +301 -0
- django_spire/ai/chat/tests/test_prompts.py +48 -0
- django_spire/ai/chat/tests/test_responses.py +208 -0
- django_spire/ai/chat/tests/test_router/test_base_chat_router.py +66 -6
- django_spire/ai/chat/tests/test_router/test_chat_workflow.py +73 -3
- django_spire/ai/chat/tests/test_router/test_integration.py +86 -6
- django_spire/ai/chat/tests/test_router/test_intent_decoder.py +93 -1
- django_spire/ai/chat/tests/test_router/test_message_intel.py +60 -1
- django_spire/ai/chat/tests/test_router/test_spire_chat_router.py +110 -0
- django_spire/ai/chat/tests/test_urls/test_json_urls.py +202 -1
- django_spire/ai/context/tests/__init__.py +0 -0
- django_spire/ai/context/tests/test_context.py +188 -0
- django_spire/ai/decorators.py +7 -6
- django_spire/ai/prompt/tests/test_bots.py +100 -10
- django_spire/ai/prompt/tests/test_prompt_intel.py +83 -0
- django_spire/ai/prompt/tests/test_prompt_tuning.py +126 -0
- django_spire/ai/sms/decorators.py +8 -2
- django_spire/ai/sms/tests/test_sms.py +240 -16
- django_spire/ai/sms/tests/test_sms_intel.py +42 -0
- django_spire/ai/sms/tests/test_webhook.py +155 -7
- django_spire/ai/sms/views.py +23 -24
- django_spire/ai/tests/test_ai.py +131 -7
- django_spire/auth/apps.py +4 -2
- django_spire/auth/controller/controller.py +36 -23
- django_spire/auth/controller/exceptions.py +9 -0
- django_spire/auth/group/admin.py +1 -0
- django_spire/auth/group/apps.py +2 -0
- django_spire/auth/group/factories.py +17 -8
- django_spire/auth/group/forms.py +7 -0
- django_spire/auth/group/tests/test_factories.py +146 -0
- django_spire/auth/group/tests/test_forms.py +282 -0
- django_spire/auth/group/tests/test_models.py +192 -0
- django_spire/auth/group/tests/test_querysets.py +98 -0
- django_spire/auth/group/tests/test_utils.py +341 -0
- django_spire/auth/group/tests/test_views.py +377 -0
- django_spire/auth/group/urls/__init__.py +3 -1
- django_spire/auth/group/urls/form_urls.py +2 -0
- django_spire/auth/group/urls/json_urls.py +3 -0
- django_spire/auth/group/urls/page_urls.py +2 -0
- django_spire/auth/group/utils.py +6 -2
- django_spire/auth/group/views/form_views.py +6 -3
- django_spire/auth/group/views/json_views.py +6 -2
- django_spire/auth/mfa/admin.py +2 -0
- django_spire/auth/mfa/apps.py +2 -0
- django_spire/auth/mfa/forms.py +1 -0
- django_spire/auth/mfa/querysets.py +9 -2
- django_spire/auth/mfa/tests/test_models.py +233 -0
- django_spire/auth/mfa/tests/test_utils.py +106 -0
- django_spire/auth/mfa/urls/__init__.py +2 -0
- django_spire/auth/mfa/urls/page_urls.py +2 -0
- django_spire/auth/mfa/urls/redirect_urls.py +2 -0
- django_spire/auth/mfa/views/page_views.py +2 -1
- django_spire/auth/permissions/consts.py +2 -2
- django_spire/auth/permissions/decorators.py +8 -8
- django_spire/auth/permissions/permissions.py +28 -35
- django_spire/auth/permissions/tests/test_decorators.py +333 -0
- django_spire/auth/permissions/tests/test_permissions.py +337 -0
- django_spire/auth/permissions/tests/test_tools.py +305 -0
- django_spire/auth/permissions/tools.py +21 -15
- django_spire/auth/seeding/seed.py +3 -0
- django_spire/auth/seeding/seeder.py +2 -0
- django_spire/auth/tests/test_controller.py +323 -0
- django_spire/auth/tests/test_url_endpoints.py +9 -9
- django_spire/auth/tests/test_views.py +406 -0
- django_spire/auth/urls/admin_urls.py +2 -0
- django_spire/auth/urls/redirect_urls.py +2 -0
- django_spire/auth/user/apps.py +2 -0
- django_spire/auth/user/forms.py +9 -0
- django_spire/auth/user/models.py +1 -1
- django_spire/auth/user/services/services.py +1 -0
- django_spire/auth/user/tests/factories.py +14 -13
- django_spire/auth/user/tests/test_factories.py +166 -2
- django_spire/auth/user/tests/test_forms.py +573 -0
- django_spire/auth/user/tests/test_models.py +257 -0
- django_spire/auth/user/tests/test_services.py +200 -0
- django_spire/auth/user/tests/test_tools.py +153 -0
- django_spire/auth/user/tests/test_user_factories.py +139 -0
- django_spire/auth/user/tests/test_views.py +363 -0
- django_spire/auth/user/tools.py +7 -1
- django_spire/auth/user/urls/form_urls.py +3 -0
- django_spire/auth/user/urls/page_urls.py +3 -0
- django_spire/auth/user/views/form_views.py +19 -10
- django_spire/auth/user/views/page_views.py +8 -2
- django_spire/auth/views/redirect_views.py +14 -9
- django_spire/comment/admin.py +2 -0
- django_spire/comment/apps.py +2 -0
- django_spire/comment/templatetags/comment_tags.py +1 -0
- django_spire/comment/tests/test_forms.py +27 -0
- django_spire/comment/tests/test_models.py +215 -0
- django_spire/comment/tests/test_querysets.py +101 -0
- django_spire/comment/tests/test_utils.py +90 -0
- django_spire/comment/urls.py +2 -0
- django_spire/comment/utils.py +22 -13
- django_spire/comment/views.py +1 -1
- django_spire/conf.py +8 -6
- django_spire/consts.py +1 -1
- django_spire/contrib/breadcrumb/apps.py +2 -0
- django_spire/contrib/breadcrumb/breadcrumbs.py +18 -18
- django_spire/contrib/breadcrumb/tests/test_breadcrumbs.py +198 -0
- django_spire/contrib/constructor/__init__.py +3 -3
- django_spire/contrib/constructor/constructor.py +15 -15
- django_spire/contrib/constructor/django_model_constructor.py +5 -4
- django_spire/contrib/constructor/exceptions.py +5 -3
- django_spire/contrib/constructor/tests/__init__.py +0 -0
- django_spire/contrib/constructor/tests/test_constructor.py +193 -0
- django_spire/contrib/form/tests/__init__.py +0 -0
- django_spire/contrib/form/tests/test_forms.py +203 -0
- django_spire/contrib/generic_views/modal_views.py +2 -1
- django_spire/contrib/generic_views/portal_views.py +20 -19
- django_spire/contrib/generic_views/tests/__init__.py +0 -0
- django_spire/contrib/generic_views/tests/test_views.py +459 -0
- django_spire/contrib/help/apps.py +2 -0
- django_spire/contrib/help/templatetags/help.py +1 -0
- django_spire/contrib/help/tests/__init__.py +0 -0
- django_spire/contrib/help/tests/test_templatetags.py +100 -0
- django_spire/contrib/options/mixins.py +6 -5
- django_spire/contrib/options/tests/factories.py +5 -1
- django_spire/contrib/options/tests/test_options.py +234 -0
- django_spire/contrib/ordering/exceptions.py +7 -3
- django_spire/contrib/ordering/mixins.py +2 -0
- django_spire/contrib/ordering/querysets.py +3 -1
- django_spire/contrib/ordering/services/processor_service.py +8 -4
- django_spire/contrib/ordering/services/service.py +1 -2
- django_spire/contrib/ordering/tests/__init__.py +0 -0
- django_spire/contrib/ordering/tests/test_ordering.py +165 -0
- django_spire/contrib/ordering/validators.py +6 -6
- django_spire/contrib/pagination/templatetags/pagination_tags.py +12 -5
- django_spire/contrib/pagination/tests/__init__.py +0 -0
- django_spire/contrib/pagination/tests/test_pagination.py +179 -0
- django_spire/contrib/performance/decorators.py +16 -6
- django_spire/contrib/performance/tests/__init__.py +0 -0
- django_spire/contrib/performance/tests/test_performance.py +107 -0
- django_spire/contrib/queryset/enums.py +3 -1
- django_spire/contrib/queryset/filter_tools.py +10 -5
- django_spire/contrib/queryset/mixins.py +16 -16
- django_spire/contrib/queryset/tests/__init__.py +0 -0
- django_spire/contrib/queryset/tests/test_queryset.py +137 -0
- django_spire/contrib/seeding/field/base.py +13 -7
- django_spire/contrib/seeding/field/callable.py +8 -1
- django_spire/contrib/seeding/field/cleaners.py +5 -5
- django_spire/contrib/seeding/field/custom.py +20 -10
- django_spire/contrib/seeding/field/django/seeder.py +8 -6
- django_spire/contrib/seeding/field/enums.py +7 -5
- django_spire/contrib/seeding/field/override.py +16 -6
- django_spire/contrib/seeding/field/static.py +9 -2
- django_spire/contrib/seeding/field/tests/test_base.py +18 -14
- django_spire/contrib/seeding/field/tests/test_callable.py +13 -9
- django_spire/contrib/seeding/field/tests/test_cleaners.py +51 -38
- django_spire/contrib/seeding/field/tests/test_static.py +13 -9
- django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +2 -0
- django_spire/contrib/seeding/intelligence/intel.py +5 -1
- django_spire/contrib/seeding/intelligence/prompts/factory.py +6 -1
- django_spire/contrib/seeding/intelligence/prompts/foreign_key_selection_prompt.py +6 -1
- django_spire/contrib/seeding/intelligence/prompts/generate_django_model_seeder_prompts.py +2 -0
- django_spire/contrib/seeding/intelligence/prompts/generic_relationship_selection_prompt.py +7 -1
- django_spire/contrib/seeding/intelligence/prompts/hierarchical_selection_prompt.py +6 -2
- django_spire/contrib/seeding/intelligence/prompts/model_field_choices_prompt.py +8 -2
- django_spire/contrib/seeding/intelligence/prompts/negation_prompt.py +2 -0
- django_spire/contrib/seeding/intelligence/prompts/objective_prompt.py +6 -1
- django_spire/contrib/seeding/management/commands/seeding.py +9 -3
- django_spire/contrib/seeding/management/example.py +2 -0
- django_spire/contrib/seeding/model/base.py +16 -7
- django_spire/contrib/seeding/model/config.py +31 -15
- django_spire/contrib/seeding/model/django/config.py +13 -13
- django_spire/contrib/seeding/model/django/seeder.py +4 -4
- django_spire/contrib/seeding/model/django/tests/test_seeder.py +34 -23
- django_spire/contrib/seeding/model/enums.py +2 -0
- django_spire/contrib/seeding/tests/test_config.py +71 -0
- django_spire/contrib/seeding/tests/test_custom.py +35 -0
- django_spire/contrib/seeding/tests/test_enums.py +40 -0
- django_spire/contrib/seeding/tests/test_intel.py +32 -0
- django_spire/contrib/seeding/tests/test_override.py +63 -0
- django_spire/contrib/service/__init__.py +2 -2
- django_spire/contrib/service/django_model_service.py +16 -15
- django_spire/contrib/service/exceptions.py +5 -3
- django_spire/contrib/service/tests/__init__.py +0 -0
- django_spire/contrib/service/tests/test_service.py +153 -0
- django_spire/contrib/session/apps.py +2 -0
- django_spire/contrib/session/controller.py +48 -42
- django_spire/contrib/session/templatetags/session_tags.py +11 -2
- django_spire/contrib/session/tests/test_session_controller.py +117 -53
- django_spire/contrib/tests/__init__.py +0 -0
- django_spire/contrib/tests/test_utils.py +37 -0
- django_spire/contrib/utils.py +4 -1
- django_spire/core/apps.py +2 -0
- django_spire/core/converters/tests/test_to_data.py +353 -0
- django_spire/core/converters/tests/test_to_enums.py +61 -41
- django_spire/core/converters/tests/test_to_pydantic.py +138 -109
- django_spire/core/converters/to_data.py +29 -10
- django_spire/core/converters/to_enums.py +4 -2
- django_spire/core/converters/to_pydantic.py +22 -22
- django_spire/core/decorators.py +19 -6
- django_spire/core/forms/widgets.py +4 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/apps.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/__init__.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/form_urls.py.template +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/page_urls.py.template +2 -0
- django_spire/core/maps.py +3 -1
- django_spire/core/middleware/maintenance.py +3 -3
- django_spire/core/middleware.py +8 -6
- django_spire/core/redirect/__init__.py +5 -0
- django_spire/core/redirect/generic_redirect.py +1 -2
- django_spire/core/redirect/tests/__init__.py +0 -0
- django_spire/core/redirect/tests/test_generic_redirect.py +34 -0
- django_spire/core/{tests/tests_redirect.py → redirect/tests/test_safe_redirect.py} +55 -81
- django_spire/core/shortcuts.py +3 -3
- django_spire/core/static/django_spire/css/app-layout.css +1 -1
- django_spire/core/static/django_spire/css/app-navigation.css +3 -3
- django_spire/core/static/django_spire/css/bootstrap-override.css +4 -0
- django_spire/core/tag/admin.py +12 -0
- django_spire/core/tag/intelligence/tag_set_bot.py +2 -0
- django_spire/core/tag/mixins.py +2 -0
- django_spire/core/tag/models.py +2 -0
- django_spire/core/tag/querysets.py +2 -0
- django_spire/core/tag/service/tag_service.py +6 -3
- django_spire/core/tag/tests/test_intelligence.py +9 -9
- django_spire/core/tag/tests/test_tags.py +44 -54
- django_spire/core/tag/tests/test_tools.py +191 -0
- django_spire/core/tag/tools.py +3 -0
- django_spire/core/templates/django_spire/card/card.html +5 -2
- django_spire/core/templates/django_spire/card/title_card.html +12 -4
- django_spire/core/templates/django_spire/infinite_scroll/base.html +1 -0
- django_spire/core/templates/django_spire/navigation/side_navigation.html +19 -24
- django_spire/core/templates/django_spire/page/full_page.html +46 -16
- django_spire/core/templates/django_spire/table/base.html +5 -3
- django_spire/core/templatetags/json.py +6 -2
- django_spire/core/templatetags/message.py +13 -8
- django_spire/core/templatetags/string_formating.py +8 -5
- django_spire/core/templatetags/tests/__init__.py +0 -0
- django_spire/core/templatetags/tests/test_templatetags.py +427 -0
- django_spire/core/templatetags/variable_types.py +17 -9
- django_spire/core/tests/test_cases.py +1 -1
- django_spire/core/tests/test_conf.py +43 -0
- django_spire/core/tests/test_consts.py +28 -0
- django_spire/core/tests/test_context_processors.py +93 -0
- django_spire/core/tests/test_decorators.py +95 -0
- django_spire/core/tests/test_django_spire_utils.py +56 -0
- django_spire/core/tests/test_exceptions.py +37 -0
- django_spire/core/tests/test_models.py +54 -0
- django_spire/core/tests/test_settings.py +45 -0
- django_spire/core/tests/test_shortcuts.py +74 -0
- django_spire/core/tests/test_urls.py +16 -0
- django_spire/core/tests/test_utils.py +58 -0
- django_spire/core/urls.py +4 -1
- django_spire/core/utils.py +12 -8
- django_spire/exceptions.py +16 -1
- django_spire/file/admin.py +4 -2
- django_spire/file/apps.py +8 -10
- django_spire/file/fields.py +7 -7
- django_spire/file/forms.py +1 -1
- django_spire/file/interfaces.py +15 -15
- django_spire/file/mixins.py +1 -4
- django_spire/file/models.py +3 -5
- django_spire/file/tests/factories.py +59 -0
- django_spire/file/tests/test_admin.py +69 -0
- django_spire/file/tests/test_apps.py +24 -0
- django_spire/file/tests/test_fields.py +114 -0
- django_spire/file/tests/test_forms.py +20 -0
- django_spire/file/tests/test_interfaces.py +183 -0
- django_spire/file/tests/test_models.py +82 -0
- django_spire/file/tests/test_querysets.py +102 -0
- django_spire/file/tests/test_utils.py +32 -0
- django_spire/file/tests/test_views.py +145 -0
- django_spire/file/tests/test_widgets.py +82 -0
- django_spire/file/tools.py +8 -2
- django_spire/file/views.py +7 -3
- django_spire/file/widgets.py +12 -12
- django_spire/help_desk/admin.py +15 -0
- django_spire/help_desk/apps.py +2 -0
- django_spire/help_desk/auth/controller.py +2 -0
- django_spire/help_desk/choices.py +2 -0
- django_spire/help_desk/enums.py +2 -0
- django_spire/help_desk/exceptions.py +31 -3
- django_spire/help_desk/forms.py +2 -0
- django_spire/help_desk/models.py +2 -0
- django_spire/help_desk/querysets.py +4 -1
- django_spire/help_desk/services/notification_service.py +26 -27
- django_spire/help_desk/services/service.py +2 -3
- django_spire/help_desk/tests/factories.py +8 -3
- django_spire/help_desk/tests/test_admin.py +41 -0
- django_spire/help_desk/tests/test_apps.py +41 -0
- django_spire/help_desk/tests/test_choices.py +50 -0
- django_spire/help_desk/tests/test_controller.py +87 -0
- django_spire/help_desk/tests/test_enums.py +18 -0
- django_spire/help_desk/tests/test_exceptions.py +37 -0
- django_spire/help_desk/tests/test_forms.py +89 -0
- django_spire/help_desk/tests/test_models.py +59 -0
- django_spire/help_desk/tests/test_querysets.py +38 -0
- django_spire/help_desk/tests/test_services/test_notification_service.py +15 -8
- django_spire/help_desk/tests/test_services/test_service.py +92 -0
- django_spire/help_desk/tests/test_urls/test_form_urls.py +6 -6
- django_spire/help_desk/tests/test_urls/test_page_urls.py +8 -9
- django_spire/help_desk/tests/test_views/test_form_views.py +46 -19
- django_spire/help_desk/tests/test_views/test_page_views.py +32 -9
- django_spire/help_desk/urls/__init__.py +4 -1
- django_spire/help_desk/urls/form_urls.py +3 -0
- django_spire/help_desk/urls/page_urls.py +3 -0
- django_spire/help_desk/views/form_views.py +13 -5
- django_spire/help_desk/views/page_views.py +11 -3
- django_spire/history/activity/admin.py +2 -0
- django_spire/history/activity/apps.py +3 -1
- django_spire/history/activity/mixins.py +13 -7
- django_spire/history/activity/models.py +6 -5
- django_spire/history/activity/querysets.py +2 -0
- django_spire/history/activity/tests/__init__.py +0 -0
- django_spire/history/activity/tests/test_activity.py +176 -0
- django_spire/history/admin.py +9 -2
- django_spire/history/choices.py +3 -0
- django_spire/history/models.py +5 -5
- django_spire/history/tests/test_admin.py +93 -0
- django_spire/history/tests/test_history.py +101 -0
- django_spire/history/tests/test_mixins.py +84 -0
- django_spire/history/viewed/admin.py +3 -1
- django_spire/history/viewed/apps.py +3 -1
- django_spire/history/viewed/models.py +2 -0
- django_spire/history/viewed/tests/__init__.py +0 -0
- django_spire/history/viewed/tests/test_viewed.py +46 -0
- django_spire/knowledge/auth/tests/__init__.py +0 -0
- django_spire/knowledge/auth/tests/test_controller.py +116 -0
- django_spire/knowledge/collection/admin.py +5 -1
- django_spire/knowledge/collection/models.py +3 -1
- django_spire/knowledge/collection/seeding/seed.py +1 -0
- django_spire/knowledge/collection/services/factory_service.py +10 -11
- django_spire/knowledge/collection/services/ordering_service.py +1 -2
- django_spire/knowledge/collection/services/service.py +5 -10
- django_spire/knowledge/collection/services/tag_service.py +5 -2
- django_spire/knowledge/collection/tests/factories.py +28 -1
- django_spire/knowledge/collection/tests/test_models.py +48 -0
- django_spire/knowledge/collection/tests/test_querysets.py +93 -0
- django_spire/knowledge/collection/tests/test_services/test_factory_service.py +100 -0
- django_spire/knowledge/collection/tests/test_services/test_services.py +160 -0
- django_spire/knowledge/collection/tests/test_urls/test_form_urls.py +21 -3
- django_spire/knowledge/collection/tests/test_urls/test_json_urls.py +39 -1
- django_spire/knowledge/collection/tests/test_urls/test_page_urls.py +12 -4
- django_spire/knowledge/collection/urls/__init__.py +3 -0
- django_spire/knowledge/collection/urls/form_urls.py +2 -0
- django_spire/knowledge/collection/urls/json_urls.py +2 -0
- django_spire/knowledge/collection/urls/page_urls.py +2 -0
- django_spire/knowledge/collection/views/form_views.py +4 -4
- django_spire/knowledge/collection/views/json_views.py +5 -1
- django_spire/knowledge/collection/views/page_views.py +5 -2
- django_spire/knowledge/entry/admin.py +7 -1
- django_spire/knowledge/entry/forms.py +2 -0
- django_spire/knowledge/entry/models.py +2 -0
- django_spire/knowledge/entry/seeding/seed.py +3 -0
- django_spire/knowledge/entry/services/automation_service.py +5 -4
- django_spire/knowledge/entry/services/factory_service.py +7 -5
- django_spire/knowledge/entry/services/service.py +4 -7
- django_spire/knowledge/entry/services/tag_service.py +0 -1
- django_spire/knowledge/entry/services/tool_service.py +1 -0
- django_spire/knowledge/entry/services/transformation_services.py +1 -5
- django_spire/knowledge/entry/tests/factories.py +1 -2
- django_spire/knowledge/entry/tests/test_factory_service.py +20 -0
- django_spire/knowledge/entry/tests/test_models.py +41 -0
- django_spire/knowledge/entry/tests/test_querysets.py +71 -0
- django_spire/knowledge/entry/tests/test_services.py +94 -0
- django_spire/knowledge/entry/tests/test_urls/test_form_urls.py +9 -14
- django_spire/knowledge/entry/tests/test_urls/test_json_urls.py +48 -5
- django_spire/knowledge/entry/tests/test_urls/test_page_urls.py +6 -8
- django_spire/knowledge/entry/tests/test_urls/test_template_urls.py +40 -0
- django_spire/knowledge/entry/urls/form_urls.py +2 -0
- django_spire/knowledge/entry/urls/json_urls.py +2 -0
- django_spire/knowledge/entry/urls/page_urls.py +2 -0
- django_spire/knowledge/entry/urls/template_urls.py +2 -0
- django_spire/knowledge/entry/version/block/choices.py +2 -0
- django_spire/knowledge/entry/version/block/data/data.py +1 -0
- django_spire/knowledge/entry/version/block/data/list/data.py +8 -13
- django_spire/knowledge/entry/version/block/data/list/maps.py +3 -0
- django_spire/knowledge/entry/version/block/data/list/meta.py +1 -2
- django_spire/knowledge/entry/version/block/data/list/tests/__init__.py +0 -0
- django_spire/knowledge/entry/version/block/data/list/tests/test_maps.py +32 -0
- django_spire/knowledge/entry/version/block/data/list/tests/test_meta.py +58 -0
- django_spire/knowledge/entry/version/block/data/maps.py +3 -6
- django_spire/knowledge/entry/version/block/models.py +7 -5
- django_spire/knowledge/entry/version/block/seeding/constants.py +5 -4
- django_spire/knowledge/entry/version/block/services/service.py +2 -3
- django_spire/knowledge/entry/version/block/tests/factories.py +4 -10
- django_spire/knowledge/entry/version/block/tests/test_choices.py +56 -0
- django_spire/knowledge/entry/version/block/tests/test_data.py +90 -0
- django_spire/knowledge/entry/version/block/tests/test_maps.py +37 -0
- django_spire/knowledge/entry/version/block/tests/test_models.py +55 -0
- django_spire/knowledge/entry/version/block/tests/test_querysets.py +35 -0
- django_spire/knowledge/entry/version/block/tests/test_services.py +65 -0
- django_spire/knowledge/entry/version/choices.py +2 -0
- django_spire/knowledge/entry/version/converters/converter.py +1 -1
- django_spire/knowledge/entry/version/converters/docx_converter.py +4 -7
- django_spire/knowledge/entry/version/converters/markdown_converter.py +20 -20
- django_spire/knowledge/entry/version/maps.py +4 -5
- django_spire/knowledge/entry/version/querysets.py +1 -1
- django_spire/knowledge/entry/version/seeding/seeder.py +1 -2
- django_spire/knowledge/entry/version/services/processor_service.py +5 -4
- django_spire/knowledge/entry/version/services/service.py +1 -2
- django_spire/knowledge/entry/version/tests/factories.py +2 -2
- django_spire/knowledge/entry/version/tests/test_choices.py +18 -0
- django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +56 -8
- django_spire/knowledge/entry/version/tests/test_converters/test_markdown_converter.py +78 -0
- django_spire/knowledge/entry/version/tests/test_maps.py +58 -0
- django_spire/knowledge/entry/version/tests/test_models.py +23 -0
- django_spire/knowledge/entry/version/tests/test_querysets.py +26 -0
- django_spire/knowledge/entry/version/tests/test_services.py +62 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +27 -8
- django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +15 -8
- django_spire/knowledge/entry/version/tests/test_urls/test_redirect_urls.py +38 -0
- django_spire/knowledge/entry/version/urls/__init__.py +3 -0
- django_spire/knowledge/entry/version/urls/json_urls.py +2 -1
- django_spire/knowledge/entry/version/urls/page_urls.py +2 -0
- django_spire/knowledge/entry/version/urls/redirect_urls.py +2 -0
- django_spire/knowledge/entry/version/views/json_views.py +5 -1
- django_spire/knowledge/entry/version/views/page_views.py +10 -3
- django_spire/knowledge/entry/version/views/redirect_views.py +5 -1
- django_spire/knowledge/entry/views/form_views.py +16 -8
- django_spire/knowledge/entry/views/json_views.py +3 -1
- django_spire/knowledge/entry/views/page_views.py +8 -2
- django_spire/knowledge/entry/views/template_views.py +7 -1
- django_spire/knowledge/exceptions.py +2 -1
- django_spire/knowledge/intelligence/intel/answer_intel.py +2 -1
- django_spire/knowledge/intelligence/intel/entry_intel.py +0 -1
- django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +4 -5
- django_spire/knowledge/models.py +1 -2
- django_spire/knowledge/tests/__init__.py +0 -0
- django_spire/knowledge/tests/test_templatetags.py +40 -0
- django_spire/knowledge/tests/test_urls/__init__.py +0 -0
- django_spire/knowledge/tests/test_urls/test_page_urls.py +24 -0
- django_spire/knowledge/urls/__init__.py +2 -0
- django_spire/knowledge/urls/page_urls.py +2 -0
- django_spire/knowledge/views/page_views.py +8 -3
- django_spire/notification/admin.py +3 -1
- django_spire/notification/app/admin.py +2 -0
- django_spire/notification/app/apps.py +3 -1
- django_spire/notification/app/exceptions.py +9 -2
- django_spire/notification/app/models.py +8 -4
- django_spire/notification/app/processor.py +22 -26
- django_spire/notification/app/querysets.py +2 -0
- django_spire/notification/app/tests/__init__.py +0 -0
- django_spire/notification/app/tests/factories.py +34 -0
- django_spire/notification/app/tests/test_apps.py +24 -0
- django_spire/notification/app/tests/test_models.py +72 -0
- django_spire/notification/app/tests/test_processor.py +111 -0
- django_spire/notification/app/tests/test_querysets.py +90 -0
- django_spire/notification/app/tests/test_views/__init__.py +0 -0
- django_spire/notification/app/tests/test_views/test_json_views.py +48 -0
- django_spire/notification/app/tests/test_views/test_page_views.py +19 -0
- django_spire/notification/app/urls/__init__.py +3 -1
- django_spire/notification/app/urls/json_urls.py +6 -4
- django_spire/notification/app/urls/page_urls.py +4 -3
- django_spire/notification/app/urls/template_urls.py +4 -2
- django_spire/notification/apps.py +4 -1
- django_spire/notification/email/admin.py +5 -1
- django_spire/notification/email/apps.py +3 -1
- django_spire/notification/email/exceptions.py +4 -2
- django_spire/notification/email/helper.py +5 -3
- django_spire/notification/email/models.py +4 -0
- django_spire/notification/email/processor.py +19 -15
- django_spire/notification/email/querysets.py +3 -0
- django_spire/notification/email/tests/__init__.py +0 -0
- django_spire/notification/email/tests/factories.py +35 -0
- django_spire/notification/email/tests/test_apps.py +24 -0
- django_spire/notification/email/tests/test_models.py +52 -0
- django_spire/notification/email/tests/test_processor.py +92 -0
- django_spire/notification/email/tests/test_querysets.py +43 -0
- django_spire/notification/exceptions.py +17 -2
- django_spire/notification/managers.py +7 -1
- django_spire/notification/maps.py +4 -1
- django_spire/notification/mixins.py +2 -0
- django_spire/notification/models.py +3 -1
- django_spire/notification/processors/notification.py +12 -5
- django_spire/notification/processors/processor.py +2 -0
- django_spire/notification/processors/tests/__init__.py +0 -0
- django_spire/notification/processors/tests/test_notification.py +106 -0
- django_spire/notification/push/admin.py +10 -1
- django_spire/notification/push/apps.py +3 -1
- django_spire/notification/push/models.py +2 -3
- django_spire/notification/push/tests/__init__.py +0 -0
- django_spire/notification/push/tests/test_apps.py +24 -0
- django_spire/notification/push/tests/test_models.py +28 -0
- django_spire/notification/querysets.py +7 -1
- django_spire/notification/sms/admin.py +2 -0
- django_spire/notification/sms/apps.py +4 -1
- django_spire/notification/sms/automations.py +2 -0
- django_spire/notification/sms/choices.py +2 -0
- django_spire/notification/sms/exceptions.py +19 -5
- django_spire/notification/sms/helper.py +33 -23
- django_spire/notification/sms/models.py +5 -1
- django_spire/notification/sms/processor.py +20 -20
- django_spire/notification/sms/querysets.py +2 -0
- django_spire/notification/sms/tests/factories.py +33 -0
- django_spire/notification/sms/tests/test_apps.py +24 -0
- django_spire/notification/sms/tests/test_automation.py +38 -0
- django_spire/notification/sms/tests/test_choices.py +15 -0
- django_spire/notification/sms/tests/test_consts.py +17 -0
- django_spire/notification/sms/tests/test_exceptions.py +27 -0
- django_spire/notification/sms/tests/test_helper.py +50 -0
- django_spire/notification/sms/tests/test_models.py +81 -0
- django_spire/notification/sms/tests/test_processor.py +107 -0
- django_spire/notification/sms/tests/test_tools.py +25 -11
- django_spire/notification/sms/tools.py +16 -5
- django_spire/notification/sms/urls/__init__.py +3 -1
- django_spire/notification/sms/urls/media_urls.py +2 -0
- django_spire/notification/sms/views/media_views.py +14 -4
- django_spire/notification/tests/__init__.py +0 -0
- django_spire/notification/tests/factories.py +26 -0
- django_spire/notification/tests/test_admin.py +55 -0
- django_spire/notification/tests/test_apps.py +30 -0
- django_spire/notification/tests/test_automation.py +18 -0
- django_spire/notification/tests/test_choices.py +59 -0
- django_spire/notification/tests/test_exceptions.py +58 -0
- django_spire/notification/tests/test_managers.py +100 -0
- django_spire/notification/tests/test_maps.py +31 -0
- django_spire/notification/tests/test_models.py +76 -0
- django_spire/notification/tests/test_querysets.py +184 -0
- django_spire/notification/tests/test_utils.py +23 -0
- django_spire/notification/urls.py +3 -1
- django_spire/notification/utils.py +3 -1
- django_spire/settings.py +3 -0
- django_spire/theme/tests/test_context_processor.py +15 -13
- django_spire/theme/tests/test_enums.py +2 -2
- django_spire/theme/tests/test_filesystem.py +2 -5
- django_spire/theme/tests/test_integration.py +12 -12
- django_spire/theme/tests/test_model.py +40 -38
- django_spire/theme/tests/test_views/test_json_views.py +33 -33
- django_spire/theme/urls/json_urls.py +3 -0
- django_spire/theme/urls/page_urls.py +3 -0
- django_spire/urls.py +19 -15
- django_spire/utils.py +13 -4
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/METADATA +2 -2
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/RECORD +534 -362
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/licenses/LICENSE.md +1 -1
- django_spire/contrib/options/tests/test_unit.py +0 -148
- django_spire/contrib/seeding/tests/test_seeding.py +0 -25
- django_spire/core/tests/test_templatetags.py +0 -117
- django_spire/core/tests/tests_shortcuts.py +0 -73
- django_spire/history/activity/tests.py +0 -3
- django_spire/history/activity/views.py +0 -3
- django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +0 -71
- django_spire/notification/app/tests.py +0 -3
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/WHEEL +0 -0
- {django_spire-0.23.7.dist-info → django_spire-0.23.9.dist-info}/top_level.txt +0 -0
|
@@ -7,14 +7,13 @@ from unittest.mock import MagicMock, patch
|
|
|
7
7
|
from django_spire.core.tests.test_cases import BaseTestCase
|
|
8
8
|
from django_spire.knowledge.entry.version.consts import MARKDOWN_AI_CHUNK_SIZE
|
|
9
9
|
from django_spire.knowledge.entry.version.converters.docx_converter import DocxConverter
|
|
10
|
-
|
|
11
|
-
from django_spire.knowledge.entry.version.tests.factories import \
|
|
12
|
-
create_test_entry_version
|
|
10
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
class DocxConverterTests(BaseTestCase):
|
|
16
14
|
def setUp(self):
|
|
17
15
|
super().setUp()
|
|
16
|
+
|
|
18
17
|
self.test_entry_version = create_test_entry_version()
|
|
19
18
|
|
|
20
19
|
@patch(
|
|
@@ -22,8 +21,8 @@ class DocxConverterTests(BaseTestCase):
|
|
|
22
21
|
'MarkdownFormatLlmBot.process'
|
|
23
22
|
)
|
|
24
23
|
def test_improve_markdown_structure_concurrent_execution(
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
self,
|
|
25
|
+
mock_process_method: MagicMock
|
|
27
26
|
):
|
|
28
27
|
def slow_process(markdown_content: str):
|
|
29
28
|
time.sleep(0.1)
|
|
@@ -40,6 +39,55 @@ class DocxConverterTests(BaseTestCase):
|
|
|
40
39
|
markdown_content=''.join(mock_chunks)
|
|
41
40
|
)
|
|
42
41
|
end_time = time.time()
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
assert end_time - start_time < 0.3
|
|
43
|
+
assert mock_process_method.call_count == 3
|
|
44
|
+
assert improved_markdown == ''.join(mock_chunks)
|
|
45
|
+
|
|
46
|
+
@patch(
|
|
47
|
+
'django_spire.knowledge.entry.version.converters.docx_converter.'
|
|
48
|
+
'MarkdownFormatLlmBot.process'
|
|
49
|
+
)
|
|
50
|
+
def test_improve_markdown_structure_returns_content(
|
|
51
|
+
self,
|
|
52
|
+
mock_process_method: MagicMock
|
|
53
|
+
):
|
|
54
|
+
mock_process_method.side_effect = lambda x: x
|
|
55
|
+
docx_converter = DocxConverter(entry_version=self.test_entry_version)
|
|
56
|
+
|
|
57
|
+
markdown = 'a' * MARKDOWN_AI_CHUNK_SIZE + 'b' * MARKDOWN_AI_CHUNK_SIZE
|
|
58
|
+
result = docx_converter.improve_markdown_structure(markdown_content=markdown)
|
|
59
|
+
|
|
60
|
+
assert len(result) == len(markdown)
|
|
61
|
+
assert mock_process_method.call_count == 2
|
|
62
|
+
|
|
63
|
+
@patch(
|
|
64
|
+
'django_spire.knowledge.entry.version.converters.docx_converter.'
|
|
65
|
+
'MarkdownFormatLlmBot.process'
|
|
66
|
+
)
|
|
67
|
+
def test_improve_markdown_structure_handles_exception(
|
|
68
|
+
self,
|
|
69
|
+
mock_process_method: MagicMock
|
|
70
|
+
):
|
|
71
|
+
original_chunk = 'x' * MARKDOWN_AI_CHUNK_SIZE
|
|
72
|
+
mock_process_method.side_effect = Exception('Test error')
|
|
73
|
+
docx_converter = DocxConverter(entry_version=self.test_entry_version)
|
|
74
|
+
|
|
75
|
+
result = docx_converter.improve_markdown_structure(markdown_content=original_chunk)
|
|
76
|
+
assert result == original_chunk
|
|
77
|
+
|
|
78
|
+
@patch(
|
|
79
|
+
'django_spire.knowledge.entry.version.converters.docx_converter.'
|
|
80
|
+
'MarkdownFormatLlmBot.process'
|
|
81
|
+
)
|
|
82
|
+
def test_improve_markdown_structure_single_chunk(
|
|
83
|
+
self,
|
|
84
|
+
mock_process_method: MagicMock
|
|
85
|
+
):
|
|
86
|
+
mock_process_method.side_effect = lambda x: x
|
|
87
|
+
docx_converter = DocxConverter(entry_version=self.test_entry_version)
|
|
88
|
+
|
|
89
|
+
short_content = 'short content'
|
|
90
|
+
result = docx_converter.improve_markdown_structure(markdown_content=short_content)
|
|
91
|
+
|
|
92
|
+
assert result == short_content
|
|
93
|
+
assert mock_process_method.call_count == 1
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.knowledge.entry.version.block.choices import BlockTypeChoices
|
|
5
|
+
from django_spire.knowledge.entry.version.converters.markdown_converter import MarkdownConverter
|
|
6
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MarkdownConverterTests(BaseTestCase):
|
|
10
|
+
def setUp(self):
|
|
11
|
+
super().setUp()
|
|
12
|
+
|
|
13
|
+
self.entry_version = create_test_entry_version()
|
|
14
|
+
self.converter = MarkdownConverter(entry_version=self.entry_version)
|
|
15
|
+
|
|
16
|
+
def test_convert_markdown_to_blocks_paragraph(self):
|
|
17
|
+
markdown = 'This is a paragraph.'
|
|
18
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
19
|
+
assert len(blocks) == 1
|
|
20
|
+
assert blocks[0].type == BlockTypeChoices.TEXT
|
|
21
|
+
|
|
22
|
+
def test_convert_markdown_to_blocks_heading(self):
|
|
23
|
+
markdown = '# Heading 1'
|
|
24
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
25
|
+
assert len(blocks) == 1
|
|
26
|
+
assert blocks[0].type == BlockTypeChoices.HEADING
|
|
27
|
+
|
|
28
|
+
def test_convert_markdown_to_blocks_multiple_headings(self):
|
|
29
|
+
markdown = '# H1\n\n## H2\n\n### H3'
|
|
30
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
31
|
+
heading_blocks = [b for b in blocks if b.type == BlockTypeChoices.HEADING]
|
|
32
|
+
assert len(heading_blocks) == 3
|
|
33
|
+
|
|
34
|
+
def test_convert_markdown_to_blocks_unordered_list(self):
|
|
35
|
+
markdown = '- Item 1\n- Item 2\n- Item 3'
|
|
36
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
37
|
+
list_blocks = [b for b in blocks if b.type == BlockTypeChoices.LIST]
|
|
38
|
+
assert len(list_blocks) == 1
|
|
39
|
+
|
|
40
|
+
def test_convert_markdown_to_blocks_ordered_list(self):
|
|
41
|
+
markdown = '1. First\n2. Second\n3. Third'
|
|
42
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
43
|
+
list_blocks = [b for b in blocks if b.type == BlockTypeChoices.LIST]
|
|
44
|
+
assert len(list_blocks) == 1
|
|
45
|
+
|
|
46
|
+
def test_convert_markdown_to_blocks_mixed_content(self):
|
|
47
|
+
markdown = '# Title\n\nSome text.\n\n- List item'
|
|
48
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
49
|
+
assert len(blocks) >= 3
|
|
50
|
+
|
|
51
|
+
def test_convert_markdown_to_blocks_blank_lines(self):
|
|
52
|
+
markdown = 'Para 1\n\n\n\nPara 2'
|
|
53
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
54
|
+
assert len(blocks) >= 2
|
|
55
|
+
|
|
56
|
+
def test_html_to_markdown(self):
|
|
57
|
+
html = '<p>Hello <b>World</b></p>'
|
|
58
|
+
result = MarkdownConverter.html_to_markdown(html)
|
|
59
|
+
assert 'Hello' in result
|
|
60
|
+
assert 'World' in result
|
|
61
|
+
|
|
62
|
+
def test_convert_markdown_to_blocks_checklist(self):
|
|
63
|
+
markdown = '- [ ] Todo\n- [x] Done'
|
|
64
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
65
|
+
list_blocks = [b for b in blocks if b.type == BlockTypeChoices.LIST]
|
|
66
|
+
assert len(list_blocks) == 1
|
|
67
|
+
|
|
68
|
+
def test_convert_markdown_to_blocks_nested_list(self):
|
|
69
|
+
markdown = '- Parent\n - Child'
|
|
70
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
71
|
+
list_blocks = [b for b in blocks if b.type == BlockTypeChoices.LIST]
|
|
72
|
+
assert len(list_blocks) == 1
|
|
73
|
+
|
|
74
|
+
def test_block_ordering(self):
|
|
75
|
+
markdown = '# H1\n\nParagraph\n\n- List'
|
|
76
|
+
blocks = self.converter.convert_markdown_to_blocks(markdown)
|
|
77
|
+
for i, block in enumerate(blocks):
|
|
78
|
+
assert block.order == i
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.knowledge.entry.version.block.data.list.choices import OrderedListCounterType
|
|
5
|
+
from django_spire.knowledge.entry.version.block.data.list.meta import (
|
|
6
|
+
ChecklistItemMeta,
|
|
7
|
+
OrderedListItemMeta,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ChecklistItemMetaTests(BaseTestCase):
|
|
12
|
+
def test_default_unchecked(self):
|
|
13
|
+
meta = ChecklistItemMeta()
|
|
14
|
+
assert meta.checked is False
|
|
15
|
+
|
|
16
|
+
def test_checked_true(self):
|
|
17
|
+
meta = ChecklistItemMeta(checked=True)
|
|
18
|
+
assert meta.checked is True
|
|
19
|
+
|
|
20
|
+
def test_checked_false(self):
|
|
21
|
+
meta = ChecklistItemMeta(checked=False)
|
|
22
|
+
assert meta.checked is False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class OrderedListItemMetaTests(BaseTestCase):
|
|
26
|
+
def test_default_values(self):
|
|
27
|
+
meta = OrderedListItemMeta()
|
|
28
|
+
assert meta.start is None
|
|
29
|
+
assert meta.counterType is None
|
|
30
|
+
|
|
31
|
+
def test_with_start(self):
|
|
32
|
+
meta = OrderedListItemMeta(start=5)
|
|
33
|
+
assert meta.start == 5
|
|
34
|
+
|
|
35
|
+
def test_with_counter_type_numeric(self):
|
|
36
|
+
meta = OrderedListItemMeta(counterType=OrderedListCounterType.NUMERIC)
|
|
37
|
+
assert meta.counterType == OrderedListCounterType.NUMERIC
|
|
38
|
+
|
|
39
|
+
def test_with_counter_type_upper_roman(self):
|
|
40
|
+
meta = OrderedListItemMeta(counterType=OrderedListCounterType.UPPER_ROMAN)
|
|
41
|
+
assert meta.counterType == OrderedListCounterType.UPPER_ROMAN
|
|
42
|
+
|
|
43
|
+
def test_with_counter_type_lower_roman(self):
|
|
44
|
+
meta = OrderedListItemMeta(counterType=OrderedListCounterType.LOWER_ROMAN)
|
|
45
|
+
assert meta.counterType == OrderedListCounterType.LOWER_ROMAN
|
|
46
|
+
|
|
47
|
+
def test_with_counter_type_upper_alpha(self):
|
|
48
|
+
meta = OrderedListItemMeta(counterType=OrderedListCounterType.UPPER_ALPHA)
|
|
49
|
+
assert meta.counterType == OrderedListCounterType.UPPER_ALPHA
|
|
50
|
+
|
|
51
|
+
def test_with_counter_type_lower_alpha(self):
|
|
52
|
+
meta = OrderedListItemMeta(counterType=OrderedListCounterType.LOWER_ALPHA)
|
|
53
|
+
assert meta.counterType == OrderedListCounterType.LOWER_ALPHA
|
|
54
|
+
|
|
55
|
+
def test_with_all_values(self):
|
|
56
|
+
meta = OrderedListItemMeta(start=10, counterType=OrderedListCounterType.NUMERIC)
|
|
57
|
+
assert meta.start == 10
|
|
58
|
+
assert meta.counterType == OrderedListCounterType.NUMERIC
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.knowledge.entry.version.choices import EntryVersionStatusChoices
|
|
5
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EntryVersionModelTests(BaseTestCase):
|
|
9
|
+
def setUp(self):
|
|
10
|
+
super().setUp()
|
|
11
|
+
self.entry_version = create_test_entry_version()
|
|
12
|
+
|
|
13
|
+
def test_is_published_false_when_draft(self):
|
|
14
|
+
self.entry_version.status = EntryVersionStatusChoices.DRAFT
|
|
15
|
+
assert self.entry_version.is_published() is False
|
|
16
|
+
|
|
17
|
+
def test_is_published_true_when_published(self):
|
|
18
|
+
self.entry_version.status = EntryVersionStatusChoices.PUBLISHED
|
|
19
|
+
assert self.entry_version.is_published() is True
|
|
20
|
+
|
|
21
|
+
def test_is_published_false_when_archived(self):
|
|
22
|
+
self.entry_version.status = EntryVersionStatusChoices.ARCHIVED
|
|
23
|
+
assert self.entry_version.is_published() is False
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.knowledge.entry.version.models import EntryVersion
|
|
5
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
6
|
+
from django_spire.knowledge.entry.version.block.tests.factories import create_test_version_block
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EntryVersionQuerySetTests(BaseTestCase):
|
|
10
|
+
def setUp(self):
|
|
11
|
+
super().setUp()
|
|
12
|
+
self.entry_version = create_test_entry_version()
|
|
13
|
+
|
|
14
|
+
def test_prefetch_blocks(self):
|
|
15
|
+
create_test_version_block(version=self.entry_version)
|
|
16
|
+
result = EntryVersion.objects.prefetch_blocks().get(pk=self.entry_version.pk)
|
|
17
|
+
assert result.blocks.count() == 1
|
|
18
|
+
|
|
19
|
+
def test_prefetch_blocks_ordering(self):
|
|
20
|
+
create_test_version_block(version=self.entry_version, order=2)
|
|
21
|
+
create_test_version_block(version=self.entry_version, order=1)
|
|
22
|
+
|
|
23
|
+
result = EntryVersion.objects.prefetch_blocks().get(pk=self.entry_version.pk)
|
|
24
|
+
blocks = list(result.blocks.all())
|
|
25
|
+
|
|
26
|
+
assert blocks[0].order < blocks[1].order
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
4
|
+
from django_spire.knowledge.entry.version.choices import EntryVersionStatusChoices
|
|
5
|
+
from django_spire.knowledge.entry.version.block.models import EntryVersionBlock
|
|
6
|
+
from django_spire.knowledge.entry.version.block.tests.factories import (
|
|
7
|
+
create_test_block_form_data,
|
|
8
|
+
create_test_version_block,
|
|
9
|
+
)
|
|
10
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EntryVersionProcessorServiceTests(BaseTestCase):
|
|
14
|
+
def setUp(self):
|
|
15
|
+
super().setUp()
|
|
16
|
+
self.entry_version = create_test_entry_version()
|
|
17
|
+
|
|
18
|
+
def test_publish(self):
|
|
19
|
+
self.entry_version.services.processor.publish()
|
|
20
|
+
self.entry_version.refresh_from_db()
|
|
21
|
+
assert self.entry_version.status == EntryVersionStatusChoices.PUBLISHED
|
|
22
|
+
assert self.entry_version.published_datetime is not None
|
|
23
|
+
|
|
24
|
+
def test_add_update_delete_blocks_add_new(self):
|
|
25
|
+
block_data = create_test_block_form_data(id='new_block_123')
|
|
26
|
+
self.entry_version.services.processor.add_update_delete_blocks([block_data])
|
|
27
|
+
assert EntryVersionBlock.objects.filter(version=self.entry_version).count() == 1
|
|
28
|
+
|
|
29
|
+
def test_add_update_delete_blocks_update_existing(self):
|
|
30
|
+
existing_block = create_test_version_block(version=self.entry_version)
|
|
31
|
+
block_data = create_test_block_form_data(
|
|
32
|
+
id=existing_block.id,
|
|
33
|
+
data={'text': 'updated text'}
|
|
34
|
+
)
|
|
35
|
+
self.entry_version.services.processor.add_update_delete_blocks([block_data])
|
|
36
|
+
|
|
37
|
+
existing_block.refresh_from_db()
|
|
38
|
+
assert existing_block._block_data['text'] == 'updated text'
|
|
39
|
+
|
|
40
|
+
def test_add_update_delete_blocks_delete_missing(self):
|
|
41
|
+
existing_block = create_test_version_block(version=self.entry_version)
|
|
42
|
+
self.entry_version.services.processor.add_update_delete_blocks([])
|
|
43
|
+
assert not EntryVersionBlock.objects.filter(pk=existing_block.pk).exists()
|
|
44
|
+
|
|
45
|
+
def test_add_update_delete_blocks_mixed_operations(self):
|
|
46
|
+
existing_block = create_test_version_block(version=self.entry_version)
|
|
47
|
+
block_to_delete = create_test_version_block(version=self.entry_version, order=2)
|
|
48
|
+
|
|
49
|
+
block_data_update = create_test_block_form_data(
|
|
50
|
+
id=existing_block.id,
|
|
51
|
+
order=0,
|
|
52
|
+
data={'text': 'updated'}
|
|
53
|
+
)
|
|
54
|
+
block_data_new = create_test_block_form_data(id='new_block', order=1)
|
|
55
|
+
|
|
56
|
+
self.entry_version.services.processor.add_update_delete_blocks([
|
|
57
|
+
block_data_update,
|
|
58
|
+
block_data_new
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
assert EntryVersionBlock.objects.filter(version=self.entry_version).count() == 2
|
|
62
|
+
assert not EntryVersionBlock.objects.filter(pk=block_to_delete.pk).exists()
|
|
@@ -5,23 +5,22 @@ import json
|
|
|
5
5
|
from django.urls import reverse
|
|
6
6
|
|
|
7
7
|
from django_spire.core.tests.test_cases import BaseTestCase
|
|
8
|
+
from django_spire.knowledge.collection.tests.factories import create_test_collection
|
|
8
9
|
from django_spire.knowledge.entry.tests.factories import create_test_entry
|
|
9
|
-
from django_spire.knowledge.entry.version.block.tests.factories import
|
|
10
|
-
|
|
11
|
-
from django_spire.knowledge.entry.version.tests.factories import \
|
|
12
|
-
create_test_entry_version
|
|
10
|
+
from django_spire.knowledge.entry.version.block.tests.factories import create_test_block_form_data
|
|
11
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class EntryVersionJsonUrlsTests(BaseTestCase):
|
|
16
15
|
def setUp(self):
|
|
17
16
|
super().setUp()
|
|
18
|
-
|
|
19
|
-
self.test_entry = create_test_entry()
|
|
17
|
+
self.collection = create_test_collection()
|
|
18
|
+
self.test_entry = create_test_entry(collection=self.collection)
|
|
20
19
|
self.test_entry_version = create_test_entry_version(entry=self.test_entry)
|
|
21
20
|
self.test_entry.current_version = self.test_entry_version
|
|
22
21
|
self.test_entry.save()
|
|
23
22
|
|
|
24
|
-
def
|
|
23
|
+
def test_update_blocks_view(self):
|
|
25
24
|
response = self.client.post(
|
|
26
25
|
reverse(
|
|
27
26
|
'django_spire:knowledge:entry:version:json:update_blocks',
|
|
@@ -30,5 +29,25 @@ class EntryVersionJsonUrlsTests(BaseTestCase):
|
|
|
30
29
|
data=json.dumps([create_test_block_form_data()]),
|
|
31
30
|
content_type='application/json'
|
|
32
31
|
)
|
|
32
|
+
assert response.status_code == 200
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
def test_update_blocks_view_empty_list(self):
|
|
35
|
+
response = self.client.post(
|
|
36
|
+
reverse(
|
|
37
|
+
'django_spire:knowledge:entry:version:json:update_blocks',
|
|
38
|
+
kwargs={'pk': self.test_entry_version.pk}
|
|
39
|
+
),
|
|
40
|
+
data=json.dumps([]),
|
|
41
|
+
content_type='application/json'
|
|
42
|
+
)
|
|
43
|
+
assert response.status_code == 200
|
|
44
|
+
|
|
45
|
+
def test_update_entry_from_version_view(self):
|
|
46
|
+
response = self.client.post(
|
|
47
|
+
reverse(
|
|
48
|
+
'django_spire:knowledge:entry:version:json:update_entry_from_version',
|
|
49
|
+
kwargs={'pk': self.test_entry_version.pk}
|
|
50
|
+
),
|
|
51
|
+
content_type='application/json'
|
|
52
|
+
)
|
|
53
|
+
assert response.status_code == 200
|
|
@@ -3,28 +3,35 @@ from __future__ import annotations
|
|
|
3
3
|
from django.urls import reverse
|
|
4
4
|
|
|
5
5
|
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.knowledge.collection.tests.factories import create_test_collection
|
|
6
7
|
from django_spire.knowledge.entry.tests.factories import create_test_entry
|
|
7
|
-
from django_spire.knowledge.entry.version.tests.factories import
|
|
8
|
-
create_test_entry_version
|
|
8
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class EntryVersionPageUrlsTests(BaseTestCase):
|
|
12
12
|
def setUp(self):
|
|
13
13
|
super().setUp()
|
|
14
14
|
|
|
15
|
-
self.
|
|
16
|
-
self.
|
|
17
|
-
|
|
18
|
-
)
|
|
15
|
+
self.collection = create_test_collection()
|
|
16
|
+
self.test_entry = create_test_entry(collection=self.collection)
|
|
17
|
+
self.test_entry_version = create_test_entry_version(entry=self.test_entry)
|
|
19
18
|
self.test_entry.current_version = self.test_entry_version
|
|
20
19
|
self.test_entry.save()
|
|
21
20
|
|
|
22
|
-
def
|
|
21
|
+
def test_editor_view_url_path(self):
|
|
23
22
|
response = self.client.get(
|
|
24
23
|
reverse(
|
|
25
24
|
'django_spire:knowledge:entry:version:page:editor',
|
|
26
25
|
kwargs={'pk': self.test_entry_version.pk}
|
|
27
26
|
)
|
|
28
27
|
)
|
|
28
|
+
assert response.status_code == 200
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
def test_editor_view_with_edit_mode(self):
|
|
31
|
+
response = self.client.get(
|
|
32
|
+
reverse(
|
|
33
|
+
'django_spire:knowledge:entry:version:page:editor',
|
|
34
|
+
kwargs={'pk': self.test_entry_version.pk}
|
|
35
|
+
) + '?view_mode=edit'
|
|
36
|
+
)
|
|
37
|
+
assert response.status_code == 200
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.urls import reverse
|
|
4
|
+
|
|
5
|
+
from django_spire.core.tests.test_cases import BaseTestCase
|
|
6
|
+
from django_spire.knowledge.collection.tests.factories import create_test_collection
|
|
7
|
+
from django_spire.knowledge.entry.tests.factories import create_test_entry
|
|
8
|
+
from django_spire.knowledge.entry.version.choices import EntryVersionStatusChoices
|
|
9
|
+
from django_spire.knowledge.entry.version.tests.factories import create_test_entry_version
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EntryVersionRedirectUrlsTests(BaseTestCase):
|
|
13
|
+
def setUp(self):
|
|
14
|
+
super().setUp()
|
|
15
|
+
self.collection = create_test_collection()
|
|
16
|
+
self.test_entry = create_test_entry(collection=self.collection)
|
|
17
|
+
self.test_entry_version = create_test_entry_version(entry=self.test_entry)
|
|
18
|
+
self.test_entry.current_version = self.test_entry_version
|
|
19
|
+
self.test_entry.save()
|
|
20
|
+
|
|
21
|
+
def test_publish_view_redirects(self):
|
|
22
|
+
response = self.client.post(
|
|
23
|
+
reverse(
|
|
24
|
+
'django_spire:knowledge:entry:version:redirect:publish',
|
|
25
|
+
kwargs={'pk': self.test_entry_version.pk}
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
assert response.status_code == 302
|
|
29
|
+
|
|
30
|
+
def test_publish_view_updates_status(self):
|
|
31
|
+
self.client.post(
|
|
32
|
+
reverse(
|
|
33
|
+
'django_spire:knowledge:entry:version:redirect:publish',
|
|
34
|
+
kwargs={'pk': self.test_entry_version.pk}
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
self.test_entry_version.refresh_from_db()
|
|
38
|
+
assert self.test_entry_version.status == EntryVersionStatusChoices.PUBLISHED
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from django.urls import path
|
|
2
4
|
|
|
3
5
|
from django_spire.knowledge.entry.version.views import json_views
|
|
@@ -5,7 +7,6 @@ from django_spire.knowledge.entry.version.views import json_views
|
|
|
5
7
|
|
|
6
8
|
app_name = 'json'
|
|
7
9
|
|
|
8
|
-
|
|
9
10
|
urlpatterns = [
|
|
10
11
|
path(
|
|
11
12
|
'<int:pk>/update_blocks/',
|
|
@@ -2,7 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
6
7
|
from django.http import JsonResponse
|
|
7
8
|
from django.shortcuts import get_object_or_404
|
|
8
9
|
|
|
@@ -10,6 +11,9 @@ from django_spire.auth.controller.controller import AppAuthController
|
|
|
10
11
|
from django_spire.core.decorators import valid_ajax_request_required
|
|
11
12
|
from django_spire.knowledge.entry.version.models import EntryVersion
|
|
12
13
|
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
@valid_ajax_request_required
|
|
15
19
|
@AppAuthController('knowledge').permission_required('can_change')
|
|
@@ -2,9 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
|
|
5
|
-
from
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
6
7
|
from django.shortcuts import get_object_or_404
|
|
7
|
-
from django.template.response import TemplateResponse
|
|
8
8
|
from django.urls import reverse
|
|
9
9
|
|
|
10
10
|
from django_spire.auth.controller.controller import AppAuthController
|
|
@@ -12,6 +12,12 @@ from django_spire.contrib.generic_views import portal_views
|
|
|
12
12
|
from django_spire.knowledge.collection.models import Collection
|
|
13
13
|
from django_spire.knowledge.entry.version.models import EntryVersion
|
|
14
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
17
|
+
from django.template.response import TemplateResponse
|
|
18
|
+
|
|
19
|
+
from django_spire.contrib.breadcrumb.breadcrumbs import Breadcrumbs
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
@AppAuthController('knowledge').permission_required('can_view')
|
|
17
23
|
def editor_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
@@ -21,11 +27,12 @@ def editor_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
|
21
27
|
top_level_collection = entry.top_level_collection
|
|
22
28
|
version_blocks = entry_version.blocks.format_for_editor()
|
|
23
29
|
|
|
24
|
-
def breadcrumbs_func(breadcrumbs):
|
|
30
|
+
def breadcrumbs_func(breadcrumbs: Breadcrumbs):
|
|
25
31
|
breadcrumbs.add_breadcrumb(
|
|
26
32
|
name='Knowledge',
|
|
27
33
|
href=reverse('django_spire:knowledge:page:home')
|
|
28
34
|
)
|
|
35
|
+
|
|
29
36
|
breadcrumbs.add_base_breadcrumb(entry)
|
|
30
37
|
|
|
31
38
|
return portal_views.detail_view(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
4
5
|
from django.http import HttpResponseRedirect
|
|
5
6
|
from django.shortcuts import get_object_or_404
|
|
6
7
|
from django.urls import reverse
|
|
@@ -8,6 +9,9 @@ from django.urls import reverse
|
|
|
8
9
|
from django_spire.auth.controller.controller import AppAuthController
|
|
9
10
|
from django_spire.knowledge.entry.version.models import EntryVersion
|
|
10
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
14
|
+
|
|
11
15
|
|
|
12
16
|
@AppAuthController('knowledge').permission_required('can_change')
|
|
13
17
|
def publish_view(request: WSGIRequest, pk: int) -> HttpResponseRedirect:
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import django_glue as dg
|
|
2
4
|
|
|
3
|
-
from
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
4
7
|
from django.http import HttpResponseRedirect
|
|
5
|
-
from django.template.response import TemplateResponse
|
|
6
8
|
from django.urls import reverse
|
|
7
9
|
|
|
8
10
|
from django_spire.auth.controller.controller import AppAuthController
|
|
@@ -16,12 +18,16 @@ from django_spire.knowledge.entry.models import Entry
|
|
|
16
18
|
from django_spire.knowledge.entry.forms import EntryForm, EntryFilesForm
|
|
17
19
|
from django_spire.knowledge.entry.version.maps import FILE_TYPE_CONVERTER_MAP
|
|
18
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from django.core.handlers.wsgi import WSGIRequest
|
|
23
|
+
from django.template.response import TemplateResponse
|
|
24
|
+
|
|
19
25
|
|
|
20
26
|
@AppAuthController('knowledge').permission_required('can_add')
|
|
21
27
|
def form_view(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
request: WSGIRequest,
|
|
29
|
+
collection_pk: int,
|
|
30
|
+
pk: int = 0
|
|
25
31
|
) -> TemplateResponse | HttpResponseRedirect:
|
|
26
32
|
entry = get_object_or_null_obj(Entry, pk=pk)
|
|
27
33
|
collection = Collection.objects.get(pk=collection_pk)
|
|
@@ -70,8 +76,8 @@ def form_view(
|
|
|
70
76
|
|
|
71
77
|
@AppAuthController('knowledge').permission_required('can_add')
|
|
72
78
|
def import_form_view(
|
|
73
|
-
|
|
74
|
-
|
|
79
|
+
request: WSGIRequest,
|
|
80
|
+
collection_pk: int
|
|
75
81
|
) -> TemplateResponse | HttpResponseRedirect:
|
|
76
82
|
dg.glue_query_set(
|
|
77
83
|
request,
|
|
@@ -88,13 +94,15 @@ def import_form_view(
|
|
|
88
94
|
related_field=None,
|
|
89
95
|
app_name='knowledge'
|
|
90
96
|
)
|
|
97
|
+
|
|
91
98
|
file_objects = file_uploader.upload(request.FILES.getlist('import_files'))
|
|
92
99
|
|
|
93
|
-
|
|
100
|
+
Entry.services.factory.create_from_files(
|
|
94
101
|
author=request.user,
|
|
95
102
|
collection=Collection.objects.get(pk=collection_pk),
|
|
96
103
|
files=file_objects
|
|
97
104
|
)
|
|
105
|
+
|
|
98
106
|
return HttpResponseRedirect(
|
|
99
107
|
reverse(
|
|
100
108
|
'django_spire:knowledge:entry:template:file_list',
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
+
|
|
4
5
|
from typing import TYPE_CHECKING
|
|
5
6
|
|
|
7
|
+
from django.http import JsonResponse
|
|
8
|
+
|
|
6
9
|
from django_spire.core.shortcuts import get_object_or_null_obj
|
|
7
10
|
from django_spire.core.decorators import valid_ajax_request_required
|
|
8
11
|
from django_spire.knowledge.collection.models import Collection
|
|
9
12
|
from django_spire.knowledge.entry.models import Entry
|
|
10
|
-
from django.http import JsonResponse
|
|
11
13
|
|
|
12
14
|
if TYPE_CHECKING:
|
|
13
15
|
from django.core.handlers.wsgi import WSGIRequest
|