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.
- notionary/__init__.py +21 -6
- notionary/{core/converters → converters}/elements/audio_element.py +7 -5
- notionary/{core/converters → converters}/elements/bookmark_element.py +1 -1
- notionary/{core/converters → converters}/elements/callout_element.py +2 -2
- notionary/{core/converters → converters}/elements/code_block_element.py +1 -1
- notionary/{core/converters → converters}/elements/column_element.py +1 -1
- notionary/{core/converters → converters}/elements/divider_element.py +1 -1
- notionary/{core/converters → converters}/elements/embed_element.py +3 -5
- notionary/{core/converters → converters}/elements/heading_element.py +2 -2
- notionary/{core/converters → converters}/elements/image_element.py +1 -1
- notionary/{core/converters → converters}/elements/list_element.py +2 -2
- notionary/{core/converters → converters}/elements/paragraph_element.py +2 -2
- notionary/{core/converters → converters}/elements/qoute_element.py +1 -1
- notionary/{core/converters → converters}/elements/table_element.py +2 -2
- notionary/{core/converters → converters}/elements/todo_lists.py +2 -2
- notionary/{core/converters → converters}/elements/toggle_element.py +24 -21
- notionary/{core/converters → converters}/elements/video_element.py +1 -1
- notionary/{core/converters → converters}/markdown_to_notion_converter.py +72 -111
- notionary/{core/converters → converters}/notion_to_markdown_converter.py +2 -2
- notionary/{core/converters → converters}/registry/block_element_registry.py +5 -5
- notionary/{core/converters → converters}/registry/block_element_registry_builder.py +18 -18
- notionary/database/database_discovery.py +142 -0
- notionary/{core/database → database}/database_info_service.py +1 -1
- notionary/{core/database/notion_database_manager.py → database/notion_database.py} +33 -57
- notionary/{core/database/notion_database_manager_factory.py → database/notion_database_factory.py} +18 -16
- notionary/{core/notion_client.py → notion_client.py} +4 -2
- notionary/page/content/notion_page_content_chunker.py +84 -0
- notionary/{core/page → page}/content/page_content_manager.py +29 -13
- notionary/{core/page → page}/metadata/metadata_editor.py +59 -46
- notionary/{core/page → page}/metadata/notion_icon_manager.py +10 -12
- notionary/{core/page → page}/metadata/notion_page_cover_manager.py +16 -21
- notionary/page/notion_page.py +504 -0
- notionary/page/notion_page_factory.py +256 -0
- notionary/{core/page → page}/properites/database_property_service.py +115 -99
- notionary/{core/page → page}/properites/page_property_manager.py +81 -52
- notionary/{core/page → page}/properites/property_formatter.py +1 -1
- notionary/{core/page → page}/properites/property_operation_result.py +43 -30
- notionary/{core/page → page}/properites/property_value_extractor.py +26 -8
- notionary/{core/page → page}/relations/notion_page_relation_manager.py +72 -53
- notionary/{core/page → page}/relations/notion_page_title_resolver.py +12 -12
- notionary/{core/page → page}/relations/page_database_relation.py +15 -15
- notionary/{core/page → page}/relations/relation_operation_result.py +50 -41
- notionary/util/page_id_utils.py +14 -8
- {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/METADATA +1 -1
- notionary-0.1.13.dist-info/RECORD +56 -0
- notionary/core/database/notion_database_schema.py +0 -104
- notionary/core/page/notion_page_manager.py +0 -322
- notionary-0.1.11.dist-info/RECORD +0 -54
- /notionary/{core/converters → converters}/__init__.py +0 -0
- /notionary/{core/converters → converters}/elements/notion_block_element.py +0 -0
- /notionary/{core/converters → converters}/elements/text_inline_formatter.py +0 -0
- /notionary/{core/database → database}/models/page_result.py +0 -0
- {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/WHEEL +0 -0
- {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/licenses/LICENSE +0 -0
- {notionary-0.1.11.dist-info → notionary-0.1.13.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import Any, Dict, Optional
|
2
|
-
from notionary.
|
3
|
-
from notionary.
|
2
|
+
from notionary.notion_client import NotionClient
|
3
|
+
from notionary.page.properites.property_formatter import NotionPropertyFormatter
|
4
4
|
from notionary.util.logging_mixin import LoggingMixin
|
5
5
|
|
6
6
|
|
@@ -20,90 +20,103 @@ class MetadataEditor(LoggingMixin):
|
|
20
20
|
},
|
21
21
|
)
|
22
22
|
|
23
|
-
async def set_property(
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
"properties": {
|
45
|
-
property_name: property_payload
|
46
|
-
}
|
47
|
-
},
|
23
|
+
async def set_property(
|
24
|
+
self, property_name: str, property_value: Any, property_type: str
|
25
|
+
) -> Optional[Dict[str, Any]]:
|
26
|
+
"""
|
27
|
+
Generic method to set any property on a Notion page.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
property_name: The name of the property in Notion
|
31
|
+
property_value: The value to set
|
32
|
+
property_type: The type of property ('select', 'multi_select', 'status', 'relation', etc.)
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Optional[Dict[str, Any]]: The API response or None if the operation fails
|
36
|
+
"""
|
37
|
+
property_payload = self._property_formatter.format_value(
|
38
|
+
property_type, property_value
|
39
|
+
)
|
40
|
+
|
41
|
+
if not property_payload:
|
42
|
+
self.logger.warning(
|
43
|
+
"Could not create payload for property type: %s", property_type
|
48
44
|
)
|
45
|
+
return None
|
49
46
|
|
47
|
+
return await self._client.patch(
|
48
|
+
f"pages/{self.page_id}",
|
49
|
+
{"properties": {property_name: property_payload}},
|
50
|
+
)
|
50
51
|
|
51
52
|
async def get_property_schema(self) -> Dict[str, Dict[str, Any]]:
|
52
53
|
"""
|
53
54
|
Retrieves the schema for all properties of the page.
|
54
|
-
|
55
|
+
|
55
56
|
Returns:
|
56
57
|
Dict[str, Dict[str, Any]]: A dictionary mapping property names to their schema
|
57
58
|
"""
|
58
59
|
page_data = await self._client.get_page(self.page_id)
|
59
60
|
property_schema = {}
|
60
|
-
|
61
|
+
|
61
62
|
if not page_data or "properties" not in page_data:
|
62
63
|
return property_schema
|
63
|
-
|
64
|
+
|
64
65
|
for prop_name, prop_data in page_data["properties"].items():
|
65
66
|
prop_type = prop_data.get("type")
|
66
67
|
property_schema[prop_name] = {
|
67
68
|
"id": prop_data.get("id"),
|
68
69
|
"type": prop_type,
|
69
|
-
"name": prop_name
|
70
|
+
"name": prop_name,
|
70
71
|
}
|
71
|
-
|
72
|
+
|
72
73
|
try:
|
73
74
|
if prop_type == "select" and "select" in prop_data:
|
74
75
|
# Make sure prop_data["select"] is a dictionary before calling .get()
|
75
76
|
if isinstance(prop_data["select"], dict):
|
76
|
-
property_schema[prop_name]["options"] = prop_data["select"].get(
|
77
|
+
property_schema[prop_name]["options"] = prop_data["select"].get(
|
78
|
+
"options", []
|
79
|
+
)
|
77
80
|
elif prop_type == "multi_select" and "multi_select" in prop_data:
|
78
81
|
# Make sure prop_data["multi_select"] is a dictionary before calling .get()
|
79
82
|
if isinstance(prop_data["multi_select"], dict):
|
80
|
-
property_schema[prop_name]["options"] = prop_data[
|
83
|
+
property_schema[prop_name]["options"] = prop_data[
|
84
|
+
"multi_select"
|
85
|
+
].get("options", [])
|
81
86
|
elif prop_type == "status" and "status" in prop_data:
|
82
87
|
# Make sure prop_data["status"] is a dictionary before calling .get()
|
83
88
|
if isinstance(prop_data["status"], dict):
|
84
|
-
property_schema[prop_name]["options"] = prop_data["status"].get(
|
89
|
+
property_schema[prop_name]["options"] = prop_data["status"].get(
|
90
|
+
"options", []
|
91
|
+
)
|
85
92
|
except Exception as e:
|
86
|
-
if hasattr(self,
|
87
|
-
self.logger.warning(
|
88
|
-
|
93
|
+
if hasattr(self, "logger") and self.logger:
|
94
|
+
self.logger.warning(
|
95
|
+
"Error processing property schema for '%s': %s", prop_name, e
|
96
|
+
)
|
97
|
+
|
89
98
|
return property_schema
|
90
|
-
|
91
|
-
async def set_property_by_name(
|
99
|
+
|
100
|
+
async def set_property_by_name(
|
101
|
+
self, property_name: str, value: Any
|
102
|
+
) -> Optional[Dict[str, Any]]:
|
92
103
|
"""
|
93
104
|
Sets a property value based on the property name, automatically detecting the property type.
|
94
|
-
|
105
|
+
|
95
106
|
Args:
|
96
107
|
property_name: The name of the property in Notion
|
97
108
|
value: The value to set
|
98
|
-
|
109
|
+
|
99
110
|
Returns:
|
100
111
|
Optional[Dict[str, Any]]: The API response or None if the operation fails
|
101
112
|
"""
|
102
113
|
property_schema = await self.get_property_schema()
|
103
|
-
|
114
|
+
|
104
115
|
if property_name not in property_schema:
|
105
|
-
self.logger.warning(
|
116
|
+
self.logger.warning(
|
117
|
+
"Property '%s' not found in database schema", property_name
|
118
|
+
)
|
106
119
|
return None
|
107
|
-
|
120
|
+
|
108
121
|
property_type = property_schema[property_name]["type"]
|
109
|
-
return await self.set_property(property_name, value, property_type)
|
122
|
+
return await self.set_property(property_name, value, property_type)
|
@@ -1,13 +1,14 @@
|
|
1
1
|
from typing import Any, Dict, Optional
|
2
2
|
|
3
|
-
from notionary.
|
3
|
+
from notionary.notion_client import NotionClient
|
4
4
|
from notionary.util.logging_mixin import LoggingMixin
|
5
5
|
|
6
|
+
|
6
7
|
class NotionPageIconManager(LoggingMixin):
|
7
8
|
def __init__(self, page_id: str, client: NotionClient):
|
8
9
|
self.page_id = page_id
|
9
10
|
self._client = client
|
10
|
-
|
11
|
+
|
11
12
|
async def set_icon(
|
12
13
|
self, emoji: Optional[str] = None, external_url: Optional[str] = None
|
13
14
|
) -> Optional[Dict[str, Any]]:
|
@@ -19,28 +20,25 @@ class NotionPageIconManager(LoggingMixin):
|
|
19
20
|
return None
|
20
21
|
|
21
22
|
return await self._client.patch(f"pages/{self.page_id}", {"icon": icon})
|
22
|
-
|
23
|
-
|
23
|
+
|
24
24
|
async def get_icon(self) -> Optional[str]:
|
25
25
|
"""
|
26
26
|
Retrieves the page icon - either emoji or external URL.
|
27
|
-
|
27
|
+
|
28
28
|
Returns:
|
29
29
|
str: Emoji character or URL if set, None if no icon
|
30
30
|
"""
|
31
31
|
page_data = await self._client.get_page(self.page_id)
|
32
|
-
|
32
|
+
|
33
33
|
if not page_data or "icon" not in page_data:
|
34
34
|
return None
|
35
|
-
|
35
|
+
|
36
36
|
icon_data = page_data.get("icon", {})
|
37
37
|
icon_type = icon_data.get("type")
|
38
|
-
|
38
|
+
|
39
39
|
if icon_type == "emoji":
|
40
40
|
return icon_data.get("emoji")
|
41
41
|
elif icon_type == "external":
|
42
42
|
return icon_data.get("external", {}).get("url")
|
43
|
-
|
44
|
-
return None
|
45
|
-
|
46
|
-
|
43
|
+
|
44
|
+
return None
|
@@ -1,48 +1,43 @@
|
|
1
|
-
|
2
1
|
import random
|
3
2
|
from typing import Any, Dict, Optional
|
4
|
-
from notionary.
|
3
|
+
from notionary.notion_client import NotionClient
|
5
4
|
from notionary.util.logging_mixin import LoggingMixin
|
6
5
|
|
6
|
+
|
7
7
|
class NotionPageCoverManager(LoggingMixin):
|
8
8
|
def __init__(self, page_id: str, client: NotionClient):
|
9
9
|
self.page_id = page_id
|
10
10
|
self._client = client
|
11
|
-
|
11
|
+
|
12
12
|
async def set_cover(self, external_url: str) -> Optional[Dict[str, Any]]:
|
13
|
-
"""Sets a cover image from an external URL.
|
14
|
-
|
15
|
-
|
13
|
+
"""Sets a cover image from an external URL."""
|
14
|
+
|
16
15
|
return await self._client.patch(
|
17
16
|
f"pages/{self.page_id}",
|
18
17
|
{"cover": {"type": "external", "external": {"url": external_url}}},
|
19
18
|
)
|
20
|
-
|
19
|
+
|
21
20
|
async def set_random_gradient_cover(self) -> Optional[Dict[str, Any]]:
|
22
|
-
"""
|
23
|
-
"""
|
21
|
+
"""Sets a random gradient cover from Notion's default gradient covers."""
|
24
22
|
default_notion_covers = [
|
25
|
-
"https://www.notion.so/images/page-cover/gradients_8.png",
|
23
|
+
"https://www.notion.so/images/page-cover/gradients_8.png",
|
26
24
|
"https://www.notion.so/images/page-cover/gradients_2.png",
|
27
25
|
"https://www.notion.so/images/page-cover/gradients_11.jpg",
|
28
26
|
"https://www.notion.so/images/page-cover/gradients_10.jpg",
|
29
27
|
"https://www.notion.so/images/page-cover/gradients_5.png",
|
30
|
-
"https://www.notion.so/images/page-cover/gradients_3.png"
|
28
|
+
"https://www.notion.so/images/page-cover/gradients_3.png",
|
31
29
|
]
|
32
|
-
|
30
|
+
|
33
31
|
random_cover_url = random.choice(default_notion_covers)
|
34
|
-
|
32
|
+
|
35
33
|
return await self.set_cover(random_cover_url)
|
36
|
-
|
37
|
-
|
34
|
+
|
38
35
|
async def get_cover_url(self) -> str:
|
39
|
-
"""Retrieves the current cover image URL of the page.
|
40
|
-
|
41
|
-
|
36
|
+
"""Retrieves the current cover image URL of the page."""
|
37
|
+
|
42
38
|
page_data = await self._client.get_page(self.page_id)
|
43
|
-
|
39
|
+
|
44
40
|
if not page_data:
|
45
41
|
return ""
|
46
|
-
|
42
|
+
|
47
43
|
return page_data.get("cover", {}).get("external", {}).get("url", "")
|
48
|
-
|