notionary 0.1.11__py3-none-any.whl → 0.1.13__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 (55) hide show
  1. notionary/__init__.py +21 -6
  2. notionary/{core/converters → converters}/elements/audio_element.py +7 -5
  3. notionary/{core/converters → converters}/elements/bookmark_element.py +1 -1
  4. notionary/{core/converters → converters}/elements/callout_element.py +2 -2
  5. notionary/{core/converters → converters}/elements/code_block_element.py +1 -1
  6. notionary/{core/converters → converters}/elements/column_element.py +1 -1
  7. notionary/{core/converters → converters}/elements/divider_element.py +1 -1
  8. notionary/{core/converters → converters}/elements/embed_element.py +3 -5
  9. notionary/{core/converters → converters}/elements/heading_element.py +2 -2
  10. notionary/{core/converters → converters}/elements/image_element.py +1 -1
  11. notionary/{core/converters → converters}/elements/list_element.py +2 -2
  12. notionary/{core/converters → converters}/elements/paragraph_element.py +2 -2
  13. notionary/{core/converters → converters}/elements/qoute_element.py +1 -1
  14. notionary/{core/converters → converters}/elements/table_element.py +2 -2
  15. notionary/{core/converters → converters}/elements/todo_lists.py +2 -2
  16. notionary/{core/converters → converters}/elements/toggle_element.py +24 -21
  17. notionary/{core/converters → converters}/elements/video_element.py +1 -1
  18. notionary/{core/converters → converters}/markdown_to_notion_converter.py +72 -111
  19. notionary/{core/converters → converters}/notion_to_markdown_converter.py +2 -2
  20. notionary/{core/converters → converters}/registry/block_element_registry.py +5 -5
  21. notionary/{core/converters → converters}/registry/block_element_registry_builder.py +18 -18
  22. notionary/database/database_discovery.py +142 -0
  23. notionary/{core/database → database}/database_info_service.py +1 -1
  24. notionary/{core/database/notion_database_manager.py → database/notion_database.py} +33 -57
  25. notionary/{core/database/notion_database_manager_factory.py → database/notion_database_factory.py} +18 -16
  26. notionary/{core/notion_client.py → notion_client.py} +4 -2
  27. notionary/page/content/notion_page_content_chunker.py +84 -0
  28. notionary/{core/page → page}/content/page_content_manager.py +29 -13
  29. notionary/{core/page → page}/metadata/metadata_editor.py +59 -46
  30. notionary/{core/page → page}/metadata/notion_icon_manager.py +10 -12
  31. notionary/{core/page → page}/metadata/notion_page_cover_manager.py +16 -21
  32. notionary/page/notion_page.py +504 -0
  33. notionary/page/notion_page_factory.py +256 -0
  34. notionary/{core/page → page}/properites/database_property_service.py +115 -99
  35. notionary/{core/page → page}/properites/page_property_manager.py +81 -52
  36. notionary/{core/page → page}/properites/property_formatter.py +1 -1
  37. notionary/{core/page → page}/properites/property_operation_result.py +43 -30
  38. notionary/{core/page → page}/properites/property_value_extractor.py +26 -8
  39. notionary/{core/page → page}/relations/notion_page_relation_manager.py +72 -53
  40. notionary/{core/page → page}/relations/notion_page_title_resolver.py +12 -12
  41. notionary/{core/page → page}/relations/page_database_relation.py +15 -15
  42. notionary/{core/page → page}/relations/relation_operation_result.py +50 -41
  43. notionary/util/page_id_utils.py +14 -8
  44. {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/METADATA +1 -1
  45. notionary-0.1.13.dist-info/RECORD +56 -0
  46. notionary/core/database/notion_database_schema.py +0 -104
  47. notionary/core/page/notion_page_manager.py +0 -322
  48. notionary-0.1.11.dist-info/RECORD +0 -54
  49. /notionary/{core/converters → converters}/__init__.py +0 -0
  50. /notionary/{core/converters → converters}/elements/notion_block_element.py +0 -0
  51. /notionary/{core/converters → converters}/elements/text_inline_formatter.py +0 -0
  52. /notionary/{core/database → database}/models/page_result.py +0 -0
  53. {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/WHEEL +0 -0
  54. {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/licenses/LICENSE +0 -0
  55. {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,24 @@
1
1
  from typing import Any, Dict, List, Optional
2
- from notionary.core.notion_client import NotionClient
3
- from notionary.core.page.relations.notion_page_title_resolver import NotionPageTitleResolver
4
- from notionary.core.page.relations.relation_operation_result import RelationOperationResult
2
+ from notionary.notion_client import NotionClient
3
+ from notionary.page.relations.notion_page_title_resolver import (
4
+ NotionPageTitleResolver,
5
+ )
6
+ from notionary.page.relations.relation_operation_result import (
7
+ RelationOperationResult,
8
+ )
5
9
  from notionary.util.logging_mixin import LoggingMixin
6
10
  from notionary.util.page_id_utils import is_valid_uuid
7
11
 
12
+
8
13
  class NotionRelationManager(LoggingMixin):
9
14
  """
10
15
  Manager for relation properties of a Notion page.
11
16
  Manages links between pages and loads available relation options.
12
17
  """
13
18
 
14
- def __init__(self, page_id: str, client: NotionClient, database_id: Optional[str] = None):
19
+ def __init__(
20
+ self, page_id: str, client: NotionClient, database_id: Optional[str] = None
21
+ ):
15
22
  """
16
23
  Initializes the relation manager.
17
24
 
@@ -75,7 +82,8 @@ class NotionRelationManager(LoggingMixin):
75
82
  properties = await self._get_page_properties()
76
83
 
77
84
  return [
78
- prop_name for prop_name, prop_data in properties.items()
85
+ prop_name
86
+ for prop_name, prop_data in properties.items()
79
87
  if prop_data.get("type") == "relation"
80
88
  ]
81
89
 
@@ -101,7 +109,9 @@ class NotionRelationManager(LoggingMixin):
101
109
 
102
110
  return [rel.get("id") for rel in prop_data["relation"]]
103
111
 
104
- async def get_relation_details(self, property_name: str) -> Optional[Dict[str, Any]]:
112
+ async def get_relation_details(
113
+ self, property_name: str
114
+ ) -> Optional[Dict[str, Any]]:
105
115
  """
106
116
  Returns details about the relation property, including the linked database.
107
117
 
@@ -153,7 +163,9 @@ class NotionRelationManager(LoggingMixin):
153
163
 
154
164
  return relation_details.get("database_id")
155
165
 
156
- async def get_relation_options(self, property_name: str, limit: int = 100) -> List[Dict[str, Any]]:
166
+ async def get_relation_options(
167
+ self, property_name: str, limit: int = 100
168
+ ) -> List[Dict[str, Any]]:
157
169
  """
158
170
  Returns available options for a relation property.
159
171
 
@@ -174,7 +186,7 @@ class NotionRelationManager(LoggingMixin):
174
186
  f"databases/{related_db_id}/query",
175
187
  {
176
188
  "page_size": limit,
177
- }
189
+ },
178
190
  )
179
191
 
180
192
  if not query_result or "results" not in query_result:
@@ -186,10 +198,7 @@ class NotionRelationManager(LoggingMixin):
186
198
  title = self._extract_title_from_page(page)
187
199
 
188
200
  if page_id and title:
189
- options.append({
190
- "id": page_id,
191
- "name": title
192
- })
201
+ options.append({"id": page_id, "name": title})
193
202
 
194
203
  return options
195
204
  except Exception as e:
@@ -214,11 +223,15 @@ class NotionRelationManager(LoggingMixin):
214
223
  for prop_data in properties.values():
215
224
  if prop_data.get("type") == "title" and "title" in prop_data:
216
225
  title_parts = prop_data["title"]
217
- return "".join([text_obj.get("plain_text", "") for text_obj in title_parts])
226
+ return "".join(
227
+ [text_obj.get("plain_text", "") for text_obj in title_parts]
228
+ )
218
229
 
219
230
  return None
220
231
 
221
- async def add_relation(self, property_name: str, page_ids: List[str]) -> Optional[Dict[str, Any]]:
232
+ async def add_relation(
233
+ self, property_name: str, page_ids: List[str]
234
+ ) -> Optional[Dict[str, Any]]:
222
235
  """
223
236
  Adds one or more relations.
224
237
 
@@ -233,18 +246,12 @@ class NotionRelationManager(LoggingMixin):
233
246
 
234
247
  all_relations = list(set(existing_relations + page_ids))
235
248
 
236
- relation_payload = {
237
- "relation": [{"id": page_id} for page_id in all_relations]
238
- }
249
+ relation_payload = {"relation": [{"id": page_id} for page_id in all_relations]}
239
250
 
240
251
  try:
241
252
  result = await self._client.patch(
242
253
  f"pages/{self._page_id}",
243
- {
244
- "properties": {
245
- property_name: relation_payload
246
- }
247
- },
254
+ {"properties": {property_name: relation_payload}},
248
255
  )
249
256
 
250
257
  self._page_properties = None
@@ -254,7 +261,9 @@ class NotionRelationManager(LoggingMixin):
254
261
  self.logger.error("Error adding relation: %s", str(e))
255
262
  return None
256
263
 
257
- async def add_relation_by_name(self, property_name: str, page_titles: List[str]) -> RelationOperationResult:
264
+ async def add_relation_by_name(
265
+ self, property_name: str, page_titles: List[str]
266
+ ) -> RelationOperationResult:
258
267
  """
259
268
  Adds one or more relations based on page titles.
260
269
 
@@ -268,9 +277,13 @@ class NotionRelationManager(LoggingMixin):
268
277
  found_pages = []
269
278
  not_found_pages = []
270
279
  page_ids = []
271
-
272
- self.logger.info("Attempting to add %d relation(s) to property '%s'", len(page_titles), property_name)
273
-
280
+
281
+ self.logger.info(
282
+ "Attempting to add %d relation(s) to property '%s'",
283
+ len(page_titles),
284
+ property_name,
285
+ )
286
+
274
287
  for page in page_titles:
275
288
  if is_valid_uuid(page):
276
289
  page_ids.append(page)
@@ -287,38 +300,51 @@ class NotionRelationManager(LoggingMixin):
287
300
  self.logger.warning("No page found with title '%s'", page)
288
301
 
289
302
  if not page_ids:
290
- self.logger.warning("No valid page IDs found for any of the titles, no changes applied")
291
- return RelationOperationResult.from_no_pages_found(property_name, not_found_pages)
303
+ self.logger.warning(
304
+ "No valid page IDs found for any of the titles, no changes applied"
305
+ )
306
+ return RelationOperationResult.from_no_pages_found(
307
+ property_name, not_found_pages
308
+ )
292
309
 
293
310
  api_response = await self.add_relation(property_name, page_ids)
294
-
311
+
295
312
  if api_response:
296
313
  result = RelationOperationResult.from_success(
297
314
  property_name=property_name,
298
315
  found_pages=found_pages,
299
316
  not_found_pages=not_found_pages,
300
317
  page_ids_added=page_ids,
301
- api_response=api_response
318
+ api_response=api_response,
302
319
  )
303
-
320
+
304
321
  if not_found_pages:
305
322
  not_found_str = "', '".join(not_found_pages)
306
- self.logger.info("Added %d relation(s) to '%s', but couldn't find pages: '%s'",
307
- len(page_ids), property_name, not_found_str)
323
+ self.logger.info(
324
+ "Added %d relation(s) to '%s', but couldn't find pages: '%s'",
325
+ len(page_ids),
326
+ property_name,
327
+ not_found_str,
328
+ )
308
329
  else:
309
- self.logger.info("Successfully added all %d relation(s) to '%s'",
310
- len(page_ids), property_name)
311
-
330
+ self.logger.info(
331
+ "Successfully added all %d relation(s) to '%s'",
332
+ len(page_ids),
333
+ property_name,
334
+ )
335
+
312
336
  return result
313
-
337
+
314
338
  self.logger.error("Failed to add relations to '%s' (API error)", property_name)
315
339
  return RelationOperationResult.from_no_api_response(
316
340
  property_name=property_name,
317
341
  found_pages=found_pages,
318
- page_ids_added=page_ids
342
+ page_ids_added=page_ids,
319
343
  )
320
344
 
321
- async def set_relations(self, property_name: str, page_ids: List[str]) -> Optional[Dict[str, Any]]:
345
+ async def set_relations(
346
+ self, property_name: str, page_ids: List[str]
347
+ ) -> Optional[Dict[str, Any]]:
322
348
  """
323
349
  Sets the relations to the specified IDs (replaces existing ones).
324
350
 
@@ -329,18 +355,12 @@ class NotionRelationManager(LoggingMixin):
329
355
  Returns:
330
356
  Optional[Dict[str, Any]]: API response or None on error
331
357
  """
332
- relation_payload = {
333
- "relation": [{"id": page_id} for page_id in page_ids]
334
- }
358
+ relation_payload = {"relation": [{"id": page_id} for page_id in page_ids]}
335
359
 
336
360
  try:
337
361
  result = await self._client.patch(
338
362
  f"pages/{self._page_id}",
339
- {
340
- "properties": {
341
- property_name: relation_payload
342
- }
343
- },
363
+ {"properties": {property_name: relation_payload}},
344
364
  )
345
365
 
346
366
  self._page_properties = None
@@ -349,16 +369,15 @@ class NotionRelationManager(LoggingMixin):
349
369
  except Exception as e:
350
370
  self.logger.error("Error setting relations: %s", str(e))
351
371
  return None
352
-
372
+
353
373
  async def get_all_relations(self) -> Dict[str, List[str]]:
354
- """ Returns all relation properties and their values.
355
- """
374
+ """Returns all relation properties and their values."""
356
375
  relation_properties = await self.get_relation_property_ids()
357
376
  if not relation_properties:
358
377
  return {}
359
-
378
+
360
379
  result = {}
361
380
  for prop_name in relation_properties:
362
381
  result[prop_name] = await self.get_relation_values(prop_name)
363
-
364
- return result
382
+
383
+ return result
@@ -1,12 +1,12 @@
1
1
  from typing import Optional
2
- from notionary.core.notion_client import NotionClient
2
+ from notionary.notion_client import NotionClient
3
3
  from notionary.util.logging_mixin import LoggingMixin
4
4
 
5
5
 
6
6
  class NotionPageTitleResolver(LoggingMixin):
7
7
  def __init__(self, client: NotionClient):
8
8
  self._client = client
9
-
9
+
10
10
  async def get_page_id_by_title(self, title: str) -> Optional[str]:
11
11
  """
12
12
  Searches for a Notion page by its title and returns the corresponding page ID if found.
@@ -14,13 +14,7 @@ class NotionPageTitleResolver(LoggingMixin):
14
14
  try:
15
15
  search_results = await self._client.post(
16
16
  "search",
17
- {
18
- "query": title,
19
- "filter": {
20
- "value": "page",
21
- "property": "object"
22
- }
23
- }
17
+ {"query": title, "filter": {"value": "page", "property": "object"}},
24
18
  )
25
19
 
26
20
  for result in search_results.get("results", []):
@@ -30,14 +24,20 @@ class NotionPageTitleResolver(LoggingMixin):
30
24
  if prop_value.get("type") == "title":
31
25
  title_texts = prop_value.get("title", [])
32
26
 
33
- page_title = " ".join([t.get("plain_text", "") for t in title_texts])
27
+ page_title = " ".join(
28
+ [t.get("plain_text", "") for t in title_texts]
29
+ )
34
30
 
35
31
  if page_title == title or title in page_title:
36
- self.logger.debug("Found page: '%s' with ID: %s", page_title, result.get("id"))
32
+ self.logger.debug(
33
+ "Found page: '%s' with ID: %s",
34
+ page_title,
35
+ result.get("id"),
36
+ )
37
37
  return result.get("id")
38
38
 
39
39
  self.logger.debug("No page found with title '%s'", title)
40
40
  return None
41
41
  except Exception as e:
42
42
  self.logger.error("Error while searching for page '%s': %s", title, e)
43
- return None
43
+ return None
@@ -1,5 +1,5 @@
1
1
  from typing import Dict, Optional, Any
2
- from notionary.core.notion_client import NotionClient
2
+ from notionary.notion_client import NotionClient
3
3
  from notionary.util.logging_mixin import LoggingMixin
4
4
 
5
5
 
@@ -8,11 +8,11 @@ class PageDatabaseRelation(LoggingMixin):
8
8
  Manages the relationship between a Notion page and its parent database.
9
9
  Provides methods to access database schema and property options.
10
10
  """
11
-
11
+
12
12
  def __init__(self, page_id: str, client: NotionClient):
13
13
  """
14
14
  Initialize the page-database relationship handler.
15
-
15
+
16
16
  Args:
17
17
  page_id: ID of the Notion page
18
18
  client: Instance of NotionClient
@@ -22,49 +22,49 @@ class PageDatabaseRelation(LoggingMixin):
22
22
  self._parent_database_id = None
23
23
  self._database_schema = None
24
24
  self._page_data = None
25
-
25
+
26
26
  async def _get_page_data(self, force_refresh=False) -> Dict[str, Any]:
27
27
  """
28
28
  Gets the page data and caches it for future use.
29
-
29
+
30
30
  Args:
31
31
  force_refresh: Whether to force a refresh of the page data
32
-
32
+
33
33
  Returns:
34
34
  Dict[str, Any]: The page data
35
35
  """
36
36
  if self._page_data is None or force_refresh:
37
37
  self._page_data = await self._client.get_page(self._page_id)
38
38
  return self._page_data
39
-
39
+
40
40
  async def get_parent_database_id(self) -> Optional[str]:
41
41
  """
42
42
  Gets the ID of the database this page belongs to, if any.
43
-
43
+
44
44
  Returns:
45
45
  Optional[str]: The database ID or None if the page doesn't belong to a database
46
46
  """
47
47
  if self._parent_database_id is not None:
48
48
  return self._parent_database_id
49
-
49
+
50
50
  page_data = await self._get_page_data()
51
-
51
+
52
52
  if not page_data or "parent" not in page_data:
53
53
  return None
54
-
54
+
55
55
  parent = page_data.get("parent", {})
56
56
  if parent.get("type") == "database_id":
57
57
  self._parent_database_id = parent.get("database_id")
58
58
  return self._parent_database_id
59
-
59
+
60
60
  return None
61
-
61
+
62
62
  async def is_database_page(self) -> bool:
63
63
  """
64
64
  Checks if this page belongs to a database.
65
-
65
+
66
66
  Returns:
67
67
  bool: True if the page belongs to a database, False otherwise
68
68
  """
69
69
  database_id = await self.get_parent_database_id()
70
- return database_id is not None
70
+ return database_id is not None
@@ -1,11 +1,12 @@
1
1
  from typing import Any, Dict, List, Optional
2
2
  from dataclasses import dataclass, field
3
3
 
4
+
4
5
  @dataclass
5
6
  class RelationOperationResult:
6
7
  """
7
8
  Result of a relation operation in Notion.
8
-
9
+
9
10
  Attributes:
10
11
  success: Whether the operation was successful overall
11
12
  property_name: Name of the affected relation property
@@ -15,6 +16,7 @@ class RelationOperationResult:
15
16
  error: Error message, if any
16
17
  api_response: The original API response
17
18
  """
19
+
18
20
  success: bool
19
21
  property_name: str
20
22
  found_pages: List[str] = field(default_factory=list)
@@ -22,32 +24,38 @@ class RelationOperationResult:
22
24
  page_ids_added: List[str] = field(default_factory=list)
23
25
  error: Optional[str] = None
24
26
  api_response: Optional[Dict[str, Any]] = None
25
-
27
+
26
28
  NO_API_RESPONSE = "Failed to update relation (no API response)"
27
29
  NO_PAGES_FOUND = "No valid pages found for relation"
28
-
30
+
29
31
  @classmethod
30
- def from_success(cls, property_name: str,
31
- found_pages: List[str],
32
- page_ids_added: List[str],
33
- api_response: Dict[str, Any],
34
- not_found_pages: Optional[List[str]] = None) -> "RelationOperationResult":
32
+ def from_success(
33
+ cls,
34
+ property_name: str,
35
+ found_pages: List[str],
36
+ page_ids_added: List[str],
37
+ api_response: Dict[str, Any],
38
+ not_found_pages: Optional[List[str]] = None,
39
+ ) -> "RelationOperationResult":
35
40
  """Creates a success result."""
36
41
  return cls(
37
- success=True,
42
+ success=True,
38
43
  property_name=property_name,
39
44
  found_pages=found_pages,
40
45
  not_found_pages=not_found_pages or [],
41
46
  page_ids_added=page_ids_added,
42
- api_response=api_response
47
+ api_response=api_response,
43
48
  )
44
-
49
+
45
50
  @classmethod
46
- def from_error(cls, property_name: str,
47
- error: str,
48
- found_pages: Optional[List[str]] = None,
49
- not_found_pages: Optional[List[str]] = None,
50
- page_ids_added: Optional[List[str]] = None) -> "RelationOperationResult":
51
+ def from_error(
52
+ cls,
53
+ property_name: str,
54
+ error: str,
55
+ found_pages: Optional[List[str]] = None,
56
+ not_found_pages: Optional[List[str]] = None,
57
+ page_ids_added: Optional[List[str]] = None,
58
+ ) -> "RelationOperationResult":
51
59
  """Creates an error result."""
52
60
  return cls(
53
61
  success=False,
@@ -55,81 +63,82 @@ class RelationOperationResult:
55
63
  found_pages=found_pages or [],
56
64
  not_found_pages=not_found_pages or [],
57
65
  page_ids_added=page_ids_added or [],
58
- error=error
66
+ error=error,
59
67
  )
60
-
68
+
61
69
  @classmethod
62
- def from_no_pages_found(cls, property_name: str,
63
- not_found_pages: List[str]) -> "RelationOperationResult":
70
+ def from_no_pages_found(
71
+ cls, property_name: str, not_found_pages: List[str]
72
+ ) -> "RelationOperationResult":
64
73
  """Creates a standardized result for when no pages were found."""
65
74
  return cls.from_error(
66
75
  property_name=property_name,
67
76
  error=cls.NO_PAGES_FOUND,
68
- not_found_pages=not_found_pages
77
+ not_found_pages=not_found_pages,
69
78
  )
70
79
 
71
80
  @classmethod
72
- def from_no_api_response(cls, property_name: str,
73
- found_pages: List[str],
74
- page_ids_added: List[str]) -> "RelationOperationResult":
81
+ def from_no_api_response(
82
+ cls, property_name: str, found_pages: List[str], page_ids_added: List[str]
83
+ ) -> "RelationOperationResult":
75
84
  """Creates a standardized result for a missing API response."""
76
85
  return cls.from_error(
77
86
  property_name=property_name,
78
87
  error=cls.NO_API_RESPONSE,
79
88
  found_pages=found_pages,
80
- page_ids_added=page_ids_added
89
+ page_ids_added=page_ids_added,
81
90
  )
82
-
91
+
83
92
  @property
84
93
  def has_not_found_pages(self) -> bool:
85
94
  """Returns True if there were any pages that couldn't be found."""
86
95
  return len(self.not_found_pages) > 0
87
-
96
+
88
97
  @property
89
98
  def has_found_pages(self) -> bool:
90
99
  """Returns True if any pages were found."""
91
100
  return len(self.found_pages) > 0
92
-
101
+
93
102
  def to_dict(self) -> Dict[str, Any]:
94
103
  """Converts the result to a dictionary."""
95
104
  result = {
96
105
  "success": self.success,
97
106
  "property": self.property_name,
98
107
  }
99
-
108
+
100
109
  if self.found_pages:
101
110
  result["found_pages"] = self.found_pages
102
-
111
+
103
112
  if self.not_found_pages:
104
113
  result["not_found_pages"] = self.not_found_pages
105
-
114
+
106
115
  if self.page_ids_added:
107
116
  result["page_ids_added"] = self.page_ids_added
108
-
117
+
109
118
  if not self.success:
110
119
  result["error"] = self.error
111
-
120
+
112
121
  if self.api_response:
113
122
  result["api_response"] = self.api_response
114
-
123
+
115
124
  return result
116
-
125
+
117
126
  def __str__(self) -> str:
118
127
  """String representation of the result."""
119
128
  if self.success:
120
129
  base = f"Success: Added {len(self.page_ids_added)} relation(s) to property '{self.property_name}'"
121
-
130
+
122
131
  if self.not_found_pages:
123
132
  pages_str = "', '".join(self.not_found_pages)
124
133
  base += f"\nWarning: Could not find pages: '{pages_str}'"
125
-
134
+
126
135
  return base
127
-
136
+
128
137
  if not self.found_pages and self.not_found_pages:
129
138
  pages_str = "', '".join(self.not_found_pages)
130
139
  return f"Error: {self.error}\nNone of the requested pages were found: '{pages_str}'"
131
-
140
+
132
141
  if self.found_pages and not self.page_ids_added:
133
142
  return f"Error: {self.error}\nPages were found but could not be added to the relation."
134
-
135
- return f"Error: {self.error}"
143
+
144
+ return f"Error: {self.error}"
@@ -4,39 +4,45 @@ from typing import Optional
4
4
  UUID_PATTERN = r"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$"
5
5
  UUID_RAW_PATTERN = r"([a-f0-9]{32})"
6
6
 
7
+
7
8
  def extract_uuid(source: str) -> Optional[str]:
8
9
  if is_valid_uuid(source):
9
10
  return source
10
-
11
+
11
12
  match = re.search(UUID_RAW_PATTERN, source.lower())
12
13
  if not match:
13
14
  return None
14
-
15
+
15
16
  uuid_raw = match.group(1)
16
17
  return f"{uuid_raw[0:8]}-{uuid_raw[8:12]}-{uuid_raw[12:16]}-{uuid_raw[16:20]}-{uuid_raw[20:32]}"
17
18
 
19
+
18
20
  def is_valid_uuid(uuid: str) -> bool:
19
21
  return bool(re.match(UUID_PATTERN, uuid.lower()))
20
22
 
23
+
21
24
  def format_uuid(value: str) -> Optional[str]:
22
25
  if is_valid_uuid(value):
23
26
  return value
24
27
  return extract_uuid(value)
25
28
 
26
- def extract_and_validate_page_id(page_id: Optional[str], url: Optional[str]) -> str:
29
+
30
+ def extract_and_validate_page_id(
31
+ page_id: Optional[str] = None, url: Optional[str] = None
32
+ ) -> str:
27
33
  if not page_id and not url:
28
34
  raise ValueError("Either page_id or url must be provided")
29
-
35
+
30
36
  candidate = page_id or url
31
-
37
+
32
38
  if is_valid_uuid(candidate):
33
39
  return candidate
34
-
40
+
35
41
  extracted_id = extract_uuid(candidate)
36
42
  if not extracted_id:
37
43
  raise ValueError(f"Could not extract a valid UUID from: {candidate}")
38
-
44
+
39
45
  formatted = format_uuid(extracted_id)
40
46
  if not formatted or not is_valid_uuid(formatted):
41
47
  raise ValueError(f"Invalid UUID format: {formatted}")
42
- return formatted
48
+ return formatted
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notionary
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: A toolkit to convert between Markdown and Notion blocks
5
5
  Home-page: https://github.com/mathisarends/notionary
6
6
  Author: Mathis Arends