notionary 0.1.29__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notionary/__init__.py +5 -5
- notionary/database/notion_database.py +50 -59
- notionary/database/notion_database_factory.py +16 -20
- notionary/elements/audio_element.py +1 -1
- notionary/elements/bookmark_element.py +1 -1
- notionary/elements/bulleted_list_element.py +2 -8
- notionary/elements/callout_element.py +1 -1
- notionary/elements/code_block_element.py +1 -1
- notionary/elements/divider_element.py +1 -1
- notionary/elements/embed_element.py +1 -1
- notionary/elements/heading_element.py +2 -8
- notionary/elements/image_element.py +1 -1
- notionary/elements/mention_element.py +1 -1
- notionary/elements/notion_block_element.py +1 -1
- notionary/elements/numbered_list_element.py +2 -7
- notionary/elements/paragraph_element.py +1 -1
- notionary/elements/qoute_element.py +1 -1
- notionary/elements/registry/{block_element_registry.py → block_registry.py} +70 -26
- notionary/elements/registry/{block_element_registry_builder.py → block_registry_builder.py} +48 -32
- notionary/elements/table_element.py +1 -1
- notionary/elements/text_inline_formatter.py +13 -9
- notionary/elements/todo_element.py +1 -1
- notionary/elements/toggle_element.py +1 -1
- notionary/elements/toggleable_heading_element.py +1 -1
- notionary/elements/video_element.py +1 -1
- notionary/models/notion_block_response.py +264 -0
- notionary/models/notion_database_response.py +63 -0
- notionary/models/notion_page_response.py +100 -0
- notionary/notion_client.py +38 -5
- notionary/page/content/page_content_retriever.py +68 -0
- notionary/page/content/page_content_writer.py +103 -0
- notionary/page/markdown_to_notion_converter.py +5 -5
- notionary/page/metadata/metadata_editor.py +91 -63
- notionary/page/metadata/notion_icon_manager.py +55 -28
- notionary/page/metadata/notion_page_cover_manager.py +23 -20
- notionary/page/notion_page.py +223 -218
- notionary/page/notion_page_factory.py +102 -151
- notionary/page/notion_to_markdown_converter.py +5 -5
- notionary/page/properites/database_property_service.py +11 -55
- notionary/page/properites/page_property_manager.py +44 -67
- notionary/page/properites/property_value_extractor.py +3 -3
- notionary/page/relations/notion_page_relation_manager.py +165 -213
- notionary/page/relations/notion_page_title_resolver.py +59 -41
- notionary/page/relations/page_database_relation.py +7 -9
- notionary/{elements/prompts → prompting}/element_prompt_content.py +19 -4
- notionary/prompting/markdown_syntax_prompt_generator.py +92 -0
- notionary/util/logging_mixin.py +17 -8
- notionary/util/warn_direct_constructor_usage.py +54 -0
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/METADATA +2 -1
- notionary-0.2.0.dist-info/RECORD +60 -0
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/WHEEL +1 -1
- notionary/database/database_info_service.py +0 -43
- notionary/elements/prompts/synthax_prompt_builder.py +0 -150
- notionary/page/content/page_content_manager.py +0 -211
- notionary/page/properites/property_operation_result.py +0 -116
- notionary/page/relations/relation_operation_result.py +0 -144
- notionary-0.1.29.dist-info/RECORD +0 -58
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.29.dist-info → notionary-0.2.0.dist-info}/top_level.txt +0 -0
notionary/page/notion_page.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import Any, Dict, List, Optional
|
1
3
|
import re
|
2
|
-
from typing import Any, Dict, List, Optional, Union
|
3
4
|
|
4
|
-
from notionary.elements.registry.
|
5
|
-
from notionary.elements.registry.
|
6
|
-
BlockElementRegistryBuilder,
|
7
|
-
)
|
5
|
+
from notionary.elements.registry.block_registry import BlockRegistry
|
6
|
+
from notionary.elements.registry.block_registry_builder import BlockRegistryBuilder
|
8
7
|
from notionary.notion_client import NotionClient
|
8
|
+
from notionary.page.content.page_content_retriever import PageContentRetriever
|
9
9
|
from notionary.page.metadata.metadata_editor import MetadataEditor
|
10
10
|
from notionary.page.metadata.notion_icon_manager import NotionPageIconManager
|
11
11
|
from notionary.page.metadata.notion_page_cover_manager import (
|
@@ -15,10 +15,12 @@ from notionary.page.properites.database_property_service import (
|
|
15
15
|
DatabasePropertyService,
|
16
16
|
)
|
17
17
|
from notionary.page.relations.notion_page_relation_manager import (
|
18
|
-
|
18
|
+
NotionPageRelationManager,
|
19
19
|
)
|
20
|
-
from notionary.page.content.
|
20
|
+
from notionary.page.content.page_content_writer import PageContentWriter
|
21
21
|
from notionary.page.properites.page_property_manager import PagePropertyManager
|
22
|
+
from notionary.page.relations.notion_page_title_resolver import NotionPageTitleResolver
|
23
|
+
from notionary.util.warn_direct_constructor_usage import warn_direct_constructor_usage
|
22
24
|
from notionary.util.logging_mixin import LoggingMixin
|
23
25
|
from notionary.util.page_id_utils import extract_and_validate_page_id
|
24
26
|
from notionary.page.relations.page_database_relation import PageDatabaseRelation
|
@@ -26,9 +28,10 @@ from notionary.page.relations.page_database_relation import PageDatabaseRelation
|
|
26
28
|
|
27
29
|
class NotionPage(LoggingMixin):
|
28
30
|
"""
|
29
|
-
|
31
|
+
Managing content and metadata of a Notion page.
|
30
32
|
"""
|
31
33
|
|
34
|
+
@warn_direct_constructor_usage
|
32
35
|
def __init__(
|
33
36
|
self,
|
34
37
|
page_id: Optional[str] = None,
|
@@ -44,15 +47,20 @@ class NotionPage(LoggingMixin):
|
|
44
47
|
self._title_loaded = title is not None
|
45
48
|
self._url_loaded = url is not None
|
46
49
|
|
47
|
-
self._block_element_registry = (
|
48
|
-
|
50
|
+
self._block_element_registry = BlockRegistryBuilder.create_full_registry()
|
51
|
+
|
52
|
+
self._page_content_writer = PageContentWriter(
|
53
|
+
page_id=self._page_id,
|
54
|
+
client=self._client,
|
55
|
+
block_registry=self._block_element_registry,
|
49
56
|
)
|
50
57
|
|
51
|
-
self.
|
58
|
+
self._page_content_retriever = PageContentRetriever(
|
52
59
|
page_id=self._page_id,
|
53
60
|
client=self._client,
|
54
61
|
block_registry=self._block_element_registry,
|
55
62
|
)
|
63
|
+
|
56
64
|
self._metadata = MetadataEditor(self._page_id, self._client)
|
57
65
|
self._page_cover_manager = NotionPageCoverManager(
|
58
66
|
page_id=self._page_id, client=self._client
|
@@ -66,7 +74,7 @@ class NotionPage(LoggingMixin):
|
|
66
74
|
)
|
67
75
|
self._db_property_service = None
|
68
76
|
|
69
|
-
self._relation_manager =
|
77
|
+
self._relation_manager = NotionPageRelationManager(
|
70
78
|
page_id=self._page_id, client=self._client
|
71
79
|
)
|
72
80
|
|
@@ -74,18 +82,68 @@ class NotionPage(LoggingMixin):
|
|
74
82
|
self._page_id, self._client, self._metadata, self._db_relation
|
75
83
|
)
|
76
84
|
|
85
|
+
@classmethod
|
86
|
+
def from_page_id(cls, page_id: str, token: Optional[str] = None) -> NotionPage:
|
87
|
+
"""
|
88
|
+
Create a NotionPage from a page ID.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
page_id: The ID of the Notion page
|
92
|
+
token: Optional Notion API token (uses environment variable if not provided)
|
93
|
+
|
94
|
+
Returns:
|
95
|
+
An initialized NotionPage instance
|
96
|
+
"""
|
97
|
+
from notionary.page.notion_page_factory import NotionPageFactory
|
98
|
+
|
99
|
+
cls.logger.info("Creating page from ID: %s", page_id)
|
100
|
+
return NotionPageFactory().from_page_id(page_id, token)
|
101
|
+
|
102
|
+
@classmethod
|
103
|
+
def from_url(cls, url: str, token: Optional[str] = None) -> NotionPage:
|
104
|
+
"""
|
105
|
+
Create a NotionPage from a Notion URL.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
url: The URL of the Notion page
|
109
|
+
token: Optional Notion API token (uses environment variable if not provided)
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
An initialized NotionPage instance
|
113
|
+
"""
|
114
|
+
from notionary.page.notion_page_factory import NotionPageFactory
|
115
|
+
|
116
|
+
cls.logger.info("Creating page from URL: %s", url)
|
117
|
+
return NotionPageFactory().from_url(url, token)
|
118
|
+
|
119
|
+
@classmethod
|
120
|
+
async def from_page_name(
|
121
|
+
cls, page_name: str, token: Optional[str] = None
|
122
|
+
) -> NotionPage:
|
123
|
+
"""
|
124
|
+
Create a NotionPage by finding a page with a matching name.
|
125
|
+
Uses fuzzy matching to find the closest match to the given name.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
page_name: The name of the Notion page to search for
|
129
|
+
token: Optional Notion API token (uses environment variable if not provided)
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
An initialized NotionPage instance
|
133
|
+
"""
|
134
|
+
from notionary.page.notion_page_factory import NotionPageFactory
|
135
|
+
|
136
|
+
return await NotionPageFactory().from_page_name(page_name, token)
|
137
|
+
|
77
138
|
@property
|
78
139
|
def id(self) -> str:
|
79
140
|
"""
|
80
141
|
Get the ID of the page.
|
81
|
-
|
82
|
-
Returns:
|
83
|
-
str: The page ID.
|
84
142
|
"""
|
85
143
|
return self._page_id
|
86
144
|
|
87
145
|
@property
|
88
|
-
def block_registry(self) ->
|
146
|
+
def block_registry(self) -> BlockRegistry:
|
89
147
|
"""
|
90
148
|
Get the block element registry associated with this page.
|
91
149
|
|
@@ -94,8 +152,18 @@ class NotionPage(LoggingMixin):
|
|
94
152
|
"""
|
95
153
|
return self._block_element_registry
|
96
154
|
|
155
|
+
@property
|
156
|
+
def block_registry_builder(self) -> BlockRegistryBuilder:
|
157
|
+
"""
|
158
|
+
Get the block element registry builder associated with this page.
|
159
|
+
|
160
|
+
Returns:
|
161
|
+
BlockElementRegistryBuilder: The builder for block elements.
|
162
|
+
"""
|
163
|
+
return self._block_element_registry.builder
|
164
|
+
|
97
165
|
@block_registry.setter
|
98
|
-
def block_registry(self, block_registry:
|
166
|
+
def block_registry(self, block_registry: BlockRegistry) -> None:
|
99
167
|
"""
|
100
168
|
Set the block element registry for the page content manager.
|
101
169
|
|
@@ -103,10 +171,22 @@ class NotionPage(LoggingMixin):
|
|
103
171
|
block_registry: The registry of block elements to use.
|
104
172
|
"""
|
105
173
|
self._block_element_registry = block_registry
|
106
|
-
self.
|
174
|
+
self._page_content_writer = PageContentRetriever(
|
175
|
+
page_id=self._page_id, client=self._client, block_registry=block_registry
|
176
|
+
)
|
177
|
+
self._page_content_retriever = PageContentRetriever(
|
107
178
|
page_id=self._page_id, client=self._client, block_registry=block_registry
|
108
179
|
)
|
109
180
|
|
181
|
+
def get_notion_markdown_system_prompt(self) -> str:
|
182
|
+
"""
|
183
|
+
Get the formatting prompt for the page content manager.
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
str: The formatting prompt.
|
187
|
+
"""
|
188
|
+
return self._block_element_registry.get_notion_markdown_syntax_prompt()
|
189
|
+
|
110
190
|
async def get_title(self) -> str:
|
111
191
|
"""
|
112
192
|
Get the title of the page, loading it if necessary.
|
@@ -115,9 +195,25 @@ class NotionPage(LoggingMixin):
|
|
115
195
|
str: The page title.
|
116
196
|
"""
|
117
197
|
if not self._title_loaded:
|
118
|
-
await self.
|
198
|
+
self._title = await self._fetch_page_title()
|
119
199
|
return self._title
|
120
200
|
|
201
|
+
async def set_title(self, title: str) -> Optional[Dict[str, Any]]:
|
202
|
+
"""
|
203
|
+
Set the title of the page.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
title: The new title.
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
Optional[Dict[str, Any]]: Response data from the API if successful, None otherwise.
|
210
|
+
"""
|
211
|
+
result = await self._metadata.set_title(title)
|
212
|
+
if result:
|
213
|
+
self._title = title
|
214
|
+
self._title_loaded = True
|
215
|
+
return result
|
216
|
+
|
121
217
|
async def get_url(self) -> str:
|
122
218
|
"""
|
123
219
|
Get the URL of the page, constructing it if necessary.
|
@@ -126,11 +222,11 @@ class NotionPage(LoggingMixin):
|
|
126
222
|
str: The page URL.
|
127
223
|
"""
|
128
224
|
if not self._url_loaded:
|
129
|
-
self._url = await self.
|
225
|
+
self._url = await self._generate_url_from_title()
|
130
226
|
self._url_loaded = True
|
131
227
|
return self._url
|
132
228
|
|
133
|
-
async def append_markdown(self, markdown: str, append_divider
|
229
|
+
async def append_markdown(self, markdown: str, append_divider=False) -> bool:
|
134
230
|
"""
|
135
231
|
Append markdown content to the page.
|
136
232
|
|
@@ -140,18 +236,20 @@ class NotionPage(LoggingMixin):
|
|
140
236
|
Returns:
|
141
237
|
str: Status or confirmation message.
|
142
238
|
"""
|
143
|
-
return await self.
|
239
|
+
return await self._page_content_writer.append_markdown(
|
240
|
+
markdown_text=markdown, append_divider=append_divider
|
241
|
+
)
|
144
242
|
|
145
|
-
async def
|
243
|
+
async def clear_page_content(self) -> bool:
|
146
244
|
"""
|
147
245
|
Clear all content from the page.
|
148
246
|
|
149
247
|
Returns:
|
150
248
|
str: Status or confirmation message.
|
151
249
|
"""
|
152
|
-
return await self.
|
250
|
+
return await self._page_content_writer.clear_page_content()
|
153
251
|
|
154
|
-
async def replace_content(self, markdown: str) ->
|
252
|
+
async def replace_content(self, markdown: str) -> bool:
|
155
253
|
"""
|
156
254
|
Replace the entire page content with new markdown content.
|
157
255
|
|
@@ -161,59 +259,58 @@ class NotionPage(LoggingMixin):
|
|
161
259
|
Returns:
|
162
260
|
str: Status or confirmation message.
|
163
261
|
"""
|
164
|
-
await self.
|
165
|
-
|
262
|
+
clear_result = await self._page_content_writer.clear_page_content()
|
263
|
+
if not clear_result:
|
264
|
+
self.logger.error("Failed to clear page content before replacement")
|
265
|
+
return False
|
266
|
+
|
267
|
+
return await self._page_content_writer.append_markdown(
|
268
|
+
markdown_text=markdown, append_divider=False
|
269
|
+
)
|
166
270
|
|
167
|
-
async def
|
271
|
+
async def get_text_content(self) -> str:
|
168
272
|
"""
|
169
273
|
Get the text content of the page.
|
170
274
|
|
171
275
|
Returns:
|
172
276
|
str: The text content of the page.
|
173
277
|
"""
|
174
|
-
return await self.
|
278
|
+
return await self._page_content_retriever.get_page_content()
|
175
279
|
|
176
|
-
async def
|
280
|
+
async def get_icon(self) -> str:
|
177
281
|
"""
|
178
|
-
|
179
|
-
|
180
|
-
Args:
|
181
|
-
title: The new title.
|
282
|
+
Retrieve the page icon - either emoji or external URL.
|
182
283
|
|
183
284
|
Returns:
|
184
|
-
Optional[
|
285
|
+
Optional[str]: The icon emoji or URL, or None if no icon is set.
|
185
286
|
"""
|
186
|
-
|
187
|
-
if result:
|
188
|
-
self._title = title
|
189
|
-
self._title_loaded = True
|
190
|
-
return result
|
287
|
+
return await self._page_icon_manager.get_icon()
|
191
288
|
|
192
|
-
async def
|
193
|
-
self, emoji: Optional[str] = None, external_url: Optional[str] = None
|
194
|
-
) -> Optional[Dict[str, Any]]:
|
289
|
+
async def set_emoji_icon(self, emoji: str) -> Optional[str]:
|
195
290
|
"""
|
196
|
-
|
291
|
+
Sets the page icon to an emoji.
|
197
292
|
|
198
293
|
Args:
|
199
|
-
emoji:
|
200
|
-
external_url: Optional URL to an external image to use as icon.
|
294
|
+
emoji (str): The emoji character to set as the icon.
|
201
295
|
|
202
296
|
Returns:
|
203
297
|
Optional[Dict[str, Any]]: Response data from the API if successful, None otherwise.
|
204
298
|
"""
|
205
|
-
return await self._page_icon_manager.
|
299
|
+
return await self._page_icon_manager.set_emoji_icon(emoji=emoji)
|
206
300
|
|
207
|
-
async def
|
301
|
+
async def set_external_icon(self, url: str) -> Optional[str]:
|
208
302
|
"""
|
209
|
-
|
303
|
+
Sets the page icon to an external image.
|
304
|
+
|
305
|
+
Args:
|
306
|
+
url (str): The URL of the external image to set as the icon.
|
210
307
|
|
211
308
|
Returns:
|
212
|
-
Optional[str]:
|
309
|
+
Optional[Dict[str, Any]]: Response data from the API if successful, None otherwise.
|
213
310
|
"""
|
214
|
-
return await self._page_icon_manager.
|
311
|
+
return await self._page_icon_manager.set_external_icon(external_icon_url=url)
|
215
312
|
|
216
|
-
async def get_cover_url(self) -> str:
|
313
|
+
async def get_cover_url(self) -> Optional[str]:
|
217
314
|
"""
|
218
315
|
Get the URL of the page cover image.
|
219
316
|
|
@@ -222,7 +319,7 @@ class NotionPage(LoggingMixin):
|
|
222
319
|
"""
|
223
320
|
return await self._page_cover_manager.get_cover_url()
|
224
321
|
|
225
|
-
async def
|
322
|
+
async def set_cover(self, external_url: str) -> Optional[str]:
|
226
323
|
"""
|
227
324
|
Set the cover image for the page using an external URL.
|
228
325
|
|
@@ -243,16 +340,7 @@ class NotionPage(LoggingMixin):
|
|
243
340
|
"""
|
244
341
|
return await self._page_cover_manager.set_random_gradient_cover()
|
245
342
|
|
246
|
-
async def
|
247
|
-
"""
|
248
|
-
Retrieve all properties of the page.
|
249
|
-
|
250
|
-
Returns:
|
251
|
-
Dict[str, Any]: Dictionary of property names and their values.
|
252
|
-
"""
|
253
|
-
return await self._property_manager.get_properties()
|
254
|
-
|
255
|
-
async def get_property_value(self, property_name: str) -> Any:
|
343
|
+
async def get_property_value_by_name(self, property_name: str) -> Any:
|
256
344
|
"""
|
257
345
|
Get the value of a specific property.
|
258
346
|
|
@@ -262,126 +350,86 @@ class NotionPage(LoggingMixin):
|
|
262
350
|
Returns:
|
263
351
|
Any: The value of the property.
|
264
352
|
"""
|
265
|
-
|
266
|
-
property_name, self._relation_manager.get_relation_values
|
267
|
-
)
|
353
|
+
properties = await self._property_manager._get_properties()
|
268
354
|
|
269
|
-
|
270
|
-
|
271
|
-
) -> Optional[Dict[str, Any]]:
|
272
|
-
"""
|
273
|
-
Set the value of a specific property by its name.
|
274
|
-
|
275
|
-
Args:
|
276
|
-
property_name: The name of the property.
|
277
|
-
value: The new value to set.
|
278
|
-
|
279
|
-
Returns:
|
280
|
-
Optional[Dict[str, Any]]: Response data from the API if successful, None otherwise.
|
281
|
-
"""
|
282
|
-
return await self._property_manager.set_property_by_name(
|
283
|
-
property_name=property_name,
|
284
|
-
value=value,
|
285
|
-
)
|
286
|
-
|
287
|
-
async def is_database_page(self) -> bool:
|
288
|
-
"""
|
289
|
-
Check if this page belongs to a database.
|
355
|
+
if property_name not in properties:
|
356
|
+
return None
|
290
357
|
|
291
|
-
|
292
|
-
|
293
|
-
"""
|
294
|
-
return await self._db_relation.is_database_page()
|
358
|
+
prop_data = properties[property_name]
|
359
|
+
prop_type = prop_data.get("type")
|
295
360
|
|
296
|
-
|
297
|
-
|
298
|
-
Get the ID of the database this page belongs to, if any.
|
361
|
+
if prop_type == "relation":
|
362
|
+
return await self._relation_manager.get_relation_values(property_name)
|
299
363
|
|
300
|
-
|
301
|
-
Optional[str]: The database ID or None if the page doesn't belong to a database.
|
302
|
-
"""
|
303
|
-
return await self._db_relation.get_parent_database_id()
|
364
|
+
return await self._property_manager.get_property_value(property_name)
|
304
365
|
|
305
|
-
async def
|
366
|
+
async def get_options_for_property(
|
367
|
+
self, property_name: str, limit: int = 100
|
368
|
+
) -> List[str]:
|
306
369
|
"""
|
307
|
-
Get the available
|
370
|
+
Get the available options for a property (select, multi_select, status, relation).
|
308
371
|
|
309
372
|
Args:
|
310
373
|
property_name: The name of the property.
|
374
|
+
limit: Maximum number of options to return (only affects relation properties).
|
311
375
|
|
312
376
|
Returns:
|
313
|
-
List[str]: List of available option names.
|
377
|
+
List[str]: List of available option names or page titles.
|
314
378
|
"""
|
315
|
-
|
316
|
-
if db_service:
|
317
|
-
return await db_service.get_option_names(property_name)
|
318
|
-
return []
|
379
|
+
property_type = await self._get_property_type(property_name)
|
319
380
|
|
320
|
-
|
321
|
-
|
322
|
-
Get the type of a specific property.
|
381
|
+
if property_type is None:
|
382
|
+
return []
|
323
383
|
|
324
|
-
|
325
|
-
|
384
|
+
if property_type == "relation":
|
385
|
+
return await self._relation_manager.get_relation_options(
|
386
|
+
property_name, limit
|
387
|
+
)
|
326
388
|
|
327
|
-
Returns:
|
328
|
-
Optional[str]: The type of the property or None if not found.
|
329
|
-
"""
|
330
389
|
db_service = await self._get_db_property_service()
|
331
390
|
if db_service:
|
332
|
-
return await db_service.
|
333
|
-
return None
|
334
|
-
|
335
|
-
async def get_database_metadata(
|
336
|
-
self, include_types: Optional[List[str]] = None
|
337
|
-
) -> Dict[str, Any]:
|
338
|
-
"""
|
339
|
-
Get complete metadata about the database this page belongs to.
|
340
|
-
|
341
|
-
Args:
|
342
|
-
include_types: Optional list of property types to include. If None, all properties are included.
|
391
|
+
return await db_service.get_option_names(property_name)
|
343
392
|
|
344
|
-
|
345
|
-
Dict[str, Any]: Database metadata or empty dict if not a database page.
|
346
|
-
"""
|
347
|
-
db_service = await self._get_db_property_service()
|
348
|
-
if db_service:
|
349
|
-
return await db_service.get_database_metadata(include_types)
|
350
|
-
return {"properties": {}}
|
393
|
+
return []
|
351
394
|
|
352
|
-
async def
|
353
|
-
self, property_name: str,
|
354
|
-
) ->
|
395
|
+
async def set_property_value_by_name(
|
396
|
+
self, property_name: str, value: Any
|
397
|
+
) -> Optional[Dict[str, Any]]:
|
355
398
|
"""
|
356
|
-
|
399
|
+
Set the value of a specific property by its name.
|
357
400
|
|
358
401
|
Args:
|
359
|
-
property_name: The name of the
|
360
|
-
|
402
|
+
property_name: The name of the property.
|
403
|
+
value: The new value to set.
|
361
404
|
|
362
405
|
Returns:
|
363
|
-
|
406
|
+
Optional[Dict[str, Any]]: Response data from the API if successful, None otherwise.
|
364
407
|
"""
|
365
|
-
return await self.
|
408
|
+
return await self._property_manager.set_property_by_name(
|
409
|
+
property_name=property_name,
|
410
|
+
value=value,
|
411
|
+
)
|
366
412
|
|
367
|
-
async def
|
368
|
-
self, relation_property_name: str, page_titles:
|
369
|
-
) ->
|
413
|
+
async def set_relation_property_values_by_name(
|
414
|
+
self, relation_property_name: str, page_titles: List[str]
|
415
|
+
) -> List[str]:
|
370
416
|
"""
|
371
417
|
Add one or more relations to a relation property.
|
372
418
|
|
373
419
|
Args:
|
374
420
|
relation_property_name: The name of the relation property.
|
375
|
-
page_titles:
|
421
|
+
page_titles: A list of page titles to relate to.
|
376
422
|
|
377
423
|
Returns:
|
378
424
|
Optional[Dict[str, Any]]: Response data from the API if successful, None otherwise.
|
379
425
|
"""
|
380
|
-
return await self._relation_manager.
|
426
|
+
return await self._relation_manager.set_relation_values_by_page_titles(
|
381
427
|
property_name=relation_property_name, page_titles=page_titles
|
382
428
|
)
|
383
429
|
|
384
|
-
async def
|
430
|
+
async def get_relation_property_values_by_name(
|
431
|
+
self, property_name: str
|
432
|
+
) -> List[str]:
|
385
433
|
"""
|
386
434
|
Return the current relation values for a property.
|
387
435
|
|
@@ -393,25 +441,7 @@ class NotionPage(LoggingMixin):
|
|
393
441
|
"""
|
394
442
|
return await self._relation_manager.get_relation_values(property_name)
|
395
443
|
|
396
|
-
async def
|
397
|
-
"""
|
398
|
-
Return a list of all relation property names.
|
399
|
-
|
400
|
-
Returns:
|
401
|
-
List[str]: List of relation property names.
|
402
|
-
"""
|
403
|
-
return await self._relation_manager.get_relation_property_ids()
|
404
|
-
|
405
|
-
async def get_all_relations(self) -> Dict[str, List[str]]:
|
406
|
-
"""
|
407
|
-
Return all relation properties and their values.
|
408
|
-
|
409
|
-
Returns:
|
410
|
-
Dict[str, List[str]]: Dictionary mapping relation property names to their values.
|
411
|
-
"""
|
412
|
-
return await self._relation_manager.get_all_relations()
|
413
|
-
|
414
|
-
async def get_last_edited_time(self) -> str:
|
444
|
+
async def get_last_edit_time(self) -> str:
|
415
445
|
"""
|
416
446
|
Get the timestamp when the page was last edited.
|
417
447
|
|
@@ -419,78 +449,35 @@ class NotionPage(LoggingMixin):
|
|
419
449
|
str: ISO 8601 formatted timestamp string of when the page was last edited.
|
420
450
|
"""
|
421
451
|
try:
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
self.logger.warning("last_edited_time not found in page data")
|
427
|
-
return ""
|
452
|
+
page_response = await self._client.get_page(self._page_id)
|
453
|
+
return (
|
454
|
+
page_response.last_edited_time if page_response.last_edited_by else ""
|
455
|
+
)
|
428
456
|
|
429
457
|
except Exception as e:
|
430
458
|
self.logger.error("Error retrieving last edited time: %s", str(e))
|
431
459
|
return ""
|
432
460
|
|
433
|
-
async def
|
461
|
+
async def _fetch_page_title(self) -> str:
|
434
462
|
"""
|
435
463
|
Load the page title from Notion API if not already loaded.
|
436
464
|
|
437
465
|
Returns:
|
438
466
|
str: The page title.
|
439
467
|
"""
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
try:
|
445
|
-
# Retrieve page data
|
446
|
-
page_data = await self._client.get(f"pages/{self._page_id}")
|
447
|
-
self._title = self._extract_title_from_page_data(page_data)
|
448
|
-
except Exception as e:
|
449
|
-
self.logger.error("Error loading page title: %s", str(e))
|
450
|
-
self._title = "Untitled"
|
451
|
-
|
452
|
-
self._title_loaded = True
|
453
|
-
self.logger.debug("Loaded page title: %s", self._title)
|
454
|
-
return self._title
|
455
|
-
|
456
|
-
def _extract_title_from_page_data(self, page_data: Dict[str, Any]) -> str:
|
457
|
-
"""
|
458
|
-
Extract title from page data.
|
459
|
-
|
460
|
-
Args:
|
461
|
-
page_data: The page data from Notion API
|
462
|
-
|
463
|
-
Returns:
|
464
|
-
str: The extracted title or "Untitled" if not found
|
465
|
-
"""
|
466
|
-
if "properties" not in page_data:
|
467
|
-
return "Untitled"
|
468
|
-
|
469
|
-
for prop_value in page_data["properties"].values():
|
470
|
-
if prop_value.get("type") != "title":
|
471
|
-
continue
|
472
|
-
|
473
|
-
title_array = prop_value.get("title", [])
|
474
|
-
if not title_array:
|
475
|
-
continue
|
476
|
-
|
477
|
-
text_parts = []
|
478
|
-
for text_obj in title_array:
|
479
|
-
if "plain_text" in text_obj:
|
480
|
-
text_parts.append(text_obj["plain_text"])
|
481
|
-
|
482
|
-
return "".join(text_parts) or "Untitled"
|
483
|
-
|
484
|
-
return "Untitled"
|
468
|
+
notion_page_title_resolver = NotionPageTitleResolver(self._client)
|
469
|
+
return await notion_page_title_resolver.get_title_by_page_id(
|
470
|
+
page_id=self._page_id
|
471
|
+
)
|
485
472
|
|
486
|
-
async def
|
473
|
+
async def _generate_url_from_title(self) -> str:
|
487
474
|
"""
|
488
475
|
Build a Notion URL from the page ID, including the title if available.
|
489
476
|
|
490
477
|
Returns:
|
491
478
|
str: The Notion URL for the page.
|
492
479
|
"""
|
493
|
-
title = await self.
|
480
|
+
title = await self._fetch_page_title()
|
494
481
|
|
495
482
|
url_title = ""
|
496
483
|
if title and title != "Untitled":
|
@@ -520,3 +507,21 @@ class NotionPage(LoggingMixin):
|
|
520
507
|
self._db_property_service = DatabasePropertyService(database_id, self._client)
|
521
508
|
await self._db_property_service.load_schema()
|
522
509
|
return self._db_property_service
|
510
|
+
|
511
|
+
async def _get_property_type(self, property_name: str) -> Optional[str]:
|
512
|
+
"""
|
513
|
+
Get the type of a specific property.
|
514
|
+
|
515
|
+
Args:
|
516
|
+
property_name: The name of the property.
|
517
|
+
|
518
|
+
Returns:
|
519
|
+
Optional[str]: The type of the property, or None if not found.
|
520
|
+
"""
|
521
|
+
properties = await self._property_manager._get_properties()
|
522
|
+
|
523
|
+
if property_name not in properties:
|
524
|
+
return None
|
525
|
+
|
526
|
+
prop_data = properties[property_name]
|
527
|
+
return prop_data.get("type")
|