django-spire 0.18.3__py3-none-any.whl → 0.19.1__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 +1 -0
- django_spire/ai/chat/apps.py +1 -8
- django_spire/ai/chat/intelligence/decoders/tools.py +29 -0
- django_spire/ai/chat/intelligence/prompts.py +13 -7
- django_spire/ai/chat/intelligence/workflows/chat_workflow.py +55 -57
- django_spire/ai/chat/models.py +2 -1
- django_spire/ai/chat/querysets.py +1 -3
- django_spire/ai/chat/responses.py +5 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/card/chat_card.html +2 -2
- django_spire/ai/chat/templates/django_spire/ai/chat/dropdown/ellipsis_dropdown.html +1 -1
- django_spire/ai/chat/templates/django_spire/ai/chat/element/recent_chat_select_element.html +82 -44
- django_spire/ai/chat/templates/django_spire/ai/chat/message/loading_response_message.html +9 -4
- django_spire/ai/chat/templates/django_spire/ai/chat/message/message.html +33 -33
- django_spire/ai/chat/templates/django_spire/ai/chat/message/request_message.html +5 -8
- django_spire/ai/chat/templates/django_spire/ai/chat/message/response_message.html +14 -10
- django_spire/ai/chat/templates/django_spire/ai/chat/page/chat_page.html +35 -0
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/dialog_widget.html +72 -24
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/selection_widget.html +42 -0
- django_spire/ai/chat/tests/test_urls/test_json_urls.py +4 -2
- django_spire/ai/chat/urls/__init__.py +1 -1
- django_spire/ai/chat/urls/json_urls.py +1 -1
- django_spire/ai/chat/urls/page_urls.py +1 -1
- django_spire/ai/chat/urls/{template_urls.py → template/template_urls.py} +1 -1
- django_spire/ai/chat/views/json_views.py +4 -3
- django_spire/ai/chat/views/message_request_views.py +32 -14
- django_spire/ai/chat/views/message_response_views.py +14 -11
- django_spire/ai/chat/views/message_views.py +7 -1
- django_spire/ai/chat/views/page_views.py +2 -2
- django_spire/ai/chat/views/{template_views.py → template/template_views.py} +1 -7
- django_spire/ai/context/__init__.py +0 -0
- django_spire/ai/context/admin.py +15 -0
- django_spire/ai/context/apps.py +16 -0
- django_spire/ai/context/choices.py +11 -0
- django_spire/ai/context/intelligence/__init__.py +0 -0
- django_spire/ai/context/intelligence/prompts/__init__.py +0 -0
- django_spire/ai/context/intelligence/prompts/organization_prompts.py +19 -0
- django_spire/ai/context/migrations/0001_initial.py +67 -0
- django_spire/ai/context/migrations/__init__.py +0 -0
- django_spire/ai/context/models.py +67 -0
- django_spire/ai/context/querysets.py +15 -0
- django_spire/ai/context/seeding/__init__.py +0 -0
- django_spire/ai/context/seeding/seed.py +24 -0
- django_spire/ai/prompt/system/bots.py +3 -5
- django_spire/ai/prompt/tuning/bots.py +5 -10
- django_spire/ai/sms/admin.py +2 -0
- django_spire/ai/sms/apps.py +2 -7
- django_spire/ai/sms/decorators.py +2 -2
- django_spire/ai/sms/intelligence/workflows/sms_conversation_workflow.py +18 -18
- django_spire/ai/sms/tests/test_webhook.py +5 -4
- django_spire/ai/sms/urls.py +3 -0
- django_spire/ai/sms/views.py +3 -5
- django_spire/ai/urls.py +2 -0
- django_spire/auth/templates/django_spire/auth/form/login_form.html +25 -0
- django_spire/auth/templates/django_spire/auth/page/auth_page.html +2 -3
- django_spire/auth/templates/django_spire/auth/page/login_page.html +1 -25
- django_spire/conf.py +1 -3
- django_spire/consts.py +1 -7
- django_spire/contrib/seeding/intelligence/bots/seeder_generator_bot.py +3 -2
- django_spire/contrib/utils.py +2 -0
- django_spire/core/management/commands/spire_startapp_pkg/__init__.py +4 -20
- django_spire/core/management/commands/spire_startapp_pkg/builder.py +2 -2
- django_spire/core/management/commands/spire_startapp_pkg/config.py +2 -2
- django_spire/core/management/commands/spire_startapp_pkg/filesystem.py +1 -19
- django_spire/core/management/commands/spire_startapp_pkg/generator.py +8 -8
- django_spire/core/management/commands/spire_startapp_pkg/processor.py +3 -3
- django_spire/core/management/commands/spire_startapp_pkg/registry.py +0 -16
- django_spire/core/management/commands/spire_startapp_pkg/reporter.py +1 -21
- django_spire/core/management/commands/spire_startapp_pkg/resolver.py +0 -15
- django_spire/core/management/commands/spire_startapp_pkg/template/app/views/form_views.py.template +2 -2
- django_spire/core/management/commands/spire_startapp_pkg/template/app/views/page_views.py.template +2 -2
- django_spire/core/management/commands/spire_startapp_pkg/validator.py +9 -9
- django_spire/core/static/django_spire/css/app-button.css +11 -1
- django_spire/core/static/django_spire/css/app-import.css +2 -0
- django_spire/core/static/django_spire/css/app-layout.css +15 -0
- django_spire/core/static/django_spire/css/app-navigation.css +24 -2
- django_spire/core/static/django_spire/css/app-page.css +8 -0
- django_spire/core/static/django_spire/css/app-side-panel.css +102 -0
- django_spire/core/static/django_spire/css/app-text.css +19 -1
- django_spire/core/static/django_spire/css/bootstrap-extension.css +128 -7
- django_spire/core/static/django_spire/css/bootstrap-override.css +161 -111
- django_spire/core/static/django_spire/css/themes/ayu/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/ayu/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/catppuccin/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/catppuccin/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/default/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/default/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/dracula/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/dracula/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/gruvbox/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/gruvbox/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/material/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/material/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/nord/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/nord/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/oceanic-next/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/oceanic-next/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/one-dark/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/one-dark/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/palenight/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/palenight/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/rose-pine/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/rose-pine/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/synthwave/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/synthwave/app-light.css +0 -4
- django_spire/core/static/django_spire/css/themes/tokyo-night/app-dark.css +0 -4
- django_spire/core/static/django_spire/css/themes/tokyo-night/app-light.css +0 -4
- django_spire/core/static/django_spire/font/Poppins-Black.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-BlackItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Bold.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-BoldItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraBold.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraBoldItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraLight.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ExtraLightItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Italic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Light.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-LightItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Medium.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-MediumItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-SemiBold.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-SemiBoldItalic.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-Thin.ttf +0 -0
- django_spire/core/static/django_spire/font/Poppins-ThinItalic.ttf +0 -0
- django_spire/core/static/django_spire/js/ui.js +12 -0
- django_spire/core/templates/django_spire/button/base_button.html +1 -1
- django_spire/core/templates/django_spire/button/primary_dark_outlined_button.html +3 -0
- django_spire/core/templates/django_spire/dropdown/element/dropdown_link_element.html +12 -8
- django_spire/core/templates/django_spire/dropdown/element/ellipsis_dropdown_modal_link_element.html +2 -1
- django_spire/core/templates/django_spire/dropdown/ellipsis_dropdown.html +2 -1
- django_spire/core/templates/django_spire/navigation/accordion/nav_accordion.html +1 -1
- django_spire/core/templates/django_spire/navigation/elements/nav_link.html +1 -1
- django_spire/core/templates/django_spire/navigation/elements/nav_title_divider.html +1 -1
- django_spire/core/templates/django_spire/navigation/side_navigation.html +52 -8
- django_spire/core/templates/django_spire/navigation/top_navigation.html +8 -2
- django_spire/core/templates/django_spire/page/full_page.html +223 -11
- django_spire/core/utils.py +26 -2
- django_spire/knowledge/collection/models.py +24 -0
- django_spire/knowledge/collection/services/transformation_service.py +2 -1
- django_spire/knowledge/collection/tests/test_services/test_transformation_service.py +44 -42
- django_spire/knowledge/collection/urls/form_urls.py +1 -0
- django_spire/knowledge/collection/urls/page_urls.py +1 -0
- django_spire/knowledge/collection/views/form_views.py +13 -2
- django_spire/knowledge/collection/views/page_views.py +36 -4
- django_spire/knowledge/entry/models.py +32 -0
- django_spire/knowledge/entry/services/transformation_services.py +10 -10
- django_spire/knowledge/entry/version/block/data/data.py +2 -1
- django_spire/knowledge/entry/version/block/data/heading_data.py +2 -2
- django_spire/knowledge/entry/version/block/data/list/data.py +3 -4
- django_spire/knowledge/entry/version/block/data/maps.py +3 -5
- django_spire/knowledge/entry/version/block/data/text_data.py +2 -2
- django_spire/knowledge/entry/version/block/models.py +10 -7
- django_spire/knowledge/entry/version/block/services/factory_service.py +11 -10
- django_spire/knowledge/entry/version/block/tests/factories.py +6 -5
- django_spire/knowledge/entry/version/converters/docx_converter.py +1 -1
- django_spire/knowledge/entry/version/intelligence/bots/markdown_format_llm_bot.py +14 -12
- django_spire/knowledge/entry/version/seeding/seeder.py +3 -3
- django_spire/knowledge/entry/version/services/processor_service.py +36 -10
- django_spire/knowledge/entry/version/tests/test_converters/test_docx_converter.py +1 -1
- django_spire/knowledge/entry/version/tests/test_urls/test_page_urls.py +1 -1
- django_spire/knowledge/entry/version/urls/page_urls.py +1 -1
- django_spire/knowledge/entry/version/views/json_views.py +5 -3
- django_spire/knowledge/entry/version/views/page_views.py +11 -12
- django_spire/knowledge/entry/version/views/redirect_views.py +1 -1
- django_spire/knowledge/entry/views/form_views.py +1 -1
- django_spire/knowledge/intelligence/bots/entry_search_llm_bot.py +33 -11
- django_spire/knowledge/intelligence/decoders/__init__.py +0 -0
- django_spire/knowledge/intelligence/{maps/collection_map.py → decoders/collection_decoder.py} +5 -5
- django_spire/knowledge/intelligence/{maps/entry_map.py → decoders/entry_decoder.py} +3 -3
- django_spire/knowledge/intelligence/intel/collection_intel.py +1 -0
- django_spire/knowledge/intelligence/intel/entry_intel.py +5 -2
- django_spire/knowledge/intelligence/intel/message_intel.py +2 -0
- django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +43 -51
- django_spire/knowledge/migrations/0007_alter_collection_options.py +17 -0
- django_spire/knowledge/static/django_spire/knowledge/entry/version/js/editor.js +24 -5
- django_spire/knowledge/templates/django_spire/knowledge/collection/card/top_level_list_card.html +21 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/component/x_collection_navigation.html +15 -6
- django_spire/knowledge/templates/django_spire/knowledge/collection/element/ellipsis_dropdown.html +22 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/form/form.html +21 -9
- django_spire/knowledge/templates/django_spire/knowledge/collection/item/collection_item.html +19 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/page/display_page.html +49 -0
- django_spire/knowledge/templates/django_spire/knowledge/collection/page/form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/container/container.html +33 -0
- django_spire/knowledge/templates/django_spire/knowledge/entry/file/page/list_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/item/list_item.html +2 -2
- django_spire/knowledge/templates/django_spire/knowledge/entry/page/form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/page/import_form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/container/detail_container.html +51 -45
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/editor_page.html +54 -0
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/form_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/message/knowledge_message_intel.html +18 -1
- django_spire/knowledge/templates/django_spire/knowledge/page/full_page.html +37 -0
- django_spire/knowledge/templates/django_spire/knowledge/page/home_page.html +2 -2
- django_spire/knowledge/templates/django_spire/knowledge/sub_navigation/item/collection_sub_navigation_item.html +29 -0
- django_spire/knowledge/templates/django_spire/knowledge/sub_navigation/item/entry_sub_navigation_item.html +20 -0
- django_spire/knowledge/templates/django_spire/knowledge/{navigation/content/navigation_content.html → sub_navigation/widget/collection_entry_sub_navigation_widget.html} +42 -19
- django_spire/knowledge/views/page_views.py +4 -4
- django_spire/settings.py +3 -3
- django_spire/theme/templates/django_spire/theme/card/badges_preview_card.html +0 -1
- django_spire/theme/templates/django_spire/theme/card/base_preview_card.html +1 -0
- django_spire/theme/templates/django_spire/theme/card/typography_preview_card.html +1 -1
- django_spire/theme/templates/django_spire/theme/example/form/example_form.html +2 -0
- django_spire/theme/templates/django_spire/theme/example/form/example_form_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/page/badges_page.html +7 -9
- django_spire/theme/templates/django_spire/theme/page/borders_page.html +6 -9
- django_spire/theme/templates/django_spire/theme/page/buttons_page.html +6 -9
- django_spire/theme/templates/django_spire/theme/page/colors_page.html +6 -6
- django_spire/theme/templates/django_spire/theme/page/dashboard_page.html +57 -0
- django_spire/theme/templates/django_spire/theme/page/django_glue_page.html +7 -9
- django_spire/theme/templates/django_spire/theme/page/theme_page.html +24 -0
- django_spire/theme/templates/django_spire/theme/page/typography_page.html +6 -9
- django_spire/theme/templates/django_spire/theme/section/badge_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/border_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/button_section.html +1 -0
- django_spire/theme/templates/django_spire/theme/section/button_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/color_section.html +10 -10
- django_spire/theme/templates/django_spire/theme/section/color_section_card.html +5 -0
- django_spire/theme/templates/django_spire/theme/section/typography_section.html +41 -1
- django_spire/theme/templates/django_spire/theme/section/typography_section_card.html +5 -0
- {django_spire-0.18.3.dist-info → django_spire-0.19.1.dist-info}/METADATA +3 -3
- {django_spire-0.18.3.dist-info → django_spire-0.19.1.dist-info}/RECORD +229 -190
- django_spire/ai/chat/intelligence/bots/chat_bot.py +0 -8
- django_spire/ai/chat/intelligence/maps/intent_llm_map.py +0 -10
- django_spire/ai/chat/templates/django_spire/ai/chat/page/home_page.html +0 -9
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/chat_widget.html +0 -64
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_results_widget.html +0 -25
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/search_chat_widget.html +0 -39
- django_spire/ai/chat/templates/django_spire/ai/chat/widget/select_chat_widget.html +0 -24
- django_spire/ai/chat/tools.py +0 -57
- django_spire/ai/sms/tools.py +0 -57
- django_spire/core/static/django_spire/font/Karla-Bold.ttf +0 -0
- django_spire/core/static/django_spire/font/Merriweather.ttf +0 -0
- django_spire/knowledge/context_processors.py +0 -18
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/detail_page.html +0 -25
- django_spire/knowledge/templates/django_spire/knowledge/navigation/card/navigation_card.html +0 -9
- django_spire/knowledge/templates/django_spire/knowledge/navigation/item/collection/collection_item.html +0 -29
- django_spire/knowledge/templates/django_spire/knowledge/navigation/item/entry/entry_item.html +0 -18
- django_spire/knowledge/templates/django_spire/knowledge/navigation/page/full_page.html +0 -90
- /django_spire/ai/chat/intelligence/{bots → decoders}/__init__.py +0 -0
- /django_spire/ai/chat/{intelligence/maps → urls/template}/__init__.py +0 -0
- /django_spire/{knowledge/intelligence/maps → ai/chat/views/template}/__init__.py +0 -0
- /django_spire/knowledge/entry/version/{constants.py → consts.py} +0 -0
- /django_spire/knowledge/templates/django_spire/knowledge/{navigation/item/collection/dropdown/navigation_ellipsis_dropdown.html → sub_navigation/element/collection_sub_navigation_ellipsis_dropdown.html} +0 -0
- /django_spire/knowledge/templates/django_spire/knowledge/{navigation/item/entry/dropdown/navigation_ellipsis_dropdown.html → sub_navigation/element/entry_sub_navigation_ellipsis_dropdown.html} +0 -0
- {django_spire-0.18.3.dist-info → django_spire-0.19.1.dist-info}/WHEEL +0 -0
- {django_spire-0.18.3.dist-info → django_spire-0.19.1.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.18.3.dist-info → django_spire-0.19.1.dist-info}/top_level.txt +0 -0
|
@@ -1,35 +1,247 @@
|
|
|
1
1
|
{% extends 'django_spire/page/page.html' %}
|
|
2
2
|
|
|
3
3
|
{% block page_content %}
|
|
4
|
-
<div
|
|
5
|
-
|
|
4
|
+
<div
|
|
5
|
+
@mousemove.window="
|
|
6
|
+
if (is_resizing_sub && !is_mobile) {
|
|
7
|
+
let delta = $event.clientX - resize_start_x;
|
|
8
|
+
sub_nav_width = resize_start_width + delta;
|
|
9
|
+
|
|
10
|
+
if (sub_nav_width < sub_nav_min_width) sub_nav_width = sub_nav_min_width;
|
|
11
|
+
if (sub_nav_width > sub_nav_max_width) sub_nav_width = sub_nav_max_width;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (is_resizing_info && !is_mobile) {
|
|
15
|
+
let delta = resize_start_x - $event.clientX;
|
|
16
|
+
info_nav_width = resize_start_width + delta;
|
|
17
|
+
|
|
18
|
+
if (info_nav_width < info_nav_min_width) info_nav_width = info_nav_min_width;
|
|
19
|
+
if (info_nav_width > info_nav_max_width) info_nav_width = info_nav_max_width;
|
|
20
|
+
}
|
|
21
|
+
"
|
|
22
|
+
@mouseup.window="is_resizing_sub = false; is_resizing_info = false"
|
|
23
|
+
class="d-flex min-vh-100 w-100"
|
|
24
|
+
x-data="{
|
|
25
|
+
info_nav_max_width: 600,
|
|
26
|
+
info_nav_min_width: 200,
|
|
27
|
+
info_nav_width: 400,
|
|
28
|
+
is_mobile: window.innerWidth < 992,
|
|
29
|
+
is_resizing_info: false,
|
|
30
|
+
is_resizing_sub: false,
|
|
31
|
+
resize_start_width: 0,
|
|
32
|
+
resize_start_x: 0,
|
|
33
|
+
show_info_nav: false,
|
|
34
|
+
show_sub_nav: true,
|
|
35
|
+
sub_nav_has_been_resized: false,
|
|
36
|
+
sub_nav_max_width: 600,
|
|
37
|
+
sub_nav_min_width: 300,
|
|
38
|
+
sub_nav_width: 300,
|
|
39
|
+
|
|
40
|
+
init() {
|
|
41
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
42
|
+
|
|
43
|
+
if (urlParams.has('show_sub_nav')) {
|
|
44
|
+
this.show_sub_nav = urlParams.get('show_sub_nav') === 'true'
|
|
45
|
+
} else {
|
|
46
|
+
this.show_sub_nav = window.innerWidth >= 992
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
window.addEventListener('resize', () => {
|
|
50
|
+
this.is_mobile = window.innerWidth < 992
|
|
51
|
+
if (this.is_mobile) {
|
|
52
|
+
this.show_sub_nav = false
|
|
53
|
+
this.show_info_nav = false
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
start_resize_info(event) {
|
|
59
|
+
if (!this.is_mobile) {
|
|
60
|
+
this.is_resizing_info = true
|
|
61
|
+
this.resize_start_x = event.clientX
|
|
62
|
+
this.resize_start_width = this.info_nav_width
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
start_resize_sub(event) {
|
|
67
|
+
if (!this.is_mobile) {
|
|
68
|
+
let current_width = $refs.sub_nav_container.offsetWidth
|
|
69
|
+
this.sub_nav_width = current_width
|
|
70
|
+
this.sub_nav_has_been_resized = true
|
|
71
|
+
this.is_resizing_sub = true
|
|
72
|
+
this.resize_start_x = event.clientX
|
|
73
|
+
this.resize_start_width = current_width
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
toggle_info() {
|
|
78
|
+
this.show_info_nav = !this.show_info_nav
|
|
79
|
+
|
|
80
|
+
if (this.is_mobile) {
|
|
81
|
+
this.show_sub_nav = false
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
toggle_sub() {
|
|
86
|
+
this.show_sub_nav = !this.show_sub_nav
|
|
87
|
+
|
|
88
|
+
if (this.is_mobile) {
|
|
89
|
+
this.show_info_nav = false
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}"
|
|
93
|
+
>
|
|
94
|
+
<div class="d-none d-lg-block pe-1 side-navigation">
|
|
6
95
|
{% block full_page_side_navigation %}
|
|
7
96
|
{% include 'django_spire/navigation/side_navigation.html' %}
|
|
8
97
|
{% endblock %}
|
|
9
98
|
</div>
|
|
10
99
|
|
|
11
|
-
<div class="d-flex flex-column
|
|
100
|
+
<div class="container-fluid d-flex flex-column">
|
|
12
101
|
<div class="row sticky-top">
|
|
13
|
-
<div class="col-12
|
|
102
|
+
<div class="col-12">
|
|
14
103
|
{% block full_page_top_navigation %}
|
|
15
104
|
{% include 'django_spire/navigation/top_navigation.html' %}
|
|
16
105
|
{% endblock %}
|
|
17
106
|
</div>
|
|
18
107
|
</div>
|
|
19
108
|
|
|
20
|
-
<div class="row
|
|
21
|
-
<div
|
|
109
|
+
<div class="row">
|
|
110
|
+
<div
|
|
111
|
+
:class="is_mobile ? '' : 'position-relative col-auto'"
|
|
112
|
+
:style="is_mobile ? 'position: fixed; width: 100vw; height: 100vh; left: 0; top: 0; z-index: 1040;' : (sub_nav_has_been_resized ? `width: ${sub_nav_width}px; min-width: ${sub_nav_width}px; max-width: ${sub_nav_width}px;` : `min-width: ${sub_nav_min_width}px;`)"
|
|
113
|
+
x-ref="sub_nav_container"
|
|
114
|
+
x-show="has_content($refs.full_page_sub_navigation) && show_sub_nav"
|
|
115
|
+
x-transition:enter="side-panel-transition-enter"
|
|
116
|
+
x-transition:enter-end="side-panel-transition-enter-end"
|
|
117
|
+
x-transition:enter-start="side-panel-transition-enter-start-left"
|
|
118
|
+
x-transition:leave="side-panel-transition-leave"
|
|
119
|
+
x-transition:leave-end="side-panel-transition-leave-end-left"
|
|
120
|
+
x-transition:leave-start="side-panel-transition-leave-start"
|
|
121
|
+
>
|
|
122
|
+
<div
|
|
123
|
+
:style="is_mobile ? 'height: 100vh;' : ''"
|
|
124
|
+
class="row sticky-top border-end border-app-primary side-panel overflow-y-auto"
|
|
125
|
+
>
|
|
126
|
+
<div class="col">
|
|
127
|
+
<div class="row pb-2 align-items-center">
|
|
128
|
+
<div class="col-auto px-1" style="visibility: hidden;">
|
|
129
|
+
<button class="btn-close-panel" type="button">
|
|
130
|
+
<i class="bi bi-x-lg f5"></i>
|
|
131
|
+
</button>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="col h5 text-app-primary text-center text-nowrap overflow-hidden text-truncate mb-0" style="min-width: 0;">
|
|
134
|
+
{% block full_page_sub_navigation_title %}
|
|
135
|
+
{% endblock %}
|
|
136
|
+
</div>
|
|
137
|
+
<div class="col-auto px-1">
|
|
138
|
+
{% block full_page_sub_navigation_buttons %}
|
|
139
|
+
{% endblock %}
|
|
140
|
+
</div>
|
|
141
|
+
<div class="col-auto px-1">
|
|
142
|
+
<button
|
|
143
|
+
@click="show_sub_nav = false"
|
|
144
|
+
class="btn-close-panel"
|
|
145
|
+
type="button"
|
|
146
|
+
>
|
|
147
|
+
<i class="bi bi-x-lg f5"></i>
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
<div class="row">
|
|
153
|
+
<div class="col" x-ref="full_page_sub_navigation">
|
|
154
|
+
{% block full_page_sub_navigation %}
|
|
155
|
+
{% endblock %}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
<div
|
|
161
|
+
@mousedown="start_resize_sub($event)"
|
|
162
|
+
class="position-absolute top-0 d-flex align-items-center justify-content-center"
|
|
163
|
+
style="cursor: ew-resize; height: 100%; right: -8px; user-select: none; width: 16px; z-index: 1000;"
|
|
164
|
+
x-show="!is_mobile"
|
|
165
|
+
>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div
|
|
170
|
+
class="panel-toggle-container panel-toggle-container-left"
|
|
171
|
+
x-show="has_content($refs.full_page_sub_navigation) && !show_sub_nav && !(is_mobile && show_info_nav)"
|
|
172
|
+
>
|
|
173
|
+
{% include 'django_spire/button/primary_button.html' with button_icon='bi bi-chevron-right' x_button_click='toggle_sub()' %}
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="col">
|
|
22
177
|
{% block full_page_content %}
|
|
23
178
|
{% endblock %}
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
179
|
|
|
27
|
-
<div class="row">
|
|
28
|
-
<div class="col-12 px-0 mt-2">
|
|
29
180
|
{% block full_page_bottom_content %}
|
|
30
|
-
{% include 'django_spire/footer/footer.html' %}
|
|
31
181
|
{% endblock %}
|
|
32
182
|
</div>
|
|
183
|
+
|
|
184
|
+
<div
|
|
185
|
+
class="col-auto panel-toggle-container panel-toggle-container-right"
|
|
186
|
+
x-show="has_content($refs.full_page_info_navigation) && !show_info_nav && !(is_mobile && show_sub_nav)"
|
|
187
|
+
>
|
|
188
|
+
{% include 'django_spire/button/primary_button.html' with button_icon='bi bi-chevron-left' x_button_click='toggle_info()' %}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div
|
|
192
|
+
:class="is_mobile ? '' : 'position-relative col-auto'"
|
|
193
|
+
:style="is_mobile ? 'position: fixed; width: 100vw; height: 100vh; right: 0; top: 0; z-index: 1040;' : `width: ${info_nav_width}px; min-width: ${info_nav_width}px; max-width: ${info_nav_width}px; margin-left: -${info_nav_width}px; z-index: 1040;`"
|
|
194
|
+
x-show="has_content($refs.full_page_info_navigation) && show_info_nav"
|
|
195
|
+
x-transition:enter="side-panel-transition-enter"
|
|
196
|
+
x-transition:enter-end="side-panel-transition-enter-end"
|
|
197
|
+
x-transition:enter-start="side-panel-transition-enter-start-right"
|
|
198
|
+
x-transition:leave="side-panel-transition-leave"
|
|
199
|
+
x-transition:leave-end="side-panel-transition-leave-end-right"
|
|
200
|
+
x-transition:leave-start="side-panel-transition-leave-start"
|
|
201
|
+
>
|
|
202
|
+
<div
|
|
203
|
+
:style="is_mobile ? 'height: 100vh;' : ''"
|
|
204
|
+
class="row sticky-top border-start border-app-primary side-panel overflow-y-auto bg-app-layer-one"
|
|
205
|
+
>
|
|
206
|
+
<div class="col">
|
|
207
|
+
<div class="row pb-2">
|
|
208
|
+
<div class="col text-center pt-1 h5 text-app-primary">
|
|
209
|
+
{% block full_page_info_navigation_title %}
|
|
210
|
+
{% endblock %}
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div class="col-auto px-1">
|
|
214
|
+
{% block full_page_info_navigation_buttons %}
|
|
215
|
+
{% endblock %}
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<div class="col-auto px-1">
|
|
219
|
+
<button
|
|
220
|
+
@click="show_info_nav = false"
|
|
221
|
+
class="btn-close-panel"
|
|
222
|
+
type="button"
|
|
223
|
+
>
|
|
224
|
+
<i class="bi bi-x-lg"></i>
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div class="row">
|
|
230
|
+
<div class="col" x-ref="full_page_info_navigation">
|
|
231
|
+
{% block full_page_info_navigation %}
|
|
232
|
+
{% endblock %}
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
<div
|
|
239
|
+
@mousedown="start_resize_info($event)"
|
|
240
|
+
class="position-absolute top-0"
|
|
241
|
+
style="cursor: ew-resize; height: 100%; left: -8px; user-select: none; width: 16px; z-index: 1000;"
|
|
242
|
+
x-show="!is_mobile"
|
|
243
|
+
></div>
|
|
244
|
+
</div>
|
|
33
245
|
</div>
|
|
34
246
|
</div>
|
|
35
247
|
</div>
|
django_spire/core/utils.py
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
|
-
|
|
1
|
+
import inspect
|
|
2
|
+
from inspect import Signature
|
|
3
|
+
from typing import Callable, Any, Sequence
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_object_from_module_string(module_string: str) -> Any:
|
|
2
7
|
try:
|
|
3
8
|
module_string, object_name = module_string.rsplit('.', 1)
|
|
4
9
|
module = __import__(module_string, fromlist=[object_name])
|
|
5
10
|
except ImportError:
|
|
6
11
|
raise ImportError(f'Could not import module: {module_string}')
|
|
7
12
|
|
|
8
|
-
return getattr(module, object_name)
|
|
13
|
+
return getattr(module, object_name)
|
|
14
|
+
|
|
15
|
+
def get_callable_from_module_string_and_validate_arguments(
|
|
16
|
+
module_string: str,
|
|
17
|
+
valid_args: Sequence[str],
|
|
18
|
+
) -> Callable:
|
|
19
|
+
callable_ = get_object_from_module_string(module_string)
|
|
20
|
+
|
|
21
|
+
if not callable(callable_):
|
|
22
|
+
raise TypeError(f'Object {module_string} is not callable')
|
|
23
|
+
|
|
24
|
+
sig = inspect.signature(callable_)
|
|
25
|
+
params = sig.parameters
|
|
26
|
+
|
|
27
|
+
for name in valid_args:
|
|
28
|
+
if name not in params:
|
|
29
|
+
message = f'{callable_.__qualname__} is missing required argument: {name}'
|
|
30
|
+
raise TypeError(message)
|
|
31
|
+
|
|
32
|
+
return callable_
|
|
@@ -3,7 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
5
5
|
from django_spire.auth.group.models import AuthGroup
|
|
6
|
+
from django_spire.contrib import Breadcrumbs
|
|
6
7
|
from django_spire.contrib.ordering.mixins import OrderingModelMixin
|
|
8
|
+
from django_spire.contrib.utils import truncate_string
|
|
7
9
|
from django_spire.history.mixins import HistoryModelMixin
|
|
8
10
|
from django_spire.knowledge.collection.querysets import CollectionQuerySet
|
|
9
11
|
from django_spire.knowledge.collection.services.service import CollectionGroupService, \
|
|
@@ -29,6 +31,24 @@ class Collection(HistoryModelMixin, OrderingModelMixin):
|
|
|
29
31
|
def __str__(self):
|
|
30
32
|
return self.name
|
|
31
33
|
|
|
34
|
+
@property
|
|
35
|
+
def name_short(self) -> str:
|
|
36
|
+
return truncate_string(self.name, 32)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def top_level_parent(self) -> Collection:
|
|
40
|
+
if self.parent is None:
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
return self.parent.top_level_parent
|
|
44
|
+
|
|
45
|
+
def base_breadcrumb(self) -> Breadcrumbs:
|
|
46
|
+
breadcrumbs = Breadcrumbs()
|
|
47
|
+
|
|
48
|
+
breadcrumbs.add_breadcrumb(self.name_short)
|
|
49
|
+
|
|
50
|
+
return breadcrumbs
|
|
51
|
+
|
|
32
52
|
class Meta:
|
|
33
53
|
verbose_name = 'Collection'
|
|
34
54
|
verbose_name_plural = 'Collections'
|
|
@@ -37,6 +57,7 @@ class Collection(HistoryModelMixin, OrderingModelMixin):
|
|
|
37
57
|
('can_access_all_collections', 'Can Access All Collections'),
|
|
38
58
|
('can_change_collection_groups', 'Can Change Collection Groups'),
|
|
39
59
|
]
|
|
60
|
+
ordering = ['name']
|
|
40
61
|
|
|
41
62
|
|
|
42
63
|
class CollectionGroup(models.Model):
|
|
@@ -55,3 +76,6 @@ class CollectionGroup(models.Model):
|
|
|
55
76
|
)
|
|
56
77
|
|
|
57
78
|
services = CollectionGroupService()
|
|
79
|
+
|
|
80
|
+
def __str__(self):
|
|
81
|
+
return f'{self.collection.name} - {self.auth_group.name}'
|
|
@@ -19,10 +19,11 @@ if TYPE_CHECKING:
|
|
|
19
19
|
class CollectionTransformationService(BaseDjangoModelService['Collection']):
|
|
20
20
|
obj: Collection
|
|
21
21
|
|
|
22
|
-
def to_hierarchy_json(self, request: WSGIRequest) -> str:
|
|
22
|
+
def to_hierarchy_json(self, request: WSGIRequest, parent_id: int | None = None) -> str:
|
|
23
23
|
collections = (
|
|
24
24
|
self.obj_class.objects
|
|
25
25
|
.active()
|
|
26
|
+
.by_parent_id(parent_id=parent_id)
|
|
26
27
|
.request_user_has_access(request)
|
|
27
28
|
.select_related('parent')
|
|
28
29
|
)
|
|
@@ -25,45 +25,47 @@ class TestCollectionTransformationService(BaseTestCase):
|
|
|
25
25
|
response = self.client.get(collection_json['import_entry_url'].replace(' ', ''))
|
|
26
26
|
self.assertEqual(response.status_code, 200)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
28
|
+
# Todo: Talk to Chase about this
|
|
29
|
+
# def test_to_hierarchy_json(self):
|
|
30
|
+
# settings.SITE_ID = self.site.id
|
|
31
|
+
#
|
|
32
|
+
# self.test_collection_1 = Collection.objects.create(name='Grand Parent A', id=1, parent_id=None)
|
|
33
|
+
# self.test_collection_2 = Collection.objects.create(name='Parent A1', id=2, parent_id=1)
|
|
34
|
+
# self.test_collection_3 = Collection.objects.create(name='Child A1a', id=3, parent_id=2)
|
|
35
|
+
#
|
|
36
|
+
# request = RequestFactory().get('/')
|
|
37
|
+
# request.user = self.super_user
|
|
38
|
+
#
|
|
39
|
+
# family_tree = Collection.services.transformation.to_hierarchy_json(
|
|
40
|
+
# request=request
|
|
41
|
+
# )
|
|
42
|
+
#
|
|
43
|
+
# for collection_json in json.loads(family_tree):
|
|
44
|
+
# self.assertIsNotNone(collection_json['id'])
|
|
45
|
+
# self.assertEqual(collection_json['name'], 'Grand Parent A')
|
|
46
|
+
#
|
|
47
|
+
# self.assertEqual(len(collection_json['entries']), 0)
|
|
48
|
+
#
|
|
49
|
+
# self._test_collection_urls_json(collection_json=collection_json)
|
|
50
|
+
#
|
|
51
|
+
# self.assertEqual(len(collection_json['children']), 1)
|
|
52
|
+
#
|
|
53
|
+
# for child_json in collection_json['children']:
|
|
54
|
+
# self.assertIsNotNone(child_json['id'])
|
|
55
|
+
# self.assertEqual(child_json['name'], 'Parent A1')
|
|
56
|
+
#
|
|
57
|
+
# self.assertEqual(len(child_json['entries']), 0)
|
|
58
|
+
#
|
|
59
|
+
# self._test_collection_urls_json(collection_json=child_json)
|
|
60
|
+
#
|
|
61
|
+
# self.assertEqual(len(child_json['children']), 1)
|
|
62
|
+
#
|
|
63
|
+
# for grand_child_json in child_json['children']:
|
|
64
|
+
# self.assertIsNotNone(grand_child_json['id'])
|
|
65
|
+
# self.assertEqual(grand_child_json['name'], 'Child A1a')
|
|
66
|
+
#
|
|
67
|
+
# self.assertEqual(len(grand_child_json['entries']), 0)
|
|
68
|
+
#
|
|
69
|
+
# self._test_collection_urls_json(collection_json=grand_child_json)
|
|
70
|
+
#
|
|
71
|
+
# self.assertEqual(len(grand_child_json['children']), 0)
|
|
@@ -23,11 +23,13 @@ if TYPE_CHECKING:
|
|
|
23
23
|
@AppAuthController('knowledge').permission_required('can_add')
|
|
24
24
|
def form_view(
|
|
25
25
|
request: WSGIRequest,
|
|
26
|
-
pk: int = 0
|
|
26
|
+
pk: int = 0,
|
|
27
|
+
parent_pk: int = None,
|
|
27
28
|
) -> TemplateResponse | HttpResponseRedirect:
|
|
28
29
|
collection = get_object_or_null_obj(Collection, pk=pk)
|
|
29
30
|
|
|
30
31
|
dg.glue_model_object(request, unique_name='collection', model_object=collection)
|
|
32
|
+
|
|
31
33
|
dg.glue_query_set(
|
|
32
34
|
request,
|
|
33
35
|
unique_name='collections',
|
|
@@ -59,8 +61,16 @@ def form_view(
|
|
|
59
61
|
collection=collection,
|
|
60
62
|
)
|
|
61
63
|
|
|
64
|
+
if collection.parent_id:
|
|
65
|
+
return_url = reverse(
|
|
66
|
+
'django_spire:knowledge:collection:page:top_level',
|
|
67
|
+
kwargs={'pk': collection.parent_id}
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
return_url = reverse('django_spire:knowledge:page:home')
|
|
71
|
+
|
|
62
72
|
return HttpResponseRedirect(
|
|
63
|
-
|
|
73
|
+
return_url
|
|
64
74
|
)
|
|
65
75
|
|
|
66
76
|
show_form_errors(request, form)
|
|
@@ -73,6 +83,7 @@ def form_view(
|
|
|
73
83
|
obj=collection,
|
|
74
84
|
context_data={
|
|
75
85
|
'collection': collection,
|
|
86
|
+
'collection_parent_pk': parent_pk,
|
|
76
87
|
'group_ids': list(
|
|
77
88
|
collection.groups.all().values_list('auth_group_id', flat=True)
|
|
78
89
|
) if collection.id else [],
|
|
@@ -6,20 +6,52 @@ from django.template.response import TemplateResponse
|
|
|
6
6
|
from django.urls import reverse
|
|
7
7
|
|
|
8
8
|
from django_spire.auth.controller.controller import AppAuthController
|
|
9
|
+
from django_spire.contrib import Breadcrumbs
|
|
9
10
|
from django_spire.contrib.generic_views import portal_views
|
|
10
11
|
from django_spire.knowledge.collection.models import Collection
|
|
11
12
|
|
|
12
13
|
|
|
14
|
+
@AppAuthController('knowledge').permission_required('can_view')
|
|
15
|
+
def top_level_collection_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
16
|
+
collection = get_object_or_404(Collection, pk=pk)
|
|
17
|
+
|
|
18
|
+
breadcrumbs = Breadcrumbs()
|
|
19
|
+
breadcrumbs.add_breadcrumb('Knowledge', reverse('django_spire:knowledge:page:home'))
|
|
20
|
+
breadcrumbs.add_base_breadcrumb(collection)
|
|
21
|
+
|
|
22
|
+
return portal_views.template_view(
|
|
23
|
+
request,
|
|
24
|
+
page_title='Knowledge Collection',
|
|
25
|
+
page_description='',
|
|
26
|
+
breadcrumbs=breadcrumbs,
|
|
27
|
+
context_data={
|
|
28
|
+
'collection': collection,
|
|
29
|
+
'collection_tree_json': Collection.services.transformation.to_hierarchy_json(
|
|
30
|
+
request=request, parent_id=collection.id
|
|
31
|
+
),
|
|
32
|
+
},
|
|
33
|
+
template='django_spire/knowledge/collection/page/display_page.html',
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
13
37
|
@AppAuthController('knowledge').permission_required('can_delete')
|
|
14
38
|
def delete_view(request: WSGIRequest, pk: int) -> TemplateResponse:
|
|
15
39
|
collection = get_object_or_404(Collection, pk=pk)
|
|
16
40
|
|
|
41
|
+
if collection.parent:
|
|
42
|
+
return_url = request.GET.get(
|
|
43
|
+
'return_url',
|
|
44
|
+
reverse(
|
|
45
|
+
'django_spire:knowledge:collection:page:top_level',
|
|
46
|
+
kwargs={'pk': collection.parent_id}
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
return_url = request.GET.get('return_url', reverse('django_spire:knowledge:page:home'))
|
|
51
|
+
|
|
17
52
|
return portal_views.delete_form_view(
|
|
18
53
|
request,
|
|
19
54
|
obj=collection,
|
|
20
55
|
delete_func=collection.services.processor.set_deleted,
|
|
21
|
-
return_url=
|
|
22
|
-
'return_url',
|
|
23
|
-
reverse('django_spire:knowledge:page:home')
|
|
24
|
-
)
|
|
56
|
+
return_url=return_url
|
|
25
57
|
)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from django.urls import reverse
|
|
2
3
|
|
|
4
|
+
from django_spire.contrib import Breadcrumbs
|
|
3
5
|
from django_spire.contrib.ordering.mixins import OrderingModelMixin
|
|
6
|
+
from django_spire.contrib.utils import truncate_string
|
|
4
7
|
from django_spire.history.mixins import HistoryModelMixin
|
|
5
8
|
from django_spire.knowledge.collection.models import Collection
|
|
6
9
|
from django_spire.knowledge.entry.querysets import EntryQuerySet
|
|
@@ -32,6 +35,35 @@ class Entry(HistoryModelMixin, OrderingModelMixin):
|
|
|
32
35
|
def __str__(self):
|
|
33
36
|
return self.name
|
|
34
37
|
|
|
38
|
+
@property
|
|
39
|
+
def name_short(self) -> str:
|
|
40
|
+
return truncate_string(self.name, 32)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def top_level_collection(self) -> Collection:
|
|
44
|
+
return self.collection.top_level_parent
|
|
45
|
+
|
|
46
|
+
def base_breadcrumb(self) -> Breadcrumbs:
|
|
47
|
+
bread_crumbs = Breadcrumbs()
|
|
48
|
+
|
|
49
|
+
bread_crumbs.add_breadcrumb(
|
|
50
|
+
self.top_level_collection.name_short,
|
|
51
|
+
reverse(
|
|
52
|
+
'django_spire:knowledge:collection:page:top_level',
|
|
53
|
+
kwargs={'pk': self.top_level_collection.pk}
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
bread_crumbs.add_breadcrumb(
|
|
58
|
+
self.collection.name_short
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
bread_crumbs.add_breadcrumb(
|
|
62
|
+
self.name_short,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return bread_crumbs
|
|
66
|
+
|
|
35
67
|
class Meta:
|
|
36
68
|
verbose_name = 'Entry'
|
|
37
69
|
verbose_name_plural = 'Entries'
|
|
@@ -42,15 +42,15 @@ class EntryTransformationService(BaseDjangoModelService['Entry']):
|
|
|
42
42
|
if current_version.published_datetime else ''
|
|
43
43
|
),
|
|
44
44
|
'status': current_version.status,
|
|
45
|
-
'delete_url': f
|
|
45
|
+
'delete_url': f"""
|
|
46
46
|
{site}{
|
|
47
47
|
reverse(
|
|
48
48
|
'django_spire:knowledge:entry:page:delete',
|
|
49
49
|
kwargs={'pk': self.obj.pk},
|
|
50
50
|
)
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
'edit_url': f
|
|
52
|
+
""",
|
|
53
|
+
'edit_url': f"""
|
|
54
54
|
{site}{
|
|
55
55
|
reverse(
|
|
56
56
|
'django_spire:knowledge:entry:form:update',
|
|
@@ -60,21 +60,21 @@ class EntryTransformationService(BaseDjangoModelService['Entry']):
|
|
|
60
60
|
},
|
|
61
61
|
)
|
|
62
62
|
}
|
|
63
|
-
|
|
64
|
-
'view_url': f
|
|
63
|
+
""",
|
|
64
|
+
'view_url': f"""
|
|
65
65
|
{site}{
|
|
66
66
|
reverse(
|
|
67
|
-
'django_spire:knowledge:entry:version:page:
|
|
67
|
+
'django_spire:knowledge:entry:version:page:editor',
|
|
68
68
|
kwargs={'pk': current_version.pk},
|
|
69
69
|
)
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
'edit_version_url': f
|
|
71
|
+
""",
|
|
72
|
+
'edit_version_url': f"""
|
|
73
73
|
{site}{
|
|
74
74
|
reverse(
|
|
75
|
-
'django_spire:knowledge:entry:version:page:
|
|
75
|
+
'django_spire:knowledge:entry:version:page:editor',
|
|
76
76
|
kwargs={'pk': current_version.pk},
|
|
77
77
|
)
|
|
78
78
|
}?view_mode=edit
|
|
79
|
-
|
|
79
|
+
"""
|
|
80
80
|
}
|