notionary 0.2.12__py3-none-any.whl → 0.2.14__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 +3 -20
- notionary/{notion_client.py → base_notion_client.py} +92 -98
- notionary/blocks/__init__.py +61 -0
- notionary/{elements → blocks}/audio_element.py +6 -4
- notionary/{elements → blocks}/bookmark_element.py +3 -6
- notionary/{elements → blocks}/bulleted_list_element.py +5 -7
- notionary/{elements → blocks}/callout_element.py +5 -8
- notionary/{elements → blocks}/code_block_element.py +4 -6
- notionary/{elements → blocks}/column_element.py +3 -6
- notionary/{elements → blocks}/divider_element.py +3 -6
- notionary/{elements → blocks}/embed_element.py +4 -6
- notionary/{elements → blocks}/heading_element.py +5 -9
- notionary/{elements → blocks}/image_element.py +4 -6
- notionary/{elements → blocks}/mention_element.py +3 -7
- notionary/blocks/notion_block_client.py +26 -0
- notionary/blocks/notion_block_element.py +34 -0
- notionary/{elements → blocks}/numbered_list_element.py +4 -7
- notionary/{elements → blocks}/paragraph_element.py +4 -7
- notionary/{prompting/element_prompt_content.py → blocks/prompts/element_prompt_builder.py} +1 -40
- notionary/blocks/prompts/element_prompt_content.py +41 -0
- notionary/{elements → blocks}/qoute_element.py +4 -6
- notionary/{elements → blocks}/registry/block_registry.py +4 -26
- notionary/{elements → blocks}/registry/block_registry_builder.py +26 -25
- notionary/{elements → blocks}/table_element.py +6 -8
- notionary/{elements → blocks}/text_inline_formatter.py +1 -4
- notionary/{elements → blocks}/todo_element.py +6 -8
- notionary/{elements → blocks}/toggle_element.py +3 -6
- notionary/{elements → blocks}/toggleable_heading_element.py +5 -8
- notionary/{elements → blocks}/video_element.py +4 -6
- notionary/cli/main.py +245 -53
- notionary/cli/onboarding.py +117 -0
- notionary/database/__init__.py +0 -0
- notionary/database/client.py +132 -0
- notionary/database/database_exceptions.py +13 -0
- notionary/database/factory.py +0 -0
- notionary/database/filter_builder.py +175 -0
- notionary/database/notion_database.py +339 -128
- notionary/database/notion_database_provider.py +230 -0
- notionary/elements/__init__.py +0 -0
- notionary/models/notion_database_response.py +294 -13
- notionary/models/notion_page_response.py +9 -31
- notionary/models/search_response.py +0 -0
- notionary/page/__init__.py +0 -0
- notionary/page/client.py +110 -0
- notionary/page/content/page_content_retriever.py +5 -20
- notionary/page/content/page_content_writer.py +3 -4
- notionary/page/formatting/markdown_to_notion_converter.py +1 -3
- notionary/{prompting → page}/markdown_syntax_prompt_generator.py +1 -2
- notionary/page/notion_page.py +354 -317
- notionary/page/notion_to_markdown_converter.py +1 -4
- notionary/page/properites/property_value_extractor.py +0 -64
- notionary/page/{properites/property_formatter.py → property_formatter.py} +7 -4
- notionary/page/search_filter_builder.py +131 -0
- notionary/page/utils.py +60 -0
- notionary/util/__init__.py +12 -3
- notionary/util/factory_decorator.py +33 -0
- notionary/util/fuzzy_matcher.py +82 -0
- notionary/util/page_id_utils.py +0 -21
- notionary/util/singleton_metaclass.py +22 -0
- notionary/workspace.py +69 -0
- {notionary-0.2.12.dist-info → notionary-0.2.14.dist-info}/METADATA +4 -1
- notionary-0.2.14.dist-info/RECORD +72 -0
- notionary/database/database_discovery.py +0 -142
- notionary/database/notion_database_factory.py +0 -193
- notionary/elements/notion_block_element.py +0 -70
- notionary/exceptions/database_exceptions.py +0 -76
- notionary/exceptions/page_creation_exception.py +0 -9
- notionary/page/metadata/metadata_editor.py +0 -150
- notionary/page/metadata/notion_icon_manager.py +0 -77
- notionary/page/metadata/notion_page_cover_manager.py +0 -56
- notionary/page/notion_page_factory.py +0 -332
- notionary/page/properites/database_property_service.py +0 -302
- notionary/page/properites/page_property_manager.py +0 -152
- notionary/page/relations/notion_page_relation_manager.py +0 -350
- notionary/page/relations/notion_page_title_resolver.py +0 -104
- notionary/page/relations/page_database_relation.py +0 -68
- notionary/telemetry/__init__.py +0 -7
- notionary/telemetry/telemetry.py +0 -226
- notionary/telemetry/track_usage_decorator.py +0 -76
- notionary/util/warn_direct_constructor_usage.py +0 -54
- notionary-0.2.12.dist-info/RECORD +0 -70
- /notionary/util/{singleton.py → singleton_decorator.py} +0 -0
- {notionary-0.2.12.dist-info → notionary-0.2.14.dist-info}/WHEEL +0 -0
- {notionary-0.2.12.dist-info → notionary-0.2.14.dist-info}/entry_points.txt +0 -0
- {notionary-0.2.12.dist-info → notionary-0.2.14.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.2.12.dist-info → notionary-0.2.14.dist-info}/top_level.txt +0 -0
notionary/__init__.py
CHANGED
@@ -1,30 +1,13 @@
|
|
1
|
-
__version__ = "0.2.
|
2
|
-
|
3
|
-
from .notion_client import NotionClient
|
1
|
+
__version__ = "0.2.14"
|
4
2
|
|
5
3
|
from .database.notion_database import NotionDatabase
|
6
|
-
from .database.notion_database_factory import NotionDatabaseFactory
|
7
|
-
from .database.database_discovery import DatabaseDiscovery
|
8
4
|
|
9
5
|
from .page.notion_page import NotionPage
|
10
|
-
from .
|
11
|
-
|
12
|
-
from .elements.registry.block_registry import BlockRegistry
|
13
|
-
from .elements.registry.block_registry_builder import (
|
14
|
-
BlockRegistryBuilder,
|
15
|
-
)
|
16
|
-
|
17
|
-
from .telemetry.telemetry import NotionaryTelemetry
|
6
|
+
from .workspace import NotionWorkspace
|
18
7
|
|
19
|
-
_telemetry = NotionaryTelemetry()
|
20
8
|
|
21
9
|
__all__ = [
|
22
|
-
"NotionClient",
|
23
10
|
"NotionDatabase",
|
24
|
-
"NotionDatabaseFactory",
|
25
|
-
"DatabaseDiscovery",
|
26
11
|
"NotionPage",
|
27
|
-
"
|
28
|
-
"BlockRegistry",
|
29
|
-
"BlockRegistryBuilder",
|
12
|
+
"NotionWorkspace",
|
30
13
|
]
|
@@ -1,32 +1,37 @@
|
|
1
1
|
import asyncio
|
2
2
|
import os
|
3
|
-
import
|
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
|
21
|
-
"""
|
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
|
-
|
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
|
44
|
+
self.client: Optional[httpx.AsyncClient] = None
|
45
|
+
self.timeout = timeout
|
46
|
+
self._is_initialized = False
|
40
47
|
|
41
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
66
|
+
async def __aenter__(self):
|
67
|
+
"""Async context manager entry."""
|
68
|
+
await self.ensure_initialized()
|
69
|
+
return self
|
63
70
|
|
64
|
-
|
65
|
-
|
71
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
72
|
+
"""Async context manager exit."""
|
73
|
+
await self.close()
|
66
74
|
|
67
|
-
|
68
|
-
A dictionary with the response data, or None if the request failed.
|
75
|
+
async def ensure_initialized(self) -> None:
|
69
76
|
"""
|
70
|
-
|
71
|
-
|
72
|
-
async def get_database(self, database_id: str) -> NotionDatabaseResponse:
|
77
|
+
Ensures the HTTP client is initialized.
|
73
78
|
"""
|
74
|
-
|
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
|
-
|
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
|
-
|
83
|
-
|
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
|
96
|
+
async def get(self, endpoint: str) -> Optional[Dict[str, Any]]:
|
87
97
|
"""
|
88
|
-
|
98
|
+
Sends a GET request to the specified Notion API endpoint.
|
89
99
|
|
90
100
|
Args:
|
91
|
-
|
92
|
-
|
93
|
-
Returns:
|
94
|
-
Ein NotionPageResponse-Objekt mit den Seitenmetadaten.
|
101
|
+
endpoint: The API endpoint (without base URL)
|
95
102
|
"""
|
96
|
-
return
|
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
|
106
|
-
data:
|
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
|
121
|
-
data:
|
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
|
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
|
-
|
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)(
|
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
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
self.logger.debug("
|
211
|
-
|
212
|
-
|
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
|
1
|
+
from __future__ import annotations
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
|
-
|
4
|
-
|
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
|
-
|
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.
|
5
|
-
from notionary.
|
6
|
-
|
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.
|
4
|
-
from notionary.
|
5
|
-
|
6
|
-
|
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.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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.
|
4
|
-
from notionary.
|
5
|
-
|
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.
|
4
|
+
from notionary.blocks import NotionBlockElement
|
5
5
|
from notionary.page.formatting.spacer_rules import SPACER_MARKER
|
6
|
-
from notionary.
|
7
|
-
|
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.
|
5
|
-
from notionary.
|
6
|
-
|
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.
|
4
|
-
from notionary.
|
5
|
-
|
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.
|
5
|
-
from notionary.
|
6
|
-
|
7
|
-
|
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.
|
4
|
-
from notionary.
|
5
|
-
|
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.
|
6
|
-
from notionary.
|
7
|
-
|
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."""
|