notionary 0.3.1__py3-none-any.whl → 0.4.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.
- notionary/__init__.py +49 -1
- notionary/blocks/client.py +37 -11
- notionary/blocks/enums.py +0 -6
- 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 +33 -78
- notionary/comments/client.py +19 -6
- notionary/comments/factory.py +10 -3
- notionary/comments/schemas.py +10 -31
- notionary/comments/service.py +12 -4
- notionary/data_source/http/data_source_instance_client.py +59 -17
- notionary/data_source/properties/schemas.py +156 -115
- 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/schemas.py +2 -2
- notionary/data_source/service.py +103 -43
- notionary/database/client.py +27 -9
- notionary/database/database_metadata_update_client.py +12 -4
- notionary/database/schemas.py +2 -2
- notionary/database/service.py +14 -9
- notionary/exceptions/__init__.py +20 -4
- notionary/exceptions/api.py +2 -2
- notionary/exceptions/base.py +1 -1
- notionary/exceptions/block_parsing.py +9 -5
- notionary/exceptions/data_source/builder.py +13 -7
- notionary/exceptions/data_source/properties.py +6 -4
- notionary/exceptions/file_upload.py +76 -0
- notionary/exceptions/properties.py +7 -5
- notionary/exceptions/search.py +10 -6
- notionary/file_upload/__init__.py +4 -0
- notionary/file_upload/client.py +128 -210
- notionary/file_upload/config/__init__.py +17 -0
- notionary/file_upload/config/config.py +39 -0
- notionary/file_upload/config/constants.py +16 -0
- notionary/file_upload/file/reader.py +28 -0
- notionary/file_upload/query/__init__.py +7 -0
- notionary/file_upload/query/builder.py +58 -0
- notionary/file_upload/query/models.py +37 -0
- notionary/file_upload/schemas.py +80 -0
- notionary/file_upload/service.py +182 -291
- notionary/file_upload/validation/factory.py +66 -0
- notionary/file_upload/validation/impl/file_name_length.py +25 -0
- notionary/file_upload/validation/models.py +134 -0
- notionary/file_upload/validation/port.py +7 -0
- notionary/file_upload/validation/service.py +17 -0
- notionary/file_upload/validation/validators/__init__.py +11 -0
- notionary/file_upload/validation/validators/file_exists.py +15 -0
- notionary/file_upload/validation/validators/file_extension.py +131 -0
- notionary/file_upload/validation/validators/file_name_length.py +21 -0
- notionary/file_upload/validation/validators/upload_limit.py +31 -0
- notionary/http/client.py +33 -30
- 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 +26 -8
- notionary/page/content/parser/parsers/audio.py +12 -32
- 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 -4
- notionary/page/content/parser/parsers/equation.py +8 -4
- notionary/page/content/parser/parsers/file.py +12 -34
- notionary/page/content/parser/parsers/file_like_block.py +109 -0
- notionary/page/content/parser/parsers/heading.py +31 -10
- notionary/page/content/parser/parsers/image.py +12 -34
- 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 +12 -34
- 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 +12 -34
- 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 +20 -23
- 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 +20 -23
- notionary/page/content/renderer/renderers/file_like_block.py +47 -0
- notionary/page/content/renderer/renderers/heading.py +22 -8
- notionary/page/content/renderer/renderers/image.py +20 -23
- 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 +20 -23
- 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 +20 -23
- 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 +46 -16
- notionary/page/properties/factory.py +6 -2
- notionary/page/properties/{models.py → schemas.py} +93 -107
- notionary/page/properties/service.py +111 -37
- notionary/page/schemas.py +3 -3
- notionary/page/service.py +21 -7
- notionary/shared/entity/client.py +6 -2
- notionary/shared/entity/dto_parsers.py +4 -37
- notionary/shared/entity/entity_metadata_update_client.py +25 -5
- notionary/shared/entity/schemas.py +6 -6
- notionary/shared/entity/service.py +89 -35
- notionary/shared/models/file.py +36 -6
- notionary/shared/models/icon.py +5 -12
- notionary/user/base.py +6 -2
- notionary/user/bot.py +22 -14
- 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 +13 -9
- notionary/utils/fuzzy.py +6 -2
- notionary/utils/mixins/logging.py +3 -1
- notionary/utils/pagination.py +14 -4
- notionary/workspace/__init__.py +6 -2
- notionary/workspace/query/__init__.py +2 -1
- notionary/workspace/query/service.py +42 -13
- notionary/workspace/service.py +74 -46
- {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/METADATA +1 -1
- notionary-0.4.1.dist-info/RECORD +236 -0
- notionary/file_upload/models.py +0 -69
- notionary/page/blocks/client.py +0 -1
- notionary/page/content/syntax/__init__.py +0 -4
- notionary/page/content/syntax/models.py +0 -66
- notionary/page/content/syntax/registry.py +0 -393
- notionary/page/page_context.py +0 -50
- notionary/shared/models/cover.py +0 -20
- notionary-0.3.1.dist-info/RECORD +0 -211
- /notionary/page/content/syntax/{grammar.py → definition/grammar.py} +0 -0
- {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/WHEEL +0 -0
- {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,12 +4,12 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
|
|
5
5
|
from notionary.blocks.schemas import Block
|
|
6
6
|
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
7
|
-
from notionary.page.content.syntax import
|
|
7
|
+
from notionary.page.content.syntax.definition import SyntaxDefinitionRegistry
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class BlockRenderer(ABC):
|
|
11
|
-
def __init__(self, syntax_registry:
|
|
12
|
-
self._syntax_registry = syntax_registry or
|
|
11
|
+
def __init__(self, syntax_registry: SyntaxDefinitionRegistry | None = None) -> None:
|
|
12
|
+
self._syntax_registry = syntax_registry or SyntaxDefinitionRegistry()
|
|
13
13
|
self._next_handler: BlockRenderer | None = None
|
|
14
14
|
|
|
15
15
|
def set_next(self, handler: BlockRenderer) -> BlockRenderer:
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
3
|
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
-
from notionary.page.content.renderer.renderers.captioned_block import
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import (
|
|
5
|
+
CaptionedBlockRenderer,
|
|
6
|
+
)
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class BookmarkRenderer(CaptionedBlockRenderer):
|
|
@@ -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 BulletedListRenderer(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:
|
|
@@ -45,4 +49,6 @@ class BulletedListRenderer(BlockRenderer):
|
|
|
45
49
|
if not block.bulleted_list_item or not block.bulleted_list_item.rich_text:
|
|
46
50
|
return None
|
|
47
51
|
|
|
48
|
-
return await self._rich_text_markdown_converter.to_markdown(
|
|
52
|
+
return await self._rich_text_markdown_converter.to_markdown(
|
|
53
|
+
block.bulleted_list_item.rich_text
|
|
54
|
+
)
|
|
@@ -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 CalloutRenderer(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,9 +34,15 @@ class CalloutRenderer(BlockRenderer):
|
|
|
30
34
|
|
|
31
35
|
icon = await self._extract_callout_icon(context.block)
|
|
32
36
|
|
|
33
|
-
callout_start_delimiter =
|
|
37
|
+
callout_start_delimiter = (
|
|
38
|
+
self._syntax_registry.get_callout_syntax().start_delimiter
|
|
39
|
+
)
|
|
34
40
|
|
|
35
|
-
result =
|
|
41
|
+
result = (
|
|
42
|
+
f'{callout_start_delimiter}({content} "{icon}")'
|
|
43
|
+
if icon
|
|
44
|
+
else f"{callout_start_delimiter}({content})"
|
|
45
|
+
)
|
|
36
46
|
|
|
37
47
|
if context.indent_level > 0:
|
|
38
48
|
result = context.indent_text(result)
|
|
@@ -47,4 +57,6 @@ class CalloutRenderer(BlockRenderer):
|
|
|
47
57
|
async def _extract_callout_content(self, block: Block) -> str:
|
|
48
58
|
if not block.callout or not block.callout.rich_text:
|
|
49
59
|
return ""
|
|
50
|
-
return await self._rich_text_markdown_converter.to_markdown(
|
|
60
|
+
return await self._rich_text_markdown_converter.to_markdown(
|
|
61
|
+
block.callout.rich_text
|
|
62
|
+
)
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from typing import override
|
|
3
3
|
|
|
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 CaptionedBlockRenderer(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
|
@abstractmethod
|
|
21
25
|
async def _render_main_content(self, block: Block) -> str:
|
|
@@ -53,6 +57,8 @@ class CaptionedBlockRenderer(BlockRenderer):
|
|
|
53
57
|
if not caption_rich_text:
|
|
54
58
|
return ""
|
|
55
59
|
|
|
56
|
-
caption_markdown = await self._rich_text_markdown_converter.to_markdown(
|
|
60
|
+
caption_markdown = await self._rich_text_markdown_converter.to_markdown(
|
|
61
|
+
caption_rich_text
|
|
62
|
+
)
|
|
57
63
|
|
|
58
64
|
return f"\n[caption] {caption_markdown}"
|
|
@@ -2,7 +2,9 @@ from typing import override
|
|
|
2
2
|
|
|
3
3
|
from notionary.blocks.enums import BlockType
|
|
4
4
|
from notionary.blocks.schemas import Block
|
|
5
|
-
from notionary.page.content.renderer.renderers.captioned_block import
|
|
5
|
+
from notionary.page.content.renderer.renderers.captioned_block import (
|
|
6
|
+
CaptionedBlockRenderer,
|
|
7
|
+
)
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class CodeRenderer(CaptionedBlockRenderer):
|
|
@@ -31,4 +33,6 @@ class CodeRenderer(CaptionedBlockRenderer):
|
|
|
31
33
|
async def _extract_code_content(self, block: Block) -> str:
|
|
32
34
|
if not block.code or not block.code.rich_text:
|
|
33
35
|
return ""
|
|
34
|
-
return await self._rich_text_markdown_converter.to_markdown(
|
|
36
|
+
return await self._rich_text_markdown_converter.to_markdown(
|
|
37
|
+
block.code.rich_text
|
|
38
|
+
)
|
|
@@ -42,7 +42,9 @@ class ColumnRenderer(BlockRenderer):
|
|
|
42
42
|
|
|
43
43
|
return delimiter
|
|
44
44
|
|
|
45
|
-
async def _render_children_with_indentation(
|
|
45
|
+
async def _render_children_with_indentation(
|
|
46
|
+
self, context: MarkdownRenderingContext
|
|
47
|
+
) -> str:
|
|
46
48
|
original_indent = context.indent_level
|
|
47
49
|
context.indent_level += 1
|
|
48
50
|
|
|
@@ -33,7 +33,9 @@ class ColumnListRenderer(BlockRenderer):
|
|
|
33
33
|
def _get_column_list_delimiter(self) -> str:
|
|
34
34
|
return self._syntax_registry.get_column_list_syntax().start_delimiter
|
|
35
35
|
|
|
36
|
-
async def _render_children_with_indentation(
|
|
36
|
+
async def _render_children_with_indentation(
|
|
37
|
+
self, context: MarkdownRenderingContext
|
|
38
|
+
) -> str:
|
|
37
39
|
original_indent = context.indent_level
|
|
38
40
|
context.indent_level += 1
|
|
39
41
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
3
|
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
-
from notionary.page.content.renderer.renderers.captioned_block import
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import (
|
|
5
|
+
CaptionedBlockRenderer,
|
|
6
|
+
)
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class EmbedRenderer(CaptionedBlockRenderer):
|
|
@@ -19,7 +19,9 @@ class EquationRenderer(BlockRenderer):
|
|
|
19
19
|
return
|
|
20
20
|
|
|
21
21
|
syntax = self._syntax_registry.get_equation_syntax()
|
|
22
|
-
equation_markdown =
|
|
22
|
+
equation_markdown = (
|
|
23
|
+
f"{syntax.start_delimiter}{expression}{syntax.end_delimiter}"
|
|
24
|
+
)
|
|
23
25
|
|
|
24
26
|
if context.indent_level > 0:
|
|
25
27
|
equation_markdown = context.indent_text(equation_markdown)
|
|
@@ -1,34 +1,31 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.schemas import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FileRenderer(FileLikeBlockRenderer):
|
|
8
16
|
@override
|
|
9
17
|
def _can_handle(self, block: Block) -> bool:
|
|
10
18
|
return block.type == BlockType.FILE
|
|
11
19
|
|
|
12
20
|
@override
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if not url:
|
|
17
|
-
return ""
|
|
18
|
-
|
|
19
|
-
syntax = self._syntax_registry.get_file_syntax()
|
|
20
|
-
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
def _get_syntax(self) -> EnclosedSyntaxDefinition:
|
|
22
|
+
return self._syntax_registry.get_file_syntax()
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return block.file.external.url or ""
|
|
28
|
-
elif block.file.file:
|
|
29
|
-
return block.file.file.url or ""
|
|
30
|
-
|
|
31
|
-
return ""
|
|
24
|
+
@override
|
|
25
|
+
def _get_file_data(
|
|
26
|
+
self, block: Block
|
|
27
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
28
|
+
return block.file
|
|
32
29
|
|
|
33
30
|
def _extract_file_name(self, block: Block) -> str:
|
|
34
31
|
if not block.file:
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
from notionary.blocks.schemas import (
|
|
5
|
+
Block,
|
|
6
|
+
ExternalFileWithCaption,
|
|
7
|
+
NotionHostedFileWithCaption,
|
|
8
|
+
)
|
|
9
|
+
from notionary.page.content.renderer.renderers.captioned_block import (
|
|
10
|
+
CaptionedBlockRenderer,
|
|
11
|
+
)
|
|
12
|
+
from notionary.page.content.syntax.definition import EnclosedSyntaxDefinition
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FileLikeBlockRenderer(CaptionedBlockRenderer):
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def _get_syntax(self) -> EnclosedSyntaxDefinition:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def _get_file_data(
|
|
22
|
+
self, block: Block
|
|
23
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@override
|
|
27
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
28
|
+
url = self._extract_url(block)
|
|
29
|
+
|
|
30
|
+
if not url:
|
|
31
|
+
return ""
|
|
32
|
+
|
|
33
|
+
syntax = self._get_syntax()
|
|
34
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
35
|
+
|
|
36
|
+
def _extract_url(self, block: Block) -> str:
|
|
37
|
+
file_data = self._get_file_data(block)
|
|
38
|
+
|
|
39
|
+
if not file_data:
|
|
40
|
+
return ""
|
|
41
|
+
|
|
42
|
+
if isinstance(file_data, ExternalFileWithCaption):
|
|
43
|
+
return file_data.external.url or ""
|
|
44
|
+
elif isinstance(file_data, NotionHostedFileWithCaption):
|
|
45
|
+
return file_data.file.url or ""
|
|
46
|
+
|
|
47
|
+
return ""
|
|
@@ -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,31 +1,28 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.schemas import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ImageRenderer(FileLikeBlockRenderer):
|
|
8
16
|
@override
|
|
9
17
|
def _can_handle(self, block: Block) -> bool:
|
|
10
18
|
return block.type == BlockType.IMAGE
|
|
11
19
|
|
|
12
20
|
@override
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if not url:
|
|
17
|
-
return ""
|
|
18
|
-
|
|
19
|
-
syntax = self._syntax_registry.get_image_syntax()
|
|
20
|
-
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
def _get_syntax(self) -> SyntaxDefinition:
|
|
22
|
+
return self._syntax_registry.get_image_syntax()
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return block.image.external.url or ""
|
|
28
|
-
elif block.image.file:
|
|
29
|
-
return block.image.file.url or ""
|
|
30
|
-
|
|
31
|
-
return ""
|
|
24
|
+
@override
|
|
25
|
+
def _get_file_data(
|
|
26
|
+
self, block: Block
|
|
27
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
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,31 +1,28 @@
|
|
|
1
1
|
from typing import override
|
|
2
2
|
|
|
3
|
-
from notionary.blocks.schemas import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PdfRenderer(FileLikeBlockRenderer):
|
|
8
16
|
@override
|
|
9
17
|
def _can_handle(self, block: Block) -> bool:
|
|
10
18
|
return block.type == BlockType.PDF
|
|
11
19
|
|
|
12
20
|
@override
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if not url:
|
|
17
|
-
return ""
|
|
18
|
-
|
|
19
|
-
syntax = self._syntax_registry.get_pdf_syntax()
|
|
20
|
-
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
def _get_syntax(self) -> EnclosedSyntaxDefinition:
|
|
22
|
+
return self._syntax_registry.get_pdf_syntax()
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return block.pdf.external.url or ""
|
|
28
|
-
elif block.pdf.file:
|
|
29
|
-
return block.pdf.file.url or ""
|
|
30
|
-
|
|
31
|
-
return ""
|
|
24
|
+
@override
|
|
25
|
+
def _get_file_data(
|
|
26
|
+
self, block: Block
|
|
27
|
+
) -> ExternalFileWithCaption | NotionHostedFileWithCaption | None:
|
|
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
|
|