notionary 0.2.19__py3-none-any.whl → 0.2.21__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 +8 -4
- notionary/base_notion_client.py +3 -1
- notionary/blocks/__init__.py +2 -91
- notionary/blocks/_bootstrap.py +263 -0
- notionary/blocks/audio/__init__.py +8 -2
- notionary/blocks/audio/audio_element.py +42 -104
- notionary/blocks/audio/audio_markdown_node.py +3 -1
- notionary/blocks/audio/audio_models.py +6 -55
- notionary/blocks/base_block_element.py +30 -0
- notionary/blocks/bookmark/__init__.py +9 -2
- notionary/blocks/bookmark/bookmark_element.py +46 -139
- notionary/blocks/bookmark/bookmark_markdown_node.py +3 -1
- notionary/blocks/bookmark/bookmark_models.py +15 -0
- notionary/blocks/breadcrumbs/__init__.py +17 -0
- notionary/blocks/breadcrumbs/breadcrumb_element.py +39 -0
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +32 -0
- notionary/blocks/breadcrumbs/breadcrumb_models.py +12 -0
- notionary/blocks/bulleted_list/__init__.py +12 -2
- notionary/blocks/bulleted_list/bulleted_list_element.py +40 -55
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +2 -1
- notionary/blocks/bulleted_list/bulleted_list_models.py +18 -0
- notionary/blocks/callout/__init__.py +9 -2
- notionary/blocks/callout/callout_element.py +40 -89
- notionary/blocks/callout/callout_markdown_node.py +3 -1
- notionary/blocks/callout/callout_models.py +33 -0
- notionary/blocks/child_database/__init__.py +7 -0
- notionary/blocks/child_database/child_database_models.py +19 -0
- notionary/blocks/child_page/__init__.py +9 -0
- notionary/blocks/child_page/child_page_models.py +12 -0
- notionary/blocks/{shared/block_client.py → client.py} +55 -54
- notionary/blocks/code/__init__.py +6 -2
- notionary/blocks/code/code_element.py +53 -187
- notionary/blocks/code/code_markdown_node.py +13 -13
- notionary/blocks/code/code_models.py +94 -0
- notionary/blocks/column/__init__.py +25 -1
- notionary/blocks/column/column_element.py +40 -314
- notionary/blocks/column/column_list_element.py +37 -0
- notionary/blocks/column/column_list_markdown_node.py +50 -0
- notionary/blocks/column/column_markdown_node.py +59 -0
- notionary/blocks/column/column_models.py +26 -0
- notionary/blocks/divider/__init__.py +9 -2
- notionary/blocks/divider/divider_element.py +26 -49
- notionary/blocks/divider/divider_markdown_node.py +2 -1
- notionary/blocks/divider/divider_models.py +12 -0
- notionary/blocks/embed/__init__.py +9 -2
- notionary/blocks/embed/embed_element.py +47 -114
- notionary/blocks/embed/embed_markdown_node.py +3 -1
- notionary/blocks/embed/embed_models.py +14 -0
- notionary/blocks/equation/__init__.py +14 -0
- notionary/blocks/equation/equation_element.py +80 -0
- notionary/blocks/equation/equation_element_markdown_node.py +36 -0
- notionary/blocks/equation/equation_models.py +11 -0
- notionary/blocks/file/__init__.py +25 -0
- notionary/blocks/file/file_element.py +93 -0
- notionary/blocks/file/file_element_markdown_node.py +35 -0
- notionary/blocks/file/file_element_models.py +39 -0
- notionary/blocks/heading/__init__.py +16 -2
- notionary/blocks/heading/heading_element.py +67 -72
- notionary/blocks/heading/heading_markdown_node.py +2 -1
- notionary/blocks/heading/heading_models.py +29 -0
- notionary/blocks/image_block/__init__.py +13 -0
- notionary/blocks/image_block/image_element.py +84 -0
- notionary/blocks/{image → image_block}/image_markdown_node.py +3 -1
- notionary/blocks/image_block/image_models.py +10 -0
- notionary/blocks/models.py +172 -0
- notionary/blocks/numbered_list/__init__.py +12 -2
- notionary/blocks/numbered_list/numbered_list_element.py +33 -58
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
- notionary/blocks/numbered_list/numbered_list_models.py +17 -0
- notionary/blocks/paragraph/__init__.py +12 -2
- notionary/blocks/paragraph/paragraph_element.py +27 -69
- notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
- notionary/blocks/paragraph/paragraph_models.py +16 -0
- notionary/blocks/pdf/__init__.py +13 -0
- notionary/blocks/pdf/pdf_element.py +91 -0
- notionary/blocks/pdf/pdf_markdown_node.py +35 -0
- notionary/blocks/pdf/pdf_models.py +11 -0
- notionary/blocks/quote/__init__.py +11 -2
- notionary/blocks/quote/quote_element.py +31 -65
- notionary/blocks/quote/quote_markdown_node.py +4 -1
- notionary/blocks/quote/quote_models.py +18 -0
- notionary/blocks/registry/__init__.py +4 -0
- notionary/blocks/registry/block_registry.py +75 -91
- notionary/blocks/registry/block_registry_builder.py +107 -59
- notionary/blocks/rich_text/__init__.py +33 -0
- notionary/blocks/rich_text/rich_text_models.py +188 -0
- notionary/blocks/rich_text/text_inline_formatter.py +125 -0
- notionary/blocks/table/__init__.py +16 -2
- notionary/blocks/table/table_element.py +48 -241
- notionary/blocks/table/table_markdown_node.py +2 -1
- notionary/blocks/table/table_models.py +28 -0
- notionary/blocks/table_of_contents/__init__.py +19 -0
- notionary/blocks/table_of_contents/table_of_contents_element.py +51 -0
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
- notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
- notionary/blocks/todo/__init__.py +9 -2
- notionary/blocks/todo/todo_element.py +38 -95
- notionary/blocks/todo/todo_markdown_node.py +2 -1
- notionary/blocks/todo/todo_models.py +19 -0
- notionary/blocks/toggle/__init__.py +13 -3
- notionary/blocks/toggle/toggle_element.py +57 -264
- notionary/blocks/toggle/toggle_markdown_node.py +24 -14
- notionary/blocks/toggle/toggle_models.py +17 -0
- notionary/blocks/toggleable_heading/__init__.py +6 -2
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +74 -244
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
- notionary/blocks/types.py +61 -0
- notionary/blocks/video/__init__.py +8 -2
- notionary/blocks/video/video_element.py +67 -143
- notionary/blocks/video/video_element_models.py +10 -0
- notionary/blocks/video/video_markdown_node.py +3 -1
- notionary/database/client.py +3 -8
- notionary/database/database.py +13 -14
- notionary/database/database_filter_builder.py +2 -2
- notionary/database/database_provider.py +5 -4
- notionary/database/models.py +337 -0
- notionary/database/notion_database.py +6 -7
- notionary/file_upload/client.py +5 -7
- notionary/file_upload/models.py +2 -1
- notionary/file_upload/notion_file_upload.py +2 -3
- notionary/markdown/markdown_builder.py +722 -0
- notionary/markdown/markdown_document_model.py +228 -0
- notionary/{blocks → markdown}/markdown_node.py +1 -0
- notionary/models/notion_database_response.py +0 -338
- notionary/page/client.py +9 -10
- notionary/page/models.py +327 -0
- notionary/page/notion_page.py +99 -52
- notionary/page/notion_text_length_utils.py +119 -0
- notionary/page/{content/page_content_writer.py → page_content_writer.py} +88 -38
- notionary/page/reader/handler/__init__.py +17 -0
- notionary/page/reader/handler/base_block_renderer.py +44 -0
- notionary/page/reader/handler/block_processing_context.py +35 -0
- notionary/page/reader/handler/block_rendering_context.py +43 -0
- notionary/page/reader/handler/column_list_renderer.py +51 -0
- notionary/page/reader/handler/column_renderer.py +60 -0
- notionary/page/reader/handler/line_renderer.py +60 -0
- notionary/page/reader/handler/toggle_renderer.py +69 -0
- notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
- notionary/page/reader/page_content_retriever.py +69 -0
- notionary/page/search_filter_builder.py +2 -1
- notionary/page/writer/handler/__init__.py +22 -0
- notionary/page/writer/handler/code_handler.py +100 -0
- notionary/page/writer/handler/column_handler.py +141 -0
- notionary/page/writer/handler/column_list_handler.py +139 -0
- notionary/page/writer/handler/line_handler.py +35 -0
- notionary/page/writer/handler/line_processing_context.py +54 -0
- notionary/page/writer/handler/regular_line_handler.py +92 -0
- notionary/page/writer/handler/table_handler.py +130 -0
- notionary/page/writer/handler/toggle_handler.py +153 -0
- notionary/page/writer/handler/toggleable_heading_handler.py +167 -0
- notionary/page/writer/markdown_to_notion_converter.py +76 -0
- notionary/telemetry/__init__.py +2 -2
- notionary/telemetry/service.py +4 -3
- notionary/user/__init__.py +2 -2
- notionary/user/base_notion_user.py +2 -1
- notionary/user/client.py +2 -3
- notionary/user/models.py +1 -0
- notionary/user/notion_bot_user.py +4 -5
- notionary/user/notion_user.py +3 -4
- notionary/user/notion_user_manager.py +3 -2
- notionary/user/notion_user_provider.py +1 -1
- notionary/util/__init__.py +3 -2
- notionary/util/fuzzy.py +2 -1
- notionary/util/logging_mixin.py +2 -2
- notionary/util/singleton_metaclass.py +1 -1
- notionary/workspace.py +3 -2
- {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/METADATA +12 -8
- notionary-0.2.21.dist-info/RECORD +185 -0
- notionary/blocks/document/__init__.py +0 -7
- notionary/blocks/document/document_element.py +0 -102
- notionary/blocks/document/document_markdown_node.py +0 -31
- notionary/blocks/image/__init__.py +0 -7
- notionary/blocks/image/image_element.py +0 -151
- notionary/blocks/markdown_builder.py +0 -356
- notionary/blocks/mention/__init__.py +0 -7
- notionary/blocks/mention/mention_element.py +0 -229
- notionary/blocks/mention/mention_markdown_node.py +0 -38
- notionary/blocks/prompts/element_prompt_builder.py +0 -83
- notionary/blocks/prompts/element_prompt_content.py +0 -41
- notionary/blocks/shared/__init__.py +0 -0
- notionary/blocks/shared/models.py +0 -713
- notionary/blocks/shared/notion_block_element.py +0 -37
- notionary/blocks/shared/text_inline_formatter.py +0 -262
- notionary/blocks/shared/text_inline_formatter_new.py +0 -139
- notionary/blocks/toggleable_heading/toggleable_heading_models.py +0 -0
- notionary/database/models/page_result.py +0 -10
- notionary/elements/__init__.py +0 -0
- notionary/models/notion_block_response.py +0 -264
- notionary/models/notion_page_response.py +0 -78
- notionary/models/search_response.py +0 -0
- notionary/page/__init__.py +0 -0
- notionary/page/content/notion_text_length_utils.py +0 -87
- notionary/page/content/page_content_retriever.py +0 -60
- notionary/page/formatting/line_processor.py +0 -153
- notionary/page/formatting/markdown_to_notion_converter.py +0 -153
- notionary/page/markdown_syntax_prompt_generator.py +0 -114
- notionary/page/notion_to_markdown_converter.py +0 -179
- notionary/page/properites/property_value_extractor.py +0 -0
- notionary-0.2.19.dist-info/RECORD +0 -150
- /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
- /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
- /notionary/page/{content/markdown_whitespace_processor.py → markdown_whitespace_processor.py} +0 -0
- /notionary/{blocks/mention/mention_models.py → page/reader/handler/context.py} +0 -0
- {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/LICENSE +0 -0
- {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/WHEEL +0 -0
@@ -1,16 +1,20 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import re
|
2
|
-
from typing import
|
3
|
-
|
4
|
-
from notionary.blocks.
|
5
|
-
from notionary.blocks import (
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
from notionary.blocks.base_block_element import BaseBlockElement
|
7
|
+
from notionary.blocks.callout.callout_models import (
|
8
|
+
CalloutBlock,
|
9
|
+
CreateCalloutBlock,
|
10
|
+
EmojiIcon,
|
11
|
+
IconObject,
|
10
12
|
)
|
13
|
+
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
14
|
+
from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
|
11
15
|
|
12
16
|
|
13
|
-
class CalloutElement(
|
17
|
+
class CalloutElement(BaseBlockElement):
|
14
18
|
"""
|
15
19
|
Handles conversion between Markdown callouts and Notion callout blocks.
|
16
20
|
|
@@ -23,110 +27,57 @@ class CalloutElement(NotionBlockElement):
|
|
23
27
|
- emoji is an optional emoji character (enclosed in quotes)
|
24
28
|
"""
|
25
29
|
|
26
|
-
# Regex pattern for callout syntax with optional emoji
|
27
30
|
PATTERN = re.compile(
|
28
|
-
r"^\[callout\]\(" #
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
r"^\[callout\]\(" # prefix
|
32
|
+
r"([^\"]+?)" # content
|
33
|
+
r"(?:\s+\"([^\"]+)\")?" # optional emoji
|
34
|
+
r"\)$"
|
32
35
|
)
|
33
36
|
|
34
|
-
# Default values
|
35
37
|
DEFAULT_EMOJI = "💡"
|
36
38
|
DEFAULT_COLOR = "gray_background"
|
37
39
|
|
38
40
|
@classmethod
|
39
|
-
def
|
40
|
-
|
41
|
-
return text.strip().startswith("[callout]") and bool(
|
42
|
-
CalloutElement.PATTERN.match(text.strip())
|
43
|
-
)
|
44
|
-
|
45
|
-
@classmethod
|
46
|
-
def match_notion(cls, block: Dict[str, Any]) -> bool:
|
47
|
-
"""Check if block is a Notion callout."""
|
48
|
-
return block.get("type") == "callout"
|
41
|
+
def match_notion(cls, block: Block) -> bool:
|
42
|
+
return block.type == BlockType.CALLOUT and block.callout
|
49
43
|
|
50
44
|
@classmethod
|
51
|
-
def markdown_to_notion(cls, text: str) ->
|
52
|
-
"""Convert markdown callout
|
53
|
-
|
54
|
-
if not
|
45
|
+
def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
46
|
+
"""Convert a markdown callout into a Notion CalloutBlock."""
|
47
|
+
match = cls.PATTERN.match(text.strip())
|
48
|
+
if not match:
|
55
49
|
return None
|
56
50
|
|
57
|
-
content =
|
58
|
-
emoji = callout_match.group(2)
|
59
|
-
|
51
|
+
content, emoji = match.group(1), match.group(2)
|
60
52
|
if not content:
|
61
53
|
return None
|
62
54
|
|
63
|
-
# Use default emoji if none provided
|
64
55
|
if not emoji:
|
65
|
-
emoji =
|
56
|
+
emoji = cls.DEFAULT_EMOJI
|
66
57
|
|
67
|
-
|
68
|
-
"rich_text": TextInlineFormatter.parse_inline_formatting(content.strip()),
|
69
|
-
"icon": {"type": "emoji", "emoji": emoji},
|
70
|
-
"color": CalloutElement.DEFAULT_COLOR,
|
71
|
-
}
|
58
|
+
rich_text = TextInlineFormatter.parse_inline_formatting(content.strip())
|
72
59
|
|
73
|
-
|
60
|
+
callout_content = CalloutBlock(
|
61
|
+
rich_text=rich_text,
|
62
|
+
icon=EmojiIcon(emoji=emoji),
|
63
|
+
color=cls.DEFAULT_COLOR,
|
64
|
+
)
|
65
|
+
return CreateCalloutBlock(callout=callout_content)
|
74
66
|
|
75
67
|
@classmethod
|
76
|
-
def notion_to_markdown(cls, block:
|
77
|
-
|
78
|
-
if block.get("type") != "callout":
|
68
|
+
def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
69
|
+
if block.type != BlockType.CALLOUT or not block.callout:
|
79
70
|
return None
|
80
71
|
|
81
|
-
|
82
|
-
rich_text = callout_data.get("rich_text", [])
|
83
|
-
icon = callout_data.get("icon", {})
|
72
|
+
data = block.callout
|
84
73
|
|
85
|
-
content = TextInlineFormatter.extract_text_with_formatting(rich_text)
|
74
|
+
content = TextInlineFormatter.extract_text_with_formatting(data.rich_text)
|
86
75
|
if not content:
|
87
76
|
return None
|
88
77
|
|
89
|
-
|
90
|
-
|
91
|
-
if emoji and emoji != CalloutElement.DEFAULT_EMOJI:
|
92
|
-
return f'[callout]({content} "{emoji}")'
|
78
|
+
icon: Optional[IconObject] = block.callout.icon
|
79
|
+
emoji_char = icon.emoji if isinstance(icon, EmojiIcon) else cls.DEFAULT_EMOJI
|
93
80
|
|
81
|
+
if emoji_char and emoji_char != cls.DEFAULT_EMOJI:
|
82
|
+
return f'[callout]({content} "{emoji_char}")'
|
94
83
|
return f"[callout]({content})"
|
95
|
-
|
96
|
-
@classmethod
|
97
|
-
def is_multiline(cls) -> bool:
|
98
|
-
"""Callouts are single-line elements."""
|
99
|
-
return False
|
100
|
-
|
101
|
-
@classmethod
|
102
|
-
def _extract_emoji(cls, icon: Dict[str, Any]) -> str:
|
103
|
-
"""Extract emoji from Notion icon object."""
|
104
|
-
if icon and icon.get("type") == "emoji":
|
105
|
-
return icon.get("emoji", "")
|
106
|
-
return ""
|
107
|
-
|
108
|
-
@classmethod
|
109
|
-
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
110
|
-
"""
|
111
|
-
Returns structured LLM prompt metadata for the callout element.
|
112
|
-
"""
|
113
|
-
return (
|
114
|
-
ElementPromptBuilder()
|
115
|
-
.with_description(
|
116
|
-
"Creates a callout block to highlight important information with an icon."
|
117
|
-
)
|
118
|
-
.with_usage_guidelines(
|
119
|
-
"Use callouts when you want to draw attention to important information, "
|
120
|
-
"tips, warnings, or notes that stand out from the main content."
|
121
|
-
)
|
122
|
-
.with_syntax('[callout](Text content "Optional emoji")')
|
123
|
-
.with_examples(
|
124
|
-
[
|
125
|
-
"[callout](This is a default callout with the light bulb emoji)",
|
126
|
-
'[callout](This is a callout with a bell emoji "🔔")',
|
127
|
-
'[callout](Warning: This is an important note "⚠️")',
|
128
|
-
'[callout](Tip: Add emoji that matches your content\'s purpose "💡")',
|
129
|
-
]
|
130
|
-
)
|
131
|
-
.build()
|
132
|
-
)
|
@@ -1,8 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from typing import Optional
|
4
|
+
|
4
5
|
from pydantic import BaseModel
|
5
|
-
|
6
|
+
|
7
|
+
from notionary.markdown.markdown_node import MarkdownNode
|
6
8
|
|
7
9
|
|
8
10
|
class CalloutMarkdownBlockParams(BaseModel):
|
@@ -0,0 +1,33 @@
|
|
1
|
+
from typing import Literal, Optional, Union
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
from notionary.blocks.file.file_element_models import FileBlock
|
6
|
+
from notionary.blocks.models import Block
|
7
|
+
from notionary.blocks.rich_text.rich_text_models import RichTextObject
|
8
|
+
from notionary.blocks.types import BlockColor
|
9
|
+
|
10
|
+
|
11
|
+
class EmojiIcon(BaseModel):
|
12
|
+
type: Literal["emoji"] = "emoji"
|
13
|
+
emoji: str
|
14
|
+
|
15
|
+
|
16
|
+
class FileIcon(BaseModel):
|
17
|
+
type: Literal["file"] = "file"
|
18
|
+
file: FileBlock
|
19
|
+
|
20
|
+
|
21
|
+
IconObject = Union[EmojiIcon, FileIcon]
|
22
|
+
|
23
|
+
|
24
|
+
class CalloutBlock(BaseModel):
|
25
|
+
rich_text: list[RichTextObject]
|
26
|
+
icon: Optional[IconObject] = None
|
27
|
+
color: BlockColor = BlockColor.DEFAULT
|
28
|
+
children: list[Block] = Field(default_factory=list)
|
29
|
+
|
30
|
+
|
31
|
+
class CreateCalloutBlock(BaseModel):
|
32
|
+
type: Literal["callout"] = "callout"
|
33
|
+
callout: CalloutBlock
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
from notionary.blocks.rich_text.rich_text_models import RichTextObject
|
6
|
+
|
7
|
+
|
8
|
+
class CreateInlineDatabaseRequest(BaseModel):
|
9
|
+
"""
|
10
|
+
Minimaler Create-Payload für eine inline Database.
|
11
|
+
Parent wird von der Page-Schicht gesetzt: {"type": "page_id", "page_id": "..."}.
|
12
|
+
"""
|
13
|
+
|
14
|
+
parent: dict[str, str] # wird von außen injiziert
|
15
|
+
title: list[
|
16
|
+
RichTextObject
|
17
|
+
] # z. B. [RichTextObject.from_plain_text("Monatsübersicht")]
|
18
|
+
properties: dict[str, dict[str, Any]] # mindestens eine Title-Property erforderlich
|
19
|
+
is_inline: bool = True # inline = erscheint als child_database-Block auf der Page
|
@@ -1,10 +1,9 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Any, Optional
|
2
|
+
|
2
3
|
from notionary.base_notion_client import BaseNotionClient
|
3
|
-
from notionary.
|
4
|
-
from notionary.blocks.shared.models import Block, BlockChildrenResponse
|
4
|
+
from notionary.blocks.models import Block, BlockChildrenResponse, BlockCreateRequest
|
5
5
|
|
6
6
|
|
7
|
-
@singleton
|
8
7
|
class NotionBlockClient(BaseNotionClient):
|
9
8
|
"""
|
10
9
|
Client for Notion Block API operations.
|
@@ -25,6 +24,37 @@ class NotionBlockClient(BaseNotionClient):
|
|
25
24
|
return None
|
26
25
|
return None
|
27
26
|
|
27
|
+
# das hier ist falsch (Columns werden nicht richtig abgebildet)
|
28
|
+
async def get_blocks_by_page_id_recursively(
|
29
|
+
self, page_id: str, parent_id: Optional[str] = None
|
30
|
+
) -> list[Block]:
|
31
|
+
response = (
|
32
|
+
await self.get_block_children(block_id=page_id)
|
33
|
+
if parent_id is None
|
34
|
+
else await self.get_block_children(block_id=parent_id)
|
35
|
+
)
|
36
|
+
|
37
|
+
if not response or not response.results:
|
38
|
+
return []
|
39
|
+
|
40
|
+
blocks = response.results
|
41
|
+
|
42
|
+
for block in blocks:
|
43
|
+
if not block.has_children:
|
44
|
+
continue
|
45
|
+
|
46
|
+
block_id = block.id
|
47
|
+
if not block_id:
|
48
|
+
continue
|
49
|
+
|
50
|
+
children = await self.get_blocks_by_page_id_recursively(
|
51
|
+
page_id=page_id, parent_id=block_id
|
52
|
+
)
|
53
|
+
if children:
|
54
|
+
block.children = children
|
55
|
+
|
56
|
+
return blocks
|
57
|
+
|
28
58
|
async def get_block_children(
|
29
59
|
self, block_id: str, start_cursor: Optional[str] = None, page_size: int = 100
|
30
60
|
) -> Optional[BlockChildrenResponse]:
|
@@ -38,13 +68,15 @@ class NotionBlockClient(BaseNotionClient):
|
|
38
68
|
params["start_cursor"] = start_cursor
|
39
69
|
|
40
70
|
response = await self.get(f"blocks/{block_id}/children", params=params)
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
71
|
+
|
72
|
+
if not response:
|
73
|
+
return None
|
74
|
+
|
75
|
+
try:
|
76
|
+
return BlockChildrenResponse.model_validate(response)
|
77
|
+
except Exception as e:
|
78
|
+
self.logger.error("Failed to parse block children response: %s", str(e))
|
79
|
+
return None
|
48
80
|
|
49
81
|
async def get_all_block_children(self, block_id: str) -> list[Block]:
|
50
82
|
"""
|
@@ -74,7 +106,10 @@ class NotionBlockClient(BaseNotionClient):
|
|
74
106
|
return all_blocks
|
75
107
|
|
76
108
|
async def append_block_children(
|
77
|
-
self,
|
109
|
+
self,
|
110
|
+
block_id: str,
|
111
|
+
children: list[BlockCreateRequest],
|
112
|
+
after: Optional[str] = None,
|
78
113
|
) -> Optional[BlockChildrenResponse]:
|
79
114
|
"""
|
80
115
|
Appends new child blocks to a parent block.
|
@@ -86,15 +121,18 @@ class NotionBlockClient(BaseNotionClient):
|
|
86
121
|
|
87
122
|
self.logger.debug("Appending %d children to block: %s", len(children), block_id)
|
88
123
|
|
124
|
+
# Convert Pydantic models to dictionaries for API
|
125
|
+
children_dicts = [block.model_dump(exclude_none=True) for block in children]
|
126
|
+
|
89
127
|
# If 100 or fewer blocks, use single request
|
90
|
-
if len(
|
91
|
-
return await self._append_single_batch(block_id,
|
128
|
+
if len(children_dicts) <= 100:
|
129
|
+
return await self._append_single_batch(block_id, children_dicts, after)
|
92
130
|
|
93
131
|
# For more than 100 blocks, use batch processing
|
94
|
-
return await self._append_multiple_batches(block_id,
|
132
|
+
return await self._append_multiple_batches(block_id, children_dicts, after)
|
95
133
|
|
96
134
|
async def _append_single_batch(
|
97
|
-
self, block_id: str, children: list[
|
135
|
+
self, block_id: str, children: list[dict[str, Any]], after: Optional[str] = None
|
98
136
|
) -> Optional[BlockChildrenResponse]:
|
99
137
|
"""
|
100
138
|
Appends a single batch of blocks (≤100).
|
@@ -113,7 +151,7 @@ class NotionBlockClient(BaseNotionClient):
|
|
113
151
|
return None
|
114
152
|
|
115
153
|
async def _append_multiple_batches(
|
116
|
-
self, block_id: str, children: list[
|
154
|
+
self, block_id: str, children: list[dict[str, Any]], after: Optional[str] = None
|
117
155
|
) -> Optional[BlockChildrenResponse]:
|
118
156
|
"""
|
119
157
|
Appends multiple batches of blocks, handling pagination.
|
@@ -206,27 +244,6 @@ class NotionBlockClient(BaseNotionClient):
|
|
206
244
|
request_id=responses[-1].request_id, # Use last request ID
|
207
245
|
)
|
208
246
|
|
209
|
-
async def update_block(
|
210
|
-
self, block_id: str, block_data: Dict[str, Any], archived: Optional[bool] = None
|
211
|
-
) -> Optional[Block]:
|
212
|
-
"""
|
213
|
-
Updates an existing block.
|
214
|
-
"""
|
215
|
-
self.logger.debug("Updating block: %s", block_id)
|
216
|
-
|
217
|
-
data = block_data.copy()
|
218
|
-
if archived is not None:
|
219
|
-
data["archived"] = archived
|
220
|
-
|
221
|
-
response = await self.patch(f"blocks/{block_id}", data)
|
222
|
-
if response:
|
223
|
-
try:
|
224
|
-
return Block.model_validate(response)
|
225
|
-
except Exception as e:
|
226
|
-
self.logger.error("Failed to parse update response: %s", str(e))
|
227
|
-
return None
|
228
|
-
return None
|
229
|
-
|
230
247
|
async def delete_block(self, block_id: str) -> Optional[Block]:
|
231
248
|
"""
|
232
249
|
Deletes (archives) a block.
|
@@ -238,19 +255,3 @@ class NotionBlockClient(BaseNotionClient):
|
|
238
255
|
# After deletion, retrieve the block to return the updated state
|
239
256
|
return await self.get_block(block_id)
|
240
257
|
return None
|
241
|
-
|
242
|
-
async def archive_block(self, block_id: str) -> Optional[Block]:
|
243
|
-
"""
|
244
|
-
Archives a block by setting archived=True.
|
245
|
-
"""
|
246
|
-
self.logger.debug("Archiving block: %s", block_id)
|
247
|
-
|
248
|
-
return await self.update_block(block_id=block_id, block_data={}, archived=True)
|
249
|
-
|
250
|
-
async def unarchive_block(self, block_id: str) -> Optional[Block]:
|
251
|
-
"""
|
252
|
-
Unarchives a block by setting archived=False.
|
253
|
-
"""
|
254
|
-
self.logger.debug("Unarchiving block: %s", block_id)
|
255
|
-
|
256
|
-
return await self.update_block(block_id=block_id, block_data={}, archived=False)
|
@@ -1,7 +1,11 @@
|
|
1
|
-
from .code_element import CodeElement
|
2
|
-
from .code_markdown_node import CodeMarkdownNode
|
1
|
+
from notionary.blocks.code.code_element import CodeElement
|
2
|
+
from notionary.blocks.code.code_markdown_node import CodeMarkdownNode
|
3
|
+
from notionary.blocks.code.code_models import CodeBlock, CodeLanguage, CreateCodeBlock
|
3
4
|
|
4
5
|
__all__ = [
|
5
6
|
"CodeElement",
|
7
|
+
"CodeBlock",
|
8
|
+
"CodeLanguage",
|
9
|
+
"CreateCodeBlock",
|
6
10
|
"CodeMarkdownNode",
|
7
11
|
]
|