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
@@ -1,182 +1,106 @@
1
- import re
2
- from typing import Dict, Any, Optional, List
1
+ from __future__ import annotations
3
2
 
4
- from notionary.blocks import (
5
- ElementPromptContent,
6
- ElementPromptBuilder,
7
- NotionBlockResult,
8
- NotionBlockElement,
3
+ import re
4
+ from typing import Optional
5
+
6
+ from notionary.blocks.base_block_element import BaseBlockElement
7
+ from notionary.blocks.file.file_element_models import ExternalFile, FileBlock, FileType
8
+ from notionary.blocks.models import Block, BlockCreateResult
9
+ from notionary.blocks.paragraph.paragraph_models import (
10
+ CreateParagraphBlock,
11
+ ParagraphBlock,
9
12
  )
13
+ from notionary.blocks.rich_text.rich_text_models import RichTextObject
14
+ from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
15
+ from notionary.blocks.types import BlockType
16
+ from notionary.blocks.video.video_element_models import CreateVideoBlock
10
17
 
11
18
 
12
- class VideoElement(NotionBlockElement):
19
+ class VideoElement(BaseBlockElement):
13
20
  """
14
21
  Handles conversion between Markdown video embeds and Notion video blocks.
15
22
 
16
23
  Markdown video syntax:
17
- - [video](https://example.com/video.mp4) - Simple video with URL only
18
- - [video](https://example.com/video.mp4 "Caption") - Video with URL and caption
24
+ - [video](https://example.com/video.mp4) - URL only
25
+ - [video](https://example.com/video.mp4 "Caption") - URL + caption
19
26
 
20
- Where:
21
- - URL is the required video URL
22
- - Caption is an optional descriptive text (enclosed in quotes)
23
-
24
- Supports various video URLs including YouTube, Vimeo, and direct video file links.
27
+ Supports YouTube, Vimeo, and direct file URLs.
25
28
  """
26
29
 
27
- # Regex pattern for video syntax with optional caption
28
30
  PATTERN = re.compile(
29
- r"^\[video\]\(" # [video]( prefix
30
- + r'(https?://[^\s"]+)' # URL (required)
31
- + r'(?:\s+"([^"]+)")?' # Optional caption in quotes
32
- + r"\)$" # closing parenthesis
31
+ r"^\[video\]\(" # prefix
32
+ r"(https?://[^\s\"]+)" # URL
33
+ r"(?:\s+\"([^\"]+)\")?" # optional caption
34
+ r"\)$"
33
35
  )
34
36
 
35
- # YouTube URL patterns
36
37
  YOUTUBE_PATTERNS = [
37
- re.compile(
38
- r"(?:https?://)?(?:www\.)?youtube\.com/watch\?v=([a-zA-Z0-9_-]{11})"
39
- ),
40
- re.compile(r"(?:https?://)?(?:www\.)?youtu\.be/([a-zA-Z0-9_-]{11})"),
38
+ re.compile(r"(?:https?://)?(?:www\.)?youtube\.com/watch\?v=([\w-]{11})"),
39
+ re.compile(r"(?:https?://)?(?:www\.)?youtu\.be/([\w-]{11})"),
41
40
  ]
42
41
 
43
42
  @classmethod
44
- def match_markdown(cls, text: str) -> bool:
45
- """Check if text is a markdown video embed."""
46
- return text.strip().startswith("[video]") and bool(
47
- VideoElement.PATTERN.match(text.strip())
48
- )
43
+ def match_notion(cls, block: Block) -> bool:
44
+ return block.type == BlockType.VIDEO and block.video
49
45
 
50
46
  @classmethod
51
- def match_notion(cls, block: Dict[str, Any]) -> bool:
52
- """Check if block is a Notion video."""
53
- return block.get("type") == "video"
54
-
55
- @classmethod
56
- def markdown_to_notion(cls, text: str) -> NotionBlockResult:
57
- """Convert markdown video embed to Notion video block."""
58
- video_match = VideoElement.PATTERN.match(text.strip())
59
- if not video_match:
60
- return None
61
-
62
- url = video_match.group(1)
63
- caption = video_match.group(2)
64
-
65
- if not url:
47
+ def markdown_to_notion(cls, text: str) -> BlockCreateResult:
48
+ """Convert markdown video syntax to a Notion VideoBlock plus an empty paragraph."""
49
+ match = cls.PATTERN.match(text.strip())
50
+ if not match:
66
51
  return None
67
52
 
68
- # Normalize YouTube URLs
69
- youtube_id = VideoElement._get_youtube_id(url)
70
- if youtube_id:
71
- url = f"https://www.youtube.com/watch?v={youtube_id}"
53
+ url, caption_text = match.group(1), match.group(2) or ""
72
54
 
73
- video_data = {"type": "external", "external": {"url": url}}
74
-
75
- # Add caption if provided
76
- if caption:
77
- video_data["caption"] = [{"type": "text", "text": {"content": caption}}]
78
- else:
79
- video_data["caption"] = []
55
+ vid_id = cls._get_youtube_id(url)
56
+ if vid_id:
57
+ url = f"https://www.youtube.com/watch?v={vid_id}"
80
58
 
81
- # Prepare the video block
82
- video_block = {"type": "video", "video": video_data}
59
+ video_block = FileBlock(
60
+ type=FileType.EXTERNAL, external=ExternalFile(url=url), caption=[]
61
+ )
62
+ if caption_text.strip():
63
+ rt = RichTextObject.from_plain_text(caption_text.strip())
64
+ video_block.caption = [rt]
83
65
 
84
- # Add empty paragraph after video
85
- empty_paragraph = {"type": "paragraph", "paragraph": {"rich_text": []}}
66
+ empty_para = ParagraphBlock(rich_text=[])
86
67
 
87
- return [video_block, empty_paragraph]
68
+ return [
69
+ CreateVideoBlock(video=video_block),
70
+ CreateParagraphBlock(paragraph=empty_para),
71
+ ]
88
72
 
89
73
  @classmethod
90
- def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
91
- """Convert Notion video block to markdown video embed."""
92
- if block.get("type") != "video":
74
+ def notion_to_markdown(cls, block: Block) -> Optional[str]:
75
+ if block.type != BlockType.VIDEO or not block.video:
93
76
  return None
94
77
 
95
- video_data = block.get("video", {})
96
-
97
- # Extract URL from video data
98
- url = VideoElement._extract_video_url(video_data)
99
- if not url:
100
- return None
78
+ fo = block.video
101
79
 
102
- caption_rich_text = video_data.get("caption", [])
80
+ # URL ermitteln
81
+ if fo.type == FileType.EXTERNAL and fo.external:
82
+ url = fo.external.url
83
+ elif fo.type == FileType.FILE and fo.file:
84
+ url = fo.file.url
85
+ else:
86
+ return None # (file_upload o.ä. hier nicht supported)
103
87
 
104
- if not caption_rich_text:
105
- # Simple video with URL only
88
+ # Captions
89
+ captions = fo.caption or []
90
+ if not captions:
106
91
  return f"[video]({url})"
107
92
 
108
- # Extract caption text
109
- caption = VideoElement._extract_text_content(caption_rich_text)
110
-
111
- if caption:
112
- return f'[video]({url} "{caption}")'
113
-
114
- return f"[video]({url})"
115
-
116
- @classmethod
117
- def is_multiline(cls) -> bool:
118
- """Videos are single-line elements."""
119
- return False
93
+ caption_text = "".join(
94
+ (rt.plain_text or TextInlineFormatter.extract_text_with_formatting([rt]))
95
+ for rt in captions
96
+ )
120
97
 
121
- @classmethod
122
- def _is_youtube_url(cls, url: str) -> bool:
123
- """Check if URL is a YouTube video."""
124
- for pattern in VideoElement.YOUTUBE_PATTERNS:
125
- if pattern.match(url):
126
- return True
127
- return False
98
+ return f'[video]({url} "{caption_text}")'
128
99
 
129
100
  @classmethod
130
101
  def _get_youtube_id(cls, url: str) -> Optional[str]:
131
- """Extract YouTube video ID from URL."""
132
- for pattern in VideoElement.YOUTUBE_PATTERNS:
133
- match = pattern.match(url)
134
- if match:
135
- return match.group(1)
102
+ for pat in cls.YOUTUBE_PATTERNS:
103
+ m = pat.match(url)
104
+ if m:
105
+ return m.group(1)
136
106
  return None
137
-
138
- @classmethod
139
- def _extract_video_url(cls, video_data: Dict[str, Any]) -> str:
140
- """Extract URL from video data, handling both external and uploaded videos."""
141
- if video_data.get("type") == "external":
142
- return video_data.get("external", {}).get("url", "")
143
- elif video_data.get("type") == "file":
144
- return video_data.get("file", {}).get("url", "")
145
- return ""
146
-
147
- @classmethod
148
- def _extract_text_content(cls, rich_text: List[Dict[str, Any]]) -> str:
149
- """Extract plain text content from Notion rich_text elements."""
150
- result = ""
151
- for text_obj in rich_text:
152
- if text_obj.get("type") == "text":
153
- result += text_obj.get("text", {}).get("content", "")
154
- elif "plain_text" in text_obj:
155
- result += text_obj.get("plain_text", "")
156
- return result
157
-
158
- @classmethod
159
- def get_llm_prompt_content(cls) -> ElementPromptContent:
160
- """
161
- Returns structured LLM prompt metadata for the video element.
162
- """
163
- return (
164
- ElementPromptBuilder()
165
- .with_description(
166
- "Embeds video content from external sources like YouTube or direct video URLs."
167
- )
168
- .with_usage_guidelines(
169
- "Use video embeds when you want to include multimedia content directly in your document. "
170
- "Videos are useful for tutorials, demonstrations, presentations, or any content that benefits from visual explanation."
171
- )
172
- .with_syntax('[video](https://example.com/video.mp4 "Optional caption")')
173
- .with_examples(
174
- [
175
- "[video](https://www.youtube.com/watch?v=dQw4w9WgXcQ)",
176
- '[video](https://example.com/videos/demo.mp4 "Product demo")',
177
- '[video](https://youtu.be/dQw4w9WgXcQ "How to use this feature")',
178
- '[video](https://example.com/tutorial.mp4 "Step-by-step tutorial")',
179
- ]
180
- )
181
- .build()
182
- )
@@ -0,0 +1,10 @@
1
+ from typing import Literal
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from notionary.blocks.file.file_element_models import FileBlock
6
+
7
+
8
+ class CreateVideoBlock(BaseModel):
9
+ type: Literal["video"] = "video"
10
+ video: FileBlock
@@ -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
- from notionary.blocks.markdown_node import MarkdownNode
6
+
7
+ from notionary.markdown.markdown_node import MarkdownNode
6
8
 
7
9
 
8
10
  class VideoMarkdownBlockParams(BaseModel):
@@ -1,19 +1,14 @@
1
- from typing import Dict, Any, Optional
2
- from dotenv import load_dotenv
3
- from notionary.base_notion_client import BaseNotionClient
1
+ from typing import Any, Dict, Optional
4
2
 
5
- from notionary.models.notion_database_response import (
3
+ from notionary.base_notion_client import BaseNotionClient
4
+ from notionary.database.models import (
6
5
  NotionDatabaseResponse,
7
6
  NotionDatabaseSearchResponse,
8
7
  NotionPageResponse,
9
8
  NotionQueryDatabaseResponse,
10
9
  )
11
- from notionary.util import singleton
12
-
13
- load_dotenv()
14
10
 
15
11
 
16
- @singleton
17
12
  class NotionDatabaseClient(BaseNotionClient):
18
13
  """
19
14
  Specialized Notion client for database operations.
@@ -1,25 +1,24 @@
1
1
  from __future__ import annotations
2
+
2
3
  import asyncio
3
4
  import random
4
- from typing import Any, AsyncGenerator, Dict, Optional
5
+ from typing import Any, AsyncGenerator, Optional
5
6
 
6
7
  from notionary.database.client import NotionDatabaseClient
7
- from notionary.models.notion_database_response import (
8
+ from notionary.database.database_filter_builder import DatabaseFilterBuilder
9
+ from notionary.database.database_provider import NotionDatabaseProvider
10
+ from notionary.database.models import (
8
11
  NotionDatabaseResponse,
9
12
  NotionPageResponse,
10
13
  NotionQueryDatabaseResponse,
11
14
  )
12
15
  from notionary.page.notion_page import NotionPage
13
-
14
- from notionary.database.database_provider import NotionDatabaseProvider
15
-
16
- from notionary.database.database_filter_builder import DatabaseFilterBuilder
17
16
  from notionary.telemetry import (
18
- ProductTelemetry,
19
17
  DatabaseFactoryUsedEvent,
18
+ ProductTelemetry,
20
19
  QueryOperationEvent,
21
20
  )
22
- from notionary.util import factory_only, LoggingMixin
21
+ from notionary.util import LoggingMixin, factory_only
23
22
 
24
23
 
25
24
  class NotionDatabase(LoggingMixin):
@@ -38,7 +37,7 @@ class NotionDatabase(LoggingMixin):
38
37
  title: str,
39
38
  url: str,
40
39
  emoji_icon: Optional[str] = None,
41
- properties: Optional[Dict[str, Any]] = None,
40
+ properties: Optional[dict[str, Any]] = None,
42
41
  token: Optional[str] = None,
43
42
  ):
44
43
  """
@@ -103,7 +102,7 @@ class NotionDatabase(LoggingMixin):
103
102
  return self._emoji_icon
104
103
 
105
104
  @property
106
- def properties(self) -> Optional[Dict[str, Any]]:
105
+ def properties(self) -> Optional[dict[str, Any]]:
107
106
  """Get the database properties (readonly)."""
108
107
  return self._properties
109
108
 
@@ -328,7 +327,7 @@ class NotionDatabase(LoggingMixin):
328
327
  async def _iter_pages(
329
328
  self,
330
329
  page_size: int = 100,
331
- filter_conditions: Optional[Dict[str, Any]] = None,
330
+ filter_conditions: Optional[dict[str, Any]] = None,
332
331
  ) -> AsyncGenerator[NotionPage, None]:
333
332
  """
334
333
  Asynchronous generator that yields NotionPage objects from the database.
@@ -422,7 +421,7 @@ class NotionDatabase(LoggingMixin):
422
421
  except (KeyError, TypeError, AttributeError):
423
422
  return None
424
423
 
425
- async def _get_relation_options(self, property_name: str) -> list[Dict[str, Any]]:
424
+ async def _get_relation_options(self, property_name: str) -> list[dict[str, Any]]:
426
425
  """
427
426
  Retrieve the titles of all pages related to a relation property.
428
427
 
@@ -445,7 +444,7 @@ class NotionDatabase(LoggingMixin):
445
444
  async def _paginate_database(
446
445
  self,
447
446
  page_size: int = 100,
448
- filter_conditions: Optional[Dict[str, Any]] = None,
447
+ filter_conditions: Optional[dict[str, Any]] = None,
449
448
  ) -> AsyncGenerator[list[NotionPageResponse], None]:
450
449
  """
451
450
  Central pagination logic for Notion Database queries.
@@ -461,7 +460,7 @@ class NotionDatabase(LoggingMixin):
461
460
  has_more = True
462
461
 
463
462
  while has_more:
464
- query_data: Dict[str, Any] = {"page_size": page_size}
463
+ query_data: dict[str, Any] = {"page_size": page_size}
465
464
 
466
465
  if start_cursor:
467
466
  query_data["start_cursor"] = start_cursor
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, Dict, List
4
- from datetime import datetime, timedelta
5
3
  from dataclasses import dataclass, field
4
+ from datetime import datetime, timedelta
5
+ from typing import Any, Dict, List
6
6
 
7
7
 
8
8
  @dataclass
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Dict, Optional, TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, Optional
4
+
4
5
  from notionary.database.client import NotionDatabaseClient
5
6
  from notionary.database.exceptions import DatabaseNotFoundException
6
- from notionary.models.notion_database_response import NotionDatabaseResponse
7
- from notionary.util import LoggingMixin, format_uuid, SingletonMetaClass
7
+ from notionary.database.models import NotionDatabaseResponse
8
+ from notionary.util import LoggingMixin, SingletonMetaClass, format_uuid
8
9
  from notionary.util.fuzzy import find_best_match
9
10
 
10
11
  if TYPE_CHECKING:
@@ -22,7 +23,7 @@ class NotionDatabaseProvider(LoggingMixin, metaclass=SingletonMetaClass):
22
23
  """
23
24
 
24
25
  def __init__(self):
25
- self._database_cache: Dict[str, NotionDatabase] = {}
26
+ self._database_cache: dict[str, NotionDatabase] = {}
26
27
 
27
28
  async def get_database_by_id(
28
29
  self, database_id: str, token: Optional[str] = None, force_refresh: bool = False