notionary 0.2.10__tar.gz → 0.2.12__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.
- {notionary-0.2.10 → notionary-0.2.12}/PKG-INFO +3 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/__init__.py +6 -0
- notionary-0.2.12/notionary/cli/main.py +184 -0
- notionary-0.2.12/notionary/cli/onboarding.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/database/database_discovery.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/database/notion_database.py +5 -4
- {notionary-0.2.10 → notionary-0.2.12}/notionary/database/notion_database_factory.py +10 -5
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/audio_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/bookmark_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/bulleted_list_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/callout_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/code_block_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/column_element.py +51 -44
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/divider_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/embed_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/heading_element.py +3 -3
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/image_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/mention_element.py +2 -2
- notionary-0.2.12/notionary/elements/notion_block_element.py +70 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/numbered_list_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/paragraph_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/qoute_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/table_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/text_inline_formatter.py +23 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/todo_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/toggle_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/toggleable_heading_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/video_element.py +2 -2
- {notionary-0.2.10 → notionary-0.2.12}/notionary/notion_client.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/content/notion_page_content_chunker.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/content/page_content_retriever.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/content/page_content_writer.py +3 -3
- {notionary-0.2.10/notionary/page → notionary-0.2.12/notionary/page/formatting}/markdown_to_notion_converter.py +44 -140
- notionary-0.2.12/notionary/page/formatting/spacer_rules.py +483 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/metadata/metadata_editor.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/metadata/notion_icon_manager.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/metadata/notion_page_cover_manager.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/notion_page.py +1 -1
- notionary-0.2.12/notionary/page/notion_page_factory.py +332 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/properites/database_property_service.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/properites/page_property_manager.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/properites/property_formatter.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/properites/property_value_extractor.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/relations/notion_page_relation_manager.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/relations/notion_page_title_resolver.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/relations/page_database_relation.py +1 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary/prompting/element_prompt_content.py +1 -0
- notionary-0.2.12/notionary/telemetry/__init__.py +7 -0
- notionary-0.2.12/notionary/telemetry/telemetry.py +226 -0
- notionary-0.2.12/notionary/telemetry/track_usage_decorator.py +76 -0
- notionary-0.2.12/notionary/util/__init__.py +5 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/util/logging_mixin.py +3 -0
- notionary-0.2.12/notionary/util/singleton.py +18 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary.egg-info/PKG-INFO +3 -1
- {notionary-0.2.10 → notionary-0.2.12}/notionary.egg-info/SOURCES.txt +10 -1
- notionary-0.2.12/notionary.egg-info/entry_points.txt +2 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary.egg-info/requires.txt +2 -0
- {notionary-0.2.10 → notionary-0.2.12}/setup.py +13 -2
- notionary-0.2.10/notionary/elements/notion_block_element.py +0 -34
- notionary-0.2.10/notionary/page/notion_page_factory.py +0 -193
- {notionary-0.2.10 → notionary-0.2.12}/LICENSE +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/README.md +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/database/models/page_result.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/registry/block_registry.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/elements/registry/block_registry_builder.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/exceptions/database_exceptions.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/exceptions/page_creation_exception.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/models/notion_block_response.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/models/notion_database_response.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/models/notion_page_response.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/page/notion_to_markdown_converter.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/prompting/markdown_syntax_prompt_generator.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/util/page_id_utils.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary/util/warn_direct_constructor_usage.py +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary.egg-info/dependency_links.txt +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/notionary.egg-info/top_level.txt +0 -0
- {notionary-0.2.10 → notionary-0.2.12}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: notionary
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.12
|
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
|
@@ -13,6 +13,8 @@ License-File: LICENSE
|
|
13
13
|
Requires-Dist: httpx>=0.28.0
|
14
14
|
Requires-Dist: python-dotenv>=1.1.0
|
15
15
|
Requires-Dist: pydantic>=2.11.4
|
16
|
+
Requires-Dist: posthog>=3.0.0
|
17
|
+
Requires-Dist: click>=8.0.0
|
16
18
|
Dynamic: author
|
17
19
|
Dynamic: author-email
|
18
20
|
Dynamic: classifier
|
@@ -1,3 +1,5 @@
|
|
1
|
+
__version__ = "0.2.10"
|
2
|
+
|
1
3
|
from .notion_client import NotionClient
|
2
4
|
|
3
5
|
from .database.notion_database import NotionDatabase
|
@@ -12,6 +14,10 @@ from .elements.registry.block_registry_builder import (
|
|
12
14
|
BlockRegistryBuilder,
|
13
15
|
)
|
14
16
|
|
17
|
+
from .telemetry.telemetry import NotionaryTelemetry
|
18
|
+
|
19
|
+
_telemetry = NotionaryTelemetry()
|
20
|
+
|
15
21
|
__all__ = [
|
16
22
|
"NotionClient",
|
17
23
|
"NotionDatabase",
|
@@ -0,0 +1,184 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Notionary CLI - Integration Key Setup
|
4
|
+
"""
|
5
|
+
|
6
|
+
import click
|
7
|
+
import os
|
8
|
+
import platform
|
9
|
+
from pathlib import Path
|
10
|
+
from dotenv import load_dotenv
|
11
|
+
from rich.console import Console
|
12
|
+
from rich.panel import Panel
|
13
|
+
from rich.prompt import Prompt, Confirm
|
14
|
+
|
15
|
+
console = Console()
|
16
|
+
|
17
|
+
def get_paste_tips():
|
18
|
+
"""Get platform-specific paste tips"""
|
19
|
+
system = platform.system().lower()
|
20
|
+
|
21
|
+
if system == "darwin": # macOS
|
22
|
+
return [
|
23
|
+
"• Terminal: [cyan]Cmd+V[/cyan]",
|
24
|
+
"• iTerm2: [cyan]Cmd+V[/cyan]",
|
25
|
+
]
|
26
|
+
elif system == "windows":
|
27
|
+
return [
|
28
|
+
"• PowerShell: [cyan]Right-click[/cyan] or [cyan]Shift+Insert[/cyan]",
|
29
|
+
"• cmd: [cyan]Right-click[/cyan]",
|
30
|
+
]
|
31
|
+
else: # Linux and others
|
32
|
+
return [
|
33
|
+
"• Terminal: [cyan]Ctrl+Shift+V[/cyan] or [cyan]Right-click[/cyan]",
|
34
|
+
"• Some terminals: [cyan]Shift+Insert[/cyan]",
|
35
|
+
]
|
36
|
+
|
37
|
+
def show_paste_tips():
|
38
|
+
"""Show platform-specific paste tips"""
|
39
|
+
console.print("\n[bold yellow]💡 Paste Tips:[/bold yellow]")
|
40
|
+
for tip in get_paste_tips():
|
41
|
+
console.print(tip)
|
42
|
+
console.print()
|
43
|
+
|
44
|
+
def get_notion_secret() -> str:
|
45
|
+
"""Get NOTION_SECRET using the same logic as NotionClient"""
|
46
|
+
load_dotenv()
|
47
|
+
return os.getenv("NOTION_SECRET", "")
|
48
|
+
|
49
|
+
@click.group()
|
50
|
+
@click.version_option() # Automatische Version aus setup.py
|
51
|
+
def main():
|
52
|
+
"""
|
53
|
+
Notionary CLI - Notion API Integration
|
54
|
+
"""
|
55
|
+
pass
|
56
|
+
|
57
|
+
@main.command()
|
58
|
+
def init():
|
59
|
+
"""
|
60
|
+
Setup your Notion Integration Key
|
61
|
+
"""
|
62
|
+
# Check if key already exists
|
63
|
+
existing_key = get_notion_secret()
|
64
|
+
|
65
|
+
if existing_key:
|
66
|
+
console.print(Panel.fit(
|
67
|
+
"[bold green]✅ You're all set![/bold green]\n"
|
68
|
+
f"Your Notion Integration Key is already configured.\n"
|
69
|
+
f"Key: [dim]{existing_key[:8]}...[/dim]",
|
70
|
+
title="Already Configured"
|
71
|
+
))
|
72
|
+
|
73
|
+
# Option to reconfigure
|
74
|
+
if Confirm.ask("\n[yellow]Would you like to update your key?[/yellow]"):
|
75
|
+
setup_new_key()
|
76
|
+
else:
|
77
|
+
console.print("\n[blue]No changes made. Happy coding! 🚀[/blue]")
|
78
|
+
else:
|
79
|
+
# No key found, start setup
|
80
|
+
console.print(Panel.fit(
|
81
|
+
"[bold green]🚀 Notionary Setup[/bold green]\n"
|
82
|
+
"Enter your Notion Integration Key to get started...\n\n"
|
83
|
+
"[bold blue]🔗 Create an Integration Key or get an existing one:[/bold blue]\n"
|
84
|
+
"[cyan]https://www.notion.so/profile/integrations[/cyan]",
|
85
|
+
title="Initialization"
|
86
|
+
))
|
87
|
+
setup_new_key()
|
88
|
+
|
89
|
+
def setup_new_key():
|
90
|
+
"""Handle the key setup process"""
|
91
|
+
try:
|
92
|
+
# Show Integration Key creation link
|
93
|
+
console.print("\n[bold blue]🔗 Create an Integration Key:[/bold blue]")
|
94
|
+
console.print("[cyan]https://www.notion.so/profile/integrations[/cyan]")
|
95
|
+
console.print()
|
96
|
+
|
97
|
+
# Get integration key
|
98
|
+
integration_key = Prompt.ask(
|
99
|
+
"[bold cyan]Notion Integration Key[/bold cyan]"
|
100
|
+
)
|
101
|
+
|
102
|
+
# Input validation
|
103
|
+
if not integration_key or not integration_key.strip():
|
104
|
+
console.print("[bold red]❌ Integration Key cannot be empty![/bold red]")
|
105
|
+
return
|
106
|
+
|
107
|
+
# Trim whitespace
|
108
|
+
integration_key = integration_key.strip()
|
109
|
+
|
110
|
+
# Check for common paste issues
|
111
|
+
if integration_key in ["^V", "^v", "^C", "^c"]:
|
112
|
+
console.print("[bold red]❌ Paste didn't work! Try:[/bold red]")
|
113
|
+
show_paste_tips()
|
114
|
+
return
|
115
|
+
|
116
|
+
# Show masked feedback that paste worked
|
117
|
+
masked_key = "•" * len(integration_key)
|
118
|
+
console.print(f"[dim]Received: {masked_key} ({len(integration_key)} characters)[/dim]")
|
119
|
+
|
120
|
+
# Basic validation for Notion keys
|
121
|
+
if not integration_key.startswith('ntn_') or len(integration_key) < 30:
|
122
|
+
console.print("[bold yellow]⚠️ Warning: This doesn't look like a valid Notion Integration Key[/bold yellow]")
|
123
|
+
console.print("[dim]Notion keys usually start with 'ntn_' and are about 50+ characters long[/dim]")
|
124
|
+
if not Confirm.ask("Continue anyway?"):
|
125
|
+
return
|
126
|
+
|
127
|
+
# Save the key
|
128
|
+
if save_integration_key(integration_key):
|
129
|
+
return # Success!
|
130
|
+
|
131
|
+
except KeyboardInterrupt:
|
132
|
+
console.print("\n[yellow]Setup cancelled.[/yellow]")
|
133
|
+
except Exception as e:
|
134
|
+
console.print(f"\n[bold red]❌ Error during setup: {e}[/bold red]")
|
135
|
+
raise click.Abort()
|
136
|
+
|
137
|
+
def save_integration_key(integration_key: str) -> bool:
|
138
|
+
"""Save the integration key to .env file"""
|
139
|
+
try:
|
140
|
+
# .env Datei im aktuellen Verzeichnis erstellen/aktualisieren
|
141
|
+
env_file = Path.cwd() / ".env"
|
142
|
+
|
143
|
+
# Bestehende .env lesen falls vorhanden
|
144
|
+
existing_lines = []
|
145
|
+
if env_file.exists():
|
146
|
+
with open(env_file, 'r', encoding='utf-8') as f:
|
147
|
+
existing_lines = [line.rstrip() for line in f.readlines()]
|
148
|
+
|
149
|
+
# NOTION_SECRET Zeile hinzufügen/ersetzen
|
150
|
+
updated_lines = []
|
151
|
+
notion_secret_found = False
|
152
|
+
|
153
|
+
for line in existing_lines:
|
154
|
+
if line.startswith('NOTION_SECRET='):
|
155
|
+
updated_lines.append(f'NOTION_SECRET={integration_key}')
|
156
|
+
notion_secret_found = True
|
157
|
+
else:
|
158
|
+
updated_lines.append(line)
|
159
|
+
|
160
|
+
# Falls NOTION_SECRET noch nicht existiert, hinzufügen
|
161
|
+
if not notion_secret_found:
|
162
|
+
updated_lines.append(f'NOTION_SECRET={integration_key}')
|
163
|
+
|
164
|
+
# .env Datei schreiben
|
165
|
+
with open(env_file, 'w', encoding='utf-8') as f:
|
166
|
+
f.write('\n'.join(updated_lines) + '\n')
|
167
|
+
|
168
|
+
# Verification
|
169
|
+
written_key = get_notion_secret()
|
170
|
+
if written_key == integration_key:
|
171
|
+
console.print("\n[bold green]✅ Integration Key saved and verified![/bold green]")
|
172
|
+
console.print(f"[dim]Configuration: {env_file}[/dim]")
|
173
|
+
console.print("\n[blue]Ready to use notionary in your Python code! 🚀[/blue]")
|
174
|
+
return True
|
175
|
+
else:
|
176
|
+
console.print("\n[bold red]❌ Error: Key verification failed![/bold red]")
|
177
|
+
return False
|
178
|
+
|
179
|
+
except Exception as e:
|
180
|
+
console.print(f"\n[bold red]❌ Error saving key: {e}[/bold red]")
|
181
|
+
return False
|
182
|
+
|
183
|
+
if __name__ == '__main__':
|
184
|
+
main()
|
File without changes
|
@@ -1,11 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
-
import json
|
3
2
|
from typing import Any, AsyncGenerator, Dict, List, Optional
|
4
3
|
|
5
4
|
from notionary.notion_client import NotionClient
|
6
5
|
from notionary.page.notion_page import NotionPage
|
6
|
+
from notionary.telemetry import NotionaryTelemetry
|
7
7
|
from notionary.util.warn_direct_constructor_usage import warn_direct_constructor_usage
|
8
|
-
from notionary.util
|
8
|
+
from notionary.util import LoggingMixin
|
9
9
|
from notionary.util.page_id_utils import format_uuid
|
10
10
|
|
11
11
|
|
@@ -26,10 +26,11 @@ class NotionDatabase(LoggingMixin):
|
|
26
26
|
token: Optional Notion API token
|
27
27
|
"""
|
28
28
|
self.database_id = database_id
|
29
|
+
self._telemetry = NotionaryTelemetry()
|
29
30
|
self._client = NotionClient(token=token)
|
30
31
|
|
31
32
|
@classmethod
|
32
|
-
|
33
|
+
def from_database_id(
|
33
34
|
cls, database_id: str, token: Optional[str] = None
|
34
35
|
) -> NotionDatabase:
|
35
36
|
"""
|
@@ -38,7 +39,7 @@ class NotionDatabase(LoggingMixin):
|
|
38
39
|
"""
|
39
40
|
from notionary.database.notion_database_factory import NotionDatabaseFactory
|
40
41
|
|
41
|
-
return
|
42
|
+
return NotionDatabaseFactory.from_database_id(database_id, token)
|
42
43
|
|
43
44
|
@classmethod
|
44
45
|
async def from_database_name(
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import logging
|
2
1
|
from typing import List, Optional, Dict, Any
|
3
2
|
from difflib import SequenceMatcher
|
4
3
|
|
@@ -11,10 +10,13 @@ from notionary.exceptions.database_exceptions import (
|
|
11
10
|
DatabaseParsingError,
|
12
11
|
NotionDatabaseException,
|
13
12
|
)
|
14
|
-
from notionary.
|
15
|
-
from notionary.util
|
13
|
+
from notionary.telemetry import track_usage
|
14
|
+
from notionary.util import LoggingMixin
|
15
|
+
from notionary.util import format_uuid
|
16
|
+
from notionary.util import singleton
|
16
17
|
|
17
18
|
|
19
|
+
@singleton
|
18
20
|
class NotionDatabaseFactory(LoggingMixin):
|
19
21
|
"""
|
20
22
|
Factory class for creating NotionDatabaseManager instances.
|
@@ -22,7 +24,8 @@ class NotionDatabaseFactory(LoggingMixin):
|
|
22
24
|
"""
|
23
25
|
|
24
26
|
@classmethod
|
25
|
-
|
27
|
+
@track_usage('page_factory_method_used', {'method': 'from_page_id'})
|
28
|
+
def from_database_id(
|
26
29
|
cls, database_id: str, token: Optional[str] = None
|
27
30
|
) -> NotionDatabase:
|
28
31
|
"""
|
@@ -35,6 +38,7 @@ class NotionDatabaseFactory(LoggingMixin):
|
|
35
38
|
Returns:
|
36
39
|
An initialized NotionDatabaseManager instance
|
37
40
|
"""
|
41
|
+
|
38
42
|
try:
|
39
43
|
formatted_id = format_uuid(database_id) or database_id
|
40
44
|
|
@@ -55,6 +59,7 @@ class NotionDatabaseFactory(LoggingMixin):
|
|
55
59
|
raise DatabaseConnectionError(error_msg) from e
|
56
60
|
|
57
61
|
@classmethod
|
62
|
+
@track_usage('page_factory_method_used', {'method': 'from_url'})
|
58
63
|
async def from_database_name(
|
59
64
|
cls, database_name: str, token: Optional[str] = None
|
60
65
|
) -> NotionDatabase:
|
@@ -72,7 +77,7 @@ class NotionDatabaseFactory(LoggingMixin):
|
|
72
77
|
cls.logger.debug("Searching for database with name: %s", database_name)
|
73
78
|
|
74
79
|
client = NotionClient(token=token)
|
75
|
-
|
80
|
+
|
76
81
|
try:
|
77
82
|
cls.logger.debug("Using search endpoint to find databases")
|
78
83
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
|
9
|
-
|
9
|
+
@auto_track_conversions
|
10
10
|
class AudioElement(NotionBlockElement):
|
11
11
|
"""
|
12
12
|
Handles conversion between Markdown audio embeds and Notion audio blocks.
|
@@ -1,13 +1,13 @@
|
|
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
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
5
5
|
from notionary.prompting.element_prompt_content import (
|
6
6
|
ElementPromptBuilder,
|
7
7
|
ElementPromptContent,
|
8
8
|
)
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class BookmarkElement(NotionBlockElement):
|
12
12
|
"""
|
13
13
|
Handles conversion between Markdown bookmarks and Notion bookmark blocks.
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class BulletedListElement(NotionBlockElement):
|
12
12
|
"""Class for converting between Markdown bullet lists and Notion bulleted list items."""
|
13
13
|
|
@@ -6,9 +6,9 @@ from notionary.prompting.element_prompt_content import (
|
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
9
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
10
|
-
|
9
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
11
10
|
|
11
|
+
@auto_track_conversions
|
12
12
|
class CalloutElement(NotionBlockElement):
|
13
13
|
"""
|
14
14
|
Handles conversion between Markdown callouts and Notion callout blocks.
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List, Tuple
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
|
9
|
-
|
9
|
+
@auto_track_conversions
|
10
10
|
class CodeBlockElement(NotionBlockElement):
|
11
11
|
"""
|
12
12
|
Handles conversion between Markdown code blocks and Notion code blocks.
|
@@ -1,10 +1,14 @@
|
|
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
|
5
|
-
from notionary.
|
6
|
-
|
7
|
-
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
5
|
+
from notionary.page.formatting.spacer_rules import SPACER_MARKER
|
6
|
+
from notionary.prompting.element_prompt_content import (
|
7
|
+
ElementPromptBuilder,
|
8
|
+
ElementPromptContent,
|
9
|
+
)
|
10
|
+
|
11
|
+
@auto_track_conversions
|
8
12
|
class ColumnElement(NotionBlockElement):
|
9
13
|
"""
|
10
14
|
Handles conversion between custom Markdown column syntax and Notion column blocks.
|
@@ -252,7 +256,7 @@ class ColumnElement(NotionBlockElement):
|
|
252
256
|
"""
|
253
257
|
if not (in_column and column_content):
|
254
258
|
return
|
255
|
-
|
259
|
+
|
256
260
|
processed_content = ColumnElement._preprocess_column_content(column_content)
|
257
261
|
|
258
262
|
column_blocks = converter_callback("\n".join(processed_content))
|
@@ -265,43 +269,45 @@ class ColumnElement(NotionBlockElement):
|
|
265
269
|
def is_multiline(cls) -> bool:
|
266
270
|
"""Column blocks span multiple lines."""
|
267
271
|
return True
|
268
|
-
|
272
|
+
|
269
273
|
@staticmethod
|
270
274
|
def _preprocess_column_content(lines: List[str]) -> List[str]:
|
271
275
|
"""
|
272
276
|
Preprocess column content to handle special cases like first headings.
|
273
|
-
|
277
|
+
|
274
278
|
This removes any spacer markers that might have been added before the first
|
275
279
|
heading in a column, as each column should have its own heading context.
|
276
|
-
|
280
|
+
|
277
281
|
Args:
|
278
282
|
lines: The lines of content for the column
|
279
|
-
|
283
|
+
|
280
284
|
Returns:
|
281
285
|
Processed lines ready for conversion
|
282
286
|
"""
|
283
|
-
from notionary.page.markdown_to_notion_converter import MarkdownToNotionConverter
|
284
|
-
|
285
287
|
processed_lines = []
|
286
288
|
found_first_heading = False
|
287
|
-
|
288
|
-
|
289
|
+
|
289
290
|
i = 0
|
290
291
|
while i < len(lines):
|
291
292
|
line = lines[i]
|
292
|
-
|
293
|
+
|
293
294
|
# Check if this is a heading line
|
294
295
|
if re.match(r"^(#{1,6})\s+(.+)$", line.strip()):
|
295
296
|
# If it's the first heading, look ahead to check for spacer
|
296
|
-
if
|
297
|
+
if (
|
298
|
+
not found_first_heading
|
299
|
+
and i > 0
|
300
|
+
and processed_lines
|
301
|
+
and processed_lines[-1] == SPACER_MARKER
|
302
|
+
):
|
297
303
|
# Remove spacer before first heading in column
|
298
304
|
processed_lines.pop()
|
299
|
-
|
305
|
+
|
300
306
|
found_first_heading = True
|
301
|
-
|
307
|
+
|
302
308
|
processed_lines.append(line)
|
303
309
|
i += 1
|
304
|
-
|
310
|
+
|
305
311
|
return processed_lines
|
306
312
|
|
307
313
|
@classmethod
|
@@ -331,30 +337,31 @@ class ColumnElement(NotionBlockElement):
|
|
331
337
|
":::\n"
|
332
338
|
":::"
|
333
339
|
)
|
334
|
-
.with_examples(
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
340
|
+
.with_examples(
|
341
|
+
[
|
342
|
+
"::: columns\n"
|
343
|
+
"::: column\n"
|
344
|
+
"## Features\n"
|
345
|
+
"- Fast response time\n"
|
346
|
+
"- Intuitive interface\n"
|
347
|
+
"- Regular updates\n"
|
348
|
+
":::\n"
|
349
|
+
"::: column\n"
|
350
|
+
"## Benefits\n"
|
351
|
+
"- Increased productivity\n"
|
352
|
+
"- Better collaboration\n"
|
353
|
+
"- Simplified workflows\n"
|
354
|
+
":::\n"
|
355
|
+
":::",
|
356
|
+
"::: columns\n"
|
357
|
+
"::: column\n"
|
358
|
+
"\n"
|
359
|
+
":::\n"
|
360
|
+
"::: column\n"
|
361
|
+
"This text appears next to the image, creating a media-with-caption style layout.\n"
|
362
|
+
":::\n"
|
363
|
+
":::",
|
364
|
+
]
|
365
|
+
)
|
359
366
|
.build()
|
360
|
-
)
|
367
|
+
)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional
|
3
3
|
|
4
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
5
5
|
from notionary.prompting.element_prompt_content import (
|
6
6
|
ElementPromptBuilder,
|
7
7
|
ElementPromptContent,
|
8
8
|
)
|
9
9
|
|
10
|
-
|
10
|
+
@auto_track_conversions
|
11
11
|
class DividerElement(NotionBlockElement):
|
12
12
|
"""
|
13
13
|
Handles conversion between Markdown horizontal dividers and Notion divider blocks.
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
|
9
|
-
|
9
|
+
@auto_track_conversions
|
10
10
|
class EmbedElement(NotionBlockElement):
|
11
11
|
"""
|
12
12
|
Handles conversion between Markdown embeds and Notion embed blocks.
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional
|
3
3
|
|
4
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
4
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
5
5
|
from notionary.prompting.element_prompt_content import (
|
6
6
|
ElementPromptBuilder,
|
7
7
|
ElementPromptContent,
|
8
8
|
)
|
9
9
|
from notionary.elements.text_inline_formatter import TextInlineFormatter
|
10
10
|
|
11
|
-
|
11
|
+
@auto_track_conversions
|
12
12
|
class HeadingElement(NotionBlockElement):
|
13
13
|
"""Handles conversion between Markdown headings and Notion heading blocks."""
|
14
14
|
|
@@ -91,4 +91,4 @@ class HeadingElement(NotionBlockElement):
|
|
91
91
|
.with_syntax("## Your Heading Text")
|
92
92
|
.with_standard_markdown()
|
93
93
|
.build()
|
94
|
-
)
|
94
|
+
)
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
3
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
4
4
|
from notionary.prompting.element_prompt_content import (
|
5
5
|
ElementPromptBuilder,
|
6
6
|
ElementPromptContent,
|
7
7
|
)
|
8
8
|
|
9
|
-
|
9
|
+
@auto_track_conversions
|
10
10
|
class ImageElement(NotionBlockElement):
|
11
11
|
"""
|
12
12
|
Handles conversion between Markdown images and Notion image blocks.
|
@@ -2,13 +2,13 @@ import re
|
|
2
2
|
from typing import Dict, Any, Optional, List
|
3
3
|
from typing_extensions import override
|
4
4
|
|
5
|
-
from notionary.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.elements.notion_block_element import NotionBlockElement, auto_track_conversions
|
6
6
|
from notionary.prompting.element_prompt_content import (
|
7
7
|
ElementPromptBuilder,
|
8
8
|
ElementPromptContent,
|
9
9
|
)
|
10
10
|
|
11
|
-
|
11
|
+
@auto_track_conversions
|
12
12
|
class MentionElement(NotionBlockElement):
|
13
13
|
"""
|
14
14
|
Handles conversion between Markdown mentions and Notion mention elements.
|