notionary 0.1.29__py3-none-any.whl → 0.2.1__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 (59) hide show
  1. notionary/__init__.py +5 -5
  2. notionary/database/notion_database.py +50 -59
  3. notionary/database/notion_database_factory.py +16 -20
  4. notionary/elements/audio_element.py +1 -1
  5. notionary/elements/bookmark_element.py +1 -1
  6. notionary/elements/bulleted_list_element.py +2 -8
  7. notionary/elements/callout_element.py +1 -1
  8. notionary/elements/code_block_element.py +1 -1
  9. notionary/elements/divider_element.py +1 -1
  10. notionary/elements/embed_element.py +1 -1
  11. notionary/elements/heading_element.py +2 -8
  12. notionary/elements/image_element.py +1 -1
  13. notionary/elements/mention_element.py +1 -1
  14. notionary/elements/notion_block_element.py +1 -1
  15. notionary/elements/numbered_list_element.py +2 -7
  16. notionary/elements/paragraph_element.py +1 -1
  17. notionary/elements/qoute_element.py +1 -1
  18. notionary/elements/registry/{block_element_registry.py → block_registry.py} +70 -26
  19. notionary/elements/registry/{block_element_registry_builder.py → block_registry_builder.py} +48 -32
  20. notionary/elements/table_element.py +1 -1
  21. notionary/elements/text_inline_formatter.py +13 -9
  22. notionary/elements/todo_element.py +1 -1
  23. notionary/elements/toggle_element.py +1 -1
  24. notionary/elements/toggleable_heading_element.py +1 -1
  25. notionary/elements/video_element.py +1 -1
  26. notionary/models/notion_block_response.py +264 -0
  27. notionary/models/notion_database_response.py +63 -0
  28. notionary/models/notion_page_response.py +100 -0
  29. notionary/notion_client.py +38 -5
  30. notionary/page/content/page_content_retriever.py +68 -0
  31. notionary/page/content/page_content_writer.py +103 -0
  32. notionary/page/markdown_to_notion_converter.py +5 -5
  33. notionary/page/metadata/metadata_editor.py +91 -63
  34. notionary/page/metadata/notion_icon_manager.py +55 -28
  35. notionary/page/metadata/notion_page_cover_manager.py +23 -20
  36. notionary/page/notion_page.py +223 -218
  37. notionary/page/notion_page_factory.py +102 -151
  38. notionary/page/notion_to_markdown_converter.py +5 -5
  39. notionary/page/properites/database_property_service.py +11 -55
  40. notionary/page/properites/page_property_manager.py +44 -67
  41. notionary/page/properites/property_value_extractor.py +3 -3
  42. notionary/page/relations/notion_page_relation_manager.py +165 -213
  43. notionary/page/relations/notion_page_title_resolver.py +59 -41
  44. notionary/page/relations/page_database_relation.py +7 -9
  45. notionary/{elements/prompts → prompting}/element_prompt_content.py +19 -4
  46. notionary/prompting/markdown_syntax_prompt_generator.py +92 -0
  47. notionary/util/logging_mixin.py +17 -8
  48. notionary/util/warn_direct_constructor_usage.py +54 -0
  49. {notionary-0.1.29.dist-info → notionary-0.2.1.dist-info}/METADATA +2 -1
  50. notionary-0.2.1.dist-info/RECORD +60 -0
  51. {notionary-0.1.29.dist-info → notionary-0.2.1.dist-info}/WHEEL +1 -1
  52. notionary/database/database_info_service.py +0 -43
  53. notionary/elements/prompts/synthax_prompt_builder.py +0 -150
  54. notionary/page/content/page_content_manager.py +0 -211
  55. notionary/page/properites/property_operation_result.py +0 -116
  56. notionary/page/relations/relation_operation_result.py +0 -144
  57. notionary-0.1.29.dist-info/RECORD +0 -58
  58. {notionary-0.1.29.dist-info → notionary-0.2.1.dist-info}/licenses/LICENSE +0 -0
  59. {notionary-0.1.29.dist-info → notionary-0.2.1.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,14 @@
1
+ import asyncio
1
2
  from typing import Any, Dict, List, Optional
3
+ from notionary.models.notion_page_response import DatabaseParent, NotionPageResponse
2
4
  from notionary.notion_client import NotionClient
3
5
  from notionary.page.relations.notion_page_title_resolver import (
4
6
  NotionPageTitleResolver,
5
7
  )
6
- from notionary.page.relations.relation_operation_result import (
7
- RelationOperationResult,
8
- )
9
8
  from notionary.util.logging_mixin import LoggingMixin
10
- from notionary.util.page_id_utils import is_valid_uuid
11
9
 
12
10
 
13
- class NotionRelationManager(LoggingMixin):
11
+ class NotionPageRelationManager(LoggingMixin):
14
12
  """
15
13
  Manager for relation properties of a Notion page.
16
14
  Manages links between pages and loads available relation options.
@@ -21,11 +19,6 @@ class NotionRelationManager(LoggingMixin):
21
19
  ):
22
20
  """
23
21
  Initializes the relation manager.
24
-
25
- Args:
26
- page_id: ID of the Notion page
27
- client: NotionClient instance
28
- database_id: Optional, ID of the database the page belongs to (loaded if needed)
29
22
  """
30
23
  self._page_id = page_id
31
24
  self._client = client
@@ -34,44 +27,6 @@ class NotionRelationManager(LoggingMixin):
34
27
 
35
28
  self._page_title_resolver = NotionPageTitleResolver(client=client)
36
29
 
37
- async def _get_page_properties(self, force_refresh: bool = False) -> Dict[str, Any]:
38
- """
39
- Loads the properties of the page.
40
-
41
- Args:
42
- force_refresh: If True, a fresh API call will be made
43
-
44
- Returns:
45
- Dict[str, Any]: The properties of the page
46
- """
47
- if self._page_properties is None or force_refresh:
48
- page_data = await self._client.get_page(self._page_id)
49
- if page_data and "properties" in page_data:
50
- self._page_properties = page_data["properties"]
51
- else:
52
- self._page_properties = {}
53
-
54
- return self._page_properties
55
-
56
- async def _ensure_database_id(self) -> Optional[str]:
57
- """
58
- Ensures the database_id is available. Loads it if necessary.
59
-
60
- Returns:
61
- Optional[str]: The database ID or None
62
- """
63
- if self._database_id:
64
- return self._database_id
65
-
66
- page_data = await self._client.get_page(self._page_id)
67
- if page_data and "parent" in page_data:
68
- parent = page_data["parent"]
69
- if parent.get("type") == "database_id":
70
- self._database_id = parent.get("database_id")
71
- return self._database_id
72
-
73
- return None
74
-
75
30
  async def get_relation_property_ids(self) -> List[str]:
76
31
  """
77
32
  Returns a list of all relation property names.
@@ -123,43 +78,6 @@ class NotionRelationManager(LoggingMixin):
123
78
 
124
79
  return titles
125
80
 
126
- async def get_relation_details(
127
- self, property_name: str
128
- ) -> Optional[Dict[str, Any]]:
129
- """
130
- Returns details about the relation property, including the linked database.
131
-
132
- Args:
133
- property_name: Name of the relation property
134
-
135
- Returns:
136
- Optional[Dict[str, Any]]: Relation details or None
137
- """
138
- database_id = await self._ensure_database_id()
139
- if not database_id:
140
- return None
141
-
142
- try:
143
- database = await self._client.get(f"databases/{database_id}")
144
- if not database or "properties" not in database:
145
- return None
146
-
147
- properties = database["properties"]
148
-
149
- if property_name not in properties:
150
- return None
151
-
152
- prop_data = properties[property_name]
153
-
154
- if prop_data.get("type") != "relation":
155
- return None
156
-
157
- return prop_data.get("relation", {})
158
-
159
- except Exception as e:
160
- self.logger.error("Error retrieving relation details: %s", str(e))
161
- return None
162
-
163
81
  async def get_relation_database_id(self, property_name: str) -> Optional[str]:
164
82
  """
165
83
  Returns the ID of the linked database for a relation property.
@@ -170,7 +88,7 @@ class NotionRelationManager(LoggingMixin):
170
88
  Returns:
171
89
  Optional[str]: ID of the linked database or None
172
90
  """
173
- relation_details = await self.get_relation_details(property_name)
91
+ relation_details = await self._get_relation_details(property_name)
174
92
 
175
93
  if not relation_details:
176
94
  return None
@@ -179,16 +97,16 @@ class NotionRelationManager(LoggingMixin):
179
97
 
180
98
  async def get_relation_options(
181
99
  self, property_name: str, limit: int = 100
182
- ) -> List[Dict[str, Any]]:
100
+ ) -> List[str]:
183
101
  """
184
- Returns available options for a relation property.
102
+ Returns available title options for a relation property.
185
103
 
186
104
  Args:
187
105
  property_name: Name of the relation property
188
106
  limit: Maximum number of options to return
189
107
 
190
108
  Returns:
191
- List[Dict[str, Any]]: List of available options with ID and name
109
+ List[str]: List of page titles that can be used for this relation
192
110
  """
193
111
  related_db_id = await self.get_relation_database_id(property_name)
194
112
 
@@ -206,193 +124,227 @@ class NotionRelationManager(LoggingMixin):
206
124
  if not query_result or "results" not in query_result:
207
125
  return []
208
126
 
209
- options = []
127
+ titles = []
210
128
  for page in query_result["results"]:
211
- page_id = page.get("id")
212
129
  title = self._extract_title_from_page(page)
130
+ if title:
131
+ titles.append(title)
213
132
 
214
- if page_id and title:
215
- options.append({"id": page_id, "name": title})
216
-
217
- return options
133
+ return titles
218
134
  except Exception as e:
219
135
  self.logger.error("Error retrieving relation options: %s", str(e))
220
136
  return []
221
137
 
222
- def _extract_title_from_page(self, page: Dict[str, Any]) -> Optional[str]:
138
+ async def set_relation_values_by_page_titles(
139
+ self, property_name: str, page_titles: List[str]
140
+ ) -> List[str]:
223
141
  """
224
- Extracts the title from a page object.
142
+ Sets relation values based on page titles, replacing any existing relations.
225
143
 
226
144
  Args:
227
- page: The page object from the Notion API
145
+ property_name: Name of the relation property
146
+ page_titles: List of page titles to set as relations
228
147
 
229
148
  Returns:
230
- Optional[str]: The page title or None
149
+ List[str]: List of page titles that were successfully set as relations
231
150
  """
232
- if "properties" not in page:
233
- return None
151
+ self.logger.info(
152
+ "Setting %d relation(s) for property '%s'",
153
+ len(page_titles),
154
+ property_name,
155
+ )
234
156
 
235
- properties = page["properties"]
157
+ resolution_results = await asyncio.gather(
158
+ *(
159
+ self._page_title_resolver.get_page_id_by_title(title)
160
+ for title in page_titles
161
+ )
162
+ )
236
163
 
237
- for prop_data in properties.values():
238
- if prop_data.get("type") == "title" and "title" in prop_data:
239
- title_parts = prop_data["title"]
240
- return "".join(
241
- [text_obj.get("plain_text", "") for text_obj in title_parts]
242
- )
164
+ found_pages = []
165
+ page_ids = []
166
+ not_found_pages = []
243
167
 
244
- return None
168
+ for title, page_id in zip(page_titles, resolution_results):
169
+ if page_id:
170
+ found_pages.append(title)
171
+ page_ids.append(page_id)
172
+ self.logger.debug("Found page ID %s for title '%s'", page_id, title)
173
+ else:
174
+ not_found_pages.append(title)
175
+ self.logger.warning("No page found with title '%s'", title)
245
176
 
246
- async def add_relation(
247
- self, property_name: str, page_ids: List[str]
177
+ self.logger.debug("Page IDs being sent to API: %s", page_ids)
178
+
179
+ if not page_ids:
180
+ self.logger.warning(
181
+ "No valid page IDs found for any of the titles, no changes applied"
182
+ )
183
+ return []
184
+
185
+ api_response = await self._set_relations_by_page_ids(property_name, page_ids)
186
+
187
+ if not api_response:
188
+ self.logger.error(
189
+ "Failed to set relations for '%s' (API error)", property_name
190
+ )
191
+ return []
192
+
193
+ if not_found_pages:
194
+ not_found_str = "', '".join(not_found_pages)
195
+ self.logger.info(
196
+ "Set %d relation(s) for '%s', but couldn't find pages: '%s'",
197
+ len(page_ids),
198
+ property_name,
199
+ not_found_str,
200
+ )
201
+ else:
202
+ self.logger.info(
203
+ "Successfully set all %d relation(s) for '%s'",
204
+ len(page_ids),
205
+ property_name,
206
+ )
207
+
208
+ return found_pages
209
+
210
+ async def get_all_relations(self) -> Dict[str, List[str]]:
211
+ """
212
+ Returns all relation properties and their values.
213
+
214
+ Returns:
215
+ Dict[str, List[str]]: Dictionary of property names and their values
216
+ """
217
+ relation_properties = await self.get_relation_property_ids()
218
+
219
+ if not relation_properties:
220
+ return {}
221
+
222
+ result = {}
223
+ for prop_name in relation_properties:
224
+ result[prop_name] = await self.get_relation_values(prop_name)
225
+
226
+ return result
227
+
228
+ async def _get_relation_details(
229
+ self, property_name: str
248
230
  ) -> Optional[Dict[str, Any]]:
249
231
  """
250
- Adds one or more relations.
232
+ Returns details about the relation property, including the linked database.
251
233
 
252
234
  Args:
253
235
  property_name: Name of the relation property
254
- page_ids: List of page IDs to add
255
236
 
256
237
  Returns:
257
- Optional[Dict[str, Any]]: API response or None on error
238
+ The "relation" field of the property, or None if not found or not of type "relation".
258
239
  """
259
- existing_relations = await self.get_relation_values(property_name) or []
240
+ database_id = await self._ensure_database_id()
241
+ if not database_id:
242
+ return None
260
243
 
261
- all_relations = list(set(existing_relations + page_ids))
244
+ try:
245
+ database = await self._client.get_database(database_id)
262
246
 
263
- relation_payload = {"relation": [{"id": page_id} for page_id in all_relations]}
247
+ prop_data = database.properties.get(property_name)
248
+ if not prop_data:
249
+ return None
264
250
 
265
- try:
266
- result = await self._client.patch(
267
- f"pages/{self._page_id}",
268
- {"properties": {property_name: relation_payload}},
269
- )
251
+ if prop_data.get("type") != "relation":
252
+ return None
270
253
 
271
- self._page_properties = None
254
+ return prop_data.get("relation")
272
255
 
273
- return result
274
256
  except Exception as e:
275
- self.logger.error("Error adding relation: %s", str(e))
257
+ self.logger.error("Error retrieving relation details: %s", str(e))
276
258
  return None
277
259
 
278
- async def add_relation_by_name(
279
- self, property_name: str, page_titles: List[str]
280
- ) -> RelationOperationResult:
260
+ async def _get_page_properties(self, force_refresh: bool = False) -> Dict[str, Any]:
281
261
  """
282
- Adds one or more relations based on page titles.
262
+ Loads the properties of the page.
283
263
 
284
264
  Args:
285
- property_name: Name of the relation property
286
- page_titles: List of page titles to link
265
+ force_refresh: If True, a new API call will be made.
287
266
 
288
267
  Returns:
289
- RelationOperationResult: Result of the operation with details on which pages were found and added
268
+ Dict[str, Any]: The properties of the page.
290
269
  """
291
- found_pages = []
292
- not_found_pages = []
293
- page_ids = []
270
+ if self._page_properties is None or force_refresh:
271
+ page_data = await self._client.get_page(self._page_id)
272
+ if page_data:
273
+ self._page_properties = page_data.properties or {}
274
+ else:
275
+ self._page_properties = {}
294
276
 
295
- self.logger.info(
296
- "Attempting to add %d relation(s) to property '%s'",
297
- len(page_titles),
298
- property_name,
299
- )
277
+ return self._page_properties
300
278
 
301
- for page in page_titles:
302
- if is_valid_uuid(page):
303
- page_ids.append(page)
304
- found_pages.append(page)
305
- self.logger.debug("Using page ID directly: %s", page)
306
- else:
307
- page_id = await self._page_title_resolver.get_page_id_by_title(page)
308
- if page_id:
309
- page_ids.append(page_id)
310
- found_pages.append(page)
311
- self.logger.debug("Found page ID %s for title '%s'", page_id, page)
312
- else:
313
- not_found_pages.append(page)
314
- self.logger.warning("No page found with title '%s'", page)
279
+ async def _ensure_database_id(self) -> Optional[str]:
280
+ """
281
+ Ensures the database_id is available. Loads it if necessary.
315
282
 
316
- if not page_ids:
317
- self.logger.warning(
318
- "No valid page IDs found for any of the titles, no changes applied"
319
- )
320
- return RelationOperationResult.from_no_pages_found(
321
- property_name, not_found_pages
322
- )
283
+ Returns:
284
+ Optional[str]: The database ID or None
285
+ """
286
+ if self._database_id:
287
+ return self._database_id
323
288
 
324
- api_response = await self.add_relation(property_name, page_ids)
289
+ page_data = await self._client.get_page(self._page_id)
325
290
 
326
- if api_response:
327
- result = RelationOperationResult.from_success(
328
- property_name=property_name,
329
- found_pages=found_pages,
330
- not_found_pages=not_found_pages,
331
- page_ids_added=page_ids,
332
- api_response=api_response,
333
- )
291
+ if not page_data or not page_data.parent:
292
+ return None
334
293
 
335
- if not_found_pages:
336
- not_found_str = "', '".join(not_found_pages)
337
- self.logger.info(
338
- "Added %d relation(s) to '%s', but couldn't find pages: '%s'",
339
- len(page_ids),
340
- property_name,
341
- not_found_str,
342
- )
343
- else:
344
- self.logger.info(
345
- "Successfully added all %d relation(s) to '%s'",
346
- len(page_ids),
347
- property_name,
348
- )
294
+ if isinstance(page_data.parent, DatabaseParent):
295
+ self._database_id = page_data.parent.database_id
296
+ return self._database_id
349
297
 
350
- return result
298
+ return None
351
299
 
352
- self.logger.error("Failed to add relations to '%s' (API error)", property_name)
353
- return RelationOperationResult.from_no_api_response(
354
- property_name=property_name,
355
- found_pages=found_pages,
356
- page_ids_added=page_ids,
357
- )
300
+ def _extract_title_from_page(self, page: Dict[str, Any]) -> Optional[str]:
301
+ """
302
+ Extracts the title from a page object.
358
303
 
359
- async def set_relations(
304
+ Args:
305
+ page: The page object from the Notion API
306
+
307
+ Returns:
308
+ Optional[str]: The page title or None
309
+ """
310
+ if "properties" not in page:
311
+ return None
312
+
313
+ properties = page["properties"]
314
+
315
+ for prop_data in properties.values():
316
+ if prop_data.get("type") == "title" and "title" in prop_data:
317
+ title_parts = prop_data["title"]
318
+ return "".join(
319
+ [text_obj.get("plain_text", "") for text_obj in title_parts]
320
+ )
321
+
322
+ return None
323
+
324
+ async def _set_relations_by_page_ids(
360
325
  self, property_name: str, page_ids: List[str]
361
- ) -> Optional[Dict[str, Any]]:
326
+ ) -> Optional[NotionPageResponse]:
362
327
  """
363
- Sets the relations to the specified IDs (replaces existing ones).
328
+ Adds one or more relations.
364
329
 
365
330
  Args:
366
331
  property_name: Name of the relation property
367
- page_ids: List of page IDs to set
332
+ page_ids: List of page IDs to add
368
333
 
369
334
  Returns:
370
- Optional[Dict[str, Any]]: API response or None on error
335
+ Optional[NotionPageResponse]: API response or None on error
371
336
  """
372
337
  relation_payload = {"relation": [{"id": page_id} for page_id in page_ids]}
373
338
 
374
339
  try:
375
- result = await self._client.patch(
376
- f"pages/{self._page_id}",
340
+ page_response: NotionPageResponse = await self._client.patch_page(
341
+ self._page_id,
377
342
  {"properties": {property_name: relation_payload}},
378
343
  )
379
344
 
380
345
  self._page_properties = None
381
346
 
382
- return result
347
+ return page_response
383
348
  except Exception as e:
384
- self.logger.error("Error setting relations: %s", str(e))
349
+ self.logger.error("Error adding relation: %s", str(e))
385
350
  return None
386
-
387
- async def get_all_relations(self) -> Dict[str, List[str]]:
388
- """Returns all relation properties and their values."""
389
- relation_properties = await self.get_relation_property_ids()
390
-
391
- if not relation_properties:
392
- return {}
393
-
394
- result = {}
395
- for prop_name in relation_properties:
396
- result[prop_name] = await self.get_relation_values(prop_name)
397
-
398
- return result
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from typing import Optional, Dict, Any, List
2
2
  from notionary.notion_client import NotionClient
3
3
  from notionary.util.logging_mixin import LoggingMixin
4
4
 
@@ -17,39 +17,25 @@ class NotionPageTitleResolver(LoggingMixin):
17
17
  {"query": title, "filter": {"value": "page", "property": "object"}},
18
18
  )
19
19
 
20
- for result in search_results.get("results", []):
21
- properties = result.get("properties", {})
22
- if not properties:
23
- continue
24
-
25
- for prop_value in properties.values():
26
- if prop_value.get("type") != "title":
27
- continue
28
-
29
- title_texts = prop_value.get("title", [])
30
- if not title_texts:
31
- continue
20
+ results = search_results.get("results", [])
32
21
 
33
- page_title = " ".join(
34
- [t.get("plain_text", "") for t in title_texts]
35
- )
22
+ if not results:
23
+ self.logger.debug(f"No page found with title '{title}'")
24
+ return None
36
25
 
37
- if not page_title:
38
- continue
26
+ # Durchsuche die Ergebnisse nach dem passenden Titel
27
+ for result in results:
28
+ properties = result.get("properties", {})
29
+ page_title = self._extract_page_title_from_properties(properties)
39
30
 
40
- if page_title == title or title in page_title:
41
- self.logger.debug(
42
- "Found page: '%s' with ID: %s",
43
- page_title,
44
- result.get("id"),
45
- )
46
- return result.get("id")
31
+ if page_title == title:
32
+ return result.get("id")
47
33
 
48
- self.logger.debug("No page found with title '%s'", title)
34
+ self.logger.debug(f"No matching page found with title '{title}'")
49
35
  return None
50
36
 
51
37
  except Exception as e:
52
- self.logger.error("Error while searching for page '%s': %s", title, e)
38
+ self.logger.error(f"Error while searching for page '{title}': {e}")
53
39
  return None
54
40
 
55
41
  async def get_title_by_page_id(self, page_id: str) -> Optional[str]:
@@ -63,24 +49,56 @@ class NotionPageTitleResolver(LoggingMixin):
63
49
  The title of the page, or None if not found.
64
50
  """
65
51
  try:
66
- page = await self._client.get(f"pages/{page_id}")
67
- properties = page.get("properties", {})
52
+ page = await self._client.get_page(page_id)
53
+ return self._extract_page_title_from_properties(page.properties)
68
54
 
69
- for prop in properties.values():
70
- if prop.get("type") != "title":
71
- continue
55
+ except Exception as e:
56
+ self.logger.error(f"Error retrieving title for page ID '{page_id}': {e}")
57
+ return None
58
+
59
+ async def get_page_titles_by_ids(self, page_ids: List[str]) -> Dict[str, str]:
60
+ """
61
+ Retrieves titles for multiple page IDs at once.
62
+
63
+ Args:
64
+ page_ids: List of page IDs to get titles for
65
+
66
+ Returns:
67
+ Dictionary mapping page IDs to their titles
68
+ """
69
+ result = {}
70
+ for page_id in page_ids:
71
+ title = await self.get_title_by_page_id(page_id)
72
+ if title:
73
+ result[page_id] = title
74
+ return result
75
+
76
+ def _extract_page_title_from_properties(self, properties: Dict[str, Any]) -> str:
77
+ """
78
+ Extract title from properties dictionary.
72
79
 
73
- title_parts = prop.get("title", [])
74
- if not title_parts:
80
+ Args:
81
+ properties: The properties dictionary from a Notion page
82
+
83
+ Returns:
84
+ str: The extracted title or "Untitled" if not found
85
+ """
86
+ try:
87
+ for prop_value in properties.values():
88
+ if not isinstance(prop_value, dict):
75
89
  continue
76
90
 
77
- title = " ".join([t.get("plain_text", "") for t in title_parts])
78
- if title:
79
- return title
91
+ if prop_value.get("type") != "title":
92
+ continue
80
93
 
81
- self.logger.debug("No title found for page ID '%s'", page_id)
82
- return None
94
+ title_array = prop_value.get("title", [])
95
+ if not title_array:
96
+ continue
83
97
 
98
+ for text_obj in title_array:
99
+ if "plain_text" in text_obj:
100
+ return text_obj["plain_text"]
84
101
  except Exception as e:
85
- self.logger.error("Error retrieving title for page ID '%s': %s", page_id, e)
86
- return None
102
+ self.logger.error(f"Error extracting page title from properties: {e}")
103
+
104
+ return "Untitled"
@@ -1,4 +1,5 @@
1
1
  from typing import Dict, Optional, Any
2
+ from notionary.models.notion_page_response import DatabaseParent, NotionPageResponse
2
3
  from notionary.notion_client import NotionClient
3
4
  from notionary.util.logging_mixin import LoggingMixin
4
5
 
@@ -23,7 +24,7 @@ class PageDatabaseRelation(LoggingMixin):
23
24
  self._database_schema = None
24
25
  self._page_data = None
25
26
 
26
- async def _get_page_data(self, force_refresh=False) -> Dict[str, Any]:
27
+ async def _get_page_data(self, force_refresh=False) -> NotionPageResponse:
27
28
  """
28
29
  Gets the page data and caches it for future use.
29
30
 
@@ -39,22 +40,19 @@ class PageDatabaseRelation(LoggingMixin):
39
40
 
40
41
  async def get_parent_database_id(self) -> Optional[str]:
41
42
  """
42
- Gets the ID of the database this page belongs to, if any.
43
-
44
- Returns:
45
- Optional[str]: The database ID or None if the page doesn't belong to a database
43
+ Returns the ID of the database this page belongs to, if any.
46
44
  """
47
45
  if self._parent_database_id is not None:
48
46
  return self._parent_database_id
49
47
 
50
48
  page_data = await self._get_page_data()
51
49
 
52
- if not page_data or "parent" not in page_data:
50
+ if not page_data:
53
51
  return None
54
52
 
55
- parent = page_data.get("parent", {})
56
- if parent.get("type") == "database_id":
57
- self._parent_database_id = parent.get("database_id")
53
+ parent = page_data.parent
54
+ if isinstance(parent, DatabaseParent):
55
+ self._parent_database_id = parent.database_id
58
56
  return self._parent_database_id
59
57
 
60
58
  return None