django-spire 0.25.1__py3-none-any.whl → 0.26.0__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.
Files changed (124) hide show
  1. django_spire/ai/chat/router.py +10 -20
  2. django_spire/ai/chat/templates/django_spire/ai/chat/dropdown/ellipsis_dropdown.html +5 -3
  3. django_spire/ai/chat/templates/django_spire/ai/chat/message/default_message.html +6 -2
  4. django_spire/ai/chat/templatetags/spire_ai_chat_tags.py +19 -0
  5. django_spire/ai/chat/tests/test_router/test_spire_chat_router.py +20 -88
  6. django_spire/auth/templates/django_spire/auth/element/android_and_chrome_app_install_element.html +33 -0
  7. django_spire/auth/templates/django_spire/auth/element/ios_app_install_element.html +48 -0
  8. django_spire/auth/templates/django_spire/auth/page/auth_page.html +2 -1
  9. django_spire/auth/templates/django_spire/auth/page/login_page.html +2 -0
  10. django_spire/comment/mixins.py +3 -3
  11. django_spire/comment/templates/django_spire/comment/card/comment_list_card.html +8 -5
  12. django_spire/comment/templates/django_spire/comment/form/comment_form.html +3 -3
  13. django_spire/comment/templates/django_spire/comment/form/content/comment_form_content.html +1 -0
  14. django_spire/comment/templates/django_spire/comment/item/comment_item_ellipsis.html +12 -8
  15. django_spire/comment/views.py +8 -8
  16. django_spire/consts.py +1 -1
  17. django_spire/contrib/form/utils.py +3 -3
  18. django_spire/contrib/progress/session.py +1 -1
  19. django_spire/contrib/queryset/filter_tools.py +56 -14
  20. django_spire/contrib/queryset/mixins.py +24 -3
  21. django_spire/contrib/service/django_model_service.py +5 -6
  22. django_spire/core/management/commands/spire_startapp.py +42 -25
  23. django_spire/core/management/commands/spire_startapp_pkg/exceptions.py +5 -0
  24. django_spire/core/management/commands/spire_startapp_pkg/maps.py +64 -32
  25. django_spire/core/management/commands/spire_startapp_pkg/template/app/constants.py.template +1 -0
  26. django_spire/core/management/commands/spire_startapp_pkg/template/app/forms.py.template +4 -0
  27. django_spire/core/management/commands/spire_startapp_pkg/template/app/models.py.template +2 -1
  28. django_spire/core/management/commands/spire_startapp_pkg/template/app/querysets.py.template +15 -6
  29. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/__init__.py.template +1 -0
  30. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/form_urls.py.template +6 -6
  31. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/page_urls.py.template +2 -2
  32. django_spire/core/management/commands/spire_startapp_pkg/template/app/urls/template_urls.py.template +12 -0
  33. django_spire/core/management/commands/spire_startapp_pkg/template/app/views/form_views.py.template +10 -11
  34. django_spire/core/management/commands/spire_startapp_pkg/template/app/views/page_views.py.template +17 -3
  35. django_spire/core/management/commands/spire_startapp_pkg/template/app/views/template_views.py.template +40 -0
  36. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/${form_card_template_name}.html.template +1 -1
  37. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/${list_base_card_template_name}.html.template +16 -0
  38. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/${list_items_card_template_name}.html.template +16 -0
  39. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/${list_table_card_template_name}.html.template +16 -0
  40. django_spire/core/management/commands/spire_startapp_pkg/template/templates/container/${list_container_template_name}.html.template +1 -0
  41. django_spire/core/management/commands/spire_startapp_pkg/template/templates/form/${list_filter_form_template_name}.html.template +30 -0
  42. django_spire/core/management/commands/spire_startapp_pkg/template/templates/item/${item_template_name}.html.template +32 -20
  43. django_spire/core/management/commands/spire_startapp_pkg/template/templates/item/${list_items_template_name}.html.template +3 -0
  44. django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/${detail_page_template_name}.html.template +3 -3
  45. django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/${form_page_template_name}.html.template +2 -2
  46. django_spire/core/management/commands/spire_startapp_pkg/template/templates/page/${list_page_template_name}.html.template +2 -2
  47. django_spire/core/management/commands/spire_startapp_pkg/template/templates/table/${table_row_template_name}.html.template +6 -0
  48. django_spire/core/management/commands/spire_startapp_pkg/template/templates/table/${table_rows_template_name}.html.template +3 -0
  49. django_spire/core/management/commands/spire_startapp_pkg/template/templates/table/${table_template_name}.html.template +6 -0
  50. django_spire/core/management/commands/spire_startapp_pkg/user_input.py +82 -9
  51. django_spire/core/management/commands/spire_startapp_pkg/validator.py +19 -6
  52. django_spire/core/middleware.py +2 -3
  53. django_spire/core/querysets.py +19 -0
  54. django_spire/core/static/django_spire/js/theme.js +10 -7
  55. django_spire/core/templates/django_spire/badge/base_badge.html +2 -3
  56. django_spire/core/templates/django_spire/base/base.html +1 -0
  57. django_spire/core/templates/django_spire/button/base_button.html +2 -1
  58. django_spire/core/templates/django_spire/card/title_card.html +13 -10
  59. django_spire/core/templates/django_spire/container/container.html +1 -1
  60. django_spire/core/templates/django_spire/filtering/form/base_session_filter_form.html +1 -1
  61. django_spire/core/templates/django_spire/form/field/_base_file_field.html +216 -0
  62. django_spire/core/templates/django_spire/form/field/_multi_checkbox_field.html +52 -0
  63. django_spire/core/templates/django_spire/form/field/base_field.html +128 -0
  64. django_spire/core/templates/django_spire/form/field/char_field.html +1 -0
  65. django_spire/core/templates/django_spire/form/field/color_field.html +1 -0
  66. django_spire/core/templates/django_spire/form/field/date_field.html +1 -0
  67. django_spire/core/templates/django_spire/form/field/datetime_field.html +1 -0
  68. django_spire/core/templates/django_spire/form/field/decimal_field.html +1 -0
  69. django_spire/core/templates/django_spire/form/field/element/select_checkmark_element.html +4 -0
  70. django_spire/core/templates/django_spire/form/field/element/select_down_arrow_element.html +5 -0
  71. django_spire/core/templates/django_spire/form/field/email_field.html +1 -0
  72. django_spire/core/templates/django_spire/form/field/input_field.html +13 -0
  73. django_spire/core/templates/django_spire/form/field/item/select_choice_item.html +15 -0
  74. django_spire/core/templates/django_spire/form/field/item/selected_choice_item.html +5 -0
  75. django_spire/core/templates/django_spire/form/field/list_field.html +112 -0
  76. django_spire/core/templates/django_spire/form/field/multi_file_field.html +90 -0
  77. django_spire/core/templates/django_spire/form/field/multi_select_field.html +155 -0
  78. django_spire/core/templates/django_spire/form/field/number_field.html +11 -0
  79. django_spire/core/templates/django_spire/form/field/password_field.html +1 -0
  80. django_spire/core/templates/django_spire/form/field/radio_field.html +24 -0
  81. django_spire/core/templates/django_spire/form/field/range_field.html +1 -0
  82. django_spire/core/templates/django_spire/form/field/search_and_select_field.html +119 -0
  83. django_spire/core/templates/django_spire/form/field/search_field.html +5 -0
  84. django_spire/core/templates/django_spire/form/field/select_field.html +78 -0
  85. django_spire/core/templates/django_spire/form/field/single_checkbox_field.html +27 -0
  86. django_spire/core/templates/django_spire/form/field/single_file_field.html +90 -0
  87. django_spire/core/templates/django_spire/form/field/telephone_field.html +1 -0
  88. django_spire/core/templates/django_spire/form/field/text_field.html +11 -0
  89. django_spire/core/templates/django_spire/form/field/time_field.html +1 -0
  90. django_spire/core/templates/django_spire/infinite_scroll/base.html +2 -1
  91. django_spire/core/templatetags/model_tags.py +34 -0
  92. django_spire/knowledge/entry/forms.py +1 -1
  93. django_spire/knowledge/entry/models.py +18 -0
  94. django_spire/knowledge/entry/querysets.py +8 -6
  95. django_spire/knowledge/entry/services/processor_service.py +1 -0
  96. django_spire/knowledge/entry/services/search_index_service.py +61 -0
  97. django_spire/knowledge/entry/services/search_service.py +99 -0
  98. django_spire/knowledge/entry/services/service.py +6 -0
  99. django_spire/knowledge/entry/version/services/processor_service.py +2 -0
  100. django_spire/knowledge/entry/version/tests/factories.py +9 -4
  101. django_spire/knowledge/entry/version/tests/test_services.py +7 -16
  102. django_spire/knowledge/intelligence/bots/knowledge_answer_bot.py +40 -6
  103. django_spire/knowledge/intelligence/bots/knowledge_entries_bot.py +4 -2
  104. django_spire/knowledge/intelligence/bots/search_preprocessing_bot.py +32 -0
  105. django_spire/knowledge/intelligence/intel/entry_intel.py +12 -0
  106. django_spire/knowledge/intelligence/router.py +47 -4
  107. django_spire/knowledge/intelligence/workflows/knowledge_workflow.py +24 -42
  108. django_spire/knowledge/intelligence/workflows/search_preprocessing_workflow.py +78 -0
  109. django_spire/knowledge/management/__init__.py +0 -0
  110. django_spire/knowledge/management/commands/__init__.py +0 -0
  111. django_spire/knowledge/management/commands/rebuild_knowledge_search_index.py +16 -0
  112. django_spire/knowledge/migrations/0010_entry__search_text_entry__search_vector_and_more.py +40 -0
  113. django_spire/knowledge/templates/django_spire/knowledge/message/knowledge_message_intel.html +31 -23
  114. django_spire/metric/report/enums.py +11 -5
  115. django_spire/metric/report/report.py +24 -12
  116. django_spire/metric/report/tools.py +14 -4
  117. django_spire/testing/playwright/fixtures.py +4 -5
  118. {django_spire-0.25.1.dist-info → django_spire-0.26.0.dist-info}/METADATA +1 -1
  119. {django_spire-0.25.1.dist-info → django_spire-0.26.0.dist-info}/RECORD +123 -69
  120. django_spire/core/management/commands/spire_startapp_pkg/template/templates/card/${list_card_template_name}.html.template +0 -18
  121. /django_spire/{core/management/commands/spire_startapp_pkg/template/app/tests/test_intelligence/__init__.py.template → ai/chat/templatetags/__init__.py} +0 -0
  122. {django_spire-0.25.1.dist-info → django_spire-0.26.0.dist-info}/WHEEL +0 -0
  123. {django_spire-0.25.1.dist-info → django_spire-0.26.0.dist-info}/licenses/LICENSE.md +0 -0
  124. {django_spire-0.25.1.dist-info → django_spire-0.26.0.dist-info}/top_level.txt +0 -0
File without changes
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from django.core.management.base import BaseCommand
4
+
5
+ from django_spire.knowledge.entry.models import Entry
6
+
7
+
8
+ class Command(BaseCommand):
9
+ help = 'Rebuilds the search index for all knowledge base entries.'
10
+
11
+ def handle(self, *args, **options):
12
+ self.stdout.write('Rebuilding search indexes...')
13
+
14
+ Entry.services.search_index.rebuild_all_search_indexes()
15
+
16
+ self.stdout.write(self.style.SUCCESS('Search indexes rebuilt successfully.'))
@@ -0,0 +1,40 @@
1
+ # Generated by Django 5.1.15 on 2026-01-23 16:22
2
+
3
+ import django.contrib.postgres.indexes
4
+ import django.contrib.postgres.search
5
+ from django.contrib.postgres.operations import TrigramExtension
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ dependencies = [
12
+ ('django_spire_core', '0001_initial'),
13
+ ('django_spire_knowledge', '0009_alter_collection_tags_alter_entry_tags'),
14
+ ]
15
+
16
+ operations = [
17
+ TrigramExtension(),
18
+ migrations.AddField(
19
+ model_name='entry',
20
+ name='_search_text',
21
+ field=models.TextField(blank=True, default=''),
22
+ ),
23
+ migrations.AddField(
24
+ model_name='entry',
25
+ name='_search_vector',
26
+ field=django.contrib.postgres.search.SearchVectorField(null=True),
27
+ ),
28
+ migrations.AddIndex(
29
+ model_name='entry',
30
+ index=django.contrib.postgres.indexes.GinIndex(fields=['_search_vector'], name='entry_search_vector_idx'),
31
+ ),
32
+ migrations.AddIndex(
33
+ model_name='entry',
34
+ index=django.contrib.postgres.indexes.GinIndex(fields=['name'], name='entry_name_trgm_idx', opclasses=['gin_trgm_ops']),
35
+ ),
36
+ migrations.AddIndex(
37
+ model_name='entry',
38
+ index=django.contrib.postgres.indexes.GinIndex(fields=['_search_text'], name='entry_search_text_trgm_idx', opclasses=['gin_trgm_ops']),
39
+ ),
40
+ ]
@@ -1,32 +1,40 @@
1
1
  {% extends 'django_spire/ai/chat/message/message.html' %}
2
2
 
3
+ {% load spire_ai_chat_tags %}
4
+
3
5
  {% block message_content %}
4
6
  <div class="row">
5
- <div class="col">
6
- {{ message_intel.answer_intel.answer|linebreaksbr }}
7
+ <div class="col ai-message-content">
8
+ {{ message_intel.answer_intel.answer|render_markdown }}
7
9
  </div>
8
10
  </div>
9
11
 
10
- <div class="row mt-2 p-1" x-data="{show_resources: false}">
11
- <div class="col-auto mx-2 ps-1 pe-2 py-1 fs-7 rounded-3 border border-primary-subtle">
12
- <span class="text-muted cursor-pointer" x-on:click="show_resources = !show_resources">
13
- <i class="bi bi-caret-right" x-show="!show_resources"></i>
14
- <i class="bi bi-caret-down" x-show="show_resources"></i>
15
- Sources
16
- </span>
17
-
18
- <ul class="my-0 me-3" x-show="show_resources">
19
- {% for entry_intel in message_intel.entries_intel %}
20
- <li class="pb-1">
21
- <a href="{% url 'django_spire:knowledge:entry:version:page:editor' pk=entry_intel.entry.id %}?show_sub_nav=false&block_id={{ entry_intel.relevant_block_id }}"
22
- target="_blank">
23
- {{ entry_intel.relevant_heading_text }}
24
- </a>
25
- </li>
26
- {% endfor %}
27
- </ul>
12
+ {% if message_intel.entries_intel %}
13
+ <div class="row mt-2 p-1" x-data="{show_resources: false}">
14
+ <div class="col-auto mx-2 ps-1 pe-2 py-1 fs-7 rounded-3 border border-primary-subtle">
15
+ <span class="text-muted cursor-pointer" x-on:click="show_resources = !show_resources">
16
+ <i class="bi bi-caret-right" x-show="!show_resources"></i>
17
+ <i class="bi bi-caret-down" x-show="show_resources"></i>
18
+ Sources
19
+ </span>
28
20
 
21
+ <ul class="my-0 me-3" x-show="show_resources">
22
+ {% for entry_intel in message_intel.entries_intel.unique %}
23
+ {% if entry_intel.entry and entry_intel.entry.id %}
24
+ <li class="pb-1">
25
+ <a href="{% url 'django_spire:knowledge:entry:version:page:editor' pk=entry_intel.entry.id %}?show_sub_nav=false&block_id={{ entry_intel.relevant_block_id }}"
26
+ target="_blank">
27
+ {{ entry_intel.relevant_heading_text }}
28
+ </a>
29
+ </li>
30
+ {% else %}
31
+ <li class="pb-1 text-muted">
32
+ {{ entry_intel.relevant_heading_text }} (source unavailable)
33
+ </li>
34
+ {% endif %}
35
+ {% endfor %}
36
+ </ul>
37
+ </div>
29
38
  </div>
30
- </div>
31
-
32
- {% endblock %}
39
+ {% endif %}
40
+ {% endblock %}
@@ -1,14 +1,20 @@
1
- from enum import Enum
1
+ from enum import StrEnum
2
2
 
3
3
 
4
- class ColumnType(str, Enum):
4
+ class ColumnType(StrEnum):
5
5
  TEXT = 'text'
6
6
  CHOICE = 'choice'
7
7
  NUMBER = 'number'
8
- DECIMAL_1 = 'decimal_1'
9
- DECIMAL_2 = 'decimal_2'
10
- DECIMAL_3 = 'decimal_3'
8
+ NUMBER_1 = 'decimal_1'
9
+ NUMBER_2 = 'decimal_2'
10
+ NUMBER_3 = 'decimal_3'
11
11
  DOLLAR = 'dollar'
12
+ DOLLAR_1 = 'dollar_1'
13
+ DOLLAR_2 = 'dollar_2'
14
+ DOLLAR_3 = 'dollar_3'
12
15
  PERCENT = 'percent'
16
+ PERCENT_1 = 'percent_1'
17
+ PERCENT_2 = 'percent_2'
18
+ PERCENT_3 = 'percent_3'
13
19
 
14
20
 
@@ -2,14 +2,12 @@ import inspect
2
2
  from abc import ABC, abstractmethod
3
3
  from dataclasses import dataclass
4
4
  from dataclasses import field
5
- from typing import Literal, Callable, Any
5
+ from typing import Callable, Any
6
6
 
7
7
  from django_spire.metric.report.enums import ColumnType
8
8
  from django_spire.metric.report.helper import Helper
9
9
  from django_spire.metric.report.tools import get_text_alignment_css_class
10
10
 
11
- ColumnLiteralType = Literal['text', 'choice', 'number', 'dollar', 'percent']
12
-
13
11
 
14
12
  @dataclass
15
13
  class ReportColumn:
@@ -35,17 +33,31 @@ class ReportCell:
35
33
  @staticmethod
36
34
  def cell_value_verbose(value, cell_type):
37
35
  if cell_type == ColumnType.DOLLAR:
38
- return f"${float(value):,.2f}"
36
+ return f"${float(value):,.0f}"
37
+ elif cell_type == ColumnType.DOLLAR_1:
38
+ return f"${float(value):,.1f}%"
39
+ elif cell_type == ColumnType.DOLLAR_2:
40
+ return f"${float(value):,.2f}%"
41
+ elif cell_type == ColumnType.DOLLAR_3:
42
+ return f"${float(value):,.3f}%"
43
+
44
+ elif cell_type == ColumnType.PERCENT:
45
+ return f"{float(value):,.0f}%"
46
+ elif cell_type == ColumnType.PERCENT_1:
47
+ return f"{float(value):,.1f}%"
48
+ elif cell_type == ColumnType.PERCENT_2:
49
+ return f"{float(value):,.2f}%"
50
+ elif cell_type == ColumnType.PERCENT_3:
51
+ return f"{float(value):,.3f}%"
52
+
39
53
  elif cell_type == ColumnType.NUMBER:
40
54
  return f"{float(value):,.0f}"
41
- elif cell_type == ColumnType.PERCENT:
42
- return f"{float(value):.1f}%"
43
- elif cell_type == ColumnType.DECIMAL_1:
44
- return f"{float(value):.1f}"
45
- elif cell_type == ColumnType.DECIMAL_2:
46
- return f"{float(value):.2f}"
47
- elif cell_type == ColumnType.DECIMAL_3:
48
- return f"{float(value):.3f}"
55
+ elif cell_type == ColumnType.NUMBER_1:
56
+ return f"{float(value):,.1f}"
57
+ elif cell_type == ColumnType.NUMBER_2:
58
+ return f"{float(value):,.2f}"
59
+ elif cell_type == ColumnType.NUMBER_3:
60
+ return f"{float(value):,.3f}"
49
61
 
50
62
  return str(value)
51
63
 
@@ -1,11 +1,21 @@
1
- import datetime
2
-
3
1
  from django_spire.metric.report.enums import ColumnType
4
2
 
5
3
 
6
4
  def get_text_alignment_css_class(column_type: ColumnType) -> str:
7
- if column_type in (ColumnType.DOLLAR, ColumnType.NUMBER, ColumnType.PERCENT, ColumnType.DECIMAL_1,
8
- ColumnType.DECIMAL_2, ColumnType.DECIMAL_3):
5
+ if column_type in (
6
+ ColumnType.DOLLAR,
7
+ ColumnType.DOLLAR_1,
8
+ ColumnType.DOLLAR_2,
9
+ ColumnType.DOLLAR_3,
10
+ ColumnType.PERCENT,
11
+ ColumnType.PERCENT_1,
12
+ ColumnType.PERCENT_2,
13
+ ColumnType.PERCENT_3,
14
+ ColumnType.NUMBER,
15
+ ColumnType.NUMBER_1,
16
+ ColumnType.NUMBER_2,
17
+ ColumnType.NUMBER_3
18
+ ):
9
19
  return 'text-end'
10
20
  if column_type == ColumnType.CHOICE:
11
21
  return 'text-center'
@@ -5,16 +5,11 @@ import pytest
5
5
 
6
6
  from typing import Any, TYPE_CHECKING
7
7
 
8
- from django.contrib.auth import get_user_model
9
-
10
8
  if TYPE_CHECKING:
11
9
  from playwright.sync_api import Page
12
10
  from pytest_django.plugin import _LiveServer
13
11
 
14
12
 
15
- User = get_user_model()
16
-
17
-
18
13
  def pytest_configure(config: Any) -> None:
19
14
  os.environ['DJANGO_ALLOW_ASYNC_UNSAFE'] = 'true'
20
15
 
@@ -38,6 +33,10 @@ def browser_type_launch_args(browser_type_launch_args: dict[str, Any]) -> dict[s
38
33
 
39
34
  @pytest.fixture
40
35
  def authenticated_page(page: Page, live_server: _LiveServer, transactional_db: None) -> Page:
36
+ from django.contrib.auth import get_user_model
37
+
38
+ User = get_user_model()
39
+
41
40
  User.objects.create_user(
42
41
  username='testuser',
43
42
  password='testpass123',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-spire
3
- Version: 0.25.1
3
+ Version: 0.26.0
4
4
  Summary: A project for Django Spire
5
5
  Author-email: Brayden Carlson <braydenc@stratusadv.com>, Nathan Johnson <nathanj@stratusadv.com>
6
6
  License: Copyright (c) 2025 Stratus Advanced Technologies and Contributors.