notionary 0.1.25__py3-none-any.whl → 0.1.27__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/elements/audio_element.py +41 -38
- notionary/elements/bookmark_element.py +36 -27
- notionary/elements/bulleted_list_element.py +28 -21
- notionary/elements/callout_element.py +39 -31
- notionary/elements/code_block_element.py +38 -26
- notionary/elements/divider_element.py +29 -18
- notionary/elements/embed_element.py +37 -28
- notionary/elements/heading_element.py +39 -24
- notionary/elements/image_element.py +33 -24
- notionary/elements/mention_element.py +40 -29
- notionary/elements/notion_block_element.py +13 -31
- notionary/elements/numbered_list_element.py +29 -20
- notionary/elements/paragraph_element.py +37 -31
- notionary/elements/prompts/element_prompt_content.py +91 -7
- notionary/elements/prompts/synthax_prompt_builder.py +63 -16
- notionary/elements/qoute_element.py +72 -74
- notionary/elements/registry/block_element_registry_builder.py +6 -9
- notionary/elements/table_element.py +49 -36
- notionary/elements/text_inline_formatter.py +23 -15
- notionary/elements/{todo_lists.py → todo_element.py} +34 -25
- notionary/elements/toggle_element.py +184 -108
- notionary/elements/toggleable_heading_element.py +269 -0
- notionary/elements/video_element.py +37 -28
- notionary/page/content/page_content_manager.py +3 -8
- notionary/page/markdown_to_notion_converter.py +269 -274
- notionary/page/metadata/notion_icon_manager.py +13 -7
- notionary/page/notion_page.py +3 -5
- notionary/page/notion_to_markdown_converter.py +20 -95
- {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/METADATA +1 -1
- notionary-0.1.27.dist-info/RECORD +58 -0
- {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/WHEEL +1 -1
- notionary/elements/column_element.py +0 -307
- notionary-0.1.25.dist-info/RECORD +0 -58
- {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.25.dist-info → notionary-0.1.27.dist-info}/top_level.txt +0 -0
@@ -26,19 +26,25 @@ class NotionPageIconManager(LoggingMixin):
|
|
26
26
|
Retrieves the page icon - either emoji or external URL.
|
27
27
|
|
28
28
|
Returns:
|
29
|
-
str: Emoji character or URL if set, None if no icon
|
29
|
+
Optional[str]: Emoji character or URL if set, None if no icon
|
30
30
|
"""
|
31
31
|
page_data = await self._client.get_page(self.page_id)
|
32
32
|
|
33
|
-
if not page_data
|
34
|
-
return
|
35
|
-
|
36
|
-
|
33
|
+
if not page_data:
|
34
|
+
return ""
|
35
|
+
|
36
|
+
# Get icon data, default to empty dict if not present or None
|
37
|
+
icon_data = page_data.get("icon")
|
38
|
+
|
39
|
+
# If icon is None or not present, return None
|
40
|
+
if not icon_data:
|
41
|
+
return ""
|
42
|
+
|
37
43
|
icon_type = icon_data.get("type")
|
38
44
|
|
39
45
|
if icon_type == "emoji":
|
40
46
|
return icon_data.get("emoji")
|
41
|
-
|
47
|
+
if icon_type == "external":
|
42
48
|
return icon_data.get("external", {}).get("url")
|
43
49
|
|
44
|
-
return
|
50
|
+
return ""
|
notionary/page/notion_page.py
CHANGED
@@ -130,7 +130,7 @@ class NotionPage(LoggingMixin):
|
|
130
130
|
self._url_loaded = True
|
131
131
|
return self._url
|
132
132
|
|
133
|
-
async def append_markdown(self, markdown: str
|
133
|
+
async def append_markdown(self, markdown: str) -> str:
|
134
134
|
"""
|
135
135
|
Append markdown content to the page.
|
136
136
|
|
@@ -140,9 +140,7 @@ class NotionPage(LoggingMixin):
|
|
140
140
|
Returns:
|
141
141
|
str: Status or confirmation message.
|
142
142
|
"""
|
143
|
-
return await self._page_content_manager.append_markdown(
|
144
|
-
markdown_text=markdown, append_divider=append_divider
|
145
|
-
)
|
143
|
+
return await self._page_content_manager.append_markdown(markdown_text=markdown)
|
146
144
|
|
147
145
|
async def clear(self) -> str:
|
148
146
|
"""
|
@@ -206,7 +204,7 @@ class NotionPage(LoggingMixin):
|
|
206
204
|
"""
|
207
205
|
return await self._page_icon_manager.set_icon(emoji, external_url)
|
208
206
|
|
209
|
-
async def get_icon(self) ->
|
207
|
+
async def get_icon(self) -> str:
|
210
208
|
"""
|
211
209
|
Retrieve the page icon - either emoji or external URL.
|
212
210
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
from typing import Dict, Any, List, Optional
|
2
2
|
|
3
|
-
from notionary.elements.registry.block_element_registry import
|
4
|
-
BlockElementRegistry,
|
5
|
-
)
|
3
|
+
from notionary.elements.registry.block_element_registry import BlockElementRegistry
|
6
4
|
from notionary.elements.registry.block_element_registry_builder import (
|
7
5
|
BlockElementRegistryBuilder,
|
8
6
|
)
|
@@ -11,12 +9,12 @@ from notionary.elements.registry.block_element_registry_builder import (
|
|
11
9
|
class NotionToMarkdownConverter:
|
12
10
|
"""Converts Notion blocks to Markdown text with support for nested structures."""
|
13
11
|
|
12
|
+
TOGGLE_ELEMENT_TYPES = ["toggle", "toggleable_heading"]
|
13
|
+
LIST_ITEM_TYPES = ["numbered_list_item", "bulleted_list_item"]
|
14
|
+
|
14
15
|
def __init__(self, block_registry: Optional[BlockElementRegistry] = None):
|
15
16
|
"""
|
16
17
|
Initialize the NotionToMarkdownConverter.
|
17
|
-
|
18
|
-
Args:
|
19
|
-
block_registry: Optional registry of Notion block elements
|
20
18
|
"""
|
21
19
|
self._block_registry = (
|
22
20
|
block_registry or BlockElementRegistryBuilder().create_full_registry()
|
@@ -25,12 +23,6 @@ class NotionToMarkdownConverter:
|
|
25
23
|
def convert(self, blocks: List[Dict[str, Any]]) -> str:
|
26
24
|
"""
|
27
25
|
Convert Notion blocks to Markdown text, handling nested structures.
|
28
|
-
|
29
|
-
Args:
|
30
|
-
blocks: List of Notion blocks
|
31
|
-
|
32
|
-
Returns:
|
33
|
-
Markdown text
|
34
26
|
"""
|
35
27
|
if not blocks:
|
36
28
|
return ""
|
@@ -47,12 +39,6 @@ class NotionToMarkdownConverter:
|
|
47
39
|
def _convert_single_block_with_children(self, block: Dict[str, Any]) -> str:
|
48
40
|
"""
|
49
41
|
Process a single block, including any children.
|
50
|
-
|
51
|
-
Args:
|
52
|
-
block: Notion block to process
|
53
|
-
|
54
|
-
Returns:
|
55
|
-
Markdown representation of the block and its children
|
56
42
|
"""
|
57
43
|
if not block:
|
58
44
|
return ""
|
@@ -68,17 +54,14 @@ class NotionToMarkdownConverter:
|
|
68
54
|
|
69
55
|
block_type = block.get("type", "")
|
70
56
|
|
71
|
-
if block_type
|
57
|
+
if block_type in self.TOGGLE_ELEMENT_TYPES:
|
72
58
|
return self._format_toggle_with_children(block_markdown, children_markdown)
|
73
59
|
|
74
|
-
if block_type in
|
60
|
+
if block_type in self.LIST_ITEM_TYPES:
|
75
61
|
return self._format_list_item_with_children(
|
76
62
|
block_markdown, children_markdown
|
77
63
|
)
|
78
64
|
|
79
|
-
if block_type in ["column_list", "column"]:
|
80
|
-
return children_markdown
|
81
|
-
|
82
65
|
return self._format_standard_block_with_children(
|
83
66
|
block_markdown, children_markdown
|
84
67
|
)
|
@@ -86,12 +69,6 @@ class NotionToMarkdownConverter:
|
|
86
69
|
def _has_children(self, block: Dict[str, Any]) -> bool:
|
87
70
|
"""
|
88
71
|
Check if block has children that need processing.
|
89
|
-
|
90
|
-
Args:
|
91
|
-
block: Notion block to check
|
92
|
-
|
93
|
-
Returns:
|
94
|
-
True if block has children to process
|
95
72
|
"""
|
96
73
|
return block.get("has_children", False) and "children" in block
|
97
74
|
|
@@ -99,14 +76,7 @@ class NotionToMarkdownConverter:
|
|
99
76
|
self, toggle_markdown: str, children_markdown: str
|
100
77
|
) -> str:
|
101
78
|
"""
|
102
|
-
Format toggle block with its children content.
|
103
|
-
|
104
|
-
Args:
|
105
|
-
toggle_markdown: Markdown for the toggle itself
|
106
|
-
children_markdown: Markdown for toggle's children
|
107
|
-
|
108
|
-
Returns:
|
109
|
-
Formatted markdown with indented children
|
79
|
+
Format toggle or toggleable_heading block with its children content.
|
110
80
|
"""
|
111
81
|
indented_children = self._indent_text(children_markdown)
|
112
82
|
return f"{toggle_markdown}\n{indented_children}"
|
@@ -116,13 +86,6 @@ class NotionToMarkdownConverter:
|
|
116
86
|
) -> str:
|
117
87
|
"""
|
118
88
|
Format list item with its children content.
|
119
|
-
|
120
|
-
Args:
|
121
|
-
item_markdown: Markdown for the list item itself
|
122
|
-
children_markdown: Markdown for item's children
|
123
|
-
|
124
|
-
Returns:
|
125
|
-
Formatted markdown with indented children
|
126
89
|
"""
|
127
90
|
indented_children = self._indent_text(children_markdown)
|
128
91
|
return f"{item_markdown}\n{indented_children}"
|
@@ -132,26 +95,12 @@ class NotionToMarkdownConverter:
|
|
132
95
|
) -> str:
|
133
96
|
"""
|
134
97
|
Format standard block with its children content.
|
135
|
-
|
136
|
-
Args:
|
137
|
-
block_markdown: Markdown for the block itself
|
138
|
-
children_markdown: Markdown for block's children
|
139
|
-
|
140
|
-
Returns:
|
141
|
-
Formatted markdown with children after block
|
142
98
|
"""
|
143
99
|
return f"{block_markdown}\n\n{children_markdown}"
|
144
100
|
|
145
101
|
def _indent_text(self, text: str, spaces: int = 4) -> str:
|
146
102
|
"""
|
147
103
|
Indent each line of text with specified number of spaces.
|
148
|
-
|
149
|
-
Args:
|
150
|
-
text: Text to indent
|
151
|
-
spaces: Number of spaces to use for indentation
|
152
|
-
|
153
|
-
Returns:
|
154
|
-
Indented text
|
155
104
|
"""
|
156
105
|
indent = " " * spaces
|
157
106
|
return "\n".join([f"{indent}{line}" for line in text.split("\n")])
|
@@ -159,12 +108,6 @@ class NotionToMarkdownConverter:
|
|
159
108
|
def extract_toggle_content(self, blocks: List[Dict[str, Any]]) -> str:
|
160
109
|
"""
|
161
110
|
Extract only the content of toggles from blocks.
|
162
|
-
|
163
|
-
Args:
|
164
|
-
blocks: List of Notion blocks
|
165
|
-
|
166
|
-
Returns:
|
167
|
-
Markdown text with toggle contents
|
168
111
|
"""
|
169
112
|
if not blocks:
|
170
113
|
return ""
|
@@ -181,12 +124,8 @@ class NotionToMarkdownConverter:
|
|
181
124
|
) -> None:
|
182
125
|
"""
|
183
126
|
Recursively extract toggle content from a block and its children.
|
184
|
-
|
185
|
-
Args:
|
186
|
-
block: Block to process
|
187
|
-
result: List to collect toggle content
|
188
127
|
"""
|
189
|
-
if self.
|
128
|
+
if self._is_toggle_or_heading_with_children(block):
|
190
129
|
self._add_toggle_header_to_result(block, result)
|
191
130
|
self._add_toggle_children_to_result(block, result)
|
192
131
|
|
@@ -194,31 +133,27 @@ class NotionToMarkdownConverter:
|
|
194
133
|
for child in block["children"]:
|
195
134
|
self._extract_toggle_content_recursive(child, result)
|
196
135
|
|
197
|
-
def
|
136
|
+
def _is_toggle_or_heading_with_children(self, block: Dict[str, Any]) -> bool:
|
198
137
|
"""
|
199
|
-
Check if block is a toggle with children.
|
200
|
-
|
201
|
-
Args:
|
202
|
-
block: Block to check
|
203
|
-
|
204
|
-
Returns:
|
205
|
-
True if block is a toggle with children
|
138
|
+
Check if block is a toggle or toggleable_heading with children.
|
206
139
|
"""
|
207
|
-
return block.get("type")
|
140
|
+
return block.get("type") in self.TOGGLE_ELEMENT_TYPES and "children" in block
|
208
141
|
|
209
142
|
def _add_toggle_header_to_result(
|
210
143
|
self, block: Dict[str, Any], result: List[str]
|
211
144
|
) -> None:
|
212
145
|
"""
|
213
146
|
Add toggle header text to result list.
|
214
|
-
|
215
|
-
Args:
|
216
|
-
block: Toggle block
|
217
|
-
result: List to add header to
|
218
147
|
"""
|
219
|
-
|
220
|
-
|
221
|
-
|
148
|
+
block_type = block.get("type")
|
149
|
+
rich_text = None
|
150
|
+
|
151
|
+
if block_type == "toggle":
|
152
|
+
rich_text = block.get("toggle", {}).get("rich_text", [])
|
153
|
+
elif block_type == "toggleable_heading":
|
154
|
+
rich_text = block.get("toggleable_heading", {}).get("rich_text", [])
|
155
|
+
|
156
|
+
toggle_text = self._extract_text_from_rich_text(rich_text or [])
|
222
157
|
|
223
158
|
if toggle_text:
|
224
159
|
result.append(f"### {toggle_text}")
|
@@ -228,10 +163,6 @@ class NotionToMarkdownConverter:
|
|
228
163
|
) -> None:
|
229
164
|
"""
|
230
165
|
Add formatted toggle children to result list.
|
231
|
-
|
232
|
-
Args:
|
233
|
-
block: Toggle block with children
|
234
|
-
result: List to add children content to
|
235
166
|
"""
|
236
167
|
for child in block.get("children", []):
|
237
168
|
child_type = child.get("type")
|
@@ -248,12 +179,6 @@ class NotionToMarkdownConverter:
|
|
248
179
|
def _extract_text_from_rich_text(self, rich_text: List[Dict[str, Any]]) -> str:
|
249
180
|
"""
|
250
181
|
Extract plain text from Notion's rich text array.
|
251
|
-
|
252
|
-
Args:
|
253
|
-
rich_text: List of rich text objects
|
254
|
-
|
255
|
-
Returns:
|
256
|
-
Concatenated plain text
|
257
182
|
"""
|
258
183
|
if not rich_text:
|
259
184
|
return ""
|
@@ -0,0 +1,58 @@
|
|
1
|
+
notionary/__init__.py,sha256=ypCkbF1YEcwy5aE2kGFmtJOZq41Vs01lahJNqRO27fU,735
|
2
|
+
notionary/notion_client.py,sha256=6YhaDK9UjH18Q0f1bxVVNGMbCvpQhatse6WRVIARGCE,6035
|
3
|
+
notionary/database/database_discovery.py,sha256=qDGFhXG9s-_6CXdRg8tMiwX4dvX7jLjgAUFPSNlYtlI,4506
|
4
|
+
notionary/database/database_info_service.py,sha256=Ig6gx8jUSPYORJvfgEV5kV6t72pZQsWU8HPMqd43B-o,1336
|
5
|
+
notionary/database/notion_database.py,sha256=YCOuzrWfKRJvMiWdVfEt8i3NAEqpKDwHj5SX4GUraEk,8017
|
6
|
+
notionary/database/notion_database_factory.py,sha256=Af57yaUHidD8TKJ8uyXOc2nnqHm7on6VGFdDRjxiq9o,6692
|
7
|
+
notionary/database/models/page_result.py,sha256=Vmm5_oYpYAkIIJVoTd1ZZGloeC3cmFLMYP255mAmtaw,233
|
8
|
+
notionary/elements/audio_element.py,sha256=WpcUpIv4U9grZegiCAmyc-Gj3XM3TQVFKr9PQM-QErg,5352
|
9
|
+
notionary/elements/bookmark_element.py,sha256=MS2K3pD-hMmS4Fmk1W8BwNL_-j4baHF42EoOWMNIfXw,8152
|
10
|
+
notionary/elements/bulleted_list_element.py,sha256=VdMKlxDlixTeYVQIqwM4vQ55xHqABtqzZDqIUATG9oE,2875
|
11
|
+
notionary/elements/callout_element.py,sha256=11qr9XGpF7jWle0wbog5eUrE4SAkThDdK9s4oplOxX0,4499
|
12
|
+
notionary/elements/code_block_element.py,sha256=6E-fc9zJegSQ_FjB08HImXL2rDQnmkFoRwoPuN-7PwI,6247
|
13
|
+
notionary/elements/divider_element.py,sha256=fLWU7Pn0JXIV8tPcuxvMMua4S54JOi2hQRjLcbVwC7g,2218
|
14
|
+
notionary/elements/embed_element.py,sha256=NEQopRLMzMIybVLbTt_y1cgaH1-VZW14xkbGJIDirkE,4586
|
15
|
+
notionary/elements/heading_element.py,sha256=Su4OUYeDvb9kvAFCmzagKQCNgkwvxsUn1KXJFTuYTFE,3186
|
16
|
+
notionary/elements/image_element.py,sha256=Qo94TavAnmJzGveDfdSkOAZeypij-zqbxsJ14rfbKIc,4765
|
17
|
+
notionary/elements/mention_element.py,sha256=B88AcU4XqpEXfhXywS_3K7TJIgoJT_3tWzJ_KcdJ4V4,8234
|
18
|
+
notionary/elements/notion_block_element.py,sha256=HN1Bu9wAaFsRIN6jkZSclo6KgB4nNvcVx7hlPWIhmy8,1265
|
19
|
+
notionary/elements/numbered_list_element.py,sha256=zW1MjNU_02mJBDGnaQFkAESY_i1rCYgp9o4zl6Au0DQ,2865
|
20
|
+
notionary/elements/paragraph_element.py,sha256=JuAN4cFRsJkePD7s7TNhDdSuojZtr6WOaCTGzJsBEGg,3262
|
21
|
+
notionary/elements/qoute_element.py,sha256=jG4FkD7Ieec3piBWcOeCZPgvw0GF0KZlxzdx2svQC0M,6129
|
22
|
+
notionary/elements/table_element.py,sha256=ujlo6bwrmKrQWRWGuUQebeDS-1T-c_R0SkzaPEPy2m8,11256
|
23
|
+
notionary/elements/text_inline_formatter.py,sha256=RdegEHvS5_AzDtqYxKb4b0uDbcpp3UhxHo6GyR84NFc,7780
|
24
|
+
notionary/elements/todo_element.py,sha256=cWuu8R55hNj6ubjrLu2yJpoeij7UfWhvmaklqcx8AI8,4131
|
25
|
+
notionary/elements/toggle_element.py,sha256=o7Utj0nKZAW5MzF_RMyD36HSWMj1cZmLEmqfGH-BFuY,11089
|
26
|
+
notionary/elements/toggleable_heading_element.py,sha256=EAkL9mU6u13JEgUOx6CeAhD8P3L_q6L_M_C8qZ_OTSs,9967
|
27
|
+
notionary/elements/video_element.py,sha256=2WwvZuG_UOVBnpEikZFxupe_x321QkczrodZHd5IeCw,5734
|
28
|
+
notionary/elements/prompts/element_prompt_content.py,sha256=Snha2FMtWO94kl1KwIJp6n7pY6Z8K-9_zVj1TUyaNNA,3729
|
29
|
+
notionary/elements/prompts/synthax_prompt_builder.py,sha256=W3ex5RDszlhMgYPGpemxiEI653hQKl147B-bh_ynR9g,6656
|
30
|
+
notionary/elements/registry/block_element_registry.py,sha256=r1V6waYQxZ_4bF6Mx_HmQ42-yhh8E54v-yNRT4PsnLk,3570
|
31
|
+
notionary/elements/registry/block_element_registry_builder.py,sha256=C6W64CwFqZBu31hZZqoD-uCZCOMOOXeFVk_SHKSp58U,9048
|
32
|
+
notionary/exceptions/database_exceptions.py,sha256=I-Tx6bYRLpi5pjGPtbT-Mqxvz3BFgYTiuZxknJeLxtI,2638
|
33
|
+
notionary/exceptions/page_creation_exception.py,sha256=4v7IuZD6GsQLrqhDLriGjuG3ML638gAO53zDCrLePuU,281
|
34
|
+
notionary/page/markdown_to_notion_converter.py,sha256=LFnv98uU3QRzxH7sZI0AE5_QxeHkNQZMxVcab-r8Qls,15877
|
35
|
+
notionary/page/notion_page.py,sha256=OXE2sw9Drkqduy96uVvXT3vHwHPh5FMJk6QyPRADoFU,17653
|
36
|
+
notionary/page/notion_page_factory.py,sha256=UUEZ-cyEWL0OMVPrgjc4vJdcplEa1bO2yHCYooACYC8,8189
|
37
|
+
notionary/page/notion_to_markdown_converter.py,sha256=EUiHeurQb73zQJ5Gatvf_67vWd-vOI8qg3yFLz8bf8Q,6452
|
38
|
+
notionary/page/content/notion_page_content_chunker.py,sha256=xRks74Dqec-De6-AVTxMPnXs-MSJBzSm1HfJfaHiKr8,3330
|
39
|
+
notionary/page/content/page_content_manager.py,sha256=0Bl52mEqKuhDGuJhOEMhGjJWHgxCiqGYjrqO1LqYzJM,6403
|
40
|
+
notionary/page/metadata/metadata_editor.py,sha256=61uiw8oB25O8ePhytoJvZDetuof5sjPoM6aoHZGo4wc,4949
|
41
|
+
notionary/page/metadata/notion_icon_manager.py,sha256=JWHeJ8yjvAnj1361C-Z3RI8c8aeR9VZw81KP4ks0b6k,1603
|
42
|
+
notionary/page/metadata/notion_page_cover_manager.py,sha256=qgQxQE-bx4oWjLFUQvpXD5GzO1Mx7w7htz1xC2BOqUg,1717
|
43
|
+
notionary/page/properites/database_property_service.py,sha256=AJuBGahbb53VQa6IGGHxBMoOgCy6vFZg08uR_eDjNUs,11570
|
44
|
+
notionary/page/properites/page_property_manager.py,sha256=Xl8Cwn8WVszqpFXT_NvASkmP5igpCTEgRVhG_F45424,6914
|
45
|
+
notionary/page/properites/property_formatter.py,sha256=d_Nr5XQxgjB6VIS0u3ey14MOUKY416o_BvdXjbkUNAQ,3667
|
46
|
+
notionary/page/properites/property_operation_result.py,sha256=PhxHJJxxG2BdDl7aswhWnMSmf9RQtoinKkRHDoqxwCs,3913
|
47
|
+
notionary/page/properites/property_value_extractor.py,sha256=1BfyCYrFzfIUmNTozavrLTjG--6P6Dy2tkewf6rHHwQ,2353
|
48
|
+
notionary/page/relations/notion_page_relation_manager.py,sha256=ooN-WxXNPBE_vkNJgbc1wH8iIWBlKa9FHgiMeJjbYdw,13032
|
49
|
+
notionary/page/relations/notion_page_title_resolver.py,sha256=XQpa5XY4hoh5UYqpewTYaaftNX52WHyk2XmM4r8B2uY,3016
|
50
|
+
notionary/page/relations/page_database_relation.py,sha256=F9aGXFjjL8ZLNbfTGeGm_QAyXhz2AEOw7GgDLdprEcE,2313
|
51
|
+
notionary/page/relations/relation_operation_result.py,sha256=NDxBzGntOxc_89ti-HG8xDSqfY6PwyGHKHrrKbCzNjM,5010
|
52
|
+
notionary/util/logging_mixin.py,sha256=fKsx9t90bwvL74ZX3dU-sXdC4TZCQyO6qU9I8txkw_U,1369
|
53
|
+
notionary/util/page_id_utils.py,sha256=EYNMxgf-7ghzL5K8lKZBZfW7g5CsdY0Xuj4IYmU8RPk,1381
|
54
|
+
notionary-0.1.27.dist-info/licenses/LICENSE,sha256=zOm3cRT1qD49eg7vgw95MI79rpUAZa1kRBFwL2FkAr8,1120
|
55
|
+
notionary-0.1.27.dist-info/METADATA,sha256=k1K58w6kqtLVWc9pvRgH1O5kFuCR-qivL-vNk9C2IJA,8342
|
56
|
+
notionary-0.1.27.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
|
57
|
+
notionary-0.1.27.dist-info/top_level.txt,sha256=fhONa6BMHQXqthx5PanWGbPL0b8rdFqhrJKVLf_adSs,10
|
58
|
+
notionary-0.1.27.dist-info/RECORD,,
|
@@ -1,307 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
from typing import Dict, Any, Optional, List, Tuple, Callable
|
3
|
-
|
4
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
-
from notionary.elements.prompts.element_prompt_content import ElementPromptContent
|
6
|
-
|
7
|
-
|
8
|
-
class ColumnElement(NotionBlockElement):
|
9
|
-
"""
|
10
|
-
Handles conversion between custom Markdown column syntax and Notion column blocks.
|
11
|
-
|
12
|
-
Markdown column syntax:
|
13
|
-
::: columns
|
14
|
-
::: column
|
15
|
-
Content for first column
|
16
|
-
:::
|
17
|
-
::: column
|
18
|
-
Content for second column
|
19
|
-
:::
|
20
|
-
:::
|
21
|
-
|
22
|
-
This creates a column layout in Notion with the specified content in each column.
|
23
|
-
"""
|
24
|
-
|
25
|
-
COLUMNS_START = re.compile(r"^:::\s*columns\s*$")
|
26
|
-
COLUMN_START = re.compile(r"^:::\s*column\s*$")
|
27
|
-
BLOCK_END = re.compile(r"^:::\s*$")
|
28
|
-
|
29
|
-
_converter_callback = None
|
30
|
-
|
31
|
-
@classmethod
|
32
|
-
def set_converter_callback(
|
33
|
-
cls, callback: Callable[[str], List[Dict[str, Any]]]
|
34
|
-
) -> None:
|
35
|
-
"""
|
36
|
-
Setze die Callback-Funktion, die zum Konvertieren von Markdown zu Notion-Blöcken verwendet wird.
|
37
|
-
|
38
|
-
Args:
|
39
|
-
callback: Funktion, die Markdown-Text annimmt und eine Liste von Notion-Blöcken zurückgibt
|
40
|
-
"""
|
41
|
-
cls._converter_callback = callback
|
42
|
-
|
43
|
-
@staticmethod
|
44
|
-
def match_markdown(text: str) -> bool:
|
45
|
-
"""Check if text starts a columns block."""
|
46
|
-
return bool(ColumnElement.COLUMNS_START.match(text.strip()))
|
47
|
-
|
48
|
-
@staticmethod
|
49
|
-
def match_notion(block: Dict[str, Any]) -> bool:
|
50
|
-
"""Check if block is a Notion column_list."""
|
51
|
-
return block.get("type") == "column_list"
|
52
|
-
|
53
|
-
@staticmethod
|
54
|
-
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
55
|
-
"""
|
56
|
-
Convert markdown column syntax to Notion column blocks.
|
57
|
-
|
58
|
-
Note: This only processes the first line (columns start).
|
59
|
-
The full column content needs to be processed separately.
|
60
|
-
"""
|
61
|
-
if not ColumnElement.COLUMNS_START.match(text.strip()):
|
62
|
-
return None
|
63
|
-
|
64
|
-
# Create an empty column_list block
|
65
|
-
# Child columns will be added by the column processor
|
66
|
-
return {"type": "column_list", "column_list": {"children": []}}
|
67
|
-
|
68
|
-
@staticmethod
|
69
|
-
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
70
|
-
"""Convert Notion column_list block to markdown column syntax."""
|
71
|
-
if block.get("type") != "column_list":
|
72
|
-
return None
|
73
|
-
|
74
|
-
column_children = block.get("column_list", {}).get("children", [])
|
75
|
-
|
76
|
-
# Start the columns block
|
77
|
-
result = ["::: columns"]
|
78
|
-
|
79
|
-
# Process each column
|
80
|
-
for column_block in column_children:
|
81
|
-
if column_block.get("type") == "column":
|
82
|
-
result.append("::: column")
|
83
|
-
|
84
|
-
for _ in column_block.get("column", {}).get("children", []):
|
85
|
-
result.append(" [Column content]") # Placeholder
|
86
|
-
|
87
|
-
result.append(":::")
|
88
|
-
|
89
|
-
# End the columns block
|
90
|
-
result.append(":::")
|
91
|
-
|
92
|
-
return "\n".join(result)
|
93
|
-
|
94
|
-
@staticmethod
|
95
|
-
def is_multiline() -> bool:
|
96
|
-
"""Column blocks span multiple lines."""
|
97
|
-
return True
|
98
|
-
|
99
|
-
@classmethod
|
100
|
-
def find_matches(
|
101
|
-
cls, text: str, converter_callback: Optional[Callable] = None
|
102
|
-
) -> List[Tuple[int, int, Dict[str, Any]]]:
|
103
|
-
"""
|
104
|
-
Find all column block matches in the text and return their positions and blocks.
|
105
|
-
|
106
|
-
Args:
|
107
|
-
text: The input markdown text
|
108
|
-
converter_callback: Optional callback to convert nested content
|
109
|
-
|
110
|
-
Returns:
|
111
|
-
List of tuples (start_pos, end_pos, block)
|
112
|
-
"""
|
113
|
-
# Wenn ein Callback übergeben wurde, nutze diesen, sonst die gespeicherte Referenz
|
114
|
-
converter = converter_callback or cls._converter_callback
|
115
|
-
if not converter:
|
116
|
-
raise ValueError(
|
117
|
-
"No converter callback provided for ColumnElement. Call set_converter_callback first or provide converter_callback parameter."
|
118
|
-
)
|
119
|
-
|
120
|
-
matches = []
|
121
|
-
lines = text.split("\n")
|
122
|
-
i = 0
|
123
|
-
|
124
|
-
while i < len(lines):
|
125
|
-
# Skip non-column lines
|
126
|
-
if not ColumnElement.COLUMNS_START.match(lines[i].strip()):
|
127
|
-
i += 1
|
128
|
-
continue
|
129
|
-
|
130
|
-
# Process a column block and add to matches
|
131
|
-
column_block_info = cls._process_column_block(lines, i, converter)
|
132
|
-
matches.append(column_block_info)
|
133
|
-
|
134
|
-
# Skip to the end of the processed column block
|
135
|
-
i = column_block_info[3] # i is returned as the 4th element in the tuple
|
136
|
-
|
137
|
-
return [(start, end, block) for start, end, block, _ in matches]
|
138
|
-
|
139
|
-
@classmethod
|
140
|
-
def _process_column_block(
|
141
|
-
cls, lines: List[str], start_index: int, converter_callback: Callable
|
142
|
-
) -> Tuple[int, int, Dict[str, Any], int]:
|
143
|
-
"""
|
144
|
-
Process a complete column block structure from the given starting line.
|
145
|
-
|
146
|
-
Args:
|
147
|
-
lines: All lines of the text
|
148
|
-
start_index: Index of the column block start line
|
149
|
-
converter_callback: Callback function to convert markdown to notion blocks
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
Tuple of (start_pos, end_pos, block, next_line_index)
|
153
|
-
"""
|
154
|
-
columns_start = start_index
|
155
|
-
columns_block = cls.markdown_to_notion(lines[start_index].strip())
|
156
|
-
columns_children = []
|
157
|
-
|
158
|
-
next_index = cls._collect_columns(
|
159
|
-
lines, start_index + 1, columns_children, converter_callback
|
160
|
-
)
|
161
|
-
|
162
|
-
# Add columns to the main block
|
163
|
-
if columns_children:
|
164
|
-
columns_block["column_list"]["children"] = columns_children
|
165
|
-
|
166
|
-
# Calculate positions
|
167
|
-
start_pos = sum(len(lines[j]) + 1 for j in range(columns_start))
|
168
|
-
end_pos = sum(len(lines[j]) + 1 for j in range(next_index))
|
169
|
-
|
170
|
-
return (start_pos, end_pos, columns_block, next_index)
|
171
|
-
|
172
|
-
@classmethod
|
173
|
-
def _collect_columns(
|
174
|
-
cls,
|
175
|
-
lines: List[str],
|
176
|
-
start_index: int,
|
177
|
-
columns_children: List[Dict[str, Any]],
|
178
|
-
converter_callback: Callable,
|
179
|
-
) -> int:
|
180
|
-
"""
|
181
|
-
Collect all columns within a column block structure.
|
182
|
-
|
183
|
-
Args:
|
184
|
-
lines: All lines of the text
|
185
|
-
start_index: Index to start collecting from
|
186
|
-
columns_children: List to append collected columns to
|
187
|
-
converter_callback: Callback function to convert column content
|
188
|
-
|
189
|
-
Returns:
|
190
|
-
Next line index after all columns have been processed
|
191
|
-
"""
|
192
|
-
i = start_index
|
193
|
-
in_column = False
|
194
|
-
column_content = []
|
195
|
-
|
196
|
-
while i < len(lines):
|
197
|
-
current_line = lines[i].strip()
|
198
|
-
|
199
|
-
if cls.COLUMNS_START.match(current_line):
|
200
|
-
break
|
201
|
-
|
202
|
-
if cls.COLUMN_START.match(current_line):
|
203
|
-
cls._finalize_column(
|
204
|
-
column_content, columns_children, in_column, converter_callback
|
205
|
-
)
|
206
|
-
column_content = []
|
207
|
-
in_column = True
|
208
|
-
i += 1
|
209
|
-
continue
|
210
|
-
|
211
|
-
if cls.BLOCK_END.match(current_line) and in_column:
|
212
|
-
cls._finalize_column(
|
213
|
-
column_content, columns_children, in_column, converter_callback
|
214
|
-
)
|
215
|
-
column_content = []
|
216
|
-
in_column = False
|
217
|
-
i += 1
|
218
|
-
continue
|
219
|
-
|
220
|
-
if cls.BLOCK_END.match(current_line) and not in_column:
|
221
|
-
i += 1
|
222
|
-
break
|
223
|
-
|
224
|
-
if in_column:
|
225
|
-
column_content.append(lines[i])
|
226
|
-
|
227
|
-
i += 1
|
228
|
-
|
229
|
-
cls._finalize_column(
|
230
|
-
column_content, columns_children, in_column, converter_callback
|
231
|
-
)
|
232
|
-
|
233
|
-
return i
|
234
|
-
|
235
|
-
@staticmethod
|
236
|
-
def _finalize_column(
|
237
|
-
column_content: List[str],
|
238
|
-
columns_children: List[Dict[str, Any]],
|
239
|
-
in_column: bool,
|
240
|
-
converter_callback: Callable,
|
241
|
-
) -> None:
|
242
|
-
"""
|
243
|
-
Finalize a column by processing its content and adding it to the columns_children list.
|
244
|
-
|
245
|
-
Args:
|
246
|
-
column_content: Content lines of the column
|
247
|
-
columns_children: List to append the column block to
|
248
|
-
in_column: Whether we're currently in a column (if False, does nothing)
|
249
|
-
converter_callback: Callback function to convert column content
|
250
|
-
"""
|
251
|
-
if not (in_column and column_content):
|
252
|
-
return
|
253
|
-
|
254
|
-
# Process column content using the provided callback
|
255
|
-
column_blocks = converter_callback("\n".join(column_content))
|
256
|
-
|
257
|
-
# Create column block
|
258
|
-
column_block = {"type": "column", "column": {"children": column_blocks}}
|
259
|
-
columns_children.append(column_block)
|
260
|
-
|
261
|
-
@classmethod
|
262
|
-
def get_llm_prompt_content(cls) -> ElementPromptContent:
|
263
|
-
"""
|
264
|
-
Returns structured LLM prompt metadata for the column layout element.
|
265
|
-
"""
|
266
|
-
return {
|
267
|
-
"description": "Creates a multi-column layout that displays content side by side.",
|
268
|
-
"when_to_use": (
|
269
|
-
"Use columns sparingly, only for direct comparisons or when parallel presentation significantly improves readability. "
|
270
|
-
"Best for pros/cons lists, feature comparisons, or pairing images with descriptions. "
|
271
|
-
"Avoid overusing as it can complicate document structure."
|
272
|
-
),
|
273
|
-
"syntax": (
|
274
|
-
"::: columns\n"
|
275
|
-
"::: column\n"
|
276
|
-
"Content for first column\n"
|
277
|
-
":::\n"
|
278
|
-
"::: column\n"
|
279
|
-
"Content for second column\n"
|
280
|
-
":::\n"
|
281
|
-
":::"
|
282
|
-
),
|
283
|
-
"examples": [
|
284
|
-
"::: columns\n"
|
285
|
-
"::: column\n"
|
286
|
-
"## Features\n"
|
287
|
-
"- Fast response time\n"
|
288
|
-
"- Intuitive interface\n"
|
289
|
-
"- Regular updates\n"
|
290
|
-
":::\n"
|
291
|
-
"::: column\n"
|
292
|
-
"## Benefits\n"
|
293
|
-
"- Increased productivity\n"
|
294
|
-
"- Better collaboration\n"
|
295
|
-
"- Simplified workflows\n"
|
296
|
-
":::\n"
|
297
|
-
":::",
|
298
|
-
"::: columns\n"
|
299
|
-
"::: column\n"
|
300
|
-
"\n"
|
301
|
-
":::\n"
|
302
|
-
"::: column\n"
|
303
|
-
"This text appears next to the image, creating a media-with-caption style layout that's perfect for documentation or articles.\n"
|
304
|
-
":::\n"
|
305
|
-
":::",
|
306
|
-
],
|
307
|
-
}
|