notionary 0.4.0__py3-none-any.whl → 0.4.2__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.
- notionary/__init__.py +44 -1
- notionary/blocks/client.py +37 -11
- notionary/blocks/rich_text/markdown_rich_text_converter.py +49 -15
- notionary/blocks/rich_text/models.py +13 -4
- notionary/blocks/rich_text/name_id_resolver/data_source.py +9 -3
- notionary/blocks/rich_text/name_id_resolver/person.py +6 -2
- notionary/blocks/rich_text/rich_text_markdown_converter.py +10 -3
- notionary/blocks/schemas.py +2 -1
- notionary/comments/client.py +19 -6
- notionary/comments/factory.py +10 -3
- notionary/comments/schemas.py +9 -3
- notionary/comments/service.py +12 -4
- notionary/data_source/http/data_source_instance_client.py +59 -17
- notionary/data_source/properties/schemas.py +30 -10
- notionary/data_source/query/builder.py +67 -18
- notionary/data_source/query/resolver.py +16 -5
- notionary/data_source/query/schema.py +24 -6
- notionary/data_source/query/validator.py +18 -6
- notionary/data_source/schema/registry.py +31 -12
- notionary/data_source/schema/service.py +66 -20
- notionary/data_source/service.py +74 -23
- notionary/database/client.py +27 -9
- notionary/database/database_metadata_update_client.py +12 -4
- notionary/database/service.py +11 -4
- notionary/exceptions/__init__.py +15 -3
- notionary/exceptions/block_parsing.py +6 -2
- notionary/exceptions/data_source/builder.py +11 -5
- notionary/exceptions/data_source/properties.py +3 -1
- notionary/exceptions/file_upload.py +12 -3
- notionary/exceptions/properties.py +3 -1
- notionary/exceptions/search.py +6 -2
- notionary/file_upload/client.py +5 -1
- notionary/file_upload/config/config.py +10 -3
- notionary/file_upload/query/builder.py +6 -2
- notionary/file_upload/schemas.py +3 -1
- notionary/file_upload/service.py +42 -14
- notionary/file_upload/validation/factory.py +3 -1
- notionary/file_upload/validation/impl/file_name_length.py +3 -1
- notionary/file_upload/validation/models.py +15 -5
- notionary/file_upload/validation/validators/file_extension.py +12 -3
- notionary/http/client.py +27 -8
- notionary/page/content/__init__.py +9 -0
- notionary/page/content/factory.py +21 -7
- notionary/page/content/markdown/builder.py +85 -23
- notionary/page/content/markdown/nodes/audio.py +8 -4
- notionary/page/content/markdown/nodes/base.py +3 -3
- notionary/page/content/markdown/nodes/bookmark.py +5 -3
- notionary/page/content/markdown/nodes/breadcrumb.py +2 -2
- notionary/page/content/markdown/nodes/bulleted_list.py +5 -3
- notionary/page/content/markdown/nodes/callout.py +2 -2
- notionary/page/content/markdown/nodes/code.py +5 -3
- notionary/page/content/markdown/nodes/columns.py +3 -3
- notionary/page/content/markdown/nodes/container.py +9 -5
- notionary/page/content/markdown/nodes/divider.py +2 -2
- notionary/page/content/markdown/nodes/embed.py +8 -4
- notionary/page/content/markdown/nodes/equation.py +4 -2
- notionary/page/content/markdown/nodes/file.py +8 -4
- notionary/page/content/markdown/nodes/heading.py +2 -2
- notionary/page/content/markdown/nodes/image.py +8 -4
- notionary/page/content/markdown/nodes/mixins/caption.py +5 -3
- notionary/page/content/markdown/nodes/numbered_list.py +5 -3
- notionary/page/content/markdown/nodes/paragraph.py +4 -2
- notionary/page/content/markdown/nodes/pdf.py +8 -4
- notionary/page/content/markdown/nodes/quote.py +2 -2
- notionary/page/content/markdown/nodes/space.py +2 -2
- notionary/page/content/markdown/nodes/table.py +8 -5
- notionary/page/content/markdown/nodes/table_of_contents.py +2 -2
- notionary/page/content/markdown/nodes/todo.py +15 -7
- notionary/page/content/markdown/nodes/toggle.py +2 -2
- notionary/page/content/markdown/nodes/video.py +8 -4
- notionary/page/content/markdown/structured_output/__init__.py +73 -0
- notionary/page/content/markdown/structured_output/models.py +391 -0
- notionary/page/content/markdown/structured_output/service.py +211 -0
- notionary/page/content/parser/context.py +1 -1
- notionary/page/content/parser/factory.py +23 -8
- notionary/page/content/parser/parsers/audio.py +7 -2
- notionary/page/content/parser/parsers/base.py +2 -2
- notionary/page/content/parser/parsers/bookmark.py +2 -2
- notionary/page/content/parser/parsers/breadcrumb.py +2 -2
- notionary/page/content/parser/parsers/bulleted_list.py +19 -6
- notionary/page/content/parser/parsers/callout.py +15 -5
- notionary/page/content/parser/parsers/caption.py +9 -3
- notionary/page/content/parser/parsers/code.py +21 -7
- notionary/page/content/parser/parsers/column.py +8 -4
- notionary/page/content/parser/parsers/column_list.py +19 -7
- notionary/page/content/parser/parsers/divider.py +2 -2
- notionary/page/content/parser/parsers/embed.py +2 -2
- notionary/page/content/parser/parsers/equation.py +8 -4
- notionary/page/content/parser/parsers/file.py +7 -2
- notionary/page/content/parser/parsers/file_like_block.py +30 -10
- notionary/page/content/parser/parsers/heading.py +31 -10
- notionary/page/content/parser/parsers/image.py +7 -2
- notionary/page/content/parser/parsers/numbered_list.py +18 -6
- notionary/page/content/parser/parsers/paragraph.py +3 -1
- notionary/page/content/parser/parsers/pdf.py +7 -2
- notionary/page/content/parser/parsers/quote.py +28 -9
- notionary/page/content/parser/parsers/space.py +2 -2
- notionary/page/content/parser/parsers/table.py +31 -10
- notionary/page/content/parser/parsers/table_of_contents.py +7 -3
- notionary/page/content/parser/parsers/todo.py +15 -5
- notionary/page/content/parser/parsers/toggle.py +15 -5
- notionary/page/content/parser/parsers/video.py +7 -2
- notionary/page/content/parser/post_processing/handlers/rich_text_length.py +8 -2
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +8 -2
- notionary/page/content/parser/post_processing/service.py +3 -1
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +21 -7
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +11 -4
- notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +13 -6
- notionary/page/content/parser/service.py +4 -1
- notionary/page/content/renderer/context.py +15 -5
- notionary/page/content/renderer/factory.py +12 -6
- notionary/page/content/renderer/post_processing/handlers/numbered_list.py +19 -9
- notionary/page/content/renderer/renderers/audio.py +14 -5
- notionary/page/content/renderer/renderers/base.py +3 -3
- notionary/page/content/renderer/renderers/bookmark.py +3 -1
- notionary/page/content/renderer/renderers/bulleted_list.py +11 -5
- notionary/page/content/renderer/renderers/callout.py +19 -7
- notionary/page/content/renderer/renderers/captioned_block.py +11 -5
- notionary/page/content/renderer/renderers/code.py +6 -2
- notionary/page/content/renderer/renderers/column.py +3 -1
- notionary/page/content/renderer/renderers/column_list.py +3 -1
- notionary/page/content/renderer/renderers/embed.py +3 -1
- notionary/page/content/renderer/renderers/equation.py +3 -1
- notionary/page/content/renderer/renderers/file.py +14 -5
- notionary/page/content/renderer/renderers/file_like_block.py +8 -4
- notionary/page/content/renderer/renderers/heading.py +22 -8
- notionary/page/content/renderer/renderers/image.py +13 -4
- notionary/page/content/renderer/renderers/numbered_list.py +8 -3
- notionary/page/content/renderer/renderers/paragraph.py +12 -4
- notionary/page/content/renderer/renderers/pdf.py +14 -5
- notionary/page/content/renderer/renderers/quote.py +14 -6
- notionary/page/content/renderer/renderers/table.py +15 -5
- notionary/page/content/renderer/renderers/todo.py +16 -6
- notionary/page/content/renderer/renderers/toggle.py +8 -4
- notionary/page/content/renderer/renderers/video.py +14 -5
- notionary/page/content/renderer/service.py +9 -3
- notionary/page/content/service.py +21 -7
- notionary/page/content/syntax/definition/__init__.py +11 -0
- notionary/page/content/syntax/definition/models.py +57 -0
- notionary/page/content/syntax/definition/registry.py +371 -0
- notionary/page/content/syntax/prompts/__init__.py +4 -0
- notionary/page/content/syntax/prompts/models.py +11 -0
- notionary/page/content/syntax/prompts/registry.py +703 -0
- notionary/page/page_metadata_update_client.py +12 -4
- notionary/page/properties/client.py +45 -15
- notionary/page/properties/factory.py +6 -2
- notionary/page/properties/service.py +110 -36
- notionary/page/service.py +20 -6
- notionary/shared/entity/client.py +6 -2
- notionary/shared/entity/dto_parsers.py +3 -1
- notionary/shared/entity/entity_metadata_update_client.py +9 -3
- notionary/shared/entity/schemas.py +1 -1
- notionary/shared/entity/service.py +53 -22
- notionary/shared/models/file.py +3 -1
- notionary/shared/models/icon.py +6 -4
- notionary/user/base.py +6 -2
- notionary/user/bot.py +10 -2
- notionary/user/client.py +3 -1
- notionary/user/person.py +3 -1
- notionary/user/schemas.py +3 -1
- notionary/user/service.py +6 -2
- notionary/utils/decorators.py +6 -2
- notionary/utils/fuzzy.py +6 -2
- notionary/utils/mixins/logging.py +3 -1
- notionary/utils/pagination.py +14 -4
- notionary/workspace/__init__.py +5 -1
- notionary/workspace/query/service.py +59 -16
- notionary/workspace/service.py +39 -11
- {notionary-0.4.0.dist-info → notionary-0.4.2.dist-info}/METADATA +1 -1
- notionary-0.4.2.dist-info/RECORD +236 -0
- notionary/page/blocks/client.py +0 -1
- notionary/page/content/syntax/__init__.py +0 -5
- notionary/page/content/syntax/models.py +0 -66
- notionary/page/content/syntax/registry.py +0 -371
- notionary-0.4.0.dist-info/RECORD +0 -230
- /notionary/page/content/syntax/{grammar.py → definition/grammar.py} +0 -0
- {notionary-0.4.0.dist-info → notionary-0.4.2.dist-info}/WHEEL +0 -0
- {notionary-0.4.0.dist-info → notionary-0.4.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -6,17 +6,21 @@ from notionary.blocks.schemas import (
|
|
|
6
6
|
ExternalFileWithCaption,
|
|
7
7
|
NotionHostedFileWithCaption,
|
|
8
8
|
)
|
|
9
|
-
from notionary.page.content.renderer.renderers.captioned_block import
|
|
10
|
-
|
|
9
|
+
from notionary.page.content.renderer.renderers.captioned_block import (
|
|
10
|
+
CaptionedBlockRenderer,
|
|
11
|
+
)
|
|
12
|
+
from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class FileLikeBlockRenderer(CaptionedBlockRenderer):
|
|
14
16
|
@abstractmethod
|
|
15
|
-
def _get_syntax(self) ->
|
|
17
|
+
def _get_syntax(self) -> EnclosedSyntaxDefinition:
|
|
16
18
|
pass
|
|
17
19
|
|
|
18
20
|
@abstractmethod
|
|
19
|
-
def _get_file_data(
|
|
21
|
+
def _get_file_data(
|
|
22
|
+
self, block: Block
|
|
23
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
20
24
|
pass
|
|
21
25
|
|
|
22
26
|
@override
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.rich_text.rich_text_markdown_converter import
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
4
|
+
RichTextToMarkdownConverter,
|
|
5
|
+
)
|
|
4
6
|
from notionary.blocks.schemas import Block, BlockType, HeadingData
|
|
5
7
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
8
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
-
from notionary.page.content.syntax import
|
|
9
|
+
from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class HeadingRenderer(BlockRenderer):
|
|
@@ -13,16 +15,22 @@ class HeadingRenderer(BlockRenderer):
|
|
|
13
15
|
|
|
14
16
|
def __init__(
|
|
15
17
|
self,
|
|
16
|
-
syntax_registry:
|
|
18
|
+
syntax_registry: SyntaxDefinitionRegistry | None = None,
|
|
17
19
|
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
18
20
|
) -> None:
|
|
19
21
|
super().__init__(syntax_registry=syntax_registry)
|
|
20
22
|
self._syntax = self._syntax_registry.get_heading_syntax()
|
|
21
|
-
self._rich_text_markdown_converter =
|
|
23
|
+
self._rich_text_markdown_converter = (
|
|
24
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
25
|
+
)
|
|
22
26
|
|
|
23
27
|
@override
|
|
24
28
|
def _can_handle(self, block: Block) -> bool:
|
|
25
|
-
return block.type in (
|
|
29
|
+
return block.type in (
|
|
30
|
+
BlockType.HEADING_1,
|
|
31
|
+
BlockType.HEADING_2,
|
|
32
|
+
BlockType.HEADING_3,
|
|
33
|
+
)
|
|
26
34
|
|
|
27
35
|
@override
|
|
28
36
|
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
@@ -35,7 +43,9 @@ class HeadingRenderer(BlockRenderer):
|
|
|
35
43
|
heading_markdown = self._format_heading(level, title, context.indent_level)
|
|
36
44
|
|
|
37
45
|
if self._is_toggleable(context.block):
|
|
38
|
-
context.markdown_result = await self._render_toggleable_heading(
|
|
46
|
+
context.markdown_result = await self._render_toggleable_heading(
|
|
47
|
+
heading_markdown, context
|
|
48
|
+
)
|
|
39
49
|
else:
|
|
40
50
|
context.markdown_result = heading_markdown
|
|
41
51
|
|
|
@@ -52,7 +62,9 @@ class HeadingRenderer(BlockRenderer):
|
|
|
52
62
|
|
|
53
63
|
return heading_markdown
|
|
54
64
|
|
|
55
|
-
async def _render_toggleable_heading(
|
|
65
|
+
async def _render_toggleable_heading(
|
|
66
|
+
self, heading_markdown: str, context: MarkdownRenderingContext
|
|
67
|
+
) -> str:
|
|
56
68
|
original_indent = context.indent_level
|
|
57
69
|
context.indent_level += 1
|
|
58
70
|
|
|
@@ -83,7 +95,9 @@ class HeadingRenderer(BlockRenderer):
|
|
|
83
95
|
if not heading_data or not heading_data.rich_text:
|
|
84
96
|
return ""
|
|
85
97
|
|
|
86
|
-
return await self._rich_text_markdown_converter.to_markdown(
|
|
98
|
+
return await self._rich_text_markdown_converter.to_markdown(
|
|
99
|
+
heading_data.rich_text
|
|
100
|
+
)
|
|
87
101
|
|
|
88
102
|
def _get_heading_data(self, block: Block) -> HeadingData | None:
|
|
89
103
|
if block.type == BlockType.HEADING_1:
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.schemas import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
from notionary.blocks.schemas import (
|
|
4
|
+
Block,
|
|
5
|
+
BlockType,
|
|
6
|
+
ExternalFileWithCaption,
|
|
7
|
+
NotionHostedFileWithCaption,
|
|
8
|
+
)
|
|
9
|
+
from notionary.page.content.renderer.renderers.file_like_block import (
|
|
10
|
+
FileLikeBlockRenderer,
|
|
11
|
+
)
|
|
12
|
+
from notionary.page.content.syntax.definition import SyntaxDefinition
|
|
6
13
|
|
|
7
14
|
|
|
8
15
|
class ImageRenderer(FileLikeBlockRenderer):
|
|
@@ -15,5 +22,7 @@ class ImageRenderer(FileLikeBlockRenderer):
|
|
|
15
22
|
return self._syntax_registry.get_image_syntax()
|
|
16
23
|
|
|
17
24
|
@override
|
|
18
|
-
def _get_file_data(
|
|
25
|
+
def _get_file_data(
|
|
26
|
+
self, block: Block
|
|
27
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
19
28
|
return block.image
|
|
@@ -6,18 +6,23 @@ from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
|
6
6
|
from notionary.blocks.schemas import Block, BlockType
|
|
7
7
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
8
8
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
9
|
-
from notionary.page.content.syntax import
|
|
9
|
+
from notionary.page.content.syntax.definition import (
|
|
10
|
+
MarkdownGrammar,
|
|
11
|
+
SyntaxDefinitionRegistry,
|
|
12
|
+
)
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class NumberedListRenderer(BlockRenderer):
|
|
13
16
|
def __init__(
|
|
14
17
|
self,
|
|
15
|
-
syntax_registry:
|
|
18
|
+
syntax_registry: SyntaxDefinitionRegistry | None = None,
|
|
16
19
|
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
17
20
|
markdown_grammar: MarkdownGrammar | None = None,
|
|
18
21
|
) -> None:
|
|
19
22
|
super().__init__(syntax_registry=syntax_registry)
|
|
20
|
-
self._rich_text_markdown_converter =
|
|
23
|
+
self._rich_text_markdown_converter = (
|
|
24
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
25
|
+
)
|
|
21
26
|
|
|
22
27
|
markdown_grammar = markdown_grammar or MarkdownGrammar()
|
|
23
28
|
self._numbered_list_placeholder = markdown_grammar.numbered_list_placeholder
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.rich_text.rich_text_markdown_converter import
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
4
|
+
RichTextToMarkdownConverter,
|
|
5
|
+
)
|
|
4
6
|
from notionary.blocks.schemas import Block, BlockType
|
|
5
7
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
8
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class ParagraphRenderer(BlockRenderer):
|
|
10
|
-
def __init__(
|
|
12
|
+
def __init__(
|
|
13
|
+
self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None
|
|
14
|
+
) -> None:
|
|
11
15
|
super().__init__()
|
|
12
|
-
self._rich_text_markdown_converter =
|
|
16
|
+
self._rich_text_markdown_converter = (
|
|
17
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
18
|
+
)
|
|
13
19
|
|
|
14
20
|
@override
|
|
15
21
|
def _can_handle(self, block: Block) -> bool:
|
|
@@ -37,4 +43,6 @@ class ParagraphRenderer(BlockRenderer):
|
|
|
37
43
|
if not block.paragraph or not block.paragraph.rich_text:
|
|
38
44
|
return None
|
|
39
45
|
|
|
40
|
-
return await self._rich_text_markdown_converter.to_markdown(
|
|
46
|
+
return await self._rich_text_markdown_converter.to_markdown(
|
|
47
|
+
block.paragraph.rich_text
|
|
48
|
+
)
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.schemas import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
from notionary.blocks.schemas import (
|
|
4
|
+
Block,
|
|
5
|
+
BlockType,
|
|
6
|
+
ExternalFileWithCaption,
|
|
7
|
+
NotionHostedFileWithCaption,
|
|
8
|
+
)
|
|
9
|
+
from notionary.page.content.renderer.renderers.file_like_block import (
|
|
10
|
+
FileLikeBlockRenderer,
|
|
11
|
+
)
|
|
12
|
+
from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
|
|
6
13
|
|
|
7
14
|
|
|
8
15
|
class PdfRenderer(FileLikeBlockRenderer):
|
|
@@ -11,9 +18,11 @@ class PdfRenderer(FileLikeBlockRenderer):
|
|
|
11
18
|
return block.type == BlockType.PDF
|
|
12
19
|
|
|
13
20
|
@override
|
|
14
|
-
def _get_syntax(self) ->
|
|
21
|
+
def _get_syntax(self) -> EnclosedSyntaxDefinition:
|
|
15
22
|
return self._syntax_registry.get_pdf_syntax()
|
|
16
23
|
|
|
17
24
|
@override
|
|
18
|
-
def _get_file_data(
|
|
25
|
+
def _get_file_data(
|
|
26
|
+
self, block: Block
|
|
27
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
19
28
|
return block.pdf
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.rich_text.rich_text_markdown_converter import
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
4
|
+
RichTextToMarkdownConverter,
|
|
5
|
+
)
|
|
4
6
|
from notionary.blocks.schemas import Block, BlockType
|
|
5
7
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
8
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
-
from notionary.page.content.syntax import
|
|
9
|
+
from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class QuoteRenderer(BlockRenderer):
|
|
11
13
|
def __init__(
|
|
12
14
|
self,
|
|
13
|
-
syntax_registry:
|
|
15
|
+
syntax_registry: SyntaxDefinitionRegistry | None = None,
|
|
14
16
|
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
15
17
|
) -> None:
|
|
16
18
|
super().__init__(syntax_registry=syntax_registry)
|
|
17
|
-
self._rich_text_markdown_converter =
|
|
19
|
+
self._rich_text_markdown_converter = (
|
|
20
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
21
|
+
)
|
|
18
22
|
|
|
19
23
|
@override
|
|
20
24
|
def _can_handle(self, block: Block) -> bool:
|
|
@@ -30,7 +34,9 @@ class QuoteRenderer(BlockRenderer):
|
|
|
30
34
|
|
|
31
35
|
syntax = self._syntax_registry.get_quote_syntax()
|
|
32
36
|
quote_lines = markdown.split("\n")
|
|
33
|
-
quote_markdown = "\n".join(
|
|
37
|
+
quote_markdown = "\n".join(
|
|
38
|
+
f"{syntax.start_delimiter}{line}" for line in quote_lines
|
|
39
|
+
)
|
|
34
40
|
|
|
35
41
|
if context.indent_level > 0:
|
|
36
42
|
quote_markdown = context.indent_text(quote_markdown)
|
|
@@ -46,4 +52,6 @@ class QuoteRenderer(BlockRenderer):
|
|
|
46
52
|
if not block.quote or not block.quote.rich_text:
|
|
47
53
|
return None
|
|
48
54
|
|
|
49
|
-
return await self._rich_text_markdown_converter.to_markdown(
|
|
55
|
+
return await self._rich_text_markdown_converter.to_markdown(
|
|
56
|
+
block.quote.rich_text
|
|
57
|
+
)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.rich_text.rich_text_markdown_converter import
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
4
|
+
RichTextToMarkdownConverter,
|
|
5
|
+
)
|
|
4
6
|
from notionary.blocks.schemas import Block, BlockType
|
|
5
7
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
8
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
@@ -9,9 +11,13 @@ from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
|
9
11
|
class TableRenderer(BlockRenderer):
|
|
10
12
|
MINIMUM_COLUMN_WIDTH = 3
|
|
11
13
|
|
|
12
|
-
def __init__(
|
|
14
|
+
def __init__(
|
|
15
|
+
self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None
|
|
16
|
+
) -> None:
|
|
13
17
|
super().__init__()
|
|
14
|
-
self._rich_text_markdown_converter =
|
|
18
|
+
self._rich_text_markdown_converter = (
|
|
19
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
20
|
+
)
|
|
15
21
|
self._table_syntax = self._syntax_registry.get_table_syntax()
|
|
16
22
|
|
|
17
23
|
@override
|
|
@@ -71,10 +77,14 @@ class TableRenderer(BlockRenderer):
|
|
|
71
77
|
|
|
72
78
|
return "\n".join(markdown_lines)
|
|
73
79
|
|
|
74
|
-
def _normalize_row_lengths(
|
|
80
|
+
def _normalize_row_lengths(
|
|
81
|
+
self, rows: list[list[str]], target_length: int
|
|
82
|
+
) -> list[list[str]]:
|
|
75
83
|
return [row + [""] * (target_length - len(row)) for row in rows]
|
|
76
84
|
|
|
77
|
-
def _calculate_column_widths(
|
|
85
|
+
def _calculate_column_widths(
|
|
86
|
+
self, rows: list[list[str]], num_columns: int
|
|
87
|
+
) -> list[int]:
|
|
78
88
|
widths = [max(len(row[i]) for row in rows) for i in range(num_columns)]
|
|
79
89
|
return [max(width, self.MINIMUM_COLUMN_WIDTH) for width in widths]
|
|
80
90
|
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
3
|
from notionary.blocks.enums import BlockType
|
|
4
|
-
from notionary.blocks.rich_text.rich_text_markdown_converter import
|
|
4
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
5
|
+
RichTextToMarkdownConverter,
|
|
6
|
+
)
|
|
5
7
|
from notionary.blocks.schemas import Block
|
|
6
8
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
7
9
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
8
|
-
from notionary.page.content.syntax import
|
|
10
|
+
from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class TodoRenderer(BlockRenderer):
|
|
12
14
|
def __init__(
|
|
13
15
|
self,
|
|
14
|
-
syntax_registry:
|
|
16
|
+
syntax_registry: SyntaxDefinitionRegistry | None = None,
|
|
15
17
|
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
16
18
|
) -> None:
|
|
17
19
|
super().__init__(syntax_registry=syntax_registry)
|
|
18
|
-
self._rich_text_markdown_converter =
|
|
20
|
+
self._rich_text_markdown_converter = (
|
|
21
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
22
|
+
)
|
|
19
23
|
|
|
20
24
|
@override
|
|
21
25
|
def _can_handle(self, block: Block) -> bool:
|
|
@@ -29,7 +33,11 @@ class TodoRenderer(BlockRenderer):
|
|
|
29
33
|
context.markdown_result = ""
|
|
30
34
|
return
|
|
31
35
|
|
|
32
|
-
syntax =
|
|
36
|
+
syntax = (
|
|
37
|
+
self._syntax_registry.get_todo_done_syntax()
|
|
38
|
+
if is_checked
|
|
39
|
+
else self._syntax_registry.get_todo_syntax()
|
|
40
|
+
)
|
|
33
41
|
|
|
34
42
|
todo_markdown = f"{syntax.start_delimiter} {content}"
|
|
35
43
|
|
|
@@ -51,6 +59,8 @@ class TodoRenderer(BlockRenderer):
|
|
|
51
59
|
|
|
52
60
|
content = ""
|
|
53
61
|
if block.to_do.rich_text:
|
|
54
|
-
content = await self._rich_text_markdown_converter.to_markdown(
|
|
62
|
+
content = await self._rich_text_markdown_converter.to_markdown(
|
|
63
|
+
block.to_do.rich_text
|
|
64
|
+
)
|
|
55
65
|
|
|
56
66
|
return is_checked, content
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
3
|
from notionary.blocks.enums import BlockType
|
|
4
|
-
from notionary.blocks.rich_text.rich_text_markdown_converter import
|
|
4
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
5
|
+
RichTextToMarkdownConverter,
|
|
6
|
+
)
|
|
5
7
|
from notionary.blocks.schemas import Block
|
|
6
8
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
7
9
|
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
8
|
-
from notionary.page.content.syntax import
|
|
10
|
+
from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class ToggleRenderer(BlockRenderer):
|
|
12
14
|
def __init__(
|
|
13
15
|
self,
|
|
14
|
-
syntax_registry:
|
|
16
|
+
syntax_registry: SyntaxDefinitionRegistry | None = None,
|
|
15
17
|
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
16
18
|
) -> None:
|
|
17
19
|
super().__init__(syntax_registry=syntax_registry)
|
|
18
|
-
self._rich_text_markdown_converter =
|
|
20
|
+
self._rich_text_markdown_converter = (
|
|
21
|
+
rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
22
|
+
)
|
|
19
23
|
|
|
20
24
|
@override
|
|
21
25
|
def _can_handle(self, block: Block) -> bool:
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.schemas import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
from notionary.blocks.schemas import (
|
|
4
|
+
Block,
|
|
5
|
+
BlockType,
|
|
6
|
+
ExternalFileWithCaption,
|
|
7
|
+
NotionHostedFileWithCaption,
|
|
8
|
+
)
|
|
9
|
+
from notionary.page.content.renderer.renderers.file_like_block import (
|
|
10
|
+
FileLikeBlockRenderer,
|
|
11
|
+
)
|
|
12
|
+
from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
|
|
6
13
|
|
|
7
14
|
|
|
8
15
|
class VideoRenderer(FileLikeBlockRenderer):
|
|
@@ -11,9 +18,11 @@ class VideoRenderer(FileLikeBlockRenderer):
|
|
|
11
18
|
return block.type == BlockType.VIDEO
|
|
12
19
|
|
|
13
20
|
@override
|
|
14
|
-
def _get_syntax(self) ->
|
|
21
|
+
def _get_syntax(self) -> EnclosedSyntaxDefinition:
|
|
15
22
|
return self._syntax_registry.get_video_syntax()
|
|
16
23
|
|
|
17
24
|
@override
|
|
18
|
-
def _get_file_data(
|
|
25
|
+
def _get_file_data(
|
|
26
|
+
self, block: Block
|
|
27
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
19
28
|
return block.video
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from notionary.blocks.schemas import Block
|
|
2
2
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
3
|
-
from notionary.page.content.renderer.post_processing.service import
|
|
3
|
+
from notionary.page.content.renderer.post_processing.service import (
|
|
4
|
+
MarkdownRenderingPostProcessor,
|
|
5
|
+
)
|
|
4
6
|
from notionary.page.content.renderer.renderers import BlockRenderer
|
|
5
7
|
from notionary.utils.mixins.logging import LoggingMixin
|
|
6
8
|
|
|
@@ -22,7 +24,9 @@ class NotionToMarkdownConverter(LoggingMixin):
|
|
|
22
24
|
current_block_index = 0
|
|
23
25
|
|
|
24
26
|
while current_block_index < len(blocks):
|
|
25
|
-
context = self._create_rendering_context(
|
|
27
|
+
context = self._create_rendering_context(
|
|
28
|
+
blocks, current_block_index, indent_level
|
|
29
|
+
)
|
|
26
30
|
await self._renderer_chain.handle(context)
|
|
27
31
|
|
|
28
32
|
if context.markdown_result:
|
|
@@ -45,6 +49,8 @@ class NotionToMarkdownConverter(LoggingMixin):
|
|
|
45
49
|
convert_children_callback=self.convert,
|
|
46
50
|
)
|
|
47
51
|
|
|
48
|
-
def _join_rendered_blocks(
|
|
52
|
+
def _join_rendered_blocks(
|
|
53
|
+
self, rendered_parts: list[str], indent_level: int
|
|
54
|
+
) -> str:
|
|
49
55
|
separator = "\n\n" if indent_level == 0 else "\n"
|
|
50
56
|
return separator.join(rendered_parts)
|
|
@@ -30,13 +30,17 @@ class PageContentService(LoggingMixin):
|
|
|
30
30
|
|
|
31
31
|
@time_execution_async()
|
|
32
32
|
async def clear(self) -> None:
|
|
33
|
-
children_response = await self._block_client.get_block_children(
|
|
33
|
+
children_response = await self._block_client.get_block_children(
|
|
34
|
+
block_id=self._page_id
|
|
35
|
+
)
|
|
34
36
|
|
|
35
37
|
if not children_response or not children_response.results:
|
|
36
38
|
self.logger.debug("No blocks to delete for page: %s", self._page_id)
|
|
37
39
|
return
|
|
38
40
|
|
|
39
|
-
await asyncio.gather(
|
|
41
|
+
await asyncio.gather(
|
|
42
|
+
*[self._delete_single_block(block) for block in children_response.results]
|
|
43
|
+
)
|
|
40
44
|
|
|
41
45
|
@async_retry(max_retries=10, initial_delay=0.2, backoff_factor=1.5)
|
|
42
46
|
async def _delete_single_block(self, block: Block) -> None:
|
|
@@ -44,16 +48,22 @@ class PageContentService(LoggingMixin):
|
|
|
44
48
|
await self._block_client.delete_block(block.id)
|
|
45
49
|
|
|
46
50
|
@time_execution_async()
|
|
47
|
-
async def append_markdown(
|
|
51
|
+
async def append_markdown(
|
|
52
|
+
self, content: str | Callable[[MarkdownBuilder], MarkdownBuilder]
|
|
53
|
+
) -> None:
|
|
48
54
|
markdown = self._extract_markdown(content)
|
|
49
55
|
if not markdown:
|
|
50
|
-
self.logger.debug(
|
|
56
|
+
self.logger.debug(
|
|
57
|
+
"No markdown content to append for page: %s", self._page_id
|
|
58
|
+
)
|
|
51
59
|
return
|
|
52
60
|
|
|
53
61
|
blocks = await self._markdown_converter.convert(markdown)
|
|
54
62
|
await self._append_blocks(blocks)
|
|
55
63
|
|
|
56
|
-
def _extract_markdown(
|
|
64
|
+
def _extract_markdown(
|
|
65
|
+
self, content: str | Callable[[MarkdownBuilder], MarkdownBuilder]
|
|
66
|
+
) -> str:
|
|
57
67
|
if isinstance(content, str):
|
|
58
68
|
return content
|
|
59
69
|
|
|
@@ -62,7 +72,11 @@ class PageContentService(LoggingMixin):
|
|
|
62
72
|
content(builder)
|
|
63
73
|
return builder.build()
|
|
64
74
|
|
|
65
|
-
raise ValueError(
|
|
75
|
+
raise ValueError(
|
|
76
|
+
"content must be either a string or a callable that takes a MarkdownBuilder"
|
|
77
|
+
)
|
|
66
78
|
|
|
67
79
|
async def _append_blocks(self, blocks: list[Block]) -> None:
|
|
68
|
-
await self._block_client.append_block_children(
|
|
80
|
+
await self._block_client.append_block_children(
|
|
81
|
+
block_id=self._page_id, children=blocks
|
|
82
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from .grammar import MarkdownGrammar
|
|
2
|
+
from .models import EnclosedSyntaxDefinition, SimpleSyntaxDefinition, SyntaxDefinition
|
|
3
|
+
from .registry import SyntaxDefinitionRegistry
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"EnclosedSyntaxDefinition",
|
|
7
|
+
"MarkdownGrammar",
|
|
8
|
+
"SimpleSyntaxDefinition",
|
|
9
|
+
"SyntaxDefinition",
|
|
10
|
+
"SyntaxDefinitionRegistry",
|
|
11
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SyntaxDefinitionRegistryKey(StrEnum):
|
|
7
|
+
AUDIO = "audio"
|
|
8
|
+
BOOKMARK = "bookmark"
|
|
9
|
+
IMAGE = "image"
|
|
10
|
+
VIDEO = "video"
|
|
11
|
+
FILE = "file"
|
|
12
|
+
PDF = "pdf"
|
|
13
|
+
|
|
14
|
+
BULLETED_LIST = "bulleted_list"
|
|
15
|
+
NUMBERED_LIST = "numbered_list"
|
|
16
|
+
TO_DO = "todo"
|
|
17
|
+
TO_DO_DONE = "todo_done"
|
|
18
|
+
|
|
19
|
+
TOGGLE = "toggle"
|
|
20
|
+
TOGGLEABLE_HEADING = "toggleable_heading"
|
|
21
|
+
CALLOUT = "callout"
|
|
22
|
+
QUOTE = "quote"
|
|
23
|
+
CODE = "code"
|
|
24
|
+
|
|
25
|
+
COLUMN_LIST = "column_list"
|
|
26
|
+
COLUMN = "column"
|
|
27
|
+
|
|
28
|
+
HEADING = "heading"
|
|
29
|
+
|
|
30
|
+
DIVIDER = "divider"
|
|
31
|
+
BREADCRUMB = "breadcrumb"
|
|
32
|
+
TABLE_OF_CONTENTS = "table_of_contents"
|
|
33
|
+
EQUATION = "equation"
|
|
34
|
+
EMBED = "embed"
|
|
35
|
+
TABLE = "table"
|
|
36
|
+
TABLE_ROW = "table_row"
|
|
37
|
+
|
|
38
|
+
CAPTION = "caption"
|
|
39
|
+
SPACE = "space"
|
|
40
|
+
PARAGRAPH = "paragraph"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class SimpleSyntaxDefinition:
|
|
45
|
+
start_delimiter: str
|
|
46
|
+
regex_pattern: re.Pattern
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class EnclosedSyntaxDefinition:
|
|
51
|
+
start_delimiter: str
|
|
52
|
+
end_delimiter: str
|
|
53
|
+
regex_pattern: re.Pattern
|
|
54
|
+
end_regex_pattern: re.Pattern
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
type SyntaxDefinition = SimpleSyntaxDefinition | EnclosedSyntaxDefinition
|