notionary 0.2.12__tar.gz → 0.2.14__tar.gz

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 (103) hide show
  1. {notionary-0.2.12 → notionary-0.2.14}/PKG-INFO +4 -1
  2. notionary-0.2.14/notionary/__init__.py +13 -0
  3. notionary-0.2.12/notionary/notion_client.py → notionary-0.2.14/notionary/base_notion_client.py +92 -98
  4. notionary-0.2.14/notionary/blocks/__init__.py +61 -0
  5. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/audio_element.py +6 -4
  6. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/bookmark_element.py +3 -6
  7. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/bulleted_list_element.py +5 -7
  8. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/callout_element.py +5 -8
  9. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/code_block_element.py +4 -6
  10. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/column_element.py +3 -6
  11. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/divider_element.py +3 -6
  12. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/embed_element.py +4 -6
  13. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/heading_element.py +5 -9
  14. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/image_element.py +4 -6
  15. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/mention_element.py +3 -7
  16. notionary-0.2.14/notionary/blocks/notion_block_client.py +26 -0
  17. notionary-0.2.14/notionary/blocks/notion_block_element.py +34 -0
  18. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/numbered_list_element.py +4 -7
  19. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/paragraph_element.py +4 -7
  20. notionary-0.2.12/notionary/prompting/element_prompt_content.py → notionary-0.2.14/notionary/blocks/prompts/element_prompt_builder.py +1 -40
  21. notionary-0.2.14/notionary/blocks/prompts/element_prompt_content.py +41 -0
  22. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/qoute_element.py +4 -6
  23. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/registry/block_registry.py +4 -26
  24. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/registry/block_registry_builder.py +26 -25
  25. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/table_element.py +6 -8
  26. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/text_inline_formatter.py +1 -4
  27. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/todo_element.py +6 -8
  28. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/toggle_element.py +3 -6
  29. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/toggleable_heading_element.py +5 -8
  30. {notionary-0.2.12/notionary/elements → notionary-0.2.14/notionary/blocks}/video_element.py +4 -6
  31. notionary-0.2.14/notionary/cli/main.py +376 -0
  32. notionary-0.2.14/notionary/cli/onboarding.py +117 -0
  33. notionary-0.2.14/notionary/database/client.py +132 -0
  34. notionary-0.2.14/notionary/database/database_exceptions.py +13 -0
  35. notionary-0.2.14/notionary/database/factory.py +0 -0
  36. notionary-0.2.14/notionary/database/filter_builder.py +175 -0
  37. notionary-0.2.14/notionary/database/notion_database.py +447 -0
  38. notionary-0.2.14/notionary/database/notion_database_provider.py +230 -0
  39. notionary-0.2.14/notionary/elements/__init__.py +0 -0
  40. notionary-0.2.14/notionary/models/notion_database_response.py +338 -0
  41. {notionary-0.2.12 → notionary-0.2.14}/notionary/models/notion_page_response.py +9 -31
  42. notionary-0.2.14/notionary/models/search_response.py +0 -0
  43. notionary-0.2.14/notionary/page/__init__.py +0 -0
  44. notionary-0.2.14/notionary/page/client.py +110 -0
  45. {notionary-0.2.12 → notionary-0.2.14}/notionary/page/content/page_content_retriever.py +5 -20
  46. {notionary-0.2.12 → notionary-0.2.14}/notionary/page/content/page_content_writer.py +3 -4
  47. {notionary-0.2.12 → notionary-0.2.14}/notionary/page/formatting/markdown_to_notion_converter.py +1 -3
  48. {notionary-0.2.12/notionary/prompting → notionary-0.2.14/notionary/page}/markdown_syntax_prompt_generator.py +1 -2
  49. notionary-0.2.14/notionary/page/notion_page.py +564 -0
  50. {notionary-0.2.12 → notionary-0.2.14}/notionary/page/notion_to_markdown_converter.py +1 -4
  51. notionary-0.2.14/notionary/page/properites/property_value_extractor.py +0 -0
  52. {notionary-0.2.12/notionary/page/properites → notionary-0.2.14/notionary/page}/property_formatter.py +7 -4
  53. notionary-0.2.14/notionary/page/search_filter_builder.py +131 -0
  54. notionary-0.2.14/notionary/page/utils.py +60 -0
  55. notionary-0.2.14/notionary/util/__init__.py +14 -0
  56. notionary-0.2.14/notionary/util/factory_decorator.py +33 -0
  57. notionary-0.2.14/notionary/util/fuzzy_matcher.py +82 -0
  58. {notionary-0.2.12 → notionary-0.2.14}/notionary/util/page_id_utils.py +0 -21
  59. notionary-0.2.14/notionary/util/singleton_metaclass.py +22 -0
  60. notionary-0.2.14/notionary/workspace.py +69 -0
  61. {notionary-0.2.12 → notionary-0.2.14}/notionary.egg-info/PKG-INFO +4 -1
  62. notionary-0.2.14/notionary.egg-info/SOURCES.txt +75 -0
  63. {notionary-0.2.12 → notionary-0.2.14}/notionary.egg-info/requires.txt +3 -0
  64. {notionary-0.2.12 → notionary-0.2.14}/setup.py +7 -4
  65. notionary-0.2.12/notionary/__init__.py +0 -30
  66. notionary-0.2.12/notionary/cli/main.py +0 -184
  67. notionary-0.2.12/notionary/database/database_discovery.py +0 -142
  68. notionary-0.2.12/notionary/database/notion_database.py +0 -236
  69. notionary-0.2.12/notionary/database/notion_database_factory.py +0 -193
  70. notionary-0.2.12/notionary/elements/notion_block_element.py +0 -70
  71. notionary-0.2.12/notionary/exceptions/database_exceptions.py +0 -76
  72. notionary-0.2.12/notionary/exceptions/page_creation_exception.py +0 -9
  73. notionary-0.2.12/notionary/models/notion_database_response.py +0 -57
  74. notionary-0.2.12/notionary/page/metadata/metadata_editor.py +0 -150
  75. notionary-0.2.12/notionary/page/metadata/notion_icon_manager.py +0 -77
  76. notionary-0.2.12/notionary/page/metadata/notion_page_cover_manager.py +0 -56
  77. notionary-0.2.12/notionary/page/notion_page.py +0 -527
  78. notionary-0.2.12/notionary/page/notion_page_factory.py +0 -332
  79. notionary-0.2.12/notionary/page/properites/database_property_service.py +0 -302
  80. notionary-0.2.12/notionary/page/properites/page_property_manager.py +0 -152
  81. notionary-0.2.12/notionary/page/properites/property_value_extractor.py +0 -64
  82. notionary-0.2.12/notionary/page/relations/notion_page_relation_manager.py +0 -350
  83. notionary-0.2.12/notionary/page/relations/notion_page_title_resolver.py +0 -104
  84. notionary-0.2.12/notionary/page/relations/page_database_relation.py +0 -68
  85. notionary-0.2.12/notionary/telemetry/__init__.py +0 -7
  86. notionary-0.2.12/notionary/telemetry/telemetry.py +0 -226
  87. notionary-0.2.12/notionary/telemetry/track_usage_decorator.py +0 -76
  88. notionary-0.2.12/notionary/util/__init__.py +0 -5
  89. notionary-0.2.12/notionary/util/warn_direct_constructor_usage.py +0 -54
  90. notionary-0.2.12/notionary.egg-info/SOURCES.txt +0 -73
  91. {notionary-0.2.12 → notionary-0.2.14}/LICENSE +0 -0
  92. {notionary-0.2.12 → notionary-0.2.14}/README.md +0 -0
  93. /notionary-0.2.12/notionary/cli/onboarding.py → /notionary-0.2.14/notionary/database/__init__.py +0 -0
  94. {notionary-0.2.12 → notionary-0.2.14}/notionary/database/models/page_result.py +0 -0
  95. {notionary-0.2.12 → notionary-0.2.14}/notionary/models/notion_block_response.py +0 -0
  96. {notionary-0.2.12 → notionary-0.2.14}/notionary/page/content/notion_page_content_chunker.py +0 -0
  97. {notionary-0.2.12 → notionary-0.2.14}/notionary/page/formatting/spacer_rules.py +0 -0
  98. {notionary-0.2.12 → notionary-0.2.14}/notionary/util/logging_mixin.py +0 -0
  99. /notionary-0.2.12/notionary/util/singleton.py → /notionary-0.2.14/notionary/util/singleton_decorator.py +0 -0
  100. {notionary-0.2.12 → notionary-0.2.14}/notionary.egg-info/dependency_links.txt +0 -0
  101. {notionary-0.2.12 → notionary-0.2.14}/notionary.egg-info/entry_points.txt +0 -0
  102. {notionary-0.2.12 → notionary-0.2.14}/notionary.egg-info/top_level.txt +0 -0
  103. {notionary-0.2.12 → notionary-0.2.14}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notionary
3
- Version: 0.2.12
3
+ Version: 0.2.14
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
@@ -15,6 +15,9 @@ Requires-Dist: python-dotenv>=1.1.0
15
15
  Requires-Dist: pydantic>=2.11.4
16
16
  Requires-Dist: posthog>=3.0.0
17
17
  Requires-Dist: click>=8.0.0
18
+ Requires-Dist: openai>=1.30.1
19
+ Requires-Dist: langchain>=0.2.0
20
+ Requires-Dist: tiktoken>=0.6.0
18
21
  Dynamic: author
19
22
  Dynamic: author-email
20
23
  Dynamic: classifier
@@ -0,0 +1,13 @@
1
+ __version__ = "0.2.14"
2
+
3
+ from .database.notion_database import NotionDatabase
4
+
5
+ from .page.notion_page import NotionPage
6
+ from .workspace import NotionWorkspace
7
+
8
+
9
+ __all__ = [
10
+ "NotionDatabase",
11
+ "NotionPage",
12
+ "NotionWorkspace",
13
+ ]
@@ -1,32 +1,37 @@
1
1
  import asyncio
2
2
  import os
3
- import weakref
3
+ from abc import ABC
4
4
  from enum import Enum
5
5
  from typing import Dict, Any, Optional, Union
6
6
  import httpx
7
7
  from dotenv import load_dotenv
8
- from notionary.models.notion_database_response import NotionDatabaseResponse
9
- from notionary.models.notion_page_response import NotionPageResponse
10
8
  from notionary.util import LoggingMixin
11
9
 
10
+ load_dotenv()
11
+
12
12
 
13
13
  class HttpMethod(Enum):
14
+ """
15
+ Enumeration of supported HTTP methods for API requests.
16
+ """
17
+
14
18
  GET = "get"
15
19
  POST = "post"
16
20
  PATCH = "patch"
17
21
  DELETE = "delete"
18
22
 
19
23
 
20
- class NotionClient(LoggingMixin):
21
- """Verbesserter Notion-Client mit automatischer Ressourcenverwaltung."""
24
+ class BaseNotionClient(LoggingMixin, ABC):
25
+ """
26
+ Base client for Notion API operations.
27
+ Handles connection management and generic HTTP requests.
28
+ """
22
29
 
23
30
  BASE_URL = "https://api.notion.com/v1"
24
31
  NOTION_VERSION = "2022-06-28"
25
- _instances = weakref.WeakSet()
26
32
 
27
33
  def __init__(self, token: Optional[str] = None, timeout: int = 30):
28
- load_dotenv()
29
- self.token = token or os.getenv("NOTION_SECRET", "")
34
+ self.token = token or self._find_token()
30
35
  if not self.token:
31
36
  raise ValueError("Notion API token is required")
32
37
 
@@ -36,64 +41,66 @@ class NotionClient(LoggingMixin):
36
41
  "Notion-Version": self.NOTION_VERSION,
37
42
  }
38
43
 
39
- self.client = httpx.AsyncClient(headers=self.headers, timeout=timeout)
44
+ self.client: Optional[httpx.AsyncClient] = None
45
+ self.timeout = timeout
46
+ self._is_initialized = False
40
47
 
41
- self._instances.add(self)
48
+ def __del__(self):
49
+ """Auto-cleanup when client is destroyed."""
50
+ if not hasattr(self, "client") or not self.client:
51
+ return
42
52
 
43
- @classmethod
44
- async def close_all(cls):
45
- """
46
- Closes all active NotionClient instances and releases resources.
47
- """
48
- for instance in list(cls._instances):
49
- await instance.close()
53
+ try:
54
+ loop = asyncio.get_event_loop()
55
+ if not loop.is_running():
56
+ self.logger.warning(
57
+ "Event loop not running, could not auto-close NotionClient"
58
+ )
59
+ return
50
60
 
51
- async def close(self):
52
- """
53
- Closes the HTTP client for this instance and releases resources.
54
- """
55
- if hasattr(self, "client") and self.client:
56
- await self.client.aclose()
57
- self.client = None
61
+ loop.create_task(self.close())
62
+ self.logger.debug("Created cleanup task for NotionClient")
63
+ except RuntimeError:
64
+ self.logger.warning("No event loop available for auto-closing NotionClient")
58
65
 
59
- # Das hier für die unterschiedlichen responses hier noch richtig typne wäre gut.
60
- async def get(self, endpoint: str) -> Optional[Dict[str, Any]]:
61
- """
62
- Sends a GET request to the specified Notion API endpoint.
66
+ async def __aenter__(self):
67
+ """Async context manager entry."""
68
+ await self.ensure_initialized()
69
+ return self
63
70
 
64
- Args:
65
- endpoint: The relative API path (e.g., 'databases/<id>').
71
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
72
+ """Async context manager exit."""
73
+ await self.close()
66
74
 
67
- Returns:
68
- A dictionary with the response data, or None if the request failed.
75
+ async def ensure_initialized(self) -> None:
69
76
  """
70
- return await self._make_request(HttpMethod.GET, endpoint)
71
-
72
- async def get_database(self, database_id: str) -> NotionDatabaseResponse:
77
+ Ensures the HTTP client is initialized.
73
78
  """
74
- Ruft die Metadaten einer Notion-Datenbank anhand ihrer ID ab und gibt sie als NotionPageResponse zurück.
79
+ if not self._is_initialized or not self.client:
80
+ self.client = httpx.AsyncClient(headers=self.headers, timeout=self.timeout)
81
+ self._is_initialized = True
82
+ self.logger.debug("NotionClient initialized")
75
83
 
76
- Args:
77
- database_id: Die Notion-Datenbank-ID.
78
-
79
- Returns:
80
- Ein NotionPageResponse-Objekt mit den Datenbankmetadaten.
84
+ async def close(self) -> None:
81
85
  """
82
- return NotionDatabaseResponse.model_validate(
83
- await self.get(f"databases/{database_id}")
84
- )
86
+ Closes the HTTP client and releases resources.
87
+ """
88
+ if not hasattr(self, "client") or not self.client:
89
+ return
90
+
91
+ await self.client.aclose()
92
+ self.client = None
93
+ self._is_initialized = False
94
+ self.logger.debug("NotionClient closed")
85
95
 
86
- async def get_page(self, page_id: str) -> NotionPageResponse:
96
+ async def get(self, endpoint: str) -> Optional[Dict[str, Any]]:
87
97
  """
88
- Ruft die Metadaten einer Notion-Seite anhand ihrer ID ab und gibt sie als NotionPageResponse zurück.
98
+ Sends a GET request to the specified Notion API endpoint.
89
99
 
90
100
  Args:
91
- page_id: Die Notion-Seiten-ID.
92
-
93
- Returns:
94
- Ein NotionPageResponse-Objekt mit den Seitenmetadaten.
101
+ endpoint: The API endpoint (without base URL)
95
102
  """
96
- return NotionPageResponse.model_validate(await self.get(f"pages/{page_id}"))
103
+ return await self._make_request(HttpMethod.GET, endpoint)
97
104
 
98
105
  async def post(
99
106
  self, endpoint: str, data: Optional[Dict[str, Any]] = None
@@ -102,11 +109,8 @@ class NotionClient(LoggingMixin):
102
109
  Sends a POST request to the specified Notion API endpoint.
103
110
 
104
111
  Args:
105
- endpoint: The relative API path.
106
- data: Optional dictionary payload to send with the request.
107
-
108
- Returns:
109
- A dictionary with the response data, or None if the request failed.
112
+ endpoint: The API endpoint (without base URL)
113
+ data: Request body data
110
114
  """
111
115
  return await self._make_request(HttpMethod.POST, endpoint, data)
112
116
 
@@ -117,37 +121,17 @@ class NotionClient(LoggingMixin):
117
121
  Sends a PATCH request to the specified Notion API endpoint.
118
122
 
119
123
  Args:
120
- endpoint: The relative API path.
121
- data: Optional dictionary payload to send with the request.
122
-
123
- Returns:
124
- A dictionary with the response data, or None if the request failed.
124
+ endpoint: The API endpoint (without base URL)
125
+ data: Request body data
125
126
  """
126
127
  return await self._make_request(HttpMethod.PATCH, endpoint, data)
127
128
 
128
- async def patch_page(
129
- self, page_id: str, data: Optional[Dict[str, Any]] = None
130
- ) -> NotionPageResponse:
131
- """
132
- Sends a PATCH request to update a Notion page.
133
-
134
- Args:
135
- page_id: The ID of the page to update.
136
- data: Optional dictionary payload to send with the request.
137
- """
138
- return NotionPageResponse.model_validate(
139
- await self.patch(f"pages/{page_id}", data=data)
140
- )
141
-
142
129
  async def delete(self, endpoint: str) -> bool:
143
130
  """
144
131
  Sends a DELETE request to the specified Notion API endpoint.
145
132
 
146
133
  Args:
147
- endpoint: The relative API path.
148
-
149
- Returns:
150
- True if the request was successful, False otherwise.
134
+ endpoint: The API endpoint (without base URL)
151
135
  """
152
136
  result = await self._make_request(HttpMethod.DELETE, endpoint)
153
137
  return result is not None
@@ -159,8 +143,15 @@ class NotionClient(LoggingMixin):
159
143
  data: Optional[Dict[str, Any]] = None,
160
144
  ) -> Optional[Dict[str, Any]]:
161
145
  """
162
- Führt eine HTTP-Anfrage aus und gibt direkt die Daten zurück oder None bei Fehler.
146
+ Executes an HTTP request and returns the data or None on error.
147
+
148
+ Args:
149
+ method: HTTP method to use
150
+ endpoint: API endpoint
151
+ data: Request body data (for POST/PATCH)
163
152
  """
153
+ await self.ensure_initialized()
154
+
164
155
  url = f"{self.BASE_URL}/{endpoint.lstrip('/')}"
165
156
  method_str = (
166
157
  method.value if isinstance(method, HttpMethod) else str(method).lower()
@@ -173,9 +164,11 @@ class NotionClient(LoggingMixin):
173
164
  method_str in [HttpMethod.POST.value, HttpMethod.PATCH.value]
174
165
  and data is not None
175
166
  ):
176
- response = await getattr(self.client, method_str)(url, json=data)
167
+ response: httpx.Response = await getattr(self.client, method_str)(
168
+ url, json=data
169
+ )
177
170
  else:
178
- response = await getattr(self.client, method_str)(url)
171
+ response: httpx.Response = await getattr(self.client, method_str)(url)
179
172
 
180
173
  response.raise_for_status()
181
174
  result_data = response.json()
@@ -194,19 +187,20 @@ class NotionClient(LoggingMixin):
194
187
  self.logger.error("Request error (%s): %s", url, error_msg)
195
188
  return None
196
189
 
197
- def __del__(self):
198
- if not hasattr(self, "client") or not self.client:
199
- return
200
-
201
- try:
202
- loop = asyncio.get_event_loop()
203
- if not loop.is_running():
204
- self.logger.warning(
205
- "Event loop not running, could not auto-close NotionClient"
206
- )
207
- return
208
-
209
- loop.create_task(self.close())
210
- self.logger.debug("Created cleanup task for NotionClient")
211
- except RuntimeError:
212
- self.logger.warning("No event loop available for auto-closing NotionClient")
190
+ def _find_token(self) -> Optional[str]:
191
+ """
192
+ Finds the Notion API token from environment variables.
193
+ """
194
+ token = next(
195
+ (
196
+ os.getenv(var)
197
+ for var in ("NOTION_SECRET", "NOTION_API_KEY", "NOTION_TOKEN")
198
+ if os.getenv(var)
199
+ ),
200
+ None,
201
+ )
202
+ if token:
203
+ self.logger.debug("Found token in environment variable.")
204
+ return token
205
+ self.logger.warning("No Notion API token found in environment variables")
206
+ return None
@@ -0,0 +1,61 @@
1
+ # Order is important here, as some imports depend on others.
2
+ from .prompts.element_prompt_content import ElementPromptContent
3
+ from .prompts.element_prompt_builder import ElementPromptBuilder
4
+
5
+ from .notion_block_element import NotionBlockElement
6
+
7
+
8
+ from .audio_element import AudioElement
9
+ from .bulleted_list_element import BulletedListElement
10
+ from .callout_element import CalloutElement
11
+ from .code_block_element import CodeBlockElement
12
+ from .column_element import ColumnElement
13
+ from .divider_element import DividerElement
14
+ from .embed_element import EmbedElement
15
+ from .heading_element import HeadingElement
16
+ from .image_element import ImageElement
17
+ from .numbered_list_element import NumberedListElement
18
+ from .paragraph_element import ParagraphElement
19
+ from .table_element import TableElement
20
+ from .toggle_element import ToggleElement
21
+ from .todo_element import TodoElement
22
+ from .video_element import VideoElement
23
+ from .toggleable_heading_element import ToggleableHeadingElement
24
+ from .bookmark_element import BookmarkElement
25
+ from .divider_element import DividerElement
26
+ from .heading_element import HeadingElement
27
+ from .mention_element import MentionElement
28
+ from .qoute_element import QuoteElement
29
+
30
+ from .registry.block_registry import BlockRegistry
31
+ from .registry.block_registry_builder import BlockRegistryBuilder
32
+ from .notion_block_client import NotionBlockClient
33
+
34
+
35
+ __all__ = [
36
+ "ElementPromptContent",
37
+ "ElementPromptBuilder",
38
+ "NotionBlockElement",
39
+ "AudioElement",
40
+ "BulletedListElement",
41
+ "CalloutElement",
42
+ "CodeBlockElement",
43
+ "ColumnElement",
44
+ "DividerElement",
45
+ "EmbedElement",
46
+ "HeadingElement",
47
+ "ImageElement",
48
+ "NumberedListElement",
49
+ "ParagraphElement",
50
+ "TableElement",
51
+ "ToggleElement",
52
+ "TodoElement",
53
+ "VideoElement",
54
+ "ToggleableHeadingElement",
55
+ "BookmarkElement",
56
+ "MentionElement",
57
+ "QuoteElement",
58
+ "BlockRegistry",
59
+ "BlockRegistryBuilder",
60
+ "NotionBlockClient",
61
+ ]
@@ -1,12 +1,14 @@
1
- import re
1
+ from __future__ import annotations
2
2
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
- from notionary.prompting.element_prompt_content import (
3
+ import re
4
+
5
+ from notionary.blocks.notion_block_element import NotionBlockElement
6
+ from notionary.blocks import (
5
7
  ElementPromptBuilder,
6
8
  ElementPromptContent,
7
9
  )
8
10
 
9
- @auto_track_conversions
11
+
10
12
  class AudioElement(NotionBlockElement):
11
13
  """
12
14
  Handles conversion between Markdown audio embeds and Notion audio blocks.
@@ -1,13 +1,10 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
- from notionary.prompting.element_prompt_content import (
6
- ElementPromptBuilder,
7
- ElementPromptContent,
8
- )
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+
9
7
 
10
- @auto_track_conversions
11
8
  class BookmarkElement(NotionBlockElement):
12
9
  """
13
10
  Handles conversion between Markdown bookmarks and Notion bookmark blocks.
@@ -1,13 +1,11 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
- from notionary.prompting.element_prompt_content import (
5
- ElementPromptBuilder,
6
- ElementPromptContent,
7
- )
8
- from notionary.elements.text_inline_formatter import TextInlineFormatter
3
+ from notionary.blocks import NotionBlockElement
4
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
5
+
6
+ from notionary.blocks.text_inline_formatter import TextInlineFormatter
7
+
9
8
 
10
- @auto_track_conversions
11
9
  class BulletedListElement(NotionBlockElement):
12
10
  """Class for converting between Markdown bullet lists and Notion bulleted list items."""
13
11
 
@@ -1,14 +1,11 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
3
 
4
- from notionary.prompting.element_prompt_content import (
5
- ElementPromptBuilder,
6
- ElementPromptContent,
7
- )
8
- from notionary.elements.text_inline_formatter import TextInlineFormatter
9
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
10
-
11
- @auto_track_conversions
4
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
5
+ from notionary.blocks.text_inline_formatter import TextInlineFormatter
6
+ from notionary.blocks.notion_block_element import NotionBlockElement
7
+
8
+
12
9
  class CalloutElement(NotionBlockElement):
13
10
  """
14
11
  Handles conversion between Markdown callouts and Notion callout blocks.
@@ -1,12 +1,10 @@
1
1
  import re
2
+
2
3
  from typing import Dict, Any, Optional, List, Tuple
3
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
- from notionary.prompting.element_prompt_content import (
5
- ElementPromptBuilder,
6
- ElementPromptContent,
7
- )
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+
8
7
 
9
- @auto_track_conversions
10
8
  class CodeBlockElement(NotionBlockElement):
11
9
  """
12
10
  Handles conversion between Markdown code blocks and Notion code blocks.
@@ -1,14 +1,11 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List, Tuple, Callable
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
+ from notionary.blocks import NotionBlockElement
5
5
  from notionary.page.formatting.spacer_rules import SPACER_MARKER
6
- from notionary.prompting.element_prompt_content import (
7
- ElementPromptBuilder,
8
- ElementPromptContent,
9
- )
6
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
7
+
10
8
 
11
- @auto_track_conversions
12
9
  class ColumnElement(NotionBlockElement):
13
10
  """
14
11
  Handles conversion between custom Markdown column syntax and Notion column blocks.
@@ -1,13 +1,10 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
- from notionary.prompting.element_prompt_content import (
6
- ElementPromptBuilder,
7
- ElementPromptContent,
8
- )
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+
9
7
 
10
- @auto_track_conversions
11
8
  class DividerElement(NotionBlockElement):
12
9
  """
13
10
  Handles conversion between Markdown horizontal dividers and Notion divider blocks.
@@ -1,12 +1,10 @@
1
1
  import re
2
+
2
3
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
- from notionary.prompting.element_prompt_content import (
5
- ElementPromptBuilder,
6
- ElementPromptContent,
7
- )
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+
8
7
 
9
- @auto_track_conversions
10
8
  class EmbedElement(NotionBlockElement):
11
9
  """
12
10
  Handles conversion between Markdown embeds and Notion embed blocks.
@@ -1,14 +1,11 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional
3
3
 
4
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
5
- from notionary.prompting.element_prompt_content import (
6
- ElementPromptBuilder,
7
- ElementPromptContent,
8
- )
9
- from notionary.elements.text_inline_formatter import TextInlineFormatter
10
-
11
- @auto_track_conversions
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+ from notionary.blocks.text_inline_formatter import TextInlineFormatter
7
+
8
+
12
9
  class HeadingElement(NotionBlockElement):
13
10
  """Handles conversion between Markdown headings and Notion heading blocks."""
14
11
 
@@ -88,7 +85,6 @@ class HeadingElement(NotionBlockElement):
88
85
  .with_avoidance_guidelines(
89
86
  "Only H1-H3 syntax is supported. H4 and deeper heading levels are not available."
90
87
  )
91
- .with_syntax("## Your Heading Text")
92
88
  .with_standard_markdown()
93
89
  .build()
94
90
  )
@@ -1,12 +1,10 @@
1
1
  import re
2
+
2
3
  from typing import Dict, Any, Optional, List
3
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
4
- from notionary.prompting.element_prompt_content import (
5
- ElementPromptBuilder,
6
- ElementPromptContent,
7
- )
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+
8
7
 
9
- @auto_track_conversions
10
8
  class ImageElement(NotionBlockElement):
11
9
  """
12
10
  Handles conversion between Markdown images and Notion image blocks.
@@ -1,14 +1,10 @@
1
1
  import re
2
2
  from typing import Dict, Any, Optional, List
3
- from typing_extensions import override
4
3
 
5
- from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
6
- from notionary.prompting.element_prompt_content import (
7
- ElementPromptBuilder,
8
- ElementPromptContent,
9
- )
4
+ from notionary.blocks import NotionBlockElement
5
+ from notionary.blocks import ElementPromptContent, ElementPromptBuilder
6
+
10
7
 
11
- @auto_track_conversions
12
8
  class MentionElement(NotionBlockElement):
13
9
  """
14
10
  Handles conversion between Markdown mentions and Notion mention elements.
@@ -0,0 +1,26 @@
1
+ from typing import Dict, Any, List
2
+ from notionary.base_notion_client import BaseNotionClient
3
+ from notionary.util import singleton
4
+
5
+
6
+ # TODO: Tyoe the block api (fix registry as well)
7
+ @singleton
8
+ class NotionBlockClient(BaseNotionClient):
9
+ """
10
+ Client for Notion page-specific operations.
11
+ Inherits base HTTP functionality from BaseNotionClient.
12
+ """
13
+
14
+ async def get_page_blocks(self, page_id: str) -> List[Dict[str, Any]]:
15
+ """
16
+ Retrieves all blocks of a Notion page.
17
+ """
18
+ response = await self.get(f"blocks/{page_id}/children")
19
+ return response.get("results", [])
20
+
21
+ async def get_block_children(self, block_id: str) -> List[Dict[str, Any]]:
22
+ """
23
+ Retrieves all children blocks of a specific block.
24
+ """
25
+ response = await self.get(f"blocks/{block_id}/children")
26
+ return response.get("results", [])
@@ -0,0 +1,34 @@
1
+ from typing import Dict, Any, Optional
2
+ from abc import ABC
3
+
4
+ from notionary.blocks.prompts.element_prompt_content import ElementPromptContent
5
+
6
+
7
+ class NotionBlockElement(ABC):
8
+ """Base class for elements that can be converted between Markdown and Notion."""
9
+
10
+ @classmethod
11
+ def markdown_to_notion(cls, text: str) -> Optional[Dict[str, Any]]:
12
+ """Convert markdown to Notion block."""
13
+
14
+ @classmethod
15
+ def notion_to_markdown(cls, block: Dict[str, Any]) -> Optional[str]:
16
+ """Convert Notion block to markdown."""
17
+
18
+ @classmethod
19
+ def match_markdown(cls, text: str) -> bool:
20
+ """Check if this element can handle the given markdown text."""
21
+ return bool(cls.markdown_to_notion(text)) # Now calls the class's version
22
+
23
+ @classmethod
24
+ def match_notion(cls, block: Dict[str, Any]) -> bool:
25
+ """Check if this element can handle the given Notion block."""
26
+ return bool(cls.notion_to_markdown(block)) # Now calls the class's version
27
+
28
+ @classmethod
29
+ def is_multiline(cls) -> bool:
30
+ return False
31
+
32
+ @classmethod
33
+ def get_llm_prompt_content(cls) -> ElementPromptContent:
34
+ """Returns a dictionary with information for LLM prompts about this element."""