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.
Files changed (205) hide show
  1. notionary/__init__.py +8 -4
  2. notionary/base_notion_client.py +3 -1
  3. notionary/blocks/__init__.py +2 -91
  4. notionary/blocks/_bootstrap.py +263 -0
  5. notionary/blocks/audio/__init__.py +8 -2
  6. notionary/blocks/audio/audio_element.py +42 -104
  7. notionary/blocks/audio/audio_markdown_node.py +3 -1
  8. notionary/blocks/audio/audio_models.py +6 -55
  9. notionary/blocks/base_block_element.py +30 -0
  10. notionary/blocks/bookmark/__init__.py +9 -2
  11. notionary/blocks/bookmark/bookmark_element.py +46 -139
  12. notionary/blocks/bookmark/bookmark_markdown_node.py +3 -1
  13. notionary/blocks/bookmark/bookmark_models.py +15 -0
  14. notionary/blocks/breadcrumbs/__init__.py +17 -0
  15. notionary/blocks/breadcrumbs/breadcrumb_element.py +39 -0
  16. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +32 -0
  17. notionary/blocks/breadcrumbs/breadcrumb_models.py +12 -0
  18. notionary/blocks/bulleted_list/__init__.py +12 -2
  19. notionary/blocks/bulleted_list/bulleted_list_element.py +40 -55
  20. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +2 -1
  21. notionary/blocks/bulleted_list/bulleted_list_models.py +18 -0
  22. notionary/blocks/callout/__init__.py +9 -2
  23. notionary/blocks/callout/callout_element.py +40 -89
  24. notionary/blocks/callout/callout_markdown_node.py +3 -1
  25. notionary/blocks/callout/callout_models.py +33 -0
  26. notionary/blocks/child_database/__init__.py +7 -0
  27. notionary/blocks/child_database/child_database_models.py +19 -0
  28. notionary/blocks/child_page/__init__.py +9 -0
  29. notionary/blocks/child_page/child_page_models.py +12 -0
  30. notionary/blocks/{shared/block_client.py → client.py} +55 -54
  31. notionary/blocks/code/__init__.py +6 -2
  32. notionary/blocks/code/code_element.py +53 -187
  33. notionary/blocks/code/code_markdown_node.py +13 -13
  34. notionary/blocks/code/code_models.py +94 -0
  35. notionary/blocks/column/__init__.py +25 -1
  36. notionary/blocks/column/column_element.py +40 -314
  37. notionary/blocks/column/column_list_element.py +37 -0
  38. notionary/blocks/column/column_list_markdown_node.py +50 -0
  39. notionary/blocks/column/column_markdown_node.py +59 -0
  40. notionary/blocks/column/column_models.py +26 -0
  41. notionary/blocks/divider/__init__.py +9 -2
  42. notionary/blocks/divider/divider_element.py +26 -49
  43. notionary/blocks/divider/divider_markdown_node.py +2 -1
  44. notionary/blocks/divider/divider_models.py +12 -0
  45. notionary/blocks/embed/__init__.py +9 -2
  46. notionary/blocks/embed/embed_element.py +47 -114
  47. notionary/blocks/embed/embed_markdown_node.py +3 -1
  48. notionary/blocks/embed/embed_models.py +14 -0
  49. notionary/blocks/equation/__init__.py +14 -0
  50. notionary/blocks/equation/equation_element.py +80 -0
  51. notionary/blocks/equation/equation_element_markdown_node.py +36 -0
  52. notionary/blocks/equation/equation_models.py +11 -0
  53. notionary/blocks/file/__init__.py +25 -0
  54. notionary/blocks/file/file_element.py +93 -0
  55. notionary/blocks/file/file_element_markdown_node.py +35 -0
  56. notionary/blocks/file/file_element_models.py +39 -0
  57. notionary/blocks/heading/__init__.py +16 -2
  58. notionary/blocks/heading/heading_element.py +67 -72
  59. notionary/blocks/heading/heading_markdown_node.py +2 -1
  60. notionary/blocks/heading/heading_models.py +29 -0
  61. notionary/blocks/image_block/__init__.py +13 -0
  62. notionary/blocks/image_block/image_element.py +84 -0
  63. notionary/blocks/{image → image_block}/image_markdown_node.py +3 -1
  64. notionary/blocks/image_block/image_models.py +10 -0
  65. notionary/blocks/models.py +172 -0
  66. notionary/blocks/numbered_list/__init__.py +12 -2
  67. notionary/blocks/numbered_list/numbered_list_element.py +33 -58
  68. notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
  69. notionary/blocks/numbered_list/numbered_list_models.py +17 -0
  70. notionary/blocks/paragraph/__init__.py +12 -2
  71. notionary/blocks/paragraph/paragraph_element.py +27 -69
  72. notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
  73. notionary/blocks/paragraph/paragraph_models.py +16 -0
  74. notionary/blocks/pdf/__init__.py +13 -0
  75. notionary/blocks/pdf/pdf_element.py +91 -0
  76. notionary/blocks/pdf/pdf_markdown_node.py +35 -0
  77. notionary/blocks/pdf/pdf_models.py +11 -0
  78. notionary/blocks/quote/__init__.py +11 -2
  79. notionary/blocks/quote/quote_element.py +31 -65
  80. notionary/blocks/quote/quote_markdown_node.py +4 -1
  81. notionary/blocks/quote/quote_models.py +18 -0
  82. notionary/blocks/registry/__init__.py +4 -0
  83. notionary/blocks/registry/block_registry.py +75 -91
  84. notionary/blocks/registry/block_registry_builder.py +107 -59
  85. notionary/blocks/rich_text/__init__.py +33 -0
  86. notionary/blocks/rich_text/rich_text_models.py +188 -0
  87. notionary/blocks/rich_text/text_inline_formatter.py +125 -0
  88. notionary/blocks/table/__init__.py +16 -2
  89. notionary/blocks/table/table_element.py +48 -241
  90. notionary/blocks/table/table_markdown_node.py +2 -1
  91. notionary/blocks/table/table_models.py +28 -0
  92. notionary/blocks/table_of_contents/__init__.py +19 -0
  93. notionary/blocks/table_of_contents/table_of_contents_element.py +51 -0
  94. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
  95. notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
  96. notionary/blocks/todo/__init__.py +9 -2
  97. notionary/blocks/todo/todo_element.py +38 -95
  98. notionary/blocks/todo/todo_markdown_node.py +2 -1
  99. notionary/blocks/todo/todo_models.py +19 -0
  100. notionary/blocks/toggle/__init__.py +13 -3
  101. notionary/blocks/toggle/toggle_element.py +57 -264
  102. notionary/blocks/toggle/toggle_markdown_node.py +24 -14
  103. notionary/blocks/toggle/toggle_models.py +17 -0
  104. notionary/blocks/toggleable_heading/__init__.py +6 -2
  105. notionary/blocks/toggleable_heading/toggleable_heading_element.py +74 -244
  106. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
  107. notionary/blocks/types.py +61 -0
  108. notionary/blocks/video/__init__.py +8 -2
  109. notionary/blocks/video/video_element.py +67 -143
  110. notionary/blocks/video/video_element_models.py +10 -0
  111. notionary/blocks/video/video_markdown_node.py +3 -1
  112. notionary/database/client.py +3 -8
  113. notionary/database/database.py +13 -14
  114. notionary/database/database_filter_builder.py +2 -2
  115. notionary/database/database_provider.py +5 -4
  116. notionary/database/models.py +337 -0
  117. notionary/database/notion_database.py +6 -7
  118. notionary/file_upload/client.py +5 -7
  119. notionary/file_upload/models.py +2 -1
  120. notionary/file_upload/notion_file_upload.py +2 -3
  121. notionary/markdown/markdown_builder.py +722 -0
  122. notionary/markdown/markdown_document_model.py +228 -0
  123. notionary/{blocks → markdown}/markdown_node.py +1 -0
  124. notionary/models/notion_database_response.py +0 -338
  125. notionary/page/client.py +9 -10
  126. notionary/page/models.py +327 -0
  127. notionary/page/notion_page.py +99 -52
  128. notionary/page/notion_text_length_utils.py +119 -0
  129. notionary/page/{content/page_content_writer.py → page_content_writer.py} +88 -38
  130. notionary/page/reader/handler/__init__.py +17 -0
  131. notionary/page/reader/handler/base_block_renderer.py +44 -0
  132. notionary/page/reader/handler/block_processing_context.py +35 -0
  133. notionary/page/reader/handler/block_rendering_context.py +43 -0
  134. notionary/page/reader/handler/column_list_renderer.py +51 -0
  135. notionary/page/reader/handler/column_renderer.py +60 -0
  136. notionary/page/reader/handler/line_renderer.py +60 -0
  137. notionary/page/reader/handler/toggle_renderer.py +69 -0
  138. notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
  139. notionary/page/reader/page_content_retriever.py +69 -0
  140. notionary/page/search_filter_builder.py +2 -1
  141. notionary/page/writer/handler/__init__.py +22 -0
  142. notionary/page/writer/handler/code_handler.py +100 -0
  143. notionary/page/writer/handler/column_handler.py +141 -0
  144. notionary/page/writer/handler/column_list_handler.py +139 -0
  145. notionary/page/writer/handler/line_handler.py +35 -0
  146. notionary/page/writer/handler/line_processing_context.py +54 -0
  147. notionary/page/writer/handler/regular_line_handler.py +92 -0
  148. notionary/page/writer/handler/table_handler.py +130 -0
  149. notionary/page/writer/handler/toggle_handler.py +153 -0
  150. notionary/page/writer/handler/toggleable_heading_handler.py +167 -0
  151. notionary/page/writer/markdown_to_notion_converter.py +76 -0
  152. notionary/telemetry/__init__.py +2 -2
  153. notionary/telemetry/service.py +4 -3
  154. notionary/user/__init__.py +2 -2
  155. notionary/user/base_notion_user.py +2 -1
  156. notionary/user/client.py +2 -3
  157. notionary/user/models.py +1 -0
  158. notionary/user/notion_bot_user.py +4 -5
  159. notionary/user/notion_user.py +3 -4
  160. notionary/user/notion_user_manager.py +3 -2
  161. notionary/user/notion_user_provider.py +1 -1
  162. notionary/util/__init__.py +3 -2
  163. notionary/util/fuzzy.py +2 -1
  164. notionary/util/logging_mixin.py +2 -2
  165. notionary/util/singleton_metaclass.py +1 -1
  166. notionary/workspace.py +3 -2
  167. {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/METADATA +12 -8
  168. notionary-0.2.21.dist-info/RECORD +185 -0
  169. notionary/blocks/document/__init__.py +0 -7
  170. notionary/blocks/document/document_element.py +0 -102
  171. notionary/blocks/document/document_markdown_node.py +0 -31
  172. notionary/blocks/image/__init__.py +0 -7
  173. notionary/blocks/image/image_element.py +0 -151
  174. notionary/blocks/markdown_builder.py +0 -356
  175. notionary/blocks/mention/__init__.py +0 -7
  176. notionary/blocks/mention/mention_element.py +0 -229
  177. notionary/blocks/mention/mention_markdown_node.py +0 -38
  178. notionary/blocks/prompts/element_prompt_builder.py +0 -83
  179. notionary/blocks/prompts/element_prompt_content.py +0 -41
  180. notionary/blocks/shared/__init__.py +0 -0
  181. notionary/blocks/shared/models.py +0 -713
  182. notionary/blocks/shared/notion_block_element.py +0 -37
  183. notionary/blocks/shared/text_inline_formatter.py +0 -262
  184. notionary/blocks/shared/text_inline_formatter_new.py +0 -139
  185. notionary/blocks/toggleable_heading/toggleable_heading_models.py +0 -0
  186. notionary/database/models/page_result.py +0 -10
  187. notionary/elements/__init__.py +0 -0
  188. notionary/models/notion_block_response.py +0 -264
  189. notionary/models/notion_page_response.py +0 -78
  190. notionary/models/search_response.py +0 -0
  191. notionary/page/__init__.py +0 -0
  192. notionary/page/content/notion_text_length_utils.py +0 -87
  193. notionary/page/content/page_content_retriever.py +0 -60
  194. notionary/page/formatting/line_processor.py +0 -153
  195. notionary/page/formatting/markdown_to_notion_converter.py +0 -153
  196. notionary/page/markdown_syntax_prompt_generator.py +0 -114
  197. notionary/page/notion_to_markdown_converter.py +0 -179
  198. notionary/page/properites/property_value_extractor.py +0 -0
  199. notionary-0.2.19.dist-info/RECORD +0 -150
  200. /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
  201. /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
  202. /notionary/page/{content/markdown_whitespace_processor.py → markdown_whitespace_processor.py} +0 -0
  203. /notionary/{blocks/mention/mention_models.py → page/reader/handler/context.py} +0 -0
  204. {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/LICENSE +0 -0
  205. {notionary-0.2.19.dist-info → notionary-0.2.21.dist-info}/WHEEL +0 -0
notionary/__init__.py CHANGED
@@ -1,9 +1,13 @@
1
- from .database import NotionDatabase, DatabaseFilterBuilder
1
+ from notionary.blocks import bootstrap_blocks
2
+
3
+ bootstrap_blocks()
4
+
5
+ from .database import DatabaseFilterBuilder, NotionDatabase
6
+ from .file_upload import NotionFileUpload
7
+ from .markdown.markdown_builder import MarkdownBuilder
2
8
  from .page.notion_page import NotionPage
9
+ from .user import NotionBotUser, NotionUser, NotionUserManager
3
10
  from .workspace import NotionWorkspace
4
- from .user import NotionUser, NotionUserManager, NotionBotUser
5
- from .file_upload import NotionFileUpload
6
- from .blocks.markdown_builder import MarkdownBuilder
7
11
 
8
12
  __all__ = [
9
13
  "NotionDatabase",
@@ -2,9 +2,11 @@ import asyncio
2
2
  import os
3
3
  from abc import ABC
4
4
  from enum import Enum
5
- from typing import Dict, Any, Optional, Union
5
+ from typing import Any, Dict, Optional, Union
6
+
6
7
  import httpx
7
8
  from dotenv import load_dotenv
9
+
8
10
  from notionary.util import LoggingMixin
9
11
 
10
12
  load_dotenv()
@@ -1,92 +1,3 @@
1
- # Order is important here, as some imports depend on others.
2
- from .prompts.element_prompt_content import ElementPromptContent
3
- from .prompts.element_prompt_builder import ElementPromptBuilder
1
+ from ._bootstrap import bootstrap_blocks
4
2
 
5
- from .shared.notion_block_element import (
6
- NotionBlockElement,
7
- NotionBlockResult,
8
- NotionBlock,
9
- )
10
-
11
- from .audio import AudioElement, AudioMarkdownNode
12
- from .bulleted_list import BulletedListElement, BulletedListMarkdownNode
13
- from .callout import CalloutElement, CalloutMarkdownNode
14
- from .code import CodeElement, CodeMarkdownNode
15
- from .column.column_element import ColumnElement
16
- from .divider import DividerElement, DividerMarkdownNode
17
- from .embed import EmbedElement, EmbedMarkdownNode
18
- from .heading import HeadingElement, HeadingMarkdownNode
19
- from .image import ImageElement, ImageMarkdownNode
20
- from .numbered_list import NumberedListElement, NumberedListMarkdownNode
21
- from .paragraph import ParagraphElement, ParagraphMarkdownNode
22
- from .table import TableElement, TableMarkdownNode
23
- from .toggle import ToggleElement, ToggleMarkdownNode
24
- from .todo import TodoElement, TodoMarkdownNode
25
- from .video import VideoElement, VideoMarkdownNode
26
- from .toggleable_heading import ToggleableHeadingElement, ToggleableHeadingMarkdownNode
27
- from .bookmark import BookmarkElement, BookmarkMarkdownNode
28
- from .divider import DividerElement, DividerMarkdownNode
29
- from .heading import HeadingElement, HeadingMarkdownNode
30
- from .mention import MentionElement, MentionMarkdownNode
31
- from .quote import QuoteElement, QuoteMarkdownNode
32
- from .document import DocumentElement, DocumentMarkdownNode
33
- from .shared.text_inline_formatter import TextInlineFormatter
34
-
35
- from .markdown_node import MarkdownNode
36
-
37
- from .registry.block_registry import BlockRegistry
38
- from .registry.block_registry_builder import BlockRegistryBuilder
39
-
40
- from .shared.block_client import NotionBlockClient
41
-
42
- __all__ = [
43
- "MarkdownNode",
44
- "ElementPromptContent",
45
- "ElementPromptBuilder",
46
- "NotionBlockElement",
47
- "AudioElement",
48
- "AudioMarkdownNode",
49
- "BulletedListElement",
50
- "BulletedListMarkdownNode",
51
- "CalloutElement",
52
- "CalloutMarkdownNode",
53
- "CodeElement",
54
- "CodeMarkdownNode",
55
- "ColumnElement",
56
- "DividerElement",
57
- "DividerMarkdownNode",
58
- "EmbedElement",
59
- "EmbedMarkdownNode",
60
- "HeadingElement",
61
- "HeadingMarkdownNode",
62
- "ImageElement",
63
- "ImageMarkdownNode",
64
- "NumberedListElement",
65
- "NumberedListMarkdownNode",
66
- "ParagraphElement",
67
- "ParagraphMarkdownNode",
68
- "TableElement",
69
- "TableMarkdownNode",
70
- "ToggleElement",
71
- "ToggleMarkdownNode",
72
- "TodoElement",
73
- "TodoMarkdownNode",
74
- "VideoElement",
75
- "VideoMarkdownNode",
76
- "ToggleableHeadingElement",
77
- "ToggleableHeadingMarkdownNode",
78
- "BookmarkElement",
79
- "BookmarkMarkdownNode",
80
- "MentionElement",
81
- "MentionMarkdownNode",
82
- "QuoteElement",
83
- "QuoteMarkdownNode",
84
- "DocumentElement",
85
- "DocumentMarkdownNode",
86
- "BlockRegistry",
87
- "BlockRegistryBuilder",
88
- "TextInlineFormatter",
89
- "NotionBlockResult",
90
- "NotionBlock",
91
- "NotionBlockClient",
92
- ]
3
+ __all__ = ["bootstrap_blocks"]
@@ -0,0 +1,263 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional, Union
4
+
5
+ _bootstrapped = False
6
+
7
+
8
+ def bootstrap_blocks() -> None:
9
+ global _bootstrapped
10
+ if _bootstrapped:
11
+ return
12
+
13
+ from notionary.blocks import (
14
+ bookmark,
15
+ breadcrumbs,
16
+ bulleted_list,
17
+ callout,
18
+ child_page,
19
+ code,
20
+ column,
21
+ divider,
22
+ embed,
23
+ equation,
24
+ file,
25
+ heading,
26
+ image_block,
27
+ models,
28
+ numbered_list,
29
+ paragraph,
30
+ quote,
31
+ table,
32
+ table_of_contents,
33
+ todo,
34
+ toggle,
35
+ toggleable_heading,
36
+ video,
37
+ )
38
+
39
+ # Collect all exports from modules
40
+ ns = {}
41
+ for m in (
42
+ bookmark,
43
+ breadcrumbs,
44
+ bulleted_list,
45
+ callout,
46
+ child_page,
47
+ code,
48
+ column,
49
+ divider,
50
+ embed,
51
+ equation,
52
+ file,
53
+ heading,
54
+ image_block,
55
+ numbered_list,
56
+ paragraph,
57
+ quote,
58
+ table,
59
+ todo,
60
+ toggle,
61
+ video,
62
+ toggleable_heading,
63
+ table_of_contents,
64
+ ):
65
+ ns.update(vars(m))
66
+
67
+ # Add missing types that are needed for model rebuilding
68
+ # These are the types that are only defined in TYPE_CHECKING in block_models
69
+ from notionary.blocks.bookmark.bookmark_models import (
70
+ BookmarkBlock,
71
+ CreateBookmarkBlock,
72
+ )
73
+ from notionary.blocks.breadcrumbs.breadcrumb_models import (
74
+ BreadcrumbBlock,
75
+ CreateBreadcrumbBlock,
76
+ )
77
+ from notionary.blocks.bulleted_list.bulleted_list_models import (
78
+ BulletedListItemBlock,
79
+ CreateBulletedListItemBlock,
80
+ )
81
+ from notionary.blocks.callout.callout_models import CalloutBlock, CreateCalloutBlock
82
+ from notionary.blocks.child_page.child_page_models import (
83
+ ChildPageBlock,
84
+ CreateChildPageBlock,
85
+ )
86
+ from notionary.blocks.code.code_models import CodeBlock, CreateCodeBlock
87
+ from notionary.blocks.column.column_models import (
88
+ ColumnBlock,
89
+ ColumnListBlock,
90
+ CreateColumnBlock,
91
+ CreateColumnListBlock,
92
+ )
93
+ from notionary.blocks.divider.divider_models import CreateDividerBlock, DividerBlock
94
+ from notionary.blocks.embed.embed_models import CreateEmbedBlock, EmbedBlock
95
+ from notionary.blocks.equation.equation_models import (
96
+ CreateEquationBlock,
97
+ EquationBlock,
98
+ )
99
+ from notionary.blocks.file.file_element_models import CreateFileBlock, FileBlock
100
+ from notionary.blocks.heading.heading_models import (
101
+ CreateHeading1Block,
102
+ CreateHeading2Block,
103
+ CreateHeading3Block,
104
+ HeadingBlock,
105
+ )
106
+ from notionary.blocks.image_block.image_models import CreateImageBlock
107
+ from notionary.blocks.numbered_list.numbered_list_models import (
108
+ CreateNumberedListItemBlock,
109
+ NumberedListItemBlock,
110
+ )
111
+ from notionary.blocks.paragraph.paragraph_models import (
112
+ CreateParagraphBlock,
113
+ ParagraphBlock,
114
+ )
115
+ from notionary.blocks.pdf.pdf_models import CreatePdfBlock
116
+ from notionary.blocks.quote.quote_models import CreateQuoteBlock, QuoteBlock
117
+ from notionary.blocks.table.table_models import TableBlock, TableRowBlock
118
+ from notionary.blocks.table_of_contents.table_of_contents_models import (
119
+ CreateTableOfContentsBlock,
120
+ TableOfContentsBlock,
121
+ )
122
+ from notionary.blocks.todo.todo_models import CreateToDoBlock, ToDoBlock
123
+ from notionary.blocks.toggle.toggle_models import CreateToggleBlock, ToggleBlock
124
+ from notionary.blocks.types import BlockType
125
+ from notionary.blocks.video.video_element_models import CreateVideoBlock
126
+
127
+ # Define the Union types that are needed for model rebuilding
128
+ BlockCreateRequest = Union[
129
+ CreateBookmarkBlock,
130
+ CreateBreadcrumbBlock,
131
+ CreateBulletedListItemBlock,
132
+ CreateCalloutBlock,
133
+ CreateChildPageBlock,
134
+ CreateCodeBlock,
135
+ CreateColumnListBlock,
136
+ CreateColumnBlock,
137
+ CreateDividerBlock,
138
+ CreateEmbedBlock,
139
+ CreateEquationBlock,
140
+ CreateFileBlock,
141
+ CreateHeading1Block,
142
+ CreateHeading2Block,
143
+ CreateHeading3Block,
144
+ CreateImageBlock,
145
+ CreateNumberedListItemBlock,
146
+ CreateParagraphBlock,
147
+ CreateQuoteBlock,
148
+ CreateToDoBlock,
149
+ CreateToggleBlock,
150
+ CreateVideoBlock,
151
+ CreateTableOfContentsBlock,
152
+ CreatePdfBlock,
153
+ ]
154
+
155
+ BlockCreateResult = Optional[Union[list[BlockCreateRequest], BlockCreateRequest]]
156
+
157
+ # Add all block types to namespace
158
+ ns.update(
159
+ {
160
+ "BlockType": BlockType,
161
+ "BookmarkBlock": BookmarkBlock,
162
+ "CreateBookmarkBlock": CreateBookmarkBlock,
163
+ "BreadcrumbBlock": BreadcrumbBlock,
164
+ "CreateBreadcrumbBlock": CreateBreadcrumbBlock,
165
+ "BulletedListItemBlock": BulletedListItemBlock,
166
+ "CreateBulletedListItemBlock": CreateBulletedListItemBlock,
167
+ "CalloutBlock": CalloutBlock,
168
+ "CreateCalloutBlock": CreateCalloutBlock,
169
+ "ChildPageBlock": ChildPageBlock,
170
+ "CreateChildPageBlock": CreateChildPageBlock,
171
+ "CodeBlock": CodeBlock,
172
+ "CreateCodeBlock": CreateCodeBlock,
173
+ "ColumnBlock": ColumnBlock,
174
+ "ColumnListBlock": ColumnListBlock,
175
+ "CreateColumnBlock": CreateColumnBlock,
176
+ "CreateColumnListBlock": CreateColumnListBlock,
177
+ "DividerBlock": DividerBlock,
178
+ "CreateDividerBlock": CreateDividerBlock,
179
+ "EmbedBlock": EmbedBlock,
180
+ "CreateEmbedBlock": CreateEmbedBlock,
181
+ "EquationBlock": EquationBlock,
182
+ "CreateEquationBlock": CreateEquationBlock,
183
+ "FileBlock": FileBlock,
184
+ "CreateFileBlock": CreateFileBlock,
185
+ "HeadingBlock": HeadingBlock,
186
+ "CreateHeading1Block": CreateHeading1Block,
187
+ "CreateHeading2Block": CreateHeading2Block,
188
+ "CreateHeading3Block": CreateHeading3Block,
189
+ "CreateImageBlock": CreateImageBlock,
190
+ "NumberedListItemBlock": NumberedListItemBlock,
191
+ "CreateNumberedListItemBlock": CreateNumberedListItemBlock,
192
+ "ParagraphBlock": ParagraphBlock,
193
+ "CreateParagraphBlock": CreateParagraphBlock,
194
+ "QuoteBlock": QuoteBlock,
195
+ "CreateQuoteBlock": CreateQuoteBlock,
196
+ "TableBlock": TableBlock,
197
+ "TableRowBlock": TableRowBlock,
198
+ "ToDoBlock": ToDoBlock,
199
+ "CreateToDoBlock": CreateToDoBlock,
200
+ "ToggleBlock": ToggleBlock,
201
+ "CreateToggleBlock": CreateToggleBlock,
202
+ "CreateVideoBlock": CreateVideoBlock,
203
+ "TableOfContentsBlock": TableOfContentsBlock,
204
+ "CreateTableOfContentsBlock": CreateTableOfContentsBlock,
205
+ # Add the Union types
206
+ "BlockCreateRequest": BlockCreateRequest,
207
+ "BlockCreateResult": BlockCreateResult,
208
+ }
209
+ )
210
+
211
+ # Now rebuild with complete namespace
212
+ models.Block.model_rebuild(_types_namespace=ns)
213
+ models.BlockChildrenResponse.model_rebuild(_types_namespace=ns)
214
+
215
+ # Rebuild all individual block models
216
+ BookmarkBlock.model_rebuild()
217
+ BreadcrumbBlock.model_rebuild()
218
+ BulletedListItemBlock.model_rebuild()
219
+ CalloutBlock.model_rebuild()
220
+ ChildPageBlock.model_rebuild()
221
+ CodeBlock.model_rebuild()
222
+ ColumnBlock.model_rebuild()
223
+ ColumnListBlock.model_rebuild()
224
+ DividerBlock.model_rebuild()
225
+ EmbedBlock.model_rebuild()
226
+ EquationBlock.model_rebuild()
227
+ FileBlock.model_rebuild()
228
+ HeadingBlock.model_rebuild()
229
+ NumberedListItemBlock.model_rebuild()
230
+ ParagraphBlock.model_rebuild()
231
+ QuoteBlock.model_rebuild()
232
+ TableBlock.model_rebuild()
233
+ TableRowBlock.model_rebuild()
234
+ ToDoBlock.model_rebuild()
235
+ ToggleBlock.model_rebuild()
236
+ TableOfContentsBlock.model_rebuild()
237
+
238
+ # Rebuild create models
239
+ CreateBookmarkBlock.model_rebuild()
240
+ CreateBreadcrumbBlock.model_rebuild()
241
+ CreateBulletedListItemBlock.model_rebuild()
242
+ CreateCalloutBlock.model_rebuild()
243
+ CreateChildPageBlock.model_rebuild()
244
+ CreateCodeBlock.model_rebuild()
245
+ CreateColumnListBlock.model_rebuild()
246
+ CreateColumnBlock.model_rebuild()
247
+ CreateDividerBlock.model_rebuild()
248
+ CreateEmbedBlock.model_rebuild()
249
+ CreateEquationBlock.model_rebuild()
250
+ CreateFileBlock.model_rebuild()
251
+ CreateHeading1Block.model_rebuild()
252
+ CreateHeading2Block.model_rebuild()
253
+ CreateHeading3Block.model_rebuild()
254
+ CreateImageBlock.model_rebuild()
255
+ CreateNumberedListItemBlock.model_rebuild()
256
+ CreateParagraphBlock.model_rebuild()
257
+ CreateQuoteBlock.model_rebuild()
258
+ CreateToDoBlock.model_rebuild()
259
+ CreateToggleBlock.model_rebuild()
260
+ CreateVideoBlock.model_rebuild()
261
+ CreateTableOfContentsBlock.model_rebuild()
262
+
263
+ _bootstrapped = True
@@ -1,7 +1,13 @@
1
- from .audio_element import AudioElement
2
- from .audio_markdown_node import AudioMarkdownNode
1
+ from notionary.blocks.audio.audio_element import AudioElement
2
+ from notionary.blocks.audio.audio_markdown_node import (
3
+ AudioMarkdownBlockParams,
4
+ AudioMarkdownNode,
5
+ )
6
+ from notionary.blocks.audio.audio_models import CreateAudioBlock
3
7
 
4
8
  __all__ = [
5
9
  "AudioElement",
10
+ "CreateAudioBlock",
6
11
  "AudioMarkdownNode",
12
+ "AudioMarkdownBlockParams",
7
13
  ]
@@ -1,152 +1,90 @@
1
+ from __future__ import annotations
2
+
1
3
  import re
2
- from typing import Any, Optional, List
4
+ from typing import Optional
3
5
 
4
- from notionary.blocks import (
5
- NotionBlockElement,
6
- ElementPromptContent,
7
- ElementPromptBuilder,
8
- NotionBlockResult,
9
- )
10
- from notionary.blocks.shared.models import RichTextObject
6
+ from notionary.blocks.audio.audio_models import CreateAudioBlock
7
+ from notionary.blocks.base_block_element import BaseBlockElement
8
+ from notionary.blocks.file.file_element_models import ExternalFile, FileBlock, FileType
9
+ from notionary.blocks.models import Block, BlockCreateResult, BlockType
10
+ from notionary.blocks.rich_text.rich_text_models import RichTextObject
11
+ from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
11
12
 
12
13
 
13
- class AudioElement(NotionBlockElement):
14
+ class AudioElement(BaseBlockElement):
14
15
  """
15
16
  Handles conversion between Markdown audio embeds and Notion audio blocks.
16
17
 
17
18
  Markdown audio syntax:
18
19
  - [audio](https://example.com/audio.mp3) - Simple audio embed
19
- - [audio](https://example.com/audio.mp3 "Caption text") - Audio with caption
20
+ - [audio](https://example.com/audio.mp3 "Caption text") - Audio with optional caption
20
21
 
21
22
  Where:
22
23
  - URL is the required audio file URL
23
24
  - Caption is optional descriptive text (enclosed in quotes)
24
25
  """
25
26
 
26
- # Regex patterns
27
27
  URL_PATTERN = r"(https?://[^\s\"]+)"
28
28
  CAPTION_PATTERN = r'(?:\s+"([^"]+)")?'
29
-
30
29
  PATTERN = re.compile(r"^\[audio\]\(" + URL_PATTERN + CAPTION_PATTERN + r"\)$")
31
30
 
32
- # Supported audio extensions
33
31
  SUPPORTED_EXTENSIONS = {".mp3", ".wav", ".ogg", ".oga", ".m4a"}
34
32
 
35
33
  @classmethod
36
- def match_markdown(cls, text: str) -> bool:
37
- m = cls.PATTERN.match(text.strip())
38
- if not m:
39
- return False
40
- url = m.group(1)
41
- return cls._is_likely_audio_url(url)
34
+ def match_notion(cls, block: Block) -> bool:
35
+ """Check if this element can handle the given Notion block."""
36
+ return block.type == BlockType.AUDIO
42
37
 
43
38
  @classmethod
44
- def match_notion(cls, block: dict[str, Any]) -> bool:
45
- """Check if block is a Notion audio block."""
46
- return block.get("type") == "audio"
47
-
48
- @classmethod
49
- def markdown_to_notion(cls, text: str) -> NotionBlockResult:
50
- """Convert markdown audio embed to Notion audio block."""
51
- audio_match = cls.PATTERN.match(text.strip())
52
- if not audio_match:
53
- return None
54
-
55
- url = audio_match.group(1)
56
- caption_text = audio_match.group(2)
57
-
58
- if not url:
39
+ def markdown_to_notion(cls, text: str) -> BlockCreateResult:
40
+ """Convert markdown audio embed to Notion audio block (or return None if not matching)."""
41
+ match = cls.PATTERN.match(text.strip())
42
+ if not match:
59
43
  return None
44
+ url = match.group(1)
60
45
 
61
- # Validate URL if possible
62
46
  if not cls._is_likely_audio_url(url):
63
- # Still proceed - user might know better
64
- pass
65
-
66
- audio_data = {"type": "external", "external": {"url": url}}
47
+ return None
48
+ caption_text = match.group(2)
67
49
 
68
- # Add caption if provided
50
+ # Create caption rich text objects
51
+ caption_objects = []
69
52
  if caption_text:
70
- caption_rich_text = RichTextObject.from_plain_text(caption_text)
71
- audio_data["caption"] = [caption_rich_text.model_dump()]
72
- else:
73
- audio_data["caption"] = []
53
+ caption_rt = RichTextObject.from_plain_text(caption_text)
54
+ caption_objects = [caption_rt]
74
55
 
75
- return {"type": "audio", "audio": audio_data}
56
+ audio_content = FileBlock(
57
+ type=FileType.EXTERNAL,
58
+ external=ExternalFile(url=url),
59
+ caption=caption_objects,
60
+ )
61
+
62
+ return CreateAudioBlock(audio=audio_content)
76
63
 
77
64
  @classmethod
78
- def notion_to_markdown(cls, block: dict[str, Any]) -> Optional[str]:
65
+ def notion_to_markdown(cls, block: Block) -> Optional[str]:
79
66
  """Convert Notion audio block to markdown audio embed."""
80
- if block.get("type") != "audio":
67
+ if block.type != BlockType.AUDIO or block.audio is None:
81
68
  return None
82
69
 
83
- audio_data = block.get("audio", {})
70
+ audio = block.audio
84
71
 
85
- # Get URL from external source
86
- if audio_data.get("type") == "external":
87
- url = audio_data.get("external", {}).get("url", "")
88
- else:
89
- # Handle file or file_upload types if needed
72
+ # Only handle external audio
73
+ if audio.type != FileType.EXTERNAL or audio.external is None:
90
74
  return None
91
-
75
+ url = audio.external.url
92
76
  if not url:
93
77
  return None
94
78
 
95
79
  # Extract caption
96
- caption = audio_data.get("caption", [])
97
- if caption:
98
- caption_text = cls._extract_text_content(caption)
80
+ captions = audio.caption or []
81
+ if captions:
82
+ # use TextInlineFormatter instead of manual extraction
83
+ caption_text = TextInlineFormatter.extract_text_with_formatting(captions)
99
84
  return f'[audio]({url} "{caption_text}")'
100
85
 
101
86
  return f"[audio]({url})"
102
87
 
103
- @classmethod
104
- def is_multiline(cls) -> bool:
105
- """Audio embeds are single-line elements."""
106
- return False
107
-
108
88
  @classmethod
109
89
  def _is_likely_audio_url(cls, url: str) -> bool:
110
- """Check if URL likely points to an audio file."""
111
90
  return any(url.lower().endswith(ext) for ext in cls.SUPPORTED_EXTENSIONS)
112
-
113
- @classmethod
114
- def _extract_text_content(cls, rich_text: List[dict[str, Any]]) -> str:
115
- """Extract plain text content from Notion rich_text elements."""
116
- result = ""
117
- for text_obj in rich_text:
118
- if text_obj.get("type") == "text":
119
- result += text_obj.get("text", {}).get("content", "")
120
- elif "plain_text" in text_obj:
121
- result += text_obj.get("plain_text", "")
122
- return result
123
-
124
- @classmethod
125
- def get_llm_prompt_content(cls) -> ElementPromptContent:
126
- """
127
- Returns structured LLM prompt metadata for the audio element.
128
- """
129
- return (
130
- ElementPromptBuilder()
131
- .with_description(
132
- "Embeds an audio file that can be played directly in the page."
133
- )
134
- .with_usage_guidelines(
135
- "Use audio embeds when you want to include sound files, music, podcasts, "
136
- "or voice recordings. Supports common audio formats like MP3, WAV, OGG, and M4A."
137
- )
138
- .with_syntax('![audio](https://example.com/audio.mp3 "Optional caption")')
139
- .with_examples(
140
- [
141
- "[audio](https://example.com/song.mp3)",
142
- '[audio](https://example.com/podcast.mp3 "Episode 1: Introduction")',
143
- '[audio](https://example.com/sound.wav "Sound effect for presentation")',
144
- '[audio](https://example.com/recording.m4a "Voice memo from meeting")',
145
- ]
146
- )
147
- .with_avoidance_guidelines(
148
- "Ensure the URL points to a valid audio file. "
149
- "Some audio formats may not be supported by all browsers."
150
- )
151
- .build()
152
- )
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
+
2
3
  from typing import Optional
4
+
3
5
  from pydantic import BaseModel
4
6
 
5
- from notionary.blocks.markdown_node import MarkdownNode
7
+ from notionary.markdown.markdown_node import MarkdownNode
6
8
 
7
9
 
8
10
  class AudioMarkdownBlockParams(BaseModel):
@@ -1,59 +1,10 @@
1
- from typing import Optional
2
- from pydantic import BaseModel
3
-
4
- from notionary.blocks.shared.models import RichTextObject
5
-
6
-
7
- # TODO: Diesen Kram hier auch verwenden
8
- class ExternalAudioSource(BaseModel):
9
- """External audio source."""
10
-
11
- url: str
12
-
13
-
14
- class NotionAudioData(BaseModel):
15
- """Audio block data."""
16
-
17
- type: str = "external"
18
- external: ExternalAudioSource
19
- caption: list[dict] = []
1
+ from typing import Literal
20
2
 
3
+ from pydantic import BaseModel
21
4
 
22
- class NotionAudioBlock(BaseModel):
23
- """Audio block result."""
24
-
25
- type: str = "audio"
26
- audio: NotionAudioData
27
-
28
-
29
- # Updated method with typed return
30
- @classmethod
31
- def markdown_to_notion(cls, text: str) -> Optional[NotionAudioBlock]:
32
- """Convert markdown audio embed to Notion audio block."""
33
- audio_match = cls.PATTERN.match(text.strip())
34
- if not audio_match:
35
- return None
36
-
37
- url = audio_match.group(1)
38
- caption_text = audio_match.group(2)
39
-
40
- if not url:
41
- return None
42
-
43
- # Validate URL if possible
44
- if not cls._is_likely_audio_url(url):
45
- # Still proceed - user might know better
46
- pass
5
+ from notionary.blocks.file.file_element_models import FileBlock
47
6
 
48
- # Build caption list
49
- caption_list = []
50
- if caption_text:
51
- caption_rich_text = RichTextObject.from_plain_text(caption_text)
52
- caption_list = [caption_rich_text.model_dump()]
53
7
 
54
- # Create typed result
55
- return NotionAudioBlock(
56
- audio=NotionAudioData(
57
- external=ExternalAudioSource(url=url), caption=caption_list
58
- )
59
- )
8
+ class CreateAudioBlock(BaseModel):
9
+ type: Literal["audio"] = "audio"
10
+ audio: FileBlock