notionary 0.1.2__py3-none-any.whl → 0.1.3__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 (49) hide show
  1. notionary/core/__init__.py +0 -0
  2. notionary/core/converters/__init__.py +50 -0
  3. notionary/core/converters/elements/__init__.py +0 -0
  4. notionary/core/converters/elements/bookmark_element.py +224 -0
  5. notionary/core/converters/elements/callout_element.py +179 -0
  6. notionary/core/converters/elements/code_block_element.py +153 -0
  7. notionary/core/converters/elements/column_element.py +294 -0
  8. notionary/core/converters/elements/divider_element.py +73 -0
  9. notionary/core/converters/elements/heading_element.py +84 -0
  10. notionary/core/converters/elements/image_element.py +130 -0
  11. notionary/core/converters/elements/list_element.py +130 -0
  12. notionary/core/converters/elements/notion_block_element.py +51 -0
  13. notionary/core/converters/elements/paragraph_element.py +73 -0
  14. notionary/core/converters/elements/qoute_element.py +242 -0
  15. notionary/core/converters/elements/table_element.py +306 -0
  16. notionary/core/converters/elements/text_inline_formatter.py +294 -0
  17. notionary/core/converters/elements/todo_lists.py +114 -0
  18. notionary/core/converters/elements/toggle_element.py +205 -0
  19. notionary/core/converters/elements/video_element.py +159 -0
  20. notionary/core/converters/markdown_to_notion_converter.py +482 -0
  21. notionary/core/converters/notion_to_markdown_converter.py +45 -0
  22. notionary/core/converters/registry/__init__.py +0 -0
  23. notionary/core/converters/registry/block_element_registry.py +234 -0
  24. notionary/core/converters/registry/block_element_registry_builder.py +280 -0
  25. notionary/core/database/database_info_service.py +43 -0
  26. notionary/core/database/database_query_service.py +73 -0
  27. notionary/core/database/database_schema_service.py +57 -0
  28. notionary/core/database/models/page_result.py +10 -0
  29. notionary/core/database/notion_database_manager.py +332 -0
  30. notionary/core/database/notion_database_manager_factory.py +233 -0
  31. notionary/core/database/notion_database_schema.py +415 -0
  32. notionary/core/database/notion_database_writer.py +390 -0
  33. notionary/core/database/page_service.py +161 -0
  34. notionary/core/notion_client.py +134 -0
  35. notionary/core/page/meta_data/metadata_editor.py +37 -0
  36. notionary/core/page/notion_page_manager.py +110 -0
  37. notionary/core/page/page_content_manager.py +85 -0
  38. notionary/core/page/property_formatter.py +97 -0
  39. notionary/exceptions/database_exceptions.py +76 -0
  40. notionary/exceptions/page_creation_exception.py +9 -0
  41. notionary/util/logging_mixin.py +47 -0
  42. notionary/util/singleton_decorator.py +20 -0
  43. notionary/util/uuid_utils.py +24 -0
  44. {notionary-0.1.2.dist-info → notionary-0.1.3.dist-info}/METADATA +1 -1
  45. notionary-0.1.3.dist-info/RECORD +49 -0
  46. notionary-0.1.2.dist-info/RECORD +0 -6
  47. {notionary-0.1.2.dist-info → notionary-0.1.3.dist-info}/WHEEL +0 -0
  48. {notionary-0.1.2.dist-info → notionary-0.1.3.dist-info}/licenses/LICENSE +0 -0
  49. {notionary-0.1.2.dist-info → notionary-0.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,234 @@
1
+ from typing import Dict, Any, Optional, List, Type
2
+
3
+ from notionary.core.converters.elements.notion_block_element import NotionBlockElement
4
+ from notionary.core.converters.elements.text_inline_formatter import TextInlineFormatter
5
+
6
+
7
+ class BlockElementRegistry:
8
+ """Registry of elements that can convert between Markdown and Notion."""
9
+
10
+ def __init__(self, elements=None):
11
+ """
12
+ Initialize a new registry instance.
13
+
14
+ Args:
15
+ elements: Optional list of NotionBlockElement classes to register at creation
16
+ """
17
+ self._elements = []
18
+
19
+ # Register initial elements if provided
20
+ if elements:
21
+ for element in elements:
22
+ self.register(element)
23
+
24
+ def register(self, element_class: Type[NotionBlockElement]):
25
+ """Register an element class."""
26
+ self._elements.append(element_class)
27
+ return self
28
+
29
+ def deregister(self, element_class: Type[NotionBlockElement]) -> bool:
30
+ """
31
+ Deregister an element class.
32
+
33
+ Args:
34
+ element_class: The element class to remove from the registry
35
+
36
+ Returns:
37
+ bool: True if the element was removed, False if it wasn't in the registry
38
+ """
39
+ if element_class in self._elements:
40
+ self._elements.remove(element_class)
41
+ return True
42
+ return False
43
+
44
+ def clear(self):
45
+ """Clear the registry completely."""
46
+ self._elements.clear()
47
+ return self
48
+
49
+ def find_markdown_handler(self, text: str) -> Optional[Type[NotionBlockElement]]:
50
+ """Find an element that can handle the given markdown text."""
51
+ for element in self._elements:
52
+ if element.match_markdown(text):
53
+ return element
54
+ return None
55
+
56
+ def find_notion_handler(
57
+ self, block: Dict[str, Any]
58
+ ) -> Optional[Type[NotionBlockElement]]:
59
+ """Find an element that can handle the given Notion block."""
60
+ for element in self._elements:
61
+ if element.match_notion(block):
62
+ return element
63
+ return None
64
+
65
+ def markdown_to_notion(self, text: str) -> Optional[Dict[str, Any]]:
66
+ """Convert markdown to Notion block using registered elements."""
67
+ handler = self.find_markdown_handler(text)
68
+ if handler:
69
+ return handler.markdown_to_notion(text)
70
+ return None
71
+
72
+ def notion_to_markdown(self, block: Dict[str, Any]) -> Optional[str]:
73
+ """Convert Notion block to markdown using registered elements."""
74
+ handler = self.find_notion_handler(block)
75
+ if handler:
76
+ return handler.notion_to_markdown(block)
77
+ return None
78
+
79
+ def get_multiline_elements(self) -> List[Type[NotionBlockElement]]:
80
+ """Get all registered multiline elements."""
81
+ return [element for element in self._elements if element.is_multiline()]
82
+
83
+ def get_elements(self) -> List[Type[NotionBlockElement]]:
84
+ """Get all registered elements."""
85
+ return self._elements.copy()
86
+
87
+ def generate_llm_prompt(self) -> str:
88
+ """
89
+ Generates an LLM system prompt that describes the Markdown syntax of all registered elements.
90
+
91
+ TextInlineFormatter is automatically added if not already registered.
92
+
93
+ Returns:
94
+ A complete system prompt for an LLM that should understand Notion-Markdown syntax
95
+ """
96
+ # Create a copy of registered elements
97
+ element_classes = self._elements.copy()
98
+ print("Elements in registry:", element_classes)
99
+
100
+ formatter_names = [e.__name__ for e in element_classes]
101
+ if "TextInlineFormatter" not in formatter_names:
102
+ element_classes = [TextInlineFormatter] + element_classes
103
+
104
+ return MarkdownSyntaxPromptBuilder.generate_system_prompt(element_classes)
105
+
106
+
107
+ class MarkdownSyntaxPromptBuilder:
108
+ """
109
+ Generator for LLM system prompts that describe Notion-Markdown syntax.
110
+
111
+ This class extracts information about supported Markdown patterns
112
+ and formats them optimally for LLMs.
113
+ """
114
+
115
+ # Standard system prompt template
116
+ SYSTEM_PROMPT_TEMPLATE = """You are a knowledgeable assistant that helps users create content for Notion pages.
117
+ Notion supports standard Markdown with some special extensions for creating rich content.
118
+
119
+ {element_docs}
120
+
121
+ Important usage guidelines:
122
+
123
+ 1. The backtick code fence syntax (```) should ONLY be used when creating actual code blocks or diagrams.
124
+ Do not wrap examples or regular content in backticks unless you're showing code.
125
+
126
+ 2. Use inline formatting (bold, italic, highlights, etc.) across all content to enhance readability.
127
+ The highlight syntax (==text== and ==color:text==) is especially useful for emphasizing important points.
128
+
129
+ 3. Notion's extensions to Markdown (like callouts, bookmarks, toggles) provide richer formatting options
130
+ than standard Markdown while maintaining the familiar Markdown syntax for basic elements.
131
+
132
+ 4. You can use these Markdown extensions alongside standard Markdown to create visually appealing
133
+ and well-structured content.
134
+
135
+ 5. Remember that features like highlighting with ==yellow:important== work in all text blocks including
136
+ paragraphs, lists, quotes, etc.
137
+ """
138
+
139
+ @staticmethod
140
+ def generate_element_doc(element_class: Type[NotionBlockElement]) -> str:
141
+ """
142
+ Generates documentation for a specific NotionBlockElement.
143
+
144
+ Uses the element's get_llm_prompt_content method if available.
145
+ """
146
+ class_name = element_class.__name__
147
+ element_name = class_name.replace("Element", "")
148
+
149
+ # Start with element name as header
150
+ result = [f"## {element_name}"]
151
+
152
+ # Use get_llm_prompt_content if available
153
+ if hasattr(element_class, "get_llm_prompt_content") and callable(
154
+ getattr(element_class, "get_llm_prompt_content")
155
+ ):
156
+ content = element_class.get_llm_prompt_content()
157
+
158
+ if content.get("description"):
159
+ result.append(content["description"])
160
+
161
+ if content.get("syntax"):
162
+ result.append("\n### Syntax:")
163
+ for syntax_item in content["syntax"]:
164
+ result.append(f"{syntax_item}")
165
+
166
+ if content.get("examples"):
167
+ result.append("\n### Examples:")
168
+ for example in content["examples"]:
169
+ result.append(example)
170
+
171
+ # Add any additional custom sections
172
+ for key, value in content.items():
173
+ if key not in ["description", "syntax", "examples"] and isinstance(
174
+ value, str
175
+ ):
176
+ result.append(f"\n### {key.replace('_', ' ').title()}:")
177
+ result.append(value)
178
+
179
+ return "\n".join(result)
180
+
181
+ @classmethod
182
+ def generate_element_docs(
183
+ cls,
184
+ element_classes: List[Type[NotionBlockElement]],
185
+ ) -> str:
186
+ """
187
+ Generates complete documentation for all provided element classes.
188
+
189
+ Args:
190
+ element_classes: List of NotionBlockElement classes
191
+
192
+ Returns:
193
+ Documentation text for all elements
194
+ """
195
+ docs = [
196
+ "# Custom Markdown Syntax for Notion Blocks",
197
+ "The following custom Markdown patterns are supported for creating Notion blocks:",
198
+ ]
199
+
200
+ text_formatter = None
201
+ other_elements = []
202
+
203
+ for element in element_classes:
204
+ if element.__name__ == "TextInlineFormatter":
205
+ text_formatter = element
206
+ else:
207
+ other_elements.append(element)
208
+
209
+ if text_formatter:
210
+ docs.append("\n" + cls.generate_element_doc(text_formatter))
211
+
212
+ for element in other_elements:
213
+ if element.__name__ != "InlineFormattingElement":
214
+ docs.append("\n" + cls.generate_element_doc(element))
215
+
216
+ return "\n".join(docs)
217
+
218
+ @classmethod
219
+ def generate_system_prompt(
220
+ cls,
221
+ element_classes: List[Type[NotionBlockElement]],
222
+ ) -> str:
223
+ """
224
+ Generates a complete system prompt for LLMs.
225
+
226
+ Args:
227
+ element_classes: List of element classes to document
228
+
229
+ Returns:
230
+ Complete system prompt for an LLM
231
+ """
232
+ element_docs = cls.generate_element_docs(element_classes)
233
+
234
+ return cls.SYSTEM_PROMPT_TEMPLATE.format(element_docs=element_docs)
@@ -0,0 +1,280 @@
1
+ from typing import List, Type
2
+ from collections import OrderedDict
3
+
4
+ from notionary.core.converters.elements.notion_block_element import NotionBlockElement
5
+ from notionary.core.converters.registry.block_element_registry import (
6
+ BlockElementRegistry,
7
+ )
8
+
9
+ from notionary.core.converters.elements.paragraph_element import ParagraphElement
10
+ from notionary.core.converters.elements.heading_element import HeadingElement
11
+ from notionary.core.converters.elements.callout_element import CalloutElement
12
+ from notionary.core.converters.elements.code_block_element import CodeBlockElement
13
+ from notionary.core.converters.elements.divider_element import DividerElement
14
+ from notionary.core.converters.elements.table_element import TableElement
15
+ from notionary.core.converters.elements.todo_lists import TodoElement
16
+ from notionary.core.converters.elements.list_element import (
17
+ BulletedListElement,
18
+ NumberedListElement,
19
+ )
20
+ from notionary.core.converters.elements.qoute_element import QuoteElement
21
+ from notionary.core.converters.elements.image_element import ImageElement
22
+ from notionary.core.converters.elements.video_element import VideoElement
23
+ from notionary.core.converters.elements.toggle_element import ToggleElement
24
+ from notionary.core.converters.elements.bookmark_element import BookmarkElement
25
+ from notionary.core.converters.elements.column_element import ColumnElement
26
+
27
+
28
+ class BlockElementRegistryBuilder:
29
+ """
30
+ True builder for constructing BlockElementRegistry instances.
31
+
32
+ This builder allows for incremental construction of registry instances
33
+ with specific configurations of block elements.
34
+ """
35
+
36
+ def __init__(self):
37
+ """Initialize a new builder with an empty element list."""
38
+ # Use OrderedDict to maintain insertion order while ensuring uniqueness
39
+ self._elements = OrderedDict()
40
+
41
+ # Profile methods - create a base configuration
42
+
43
+ @classmethod
44
+ def start_empty(cls) -> "BlockElementRegistryBuilder":
45
+ """
46
+ Start with a completely empty registry builder.
47
+
48
+ Returns:
49
+ A new builder instance with no elements
50
+ """
51
+ return cls()
52
+
53
+ @classmethod
54
+ def start_minimal(cls) -> "BlockElementRegistryBuilder":
55
+ """
56
+ Start with a minimal set of essential elements.
57
+
58
+ Returns:
59
+ A new builder instance with basic elements
60
+ """
61
+ builder = cls()
62
+ return (
63
+ builder.add_element(HeadingElement)
64
+ .add_element(BulletedListElement)
65
+ .add_element(NumberedListElement)
66
+ .add_element(ParagraphElement)
67
+ ) # Add paragraph last as fallback
68
+
69
+ @classmethod
70
+ def start_standard(cls) -> "BlockElementRegistryBuilder":
71
+ """
72
+ Start with all standard elements in recommended order.
73
+
74
+ Returns:
75
+ A new builder instance with all standard elements
76
+ """
77
+ builder = cls()
78
+ return (
79
+ builder.add_element(HeadingElement)
80
+ .add_element(CalloutElement)
81
+ .add_element(CodeBlockElement)
82
+ .add_element(DividerElement)
83
+ .add_element(TableElement)
84
+ .add_element(ColumnElement)
85
+ .add_element(BulletedListElement)
86
+ .add_element(NumberedListElement)
87
+ .add_element(ToggleElement)
88
+ .add_element(QuoteElement)
89
+ .add_element(TodoElement)
90
+ .add_element(BookmarkElement)
91
+ .add_element(ImageElement)
92
+ .add_element(VideoElement)
93
+ .add_element(ParagraphElement)
94
+ ) # Add paragraph last as fallback
95
+
96
+ # Element manipulation methods
97
+
98
+ def add_element(
99
+ self, element_class: Type[NotionBlockElement]
100
+ ) -> "BlockElementRegistryBuilder":
101
+ """
102
+ Add an element class to the registry configuration.
103
+ If the element already exists, it's moved to the end.
104
+
105
+ Args:
106
+ element_class: The element class to add
107
+
108
+ Returns:
109
+ Self for method chaining
110
+ """
111
+ # Remove if exists (to update the order) and add to the end
112
+ self._elements.pop(element_class.__name__, None)
113
+ self._elements[element_class.__name__] = element_class
114
+ return self
115
+
116
+ def add_elements(
117
+ self, element_classes: List[Type[NotionBlockElement]]
118
+ ) -> "BlockElementRegistryBuilder":
119
+ """
120
+ Add multiple element classes to the registry configuration.
121
+
122
+ Args:
123
+ element_classes: List of element classes to add
124
+
125
+ Returns:
126
+ Self for method chaining
127
+ """
128
+ for element_class in element_classes:
129
+ self.add_element(element_class)
130
+ return self
131
+
132
+ def remove_element(
133
+ self, element_class: Type[NotionBlockElement]
134
+ ) -> "BlockElementRegistryBuilder":
135
+ """
136
+ Remove an element class from the registry configuration.
137
+
138
+ Args:
139
+ element_class: The element class to remove
140
+
141
+ Returns:
142
+ Self for method chaining
143
+ """
144
+ self._elements.pop(element_class.__name__, None)
145
+ return self
146
+
147
+ def move_element_to_end(
148
+ self, element_class: Type[NotionBlockElement]
149
+ ) -> "BlockElementRegistryBuilder":
150
+ """
151
+ Move an existing element to the end of the registry.
152
+ If the element doesn't exist, it will be added.
153
+
154
+ Args:
155
+ element_class: The element class to move
156
+
157
+ Returns:
158
+ Self for method chaining
159
+ """
160
+ return self.add_element(element_class) # add_element already handles this logic
161
+
162
+ def ensure_paragraph_at_end(self) -> "BlockElementRegistryBuilder":
163
+ """
164
+ Ensure ParagraphElement is the last element in the registry.
165
+ If it doesn't exist, it will be added.
166
+
167
+ Returns:
168
+ Self for method chaining
169
+ """
170
+ return self.move_element_to_end(ParagraphElement)
171
+
172
+ # Specialized configuration methods
173
+
174
+ def with_list_support(self) -> "BlockElementRegistryBuilder":
175
+ """
176
+ Add support for list elements.
177
+
178
+ Returns:
179
+ Self for method chaining
180
+ """
181
+ return self.add_element(BulletedListElement).add_element(NumberedListElement)
182
+
183
+ def with_code_support(self) -> "BlockElementRegistryBuilder":
184
+ """
185
+ Add support for code blocks.
186
+
187
+ Returns:
188
+ Self for method chaining
189
+ """
190
+ return self.add_element(CodeBlockElement)
191
+
192
+ def with_table_support(self) -> "BlockElementRegistryBuilder":
193
+ """
194
+ Add support for tables.
195
+
196
+ Returns:
197
+ Self for method chaining
198
+ """
199
+ return self.add_element(TableElement)
200
+
201
+ def with_rich_content(self) -> "BlockElementRegistryBuilder":
202
+ """
203
+ Add support for rich content elements (callouts, toggles, etc.).
204
+
205
+ Returns:
206
+ Self for method chaining
207
+ """
208
+ return (
209
+ self.add_element(CalloutElement)
210
+ .add_element(ToggleElement)
211
+ .add_element(QuoteElement)
212
+ )
213
+
214
+ def with_media_support(self) -> "BlockElementRegistryBuilder":
215
+ """
216
+ Add support for media elements (images, videos).
217
+
218
+ Returns:
219
+ Self for method chaining
220
+ """
221
+ return self.add_element(ImageElement).add_element(VideoElement)
222
+
223
+ def with_task_support(self) -> "BlockElementRegistryBuilder":
224
+ """
225
+ Add support for task-related elements (todos).
226
+
227
+ Returns:
228
+ Self for method chaining
229
+ """
230
+ return self.add_element(TodoElement)
231
+
232
+ def build(self) -> BlockElementRegistry:
233
+ """
234
+ Build and return the configured BlockElementRegistry instance.
235
+
236
+ Returns:
237
+ A configured BlockElementRegistry instance
238
+ """
239
+ registry = BlockElementRegistry()
240
+
241
+ # Add elements in the recorded order
242
+ for element_class in self._elements.values():
243
+ registry.register(element_class)
244
+
245
+ return registry
246
+
247
+ @classmethod
248
+ def create_standard_registry(cls) -> BlockElementRegistry:
249
+ """
250
+ Factory method to directly create a standard registry.
251
+
252
+ Returns:
253
+ A fully configured registry instance
254
+ """
255
+ return cls.start_standard().build()
256
+
257
+ @classmethod
258
+ def create_minimal_registry(cls) -> BlockElementRegistry:
259
+ """
260
+ Factory method to directly create a minimal registry.
261
+
262
+ Returns:
263
+ A minimal registry instance
264
+ """
265
+ return cls.start_minimal().build()
266
+
267
+ @classmethod
268
+ def create_custom_registry(
269
+ cls, element_classes: List[Type[NotionBlockElement]]
270
+ ) -> BlockElementRegistry:
271
+ """
272
+ Factory method to directly create a custom registry.
273
+
274
+ Args:
275
+ element_classes: List of element classes to register
276
+
277
+ Returns:
278
+ A custom configured registry instance
279
+ """
280
+ return cls().add_elements(element_classes).build()
@@ -0,0 +1,43 @@
1
+ from typing import Optional
2
+ from notionary.core.notion_client import NotionClient
3
+
4
+
5
+ class DatabaseInfoService:
6
+ """Service für den Zugriff auf Datenbankinformationen"""
7
+
8
+ def __init__(self, client: NotionClient, database_id: str):
9
+ self._client = client
10
+ self.database_id = database_id
11
+ self._title = None
12
+
13
+ async def fetch_database_title(self) -> str:
14
+ """
15
+ Fetch the database title from the Notion API.
16
+
17
+ Returns:
18
+ The database title or "Untitled" if no title is found
19
+ """
20
+ db_details = await self._client.get(f"databases/{self.database_id}")
21
+ if not db_details:
22
+ return "Untitled"
23
+
24
+ title = "Untitled"
25
+ if "title" in db_details:
26
+ title_parts = []
27
+ for text_obj in db_details["title"]:
28
+ if "plain_text" in text_obj:
29
+ title_parts.append(text_obj["plain_text"])
30
+
31
+ if title_parts:
32
+ title = "".join(title_parts)
33
+
34
+ return title
35
+
36
+ @property
37
+ def title(self) -> Optional[str]:
38
+ return self._title
39
+
40
+ async def load_title(self) -> str:
41
+ """Lädt den Titel der Datenbank und speichert ihn im Cache"""
42
+ self._title = await self.fetch_database_title()
43
+ return self._title
@@ -0,0 +1,73 @@
1
+ from typing import Any, AsyncGenerator, Dict, List, Optional
2
+ from notionary.core.database.notion_database_schema import NotionDatabaseSchema
3
+ from notionary.core.page.notion_page_manager import NotionPageManager
4
+
5
+
6
+ class DatabaseQueryService:
7
+ """Service für Datenbankabfragen und Iterations"""
8
+
9
+ def __init__(self, schema: NotionDatabaseSchema):
10
+ self._schema = schema
11
+
12
+ async def get_pages(
13
+ self,
14
+ database_id: str,
15
+ limit: int = 100,
16
+ filter_conditions: Optional[Dict[str, Any]] = None,
17
+ sorts: Optional[List[Dict[str, Any]]] = None,
18
+ ) -> List[NotionPageManager]:
19
+ """
20
+ Get all pages from the database.
21
+
22
+ Args:
23
+ database_id: The database ID to query
24
+ limit: Maximum number of pages to retrieve
25
+ filter_conditions: Optional filter to apply to the database query
26
+ sorts: Optional sort instructions for the database query
27
+
28
+ Returns:
29
+ List of NotionPageManager instances for each page
30
+ """
31
+ pages: List[NotionPageManager] = []
32
+ count = 0
33
+
34
+ async for page in self.iter_pages(
35
+ database_id,
36
+ page_size=min(limit, 100),
37
+ filter_conditions=filter_conditions,
38
+ sorts=sorts,
39
+ ):
40
+ pages.append(page)
41
+ count += 1
42
+
43
+ if count >= limit:
44
+ break
45
+
46
+ return pages
47
+
48
+ async def iter_pages(
49
+ self,
50
+ database_id: str,
51
+ page_size: int = 100,
52
+ filter_conditions: Optional[Dict[str, Any]] = None,
53
+ sorts: Optional[List[Dict[str, Any]]] = None,
54
+ ) -> AsyncGenerator[NotionPageManager, None]:
55
+ """
56
+ Asynchronous generator that yields pages from the database.
57
+
58
+ Args:
59
+ database_id: The database ID to query
60
+ page_size: Number of pages to fetch per request
61
+ filter_conditions: Optional filter to apply to the database query
62
+ sorts: Optional sort instructions for the database query
63
+
64
+ Yields:
65
+ NotionPageManager instances for each page
66
+ """
67
+ async for page_manager in self._schema.iter_database_pages(
68
+ database_id=database_id,
69
+ page_size=page_size,
70
+ filter_conditions=filter_conditions,
71
+ sorts=sorts,
72
+ ):
73
+ yield page_manager
@@ -0,0 +1,57 @@
1
+ from typing import Dict, List
2
+ from notionary.core.database.notion_database_schema import NotionDatabaseSchema
3
+
4
+
5
+ class DatabaseSchemaService:
6
+ """Service für den Zugriff auf Datenbankschema-Informationen"""
7
+
8
+ def __init__(self, schema: NotionDatabaseSchema):
9
+ self._schema = schema
10
+
11
+ async def get_property_types(self) -> Dict[str, str]:
12
+ """
13
+ Get all property types for the database.
14
+
15
+ Returns:
16
+ Dictionary mapping property names to their types
17
+ """
18
+ return await self._schema.get_property_types()
19
+
20
+ async def get_select_options(self, property_name: str) -> List[Dict[str, str]]:
21
+ """
22
+ Get options for a select, multi-select, or status property.
23
+
24
+ Args:
25
+ property_name: Name of the property
26
+
27
+ Returns:
28
+ List of select options with name, id, and color (if available)
29
+ """
30
+ options = await self._schema.get_select_options(property_name)
31
+ return [
32
+ {
33
+ "name": option.get("name", ""),
34
+ "id": option.get("id", ""),
35
+ "color": option.get("color", ""),
36
+ }
37
+ for option in options
38
+ ]
39
+
40
+ async def get_relation_options(
41
+ self, property_name: str, limit: int = 100
42
+ ) -> List[Dict[str, str]]:
43
+ """
44
+ Get available options for a relation property.
45
+
46
+ Args:
47
+ property_name: Name of the relation property
48
+ limit: Maximum number of options to retrieve
49
+
50
+ Returns:
51
+ List of relation options with id and title
52
+ """
53
+ options = await self._schema.get_relation_options(property_name, limit)
54
+ return [
55
+ {"id": option.get("id", ""), "title": option.get("title", "")}
56
+ for option in options
57
+ ]
@@ -0,0 +1,10 @@
1
+ from typing import Optional, TypedDict
2
+
3
+
4
+ class PageResult(TypedDict, total=False):
5
+ """Type definition for page operation results."""
6
+
7
+ success: bool
8
+ page_id: str
9
+ url: Optional[str]
10
+ message: Optional[str]