django-spire 0.17.11__py3-none-any.whl → 0.18.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.
- django_spire/consts.py +1 -1
- django_spire/contrib/seeding/model/base.py +1 -1
- django_spire/knowledge/entry/services/automation_service.py +7 -4
- django_spire/knowledge/entry/services/transformation_services.py +2 -2
- django_spire/knowledge/entry/version/block/choices.py +1 -2
- django_spire/knowledge/entry/version/block/data/data.py +33 -0
- django_spire/knowledge/entry/version/block/data/heading_data.py +15 -0
- django_spire/knowledge/entry/version/block/data/list/choices.py +17 -0
- django_spire/knowledge/entry/version/block/data/list/data.py +102 -0
- django_spire/knowledge/entry/version/block/data/list/maps.py +14 -0
- django_spire/knowledge/entry/version/block/data/list/meta.py +15 -0
- django_spire/knowledge/entry/version/block/data/maps.py +23 -0
- django_spire/knowledge/entry/version/block/data/text_data.py +13 -0
- django_spire/knowledge/entry/version/block/models.py +22 -14
- django_spire/knowledge/entry/version/block/querysets.py +17 -7
- django_spire/knowledge/entry/version/block/seeding/constants.py +271 -188
- django_spire/knowledge/entry/version/block/services/factory_service.py +14 -47
- django_spire/knowledge/entry/version/block/services/service.py +2 -12
- django_spire/knowledge/entry/version/block/tests/factories.py +18 -2
- django_spire/knowledge/entry/version/converters/markdown_converter.py +180 -105
- django_spire/knowledge/entry/version/maps.py +1 -1
- django_spire/knowledge/entry/version/models.py +1 -0
- django_spire/knowledge/entry/version/querysets.py +11 -1
- django_spire/knowledge/entry/version/seeding/seeder.py +4 -2
- django_spire/knowledge/entry/version/services/processor_service.py +18 -0
- django_spire/knowledge/entry/version/services/tests/test_processor_service.py +0 -0
- django_spire/knowledge/entry/version/tests/test_urls/test_json_urls.py +7 -24
- django_spire/knowledge/entry/version/urls/__init__.py +0 -3
- django_spire/knowledge/entry/version/urls/json_urls.py +5 -13
- django_spire/knowledge/entry/version/views/json_views.py +8 -76
- django_spire/knowledge/entry/version/views/page_views.py +10 -5
- django_spire/knowledge/migrations/0005_entryversionblock__tunes_data_and_more.py +23 -0
- django_spire/knowledge/static/django_spire/knowledge/css/navigation_items.css +1 -1
- django_spire/knowledge/static/django_spire/knowledge/entry/version/js/editor.js +87 -0
- django_spire/knowledge/static/django_spire/knowledge/entry/version/js/null_paragraph.js +15 -0
- django_spire/knowledge/templates/django_spire/knowledge/entry/file/page/list_page.html +1 -1
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/container/detail_container.html +43 -12
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/page/detail_page.html +17 -1
- django_spire/knowledge/templates/django_spire/knowledge/navigation/page/full_page.html +1 -1
- {django_spire-0.17.11.dist-info → django_spire-0.18.0.dist-info}/METADATA +2 -1
- {django_spire-0.17.11.dist-info → django_spire-0.18.0.dist-info}/RECORD +47 -60
- django_spire/knowledge/entry/version/block/blocks/block.py +0 -51
- django_spire/knowledge/entry/version/block/blocks/heading_block.py +0 -14
- django_spire/knowledge/entry/version/block/blocks/list_block.py +0 -31
- django_spire/knowledge/entry/version/block/blocks/sub_heading_block.py +0 -14
- django_spire/knowledge/entry/version/block/blocks/text_block.py +0 -14
- django_spire/knowledge/entry/version/block/maps.py +0 -16
- django_spire/knowledge/entry/version/block/services/processor_service.py +0 -33
- django_spire/knowledge/entry/version/block/services/transformation_service.py +0 -35
- django_spire/knowledge/entry/version/block/tests/test_urls/test_json_urls.py +0 -28
- django_spire/knowledge/entry/version/block/urls/__init__.py +0 -13
- django_spire/knowledge/entry/version/block/urls/json_urls.py +0 -9
- django_spire/knowledge/entry/version/block/views/json_views.py +0 -30
- django_spire/knowledge/entry/version/tests/test_urls/test_form_urls.py +0 -28
- django_spire/knowledge/entry/version/urls/form_urls.py +0 -10
- django_spire/knowledge/entry/version/views/form_views.py +0 -63
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/detail/component/heading_component.html +0 -3
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/detail/component/list_item_component.html +0 -38
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/detail/component/sub_heading_component.html +0 -3
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/detail/component/text_component.html +0 -3
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/dropdown/add_dropdown.html +0 -25
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/update/component/heading_component.html +0 -3
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/update/component/list_item_component.html +0 -13
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/update/component/sub_heading_component.html +0 -3
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/block/update/component/text_component.html +0 -50
- django_spire/knowledge/templates/django_spire/knowledge/entry/version/container/form_container.html +0 -129
- /django_spire/knowledge/entry/version/block/{blocks → data}/__init__.py +0 -0
- /django_spire/knowledge/entry/version/block/{tests/test_urls → data/list}/__init__.py +0 -0
- /django_spire/knowledge/entry/version/{block/views → services/tests}/__init__.py +0 -0
- {django_spire-0.17.11.dist-info → django_spire-0.18.0.dist-info}/WHEEL +0 -0
- {django_spire-0.17.11.dist-info → django_spire-0.18.0.dist-info}/licenses/LICENSE.md +0 -0
- {django_spire-0.17.11.dist-info → django_spire-0.18.0.dist-info}/top_level.txt +0 -0
django_spire/consts.py
CHANGED
|
@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
|
|
|
2
2
|
|
|
3
3
|
from dandy.recorder import recorder_to_html_file
|
|
4
4
|
from dandy import SqliteCache
|
|
5
|
-
from dandy import generate_cache_key
|
|
5
|
+
from dandy.cache.tools import generate_cache_key
|
|
6
6
|
|
|
7
7
|
from django_spire.contrib.seeding.field.override import FieldOverride
|
|
8
8
|
from django_spire.contrib.seeding.model.config import FieldsConfig
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import traceback
|
|
3
4
|
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
5
6
|
import json
|
|
@@ -34,15 +35,17 @@ class EntryAutomationService(BaseDjangoModelService['Entry']):
|
|
|
34
35
|
entry_version=entry_pk_map[file_object.object_id].current_version,
|
|
35
36
|
)
|
|
36
37
|
except Exception as e:
|
|
37
|
-
errored.append({'file': file_object.name, 'error':
|
|
38
|
+
errored.append({'file': file_object.name, 'error': traceback.format_exc()})
|
|
38
39
|
file_object.set_deleted()
|
|
39
40
|
else:
|
|
40
41
|
file_object.set_deleted()
|
|
41
42
|
|
|
42
43
|
message = f'Files Converted: {len(file_objects) - len(errored)}'
|
|
43
44
|
if errored:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
error_string = f'\n{message}\nFiles Errored:'
|
|
46
|
+
for error in errored:
|
|
47
|
+
error_string += f' File Name: {error["file"]}\n Error: {error["error"]}'
|
|
48
|
+
|
|
49
|
+
raise KnowledgeBaseConversionException(error_string)
|
|
47
50
|
|
|
48
51
|
return message
|
|
@@ -72,9 +72,9 @@ class EntryTransformationService(BaseDjangoModelService['Entry']):
|
|
|
72
72
|
'edit_version_url': f'''
|
|
73
73
|
{site}{
|
|
74
74
|
reverse(
|
|
75
|
-
'django_spire:knowledge:entry:version:
|
|
75
|
+
'django_spire:knowledge:entry:version:page:detail',
|
|
76
76
|
kwargs={'pk': current_version.pk},
|
|
77
77
|
)
|
|
78
|
-
}
|
|
78
|
+
}?view_mode=edit
|
|
79
79
|
'''
|
|
80
80
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseEditorBlockData(BaseModel):
|
|
7
|
+
"""
|
|
8
|
+
This class serves as a foundational abstract model for EditorJS tool data objects.
|
|
9
|
+
|
|
10
|
+
Note that it does not represent the top level editor block itself,
|
|
11
|
+
but rather the data that is stored within it.
|
|
12
|
+
See https://editorjs.io/getting-started/#tools-installation for a
|
|
13
|
+
list of EditorJS and their associated data models.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def render_to_text(self) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Renders the content to text format.
|
|
19
|
+
|
|
20
|
+
This method should be implemented in a subclass to define how the
|
|
21
|
+
content will be rendered into a string. It is meant to be overridden and
|
|
22
|
+
raises a NotImplementedError by default.
|
|
23
|
+
|
|
24
|
+
This method is mainly used for providing knowledge base
|
|
25
|
+
content in a digestible format for the AI Chat's LLM connector.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
str: The rendered text output.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
NotImplementedError: If the method is not implemented in a subclass.
|
|
32
|
+
"""
|
|
33
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HeadingEditorBlockData(BaseEditorBlockData):
|
|
7
|
+
text: str
|
|
8
|
+
level: int
|
|
9
|
+
|
|
10
|
+
def render_to_text(self) -> str:
|
|
11
|
+
from django_spire.knowledge.entry.version.converters.markdown_converter import \
|
|
12
|
+
MarkdownConverter
|
|
13
|
+
|
|
14
|
+
text = MarkdownConverter.html_to_markdown(self.text)
|
|
15
|
+
return f'{"#" * self.level} {text}\n'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django.db.models import TextChoices
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ListEditorBlockDataStyle(TextChoices):
|
|
7
|
+
UNORDERED = 'unordered'
|
|
8
|
+
ORDERED = 'ordered'
|
|
9
|
+
CHECKLIST = 'checklist'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class OrderedListCounterType(TextChoices):
|
|
13
|
+
NUMERIC = 'numeric'
|
|
14
|
+
UPPER_ROMAN = 'upper-roman'
|
|
15
|
+
LOWER_ROMAN = 'lower-roman'
|
|
16
|
+
UPPER_ALPHA = 'upper-alpha'
|
|
17
|
+
LOWER_ALPHA = 'lower-alpha'
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import model_validator, BaseModel
|
|
6
|
+
|
|
7
|
+
from django_spire.knowledge.entry.version.block.constants import SPACES_PER_INDENT
|
|
8
|
+
from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
|
|
9
|
+
from django_spire.knowledge.entry.version.block.data.list.meta import ChecklistItemMeta, \
|
|
10
|
+
OrderedListItemMeta
|
|
11
|
+
from django_spire.knowledge.entry.version.block.data.list.choices import \
|
|
12
|
+
ListEditorBlockDataStyle
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ListEditorBlockData(BaseEditorBlockData):
|
|
16
|
+
style: ListEditorBlockDataStyle | str
|
|
17
|
+
meta: ChecklistItemMeta | OrderedListItemMeta | None = None
|
|
18
|
+
items: list[ListItemEditorBlockData]
|
|
19
|
+
|
|
20
|
+
def render_to_text(self) -> str:
|
|
21
|
+
render_string = ''
|
|
22
|
+
for i, item in enumerate(self.items):
|
|
23
|
+
render_string += item.render_to_text(self.style, 0, i)
|
|
24
|
+
|
|
25
|
+
return render_string
|
|
26
|
+
|
|
27
|
+
@model_validator(mode='before')
|
|
28
|
+
@classmethod
|
|
29
|
+
def validate_meta(cls, values: dict) -> dict:
|
|
30
|
+
values['style'] = ListEditorBlockDataStyle(values.get('style'))
|
|
31
|
+
|
|
32
|
+
if values['style'] == ListEditorBlockDataStyle.ORDERED and 'meta' in values:
|
|
33
|
+
values['meta'] = OrderedListItemMeta(**values['meta'])
|
|
34
|
+
|
|
35
|
+
for item in values['items']:
|
|
36
|
+
item = ListItemEditorBlockData.style_aware_create_from_dict(item, values['style'])
|
|
37
|
+
|
|
38
|
+
return values
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ListItemEditorBlockData(BaseModel):
|
|
42
|
+
content: str
|
|
43
|
+
meta: ChecklistItemMeta | OrderedListItemMeta | dict | None = None
|
|
44
|
+
items: Optional[list[ListItemEditorBlockData]] = []
|
|
45
|
+
|
|
46
|
+
def get_prefix(
|
|
47
|
+
self,
|
|
48
|
+
style: ListEditorBlockDataStyle,
|
|
49
|
+
indent_level: int,
|
|
50
|
+
index = None
|
|
51
|
+
):
|
|
52
|
+
prefix = ' ' * indent_level * SPACES_PER_INDENT
|
|
53
|
+
|
|
54
|
+
if style == ListEditorBlockDataStyle.ORDERED:
|
|
55
|
+
index = index or 0
|
|
56
|
+
start = self.meta.start or 1
|
|
57
|
+
prefix += f'{start + index}.'
|
|
58
|
+
|
|
59
|
+
elif style == ListEditorBlockDataStyle.CHECKLIST:
|
|
60
|
+
prefix += f'[{"X" if self.meta.checked else " "}]'
|
|
61
|
+
|
|
62
|
+
else:
|
|
63
|
+
prefix += '-'
|
|
64
|
+
|
|
65
|
+
return prefix
|
|
66
|
+
|
|
67
|
+
def render_to_text(
|
|
68
|
+
self,
|
|
69
|
+
style: ListEditorBlockDataStyle,
|
|
70
|
+
indent_level: int,
|
|
71
|
+
index: int
|
|
72
|
+
) -> str:
|
|
73
|
+
from django_spire.knowledge.entry.version.converters.markdown_converter import \
|
|
74
|
+
MarkdownConverter
|
|
75
|
+
|
|
76
|
+
prefix = self.get_prefix(style, indent_level, index)
|
|
77
|
+
parsed_content = MarkdownConverter.html_to_markdown(self.content)
|
|
78
|
+
render_string = f'{prefix} {parsed_content}\n'
|
|
79
|
+
for i, item in enumerate(self.items):
|
|
80
|
+
render_string += item.render_to_text(style, indent_level + 1, i)
|
|
81
|
+
|
|
82
|
+
return render_string
|
|
83
|
+
|
|
84
|
+
@classmethod
|
|
85
|
+
def style_aware_create_from_dict(
|
|
86
|
+
cls,
|
|
87
|
+
values: dict,
|
|
88
|
+
style: ListEditorBlockDataStyle
|
|
89
|
+
) -> ListItemEditorBlockData:
|
|
90
|
+
from django_spire.knowledge.entry.version.block.data.list.maps import \
|
|
91
|
+
LIST_BLOCK_DATA_META_MAP
|
|
92
|
+
|
|
93
|
+
if 'meta' in values:
|
|
94
|
+
meta_type = LIST_BLOCK_DATA_META_MAP[style]
|
|
95
|
+
|
|
96
|
+
if isinstance(values['meta'], dict):
|
|
97
|
+
values['meta'] = meta_type(**values['meta']) if meta_type else None
|
|
98
|
+
|
|
99
|
+
for item in values['items']:
|
|
100
|
+
item = ListItemEditorBlockData.style_aware_create_from_dict(item, style)
|
|
101
|
+
|
|
102
|
+
return ListItemEditorBlockData(**values)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.knowledge.entry.version.block.data.list import choices, meta
|
|
4
|
+
|
|
5
|
+
LIST_BLOCK_DATA_META_MAP = {
|
|
6
|
+
choices.ListEditorBlockDataStyle.ORDERED: meta.OrderedListItemMeta,
|
|
7
|
+
choices.ListEditorBlockDataStyle.CHECKLIST: meta.ChecklistItemMeta,
|
|
8
|
+
choices.ListEditorBlockDataStyle.UNORDERED: None,
|
|
9
|
+
}
|
|
10
|
+
LIST_BLOCK_DATA_REVERSE_META_MAP = {
|
|
11
|
+
meta.OrderedListItemMeta: choices.ListEditorBlockDataStyle.ORDERED,
|
|
12
|
+
meta.ChecklistItemMeta: choices.ListEditorBlockDataStyle.CHECKLIST,
|
|
13
|
+
None: choices.ListEditorBlockDataStyle.UNORDERED,
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from django_spire.knowledge.entry.version.block.data.list.choices import \
|
|
6
|
+
OrderedListCounterType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ChecklistItemMeta(BaseModel):
|
|
10
|
+
checked: bool = False
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OrderedListItemMeta(BaseModel):
|
|
14
|
+
start: int | None = None
|
|
15
|
+
counterType: OrderedListCounterType | str | None = None
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.knowledge.entry.version.block.choices import BlockTypeChoices
|
|
4
|
+
from django_spire.knowledge.entry.version.block.data.heading_data import \
|
|
5
|
+
HeadingEditorBlockData
|
|
6
|
+
from django_spire.knowledge.entry.version.block.data.list.data import \
|
|
7
|
+
ListEditorBlockData
|
|
8
|
+
from django_spire.knowledge.entry.version.block.data.text_data import \
|
|
9
|
+
TextEditorBlockData
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
EDITOR_BLOCK_DATA_MAP = {
|
|
13
|
+
BlockTypeChoices.TEXT: TextEditorBlockData,
|
|
14
|
+
BlockTypeChoices.HEADING: HeadingEditorBlockData,
|
|
15
|
+
BlockTypeChoices.LIST: ListEditorBlockData,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
EDITOR_BLOCK_DATA_REVERSE_MAP = {
|
|
20
|
+
TextEditorBlockData: BlockTypeChoices.TEXT,
|
|
21
|
+
HeadingEditorBlockData: BlockTypeChoices.HEADING,
|
|
22
|
+
ListEditorBlockData: BlockTypeChoices.LIST
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TextEditorBlockData(BaseEditorBlockData):
|
|
7
|
+
text: str
|
|
8
|
+
|
|
9
|
+
def render_to_text(self) -> str:
|
|
10
|
+
from django_spire.knowledge.entry.version.converters.markdown_converter import \
|
|
11
|
+
MarkdownConverter
|
|
12
|
+
|
|
13
|
+
return f'{MarkdownConverter.html_to_markdown(self.text)}\n'
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
|
-
from django.forms import model_to_dict
|
|
5
|
-
from django.template.loader import render_to_string
|
|
6
4
|
|
|
7
5
|
from django_spire.contrib.ordering.mixins import OrderingModelMixin
|
|
8
6
|
from django_spire.history.mixins import HistoryModelMixin
|
|
9
7
|
from django_spire.knowledge.entry.version.block.choices import BlockTypeChoices
|
|
10
|
-
from django_spire.knowledge.entry.version.block.
|
|
11
|
-
from django_spire.knowledge.entry.version.block.
|
|
12
|
-
from django_spire.knowledge.entry.version.block.
|
|
8
|
+
from django_spire.knowledge.entry.version.block.data.data import BaseEditorBlockData
|
|
9
|
+
from django_spire.knowledge.entry.version.block.data.maps import EDITOR_BLOCK_DATA_MAP
|
|
10
|
+
from django_spire.knowledge.entry.version.block.querysets import \
|
|
11
|
+
EntryVersionBlockQuerySet
|
|
12
|
+
from django_spire.knowledge.entry.version.block.services.service import \
|
|
13
|
+
EntryVersionBlockService
|
|
13
14
|
from django_spire.knowledge.entry.version.models import EntryVersion
|
|
14
|
-
from django_spire.knowledge.entry.version.block.querysets import EntryVersionBlockQuerySet
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class EntryVersionBlock(HistoryModelMixin, OrderingModelMixin):
|
|
@@ -21,29 +21,37 @@ class EntryVersionBlock(HistoryModelMixin, OrderingModelMixin):
|
|
|
21
21
|
related_name='blocks',
|
|
22
22
|
related_query_name='block'
|
|
23
23
|
)
|
|
24
|
+
|
|
24
25
|
type = models.CharField(
|
|
25
26
|
max_length=32,
|
|
26
27
|
choices=BlockTypeChoices,
|
|
27
28
|
default=BlockTypeChoices.TEXT
|
|
28
29
|
)
|
|
30
|
+
|
|
29
31
|
_block_data = models.JSONField()
|
|
30
32
|
_text_data = models.TextField()
|
|
31
33
|
|
|
34
|
+
# contains data related to EditorJS tunes,
|
|
35
|
+
# which are additional modifications to blocks (e.g. footnotes, etc.)
|
|
36
|
+
# https://editorjs.io/block-tunes-api/
|
|
37
|
+
_tunes_data = models.JSONField(null=True, blank=True)
|
|
38
|
+
|
|
32
39
|
objects = EntryVersionBlockQuerySet.as_manager()
|
|
33
40
|
services = EntryVersionBlockService()
|
|
34
41
|
|
|
35
42
|
@property
|
|
36
|
-
def
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
@
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
def editor_block_data(self) -> BaseEditorBlockData:
|
|
44
|
+
return EDITOR_BLOCK_DATA_MAP[self.type](**self._block_data)
|
|
45
|
+
|
|
46
|
+
@editor_block_data.setter
|
|
47
|
+
def editor_block_data(self, value: BaseEditorBlockData):
|
|
48
|
+
# exclude_none=True ensures that block meta objects aren't autofilled with keys,
|
|
49
|
+
# which can mess up editor rendering
|
|
50
|
+
self._block_data = value.model_dump(exclude_none=True)
|
|
43
51
|
self._text_data = value.render_to_text()
|
|
44
52
|
|
|
45
53
|
def render_to_text(self) -> str:
|
|
46
|
-
return self.
|
|
54
|
+
return self.editor_block_data.render_to_text()
|
|
47
55
|
|
|
48
56
|
class Meta:
|
|
49
57
|
verbose_name = 'Block'
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from django.db.models.functions import Coalesce
|
|
4
|
+
from django.db.models import JSONField
|
|
4
5
|
|
|
5
6
|
from django_spire.contrib.ordering.querysets import OrderingQuerySetMixin
|
|
6
7
|
from django_spire.history.querysets import HistoryQuerySet
|
|
7
|
-
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from django.db.models import QuerySet
|
|
10
|
-
from django_spire.knowledge.entry.version.block.models import EntryVersionBlock
|
|
8
|
+
from django.db.models import Value
|
|
11
9
|
|
|
12
10
|
|
|
13
11
|
class EntryVersionBlockQuerySet(HistoryQuerySet, OrderingQuerySetMixin):
|
|
14
|
-
def
|
|
15
|
-
|
|
12
|
+
def format_for_editor(self):
|
|
13
|
+
coalesce_json_field = lambda field_name: Coalesce(
|
|
14
|
+
field_name,
|
|
15
|
+
Value({}, output_field=JSONField())
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
self.annotate(
|
|
20
|
+
data=coalesce_json_field('_block_data'),
|
|
21
|
+
tunes=coalesce_json_field('_tunes_data'),
|
|
22
|
+
)
|
|
23
|
+
.order_by('order')
|
|
24
|
+
.values('id', 'type', 'data', 'tunes')
|
|
25
|
+
)
|