notionary 0.2.18__py3-none-any.whl → 0.2.21__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 +8 -4
- notionary/base_notion_client.py +3 -1
- notionary/blocks/__init__.py +2 -91
- notionary/blocks/_bootstrap.py +263 -0
- notionary/blocks/audio/__init__.py +8 -2
- notionary/blocks/audio/audio_element.py +42 -104
- notionary/blocks/audio/audio_markdown_node.py +3 -1
- notionary/blocks/audio/audio_models.py +6 -55
- notionary/blocks/base_block_element.py +30 -0
- notionary/blocks/bookmark/__init__.py +9 -2
- notionary/blocks/bookmark/bookmark_element.py +46 -139
- notionary/blocks/bookmark/bookmark_markdown_node.py +3 -1
- notionary/blocks/bookmark/bookmark_models.py +15 -0
- notionary/blocks/breadcrumbs/__init__.py +17 -0
- notionary/blocks/breadcrumbs/breadcrumb_element.py +39 -0
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +32 -0
- notionary/blocks/breadcrumbs/breadcrumb_models.py +12 -0
- notionary/blocks/bulleted_list/__init__.py +12 -2
- notionary/blocks/bulleted_list/bulleted_list_element.py +40 -55
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +2 -1
- notionary/blocks/bulleted_list/bulleted_list_models.py +18 -0
- notionary/blocks/callout/__init__.py +9 -2
- notionary/blocks/callout/callout_element.py +40 -89
- notionary/blocks/callout/callout_markdown_node.py +3 -1
- notionary/blocks/callout/callout_models.py +33 -0
- notionary/blocks/child_database/__init__.py +7 -0
- notionary/blocks/child_database/child_database_models.py +19 -0
- notionary/blocks/child_page/__init__.py +9 -0
- notionary/blocks/child_page/child_page_models.py +12 -0
- notionary/blocks/{shared/block_client.py → client.py} +55 -54
- notionary/blocks/code/__init__.py +6 -2
- notionary/blocks/code/code_element.py +53 -187
- notionary/blocks/code/code_markdown_node.py +13 -13
- notionary/blocks/code/code_models.py +94 -0
- notionary/blocks/column/__init__.py +25 -1
- notionary/blocks/column/column_element.py +40 -314
- notionary/blocks/column/column_list_element.py +37 -0
- notionary/blocks/column/column_list_markdown_node.py +50 -0
- notionary/blocks/column/column_markdown_node.py +59 -0
- notionary/blocks/column/column_models.py +26 -0
- notionary/blocks/divider/__init__.py +9 -2
- notionary/blocks/divider/divider_element.py +26 -49
- notionary/blocks/divider/divider_markdown_node.py +2 -1
- notionary/blocks/divider/divider_models.py +12 -0
- notionary/blocks/embed/__init__.py +9 -2
- notionary/blocks/embed/embed_element.py +47 -114
- notionary/blocks/embed/embed_markdown_node.py +3 -1
- notionary/blocks/embed/embed_models.py +14 -0
- notionary/blocks/equation/__init__.py +14 -0
- notionary/blocks/equation/equation_element.py +80 -0
- notionary/blocks/equation/equation_element_markdown_node.py +36 -0
- notionary/blocks/equation/equation_models.py +11 -0
- notionary/blocks/file/__init__.py +25 -0
- notionary/blocks/file/file_element.py +93 -0
- notionary/blocks/file/file_element_markdown_node.py +35 -0
- notionary/blocks/file/file_element_models.py +39 -0
- notionary/blocks/heading/__init__.py +16 -2
- notionary/blocks/heading/heading_element.py +67 -72
- notionary/blocks/heading/heading_markdown_node.py +2 -1
- notionary/blocks/heading/heading_models.py +29 -0
- notionary/blocks/image_block/__init__.py +13 -0
- notionary/blocks/image_block/image_element.py +84 -0
- notionary/blocks/{image → image_block}/image_markdown_node.py +3 -1
- notionary/blocks/image_block/image_models.py +10 -0
- notionary/blocks/models.py +172 -0
- notionary/blocks/numbered_list/__init__.py +12 -2
- notionary/blocks/numbered_list/numbered_list_element.py +33 -58
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +3 -1
- notionary/blocks/numbered_list/numbered_list_models.py +17 -0
- notionary/blocks/paragraph/__init__.py +12 -2
- notionary/blocks/paragraph/paragraph_element.py +27 -69
- notionary/blocks/paragraph/paragraph_markdown_node.py +2 -1
- notionary/blocks/paragraph/paragraph_models.py +16 -0
- notionary/blocks/pdf/__init__.py +13 -0
- notionary/blocks/pdf/pdf_element.py +91 -0
- notionary/blocks/pdf/pdf_markdown_node.py +35 -0
- notionary/blocks/pdf/pdf_models.py +11 -0
- notionary/blocks/quote/__init__.py +11 -2
- notionary/blocks/quote/quote_element.py +31 -65
- notionary/blocks/quote/quote_markdown_node.py +4 -1
- notionary/blocks/quote/quote_models.py +18 -0
- notionary/blocks/registry/__init__.py +4 -0
- notionary/blocks/registry/block_registry.py +75 -91
- notionary/blocks/registry/block_registry_builder.py +107 -59
- notionary/blocks/rich_text/__init__.py +33 -0
- notionary/blocks/rich_text/rich_text_models.py +188 -0
- notionary/blocks/rich_text/text_inline_formatter.py +125 -0
- notionary/blocks/table/__init__.py +16 -2
- notionary/blocks/table/table_element.py +48 -241
- notionary/blocks/table/table_markdown_node.py +2 -1
- notionary/blocks/table/table_models.py +28 -0
- notionary/blocks/table_of_contents/__init__.py +19 -0
- notionary/blocks/table_of_contents/table_of_contents_element.py +51 -0
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +35 -0
- notionary/blocks/table_of_contents/table_of_contents_models.py +18 -0
- notionary/blocks/todo/__init__.py +9 -2
- notionary/blocks/todo/todo_element.py +38 -95
- notionary/blocks/todo/todo_markdown_node.py +2 -1
- notionary/blocks/todo/todo_models.py +19 -0
- notionary/blocks/toggle/__init__.py +13 -3
- notionary/blocks/toggle/toggle_element.py +57 -264
- notionary/blocks/toggle/toggle_markdown_node.py +24 -14
- notionary/blocks/toggle/toggle_models.py +17 -0
- notionary/blocks/toggleable_heading/__init__.py +6 -2
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +74 -244
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +26 -18
- notionary/blocks/types.py +61 -0
- notionary/blocks/video/__init__.py +8 -2
- notionary/blocks/video/video_element.py +67 -143
- notionary/blocks/video/video_element_models.py +10 -0
- notionary/blocks/video/video_markdown_node.py +3 -1
- notionary/database/client.py +3 -8
- notionary/database/database.py +13 -14
- notionary/database/database_filter_builder.py +2 -2
- notionary/database/database_provider.py +5 -4
- notionary/database/models.py +337 -0
- notionary/database/notion_database.py +6 -7
- notionary/file_upload/client.py +5 -7
- notionary/file_upload/models.py +2 -1
- notionary/file_upload/notion_file_upload.py +2 -3
- notionary/markdown/markdown_builder.py +722 -0
- notionary/markdown/markdown_document_model.py +228 -0
- notionary/{blocks → markdown}/markdown_node.py +1 -0
- notionary/models/notion_database_response.py +0 -338
- notionary/page/client.py +9 -10
- notionary/page/models.py +327 -0
- notionary/page/notion_page.py +99 -52
- notionary/page/notion_text_length_utils.py +119 -0
- notionary/page/{content/page_content_writer.py → page_content_writer.py} +88 -38
- notionary/page/reader/handler/__init__.py +17 -0
- notionary/page/reader/handler/base_block_renderer.py +44 -0
- notionary/page/reader/handler/block_processing_context.py +35 -0
- notionary/page/reader/handler/block_rendering_context.py +43 -0
- notionary/page/reader/handler/column_list_renderer.py +51 -0
- notionary/page/reader/handler/column_renderer.py +60 -0
- notionary/page/reader/handler/line_renderer.py +60 -0
- notionary/page/reader/handler/toggle_renderer.py +69 -0
- notionary/page/reader/handler/toggleable_heading_renderer.py +89 -0
- notionary/page/reader/page_content_retriever.py +69 -0
- notionary/page/search_filter_builder.py +2 -1
- notionary/page/writer/handler/__init__.py +22 -0
- notionary/page/writer/handler/code_handler.py +100 -0
- notionary/page/writer/handler/column_handler.py +141 -0
- notionary/page/writer/handler/column_list_handler.py +139 -0
- notionary/page/writer/handler/line_handler.py +35 -0
- notionary/page/writer/handler/line_processing_context.py +54 -0
- notionary/page/writer/handler/regular_line_handler.py +92 -0
- notionary/page/writer/handler/table_handler.py +130 -0
- notionary/page/writer/handler/toggle_handler.py +153 -0
- notionary/page/writer/handler/toggleable_heading_handler.py +167 -0
- notionary/page/writer/markdown_to_notion_converter.py +76 -0
- notionary/telemetry/__init__.py +2 -2
- notionary/telemetry/service.py +4 -3
- notionary/user/__init__.py +2 -2
- notionary/user/base_notion_user.py +2 -1
- notionary/user/client.py +2 -3
- notionary/user/models.py +1 -0
- notionary/user/notion_bot_user.py +4 -5
- notionary/user/notion_user.py +3 -4
- notionary/user/notion_user_manager.py +3 -2
- notionary/user/notion_user_provider.py +1 -1
- notionary/util/__init__.py +3 -2
- notionary/util/fuzzy.py +2 -1
- notionary/util/logging_mixin.py +2 -2
- notionary/util/singleton_metaclass.py +1 -1
- notionary/workspace.py +3 -2
- {notionary-0.2.18.dist-info → notionary-0.2.21.dist-info}/METADATA +12 -8
- notionary-0.2.21.dist-info/RECORD +185 -0
- notionary/blocks/document/__init__.py +0 -7
- notionary/blocks/document/document_element.py +0 -102
- notionary/blocks/document/document_markdown_node.py +0 -31
- notionary/blocks/image/__init__.py +0 -7
- notionary/blocks/image/image_element.py +0 -151
- notionary/blocks/markdown_builder.py +0 -356
- notionary/blocks/mention/__init__.py +0 -7
- notionary/blocks/mention/mention_element.py +0 -229
- notionary/blocks/mention/mention_markdown_node.py +0 -38
- notionary/blocks/prompts/element_prompt_builder.py +0 -83
- notionary/blocks/prompts/element_prompt_content.py +0 -41
- notionary/blocks/shared/__init__.py +0 -0
- notionary/blocks/shared/models.py +0 -710
- notionary/blocks/shared/notion_block_element.py +0 -37
- notionary/blocks/shared/text_inline_formatter.py +0 -262
- notionary/blocks/shared/text_inline_formatter_new.py +0 -139
- notionary/blocks/toggleable_heading/toggleable_heading_models.py +0 -0
- notionary/database/models/page_result.py +0 -10
- notionary/models/notion_block_response.py +0 -264
- notionary/models/notion_page_response.py +0 -78
- notionary/models/search_response.py +0 -0
- notionary/page/__init__.py +0 -0
- notionary/page/content/notion_text_length_utils.py +0 -87
- notionary/page/content/page_content_retriever.py +0 -52
- notionary/page/formatting/line_processor.py +0 -153
- notionary/page/formatting/markdown_to_notion_converter.py +0 -153
- notionary/page/markdown_syntax_prompt_generator.py +0 -114
- notionary/page/notion_to_markdown_converter.py +0 -179
- notionary/page/properites/property_value_extractor.py +0 -0
- notionary-0.2.18.dist-info/RECORD +0 -149
- /notionary/{blocks/document/document_models.py → markdown/___init__.py} +0 -0
- /notionary/{blocks/image/image_models.py → markdown/makdown_document_model.py} +0 -0
- /notionary/page/{content/markdown_whitespace_processor.py → markdown_whitespace_processor.py} +0 -0
- /notionary/{blocks/mention/mention_models.py → page/reader/handler/context.py} +0 -0
- {notionary-0.2.18.dist-info → notionary-0.2.21.dist-info}/LICENSE +0 -0
- {notionary-0.2.18.dist-info → notionary-0.2.21.dist-info}/WHEEL +0 -0
notionary/page/models.py
ADDED
@@ -0,0 +1,327 @@
|
|
1
|
+
from typing import Any, Literal, Optional, Union
|
2
|
+
|
3
|
+
from pydantic import BaseModel, ConfigDict
|
4
|
+
|
5
|
+
|
6
|
+
class TextContent(BaseModel):
|
7
|
+
content: str
|
8
|
+
link: Optional[str] = None
|
9
|
+
|
10
|
+
|
11
|
+
class RichText(BaseModel):
|
12
|
+
type: str
|
13
|
+
text: TextContent
|
14
|
+
plain_text: str
|
15
|
+
href: Optional[str] = None
|
16
|
+
|
17
|
+
|
18
|
+
class User(BaseModel):
|
19
|
+
object: str
|
20
|
+
id: str
|
21
|
+
|
22
|
+
|
23
|
+
class Parent(BaseModel):
|
24
|
+
type: Literal["page_id", "workspace", "block_id", "database_id"]
|
25
|
+
page_id: Optional[str] = None
|
26
|
+
block_id: Optional[str] = None
|
27
|
+
database_id: Optional[str] = None
|
28
|
+
|
29
|
+
|
30
|
+
# Rich text types for Pydantic models
|
31
|
+
class TextContentPydantic(BaseModel):
|
32
|
+
content: str
|
33
|
+
link: Optional[dict[str, str]] = None
|
34
|
+
|
35
|
+
|
36
|
+
class Annotations(BaseModel):
|
37
|
+
bold: bool
|
38
|
+
italic: bool
|
39
|
+
strikethrough: bool
|
40
|
+
underline: bool
|
41
|
+
code: bool
|
42
|
+
color: str
|
43
|
+
|
44
|
+
|
45
|
+
class RichTextItemPydantic(BaseModel):
|
46
|
+
type: str # 'text', 'mention', 'equation'
|
47
|
+
text: Optional[TextContentPydantic] = None
|
48
|
+
annotations: Annotations
|
49
|
+
plain_text: str
|
50
|
+
href: Optional[str] = None
|
51
|
+
|
52
|
+
|
53
|
+
# Cover and Icon types
|
54
|
+
class ExternalFile(BaseModel):
|
55
|
+
"""Represents an external file, e.g., for cover images."""
|
56
|
+
|
57
|
+
url: str
|
58
|
+
|
59
|
+
|
60
|
+
class Cover(BaseModel):
|
61
|
+
"""Cover image for a Notion page."""
|
62
|
+
|
63
|
+
type: str
|
64
|
+
external: ExternalFile
|
65
|
+
|
66
|
+
|
67
|
+
class EmojiIcon(BaseModel):
|
68
|
+
type: Literal["emoji"]
|
69
|
+
emoji: str
|
70
|
+
|
71
|
+
|
72
|
+
class ExternalIcon(BaseModel):
|
73
|
+
type: Literal["external"]
|
74
|
+
external: ExternalFile
|
75
|
+
|
76
|
+
|
77
|
+
Icon = Union[EmojiIcon, ExternalIcon]
|
78
|
+
|
79
|
+
|
80
|
+
# Database property schema types (these are schema definitions, not values)
|
81
|
+
class StatusOption(BaseModel):
|
82
|
+
id: str
|
83
|
+
name: str
|
84
|
+
color: str
|
85
|
+
description: Optional[str] = None
|
86
|
+
|
87
|
+
|
88
|
+
class StatusGroup(BaseModel):
|
89
|
+
id: str
|
90
|
+
name: str
|
91
|
+
color: str
|
92
|
+
option_ids: list[str]
|
93
|
+
|
94
|
+
|
95
|
+
class StatusPropertySchema(BaseModel):
|
96
|
+
options: list[StatusOption]
|
97
|
+
groups: list[StatusGroup]
|
98
|
+
|
99
|
+
|
100
|
+
class DatabaseStatusProperty(BaseModel):
|
101
|
+
id: str
|
102
|
+
name: str
|
103
|
+
type: Literal["status"]
|
104
|
+
status: StatusPropertySchema
|
105
|
+
|
106
|
+
|
107
|
+
class RelationPropertySchema(BaseModel):
|
108
|
+
database_id: str
|
109
|
+
type: str # "single_property"
|
110
|
+
single_property: dict[str, Any]
|
111
|
+
|
112
|
+
|
113
|
+
class DatabaseRelationProperty(BaseModel):
|
114
|
+
id: str
|
115
|
+
name: str
|
116
|
+
type: Literal["relation"]
|
117
|
+
relation: RelationPropertySchema
|
118
|
+
|
119
|
+
|
120
|
+
class DatabaseUrlProperty(BaseModel):
|
121
|
+
id: str
|
122
|
+
name: str
|
123
|
+
type: Literal["url"]
|
124
|
+
url: dict[str, Any] # Usually empty dict
|
125
|
+
|
126
|
+
|
127
|
+
class DatabaseRichTextProperty(BaseModel):
|
128
|
+
id: str
|
129
|
+
name: str
|
130
|
+
type: Literal["rich_text"]
|
131
|
+
rich_text: dict[str, Any] # Usually empty dict
|
132
|
+
|
133
|
+
|
134
|
+
class MultiSelectOption(BaseModel):
|
135
|
+
id: str
|
136
|
+
name: str
|
137
|
+
color: str
|
138
|
+
description: Optional[str] = None
|
139
|
+
|
140
|
+
|
141
|
+
class MultiSelectPropertySchema(BaseModel):
|
142
|
+
options: list[MultiSelectOption]
|
143
|
+
|
144
|
+
|
145
|
+
class DatabaseMultiSelectProperty(BaseModel):
|
146
|
+
id: str
|
147
|
+
name: str
|
148
|
+
type: Literal["multi_select"]
|
149
|
+
multi_select: MultiSelectPropertySchema
|
150
|
+
|
151
|
+
|
152
|
+
class DatabaseTitleProperty(BaseModel):
|
153
|
+
id: str
|
154
|
+
name: str
|
155
|
+
type: Literal["title"]
|
156
|
+
title: dict[str, Any] # Usually empty dict
|
157
|
+
|
158
|
+
|
159
|
+
# Generic database property for unknown types
|
160
|
+
class GenericDatabaseProperty(BaseModel):
|
161
|
+
id: str
|
162
|
+
name: str
|
163
|
+
type: str
|
164
|
+
|
165
|
+
model_config = ConfigDict(extra="allow")
|
166
|
+
|
167
|
+
|
168
|
+
# Union of all database property types
|
169
|
+
DatabaseProperty = Union[
|
170
|
+
DatabaseStatusProperty,
|
171
|
+
DatabaseRelationProperty,
|
172
|
+
DatabaseUrlProperty,
|
173
|
+
DatabaseRichTextProperty,
|
174
|
+
DatabaseMultiSelectProperty,
|
175
|
+
DatabaseTitleProperty,
|
176
|
+
GenericDatabaseProperty,
|
177
|
+
]
|
178
|
+
|
179
|
+
|
180
|
+
# Page property value types (these are actual values, not schemas)
|
181
|
+
class StatusValue(BaseModel):
|
182
|
+
id: str
|
183
|
+
name: str
|
184
|
+
color: str
|
185
|
+
|
186
|
+
|
187
|
+
class StatusProperty(BaseModel):
|
188
|
+
id: str
|
189
|
+
type: str # 'status'
|
190
|
+
status: Optional[StatusValue] = None
|
191
|
+
|
192
|
+
|
193
|
+
class RelationItem(BaseModel):
|
194
|
+
id: str
|
195
|
+
|
196
|
+
|
197
|
+
class RelationProperty(BaseModel):
|
198
|
+
id: str
|
199
|
+
type: str # 'relation'
|
200
|
+
relation: list[RelationItem]
|
201
|
+
has_more: bool
|
202
|
+
|
203
|
+
|
204
|
+
class UrlProperty(BaseModel):
|
205
|
+
id: str
|
206
|
+
type: str # 'url'
|
207
|
+
url: Optional[str] = None
|
208
|
+
|
209
|
+
|
210
|
+
class RichTextProperty(BaseModel):
|
211
|
+
id: str
|
212
|
+
type: str # 'rich_text'
|
213
|
+
rich_text: list[RichTextItemPydantic]
|
214
|
+
|
215
|
+
|
216
|
+
class MultiSelectItem(BaseModel):
|
217
|
+
id: str
|
218
|
+
name: str
|
219
|
+
color: str
|
220
|
+
|
221
|
+
|
222
|
+
class MultiSelectProperty(BaseModel):
|
223
|
+
id: str
|
224
|
+
type: str # 'multi_select'
|
225
|
+
multi_select: list[MultiSelectItem]
|
226
|
+
|
227
|
+
|
228
|
+
class TitleProperty(BaseModel):
|
229
|
+
id: str
|
230
|
+
type: str # 'title'
|
231
|
+
title: list[RichTextItemPydantic]
|
232
|
+
|
233
|
+
|
234
|
+
# Cover types
|
235
|
+
class ExternalCover(BaseModel):
|
236
|
+
url: str
|
237
|
+
|
238
|
+
|
239
|
+
class NotionCover(BaseModel):
|
240
|
+
type: str # 'external', 'file'
|
241
|
+
external: Optional[ExternalCover] = None
|
242
|
+
|
243
|
+
|
244
|
+
# Parent types for Pydantic
|
245
|
+
class NotionParent(BaseModel):
|
246
|
+
type: str # 'database_id', 'page_id', 'workspace'
|
247
|
+
database_id: Optional[str] = None
|
248
|
+
page_id: Optional[str] = None
|
249
|
+
|
250
|
+
|
251
|
+
# User type for Pydantic
|
252
|
+
class NotionUser(BaseModel):
|
253
|
+
object: str # 'user'
|
254
|
+
id: str
|
255
|
+
|
256
|
+
|
257
|
+
# Database object
|
258
|
+
class NotionDatabaseResponse(BaseModel):
|
259
|
+
"""
|
260
|
+
Represents the response from the Notion API when retrieving a database.
|
261
|
+
"""
|
262
|
+
|
263
|
+
object: Literal["database"]
|
264
|
+
id: str
|
265
|
+
cover: Optional[Cover] = None
|
266
|
+
icon: Optional[Icon] = None
|
267
|
+
created_time: str
|
268
|
+
last_edited_time: str
|
269
|
+
created_by: NotionUser
|
270
|
+
last_edited_by: NotionUser
|
271
|
+
title: list[RichTextItemPydantic]
|
272
|
+
description: list[Any]
|
273
|
+
is_inline: bool
|
274
|
+
properties: dict[
|
275
|
+
str, Any
|
276
|
+
] # Using Any for flexibility with different property schemas
|
277
|
+
parent: NotionParent
|
278
|
+
url: str
|
279
|
+
public_url: Optional[str] = None
|
280
|
+
archived: bool
|
281
|
+
in_trash: bool
|
282
|
+
|
283
|
+
|
284
|
+
class NotionPageResponse(BaseModel):
|
285
|
+
object: Literal["page"]
|
286
|
+
id: str
|
287
|
+
created_time: str
|
288
|
+
last_edited_time: str
|
289
|
+
created_by: NotionUser
|
290
|
+
last_edited_by: NotionUser
|
291
|
+
cover: Optional[NotionCover] = None
|
292
|
+
icon: Optional[Icon] = None
|
293
|
+
parent: NotionParent
|
294
|
+
archived: bool
|
295
|
+
in_trash: bool
|
296
|
+
properties: dict[str, Any]
|
297
|
+
url: str
|
298
|
+
public_url: Optional[str] = None
|
299
|
+
|
300
|
+
|
301
|
+
# Specific response type for database queries (pages only)
|
302
|
+
class NotionQueryDatabaseResponse(BaseModel):
|
303
|
+
"""
|
304
|
+
Notion database query response model for querying pages within a database.
|
305
|
+
"""
|
306
|
+
|
307
|
+
object: Literal["list"]
|
308
|
+
results: list[NotionPageResponse]
|
309
|
+
next_cursor: Optional[str] = None
|
310
|
+
has_more: bool
|
311
|
+
type: Literal["page_or_database"]
|
312
|
+
page_or_database: dict[str, Any]
|
313
|
+
request_id: str
|
314
|
+
|
315
|
+
|
316
|
+
class NotionDatabaseSearchResponse(BaseModel):
|
317
|
+
"""
|
318
|
+
Notion search response model for database-only searches.
|
319
|
+
"""
|
320
|
+
|
321
|
+
object: Literal["list"]
|
322
|
+
results: list[NotionDatabaseResponse]
|
323
|
+
next_cursor: Optional[str] = None
|
324
|
+
has_more: bool
|
325
|
+
type: Literal["page_or_database"]
|
326
|
+
page_or_database: dict[str, Any]
|
327
|
+
request_id: str
|
notionary/page/notion_page.py
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
+
|
2
3
|
import asyncio
|
3
|
-
from typing import Any, Dict, Optional, TYPE_CHECKING
|
4
4
|
import random
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
5
6
|
|
6
|
-
from notionary.blocks import
|
7
|
-
from notionary.models
|
8
|
-
from notionary.
|
7
|
+
from notionary.blocks.client import NotionBlockClient
|
8
|
+
from notionary.blocks.models import DatabaseParent
|
9
|
+
from notionary.blocks.registry.block_registry import BlockRegistry
|
10
|
+
from notionary.blocks.registry.block_registry_builder import BlockRegistryBuilder
|
11
|
+
from notionary.markdown.markdown_builder import MarkdownBuilder
|
9
12
|
from notionary.page.client import NotionPageClient
|
10
|
-
from notionary.page.
|
11
|
-
|
12
|
-
|
13
|
-
from notionary.page.content.page_content_writer import PageContentWriter
|
13
|
+
from notionary.page.models import NotionPageResponse
|
14
|
+
from notionary.page.page_content_writer import PageContentWriter
|
14
15
|
from notionary.page.property_formatter import NotionPropertyFormatter
|
16
|
+
from notionary.page.reader.page_content_retriever import PageContentRetriever
|
15
17
|
from notionary.page.utils import extract_property_value
|
16
|
-
|
17
|
-
from notionary.util import LoggingMixin, format_uuid, factory_only
|
18
|
+
from notionary.util import LoggingMixin, extract_uuid, factory_only, format_uuid
|
18
19
|
from notionary.util.fuzzy import find_best_match
|
19
20
|
|
20
|
-
|
21
21
|
if TYPE_CHECKING:
|
22
22
|
from notionary import NotionDatabase
|
23
23
|
|
@@ -27,14 +27,16 @@ class NotionPage(LoggingMixin):
|
|
27
27
|
Managing content and metadata of a Notion page.
|
28
28
|
"""
|
29
29
|
|
30
|
-
@factory_only("from_page_id", "from_page_name")
|
30
|
+
@factory_only("from_page_id", "from_page_name", "from_url")
|
31
31
|
def __init__(
|
32
32
|
self,
|
33
33
|
page_id: str,
|
34
34
|
title: str,
|
35
35
|
url: str,
|
36
|
+
archived: bool,
|
37
|
+
in_trash: bool,
|
36
38
|
emoji_icon: Optional[str] = None,
|
37
|
-
properties: Optional[
|
39
|
+
properties: Optional[dict[str, Any]] = None,
|
38
40
|
parent_database: Optional[NotionDatabase] = None,
|
39
41
|
token: Optional[str] = None,
|
40
42
|
):
|
@@ -44,23 +46,25 @@ class NotionPage(LoggingMixin):
|
|
44
46
|
self._page_id = page_id
|
45
47
|
self._title = title
|
46
48
|
self._url = url
|
49
|
+
self._is_archived = archived
|
50
|
+
self._is_in_trash = in_trash
|
47
51
|
self._emoji_icon = emoji_icon
|
48
52
|
self._properties = properties
|
49
53
|
self._parent_database = parent_database
|
50
54
|
|
51
55
|
self._client = NotionPageClient(token=token)
|
56
|
+
self._block_client = NotionBlockClient(token=token)
|
52
57
|
self._page_data = None
|
53
58
|
|
54
|
-
self.
|
59
|
+
self.block_element_registry = BlockRegistry.create_registry()
|
55
60
|
|
56
61
|
self._page_content_writer = PageContentWriter(
|
57
62
|
page_id=self._page_id,
|
58
|
-
block_registry=self.
|
63
|
+
block_registry=self.block_element_registry,
|
59
64
|
)
|
60
65
|
|
61
66
|
self._page_content_retriever = PageContentRetriever(
|
62
|
-
|
63
|
-
block_registry=self._block_element_registry,
|
67
|
+
block_registry=self.block_element_registry,
|
64
68
|
)
|
65
69
|
|
66
70
|
@classmethod
|
@@ -69,10 +73,6 @@ class NotionPage(LoggingMixin):
|
|
69
73
|
) -> NotionPage:
|
70
74
|
"""
|
71
75
|
Create a NotionPage from a page ID.
|
72
|
-
|
73
|
-
Args:
|
74
|
-
page_id: The ID of the Notion page
|
75
|
-
token: Optional Notion API token (uses environment variable if not provided)
|
76
76
|
"""
|
77
77
|
formatted_id = format_uuid(page_id) or page_id
|
78
78
|
|
@@ -131,6 +131,26 @@ class NotionPage(LoggingMixin):
|
|
131
131
|
cls.logger.error("Error finding page by name: %s", str(e))
|
132
132
|
raise
|
133
133
|
|
134
|
+
@classmethod
|
135
|
+
async def from_url(cls, url: str, token: Optional[str] = None) -> NotionPage:
|
136
|
+
"""
|
137
|
+
Create a NotionPage from a Notion page URL.
|
138
|
+
"""
|
139
|
+
try:
|
140
|
+
page_id = extract_uuid(url)
|
141
|
+
if not page_id:
|
142
|
+
raise ValueError(f"Could not extract page ID from URL: {url}")
|
143
|
+
|
144
|
+
formatted_id = format_uuid(page_id) or page_id
|
145
|
+
|
146
|
+
async with NotionPageClient(token=token) as client:
|
147
|
+
page_response = await client.get_page(formatted_id)
|
148
|
+
return await cls._create_from_response(page_response, token)
|
149
|
+
|
150
|
+
except Exception as e:
|
151
|
+
cls.logger.error("Error creating page from URL '%s': %s", url, str(e))
|
152
|
+
raise
|
153
|
+
|
134
154
|
@property
|
135
155
|
def id(self) -> str:
|
136
156
|
"""
|
@@ -161,30 +181,26 @@ class NotionPage(LoggingMixin):
|
|
161
181
|
return self._emoji_icon
|
162
182
|
|
163
183
|
@property
|
164
|
-
def properties(self) -> Optional[
|
184
|
+
def properties(self) -> Optional[dict[str, Any]]:
|
165
185
|
"""
|
166
186
|
Get the properties of the page.
|
167
187
|
"""
|
168
188
|
return self._properties
|
169
189
|
|
170
190
|
@property
|
171
|
-
def
|
172
|
-
|
173
|
-
Get the block element registry associated with this page.
|
191
|
+
def is_archived(self) -> bool:
|
192
|
+
return self._is_archived
|
174
193
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
return self._block_element_registry
|
194
|
+
@property
|
195
|
+
def is_in_trash(self) -> bool:
|
196
|
+
return self._is_in_trash
|
179
197
|
|
180
|
-
|
198
|
+
@property
|
199
|
+
def block_registry_builder(self) -> BlockRegistryBuilder:
|
181
200
|
"""
|
182
|
-
|
183
|
-
|
184
|
-
Returns:
|
185
|
-
str: The formatting prompt.
|
201
|
+
Returns the block registry builder for this page.
|
186
202
|
"""
|
187
|
-
return self.
|
203
|
+
return self.block_element_registry.builder
|
188
204
|
|
189
205
|
async def set_title(self, title: str) -> str:
|
190
206
|
"""
|
@@ -205,38 +221,65 @@ class NotionPage(LoggingMixin):
|
|
205
221
|
except Exception as e:
|
206
222
|
self.logger.error("Error setting page title: %s", str(e))
|
207
223
|
|
208
|
-
async def append_markdown(
|
224
|
+
async def append_markdown(
|
225
|
+
self,
|
226
|
+
content: Union[str, Callable[[MarkdownBuilder], MarkdownBuilder]],
|
227
|
+
*,
|
228
|
+
prepend_table_of_contents: bool = False,
|
229
|
+
append_divider: bool = False,
|
230
|
+
) -> bool:
|
209
231
|
"""
|
210
232
|
Append markdown content to the page.
|
211
|
-
"""
|
212
|
-
return await self._page_content_writer.append_markdown(
|
213
|
-
markdown_text=markdown, append_divider=append_divider
|
214
|
-
)
|
215
233
|
|
216
|
-
|
217
|
-
|
218
|
-
|
234
|
+
Args:
|
235
|
+
content: Either raw markdown text OR a callback function that receives a MarkdownBuilder
|
236
|
+
prepend_table_of_contents: Whether to prepend table of contents
|
237
|
+
append_divider: Whether to append a divider
|
238
|
+
|
239
|
+
Returns:
|
240
|
+
bool: True if successful, False otherwise
|
219
241
|
"""
|
220
|
-
|
242
|
+
result = await self._page_content_writer.append_markdown(
|
243
|
+
content=content,
|
244
|
+
append_divider=append_divider,
|
245
|
+
prepend_table_of_contents=prepend_table_of_contents,
|
246
|
+
)
|
247
|
+
return result is not None
|
221
248
|
|
222
|
-
async def replace_content(
|
249
|
+
async def replace_content(
|
250
|
+
self,
|
251
|
+
content: Union[str, Callable[[MarkdownBuilder], MarkdownBuilder]],
|
252
|
+
*,
|
253
|
+
prepend_table_of_contents: bool = False,
|
254
|
+
append_divider: bool = False,
|
255
|
+
) -> bool:
|
223
256
|
"""
|
224
257
|
Replace the entire page content with new markdown content.
|
225
258
|
|
226
259
|
Args:
|
227
|
-
|
260
|
+
content: Either raw markdown text OR a callback function that receives a MarkdownBuilder
|
261
|
+
prepend_table_of_contents: Whether to prepend table of contents
|
262
|
+
append_divider: Whether to append a divider
|
228
263
|
|
229
264
|
Returns:
|
230
|
-
|
265
|
+
bool: True if successful, False otherwise
|
231
266
|
"""
|
232
267
|
clear_result = await self._page_content_writer.clear_page_content()
|
233
268
|
if not clear_result:
|
234
269
|
self.logger.error("Failed to clear page content before replacement")
|
235
|
-
return False
|
236
270
|
|
237
|
-
|
238
|
-
|
271
|
+
result = await self._page_content_writer.append_markdown(
|
272
|
+
content=content,
|
273
|
+
prepend_table_of_contents=prepend_table_of_contents,
|
274
|
+
append_divider=append_divider,
|
239
275
|
)
|
276
|
+
return result is not None
|
277
|
+
|
278
|
+
async def clear_page_content(self) -> str:
|
279
|
+
"""
|
280
|
+
Clear all content from the page.
|
281
|
+
"""
|
282
|
+
return await self._page_content_writer.clear_page_content()
|
240
283
|
|
241
284
|
async def get_text_content(self) -> str:
|
242
285
|
"""
|
@@ -245,7 +288,10 @@ class NotionPage(LoggingMixin):
|
|
245
288
|
Returns:
|
246
289
|
str: The text content of the page.
|
247
290
|
"""
|
248
|
-
|
291
|
+
blocks = await self._block_client.get_blocks_by_page_id_recursively(
|
292
|
+
page_id=self._page_id
|
293
|
+
)
|
294
|
+
return await self._page_content_retriever.convert_to_markdown(blocks=blocks)
|
249
295
|
|
250
296
|
async def set_emoji_icon(self, emoji: str) -> Optional[str]:
|
251
297
|
"""
|
@@ -385,7 +431,6 @@ class NotionPage(LoggingMixin):
|
|
385
431
|
)
|
386
432
|
return []
|
387
433
|
|
388
|
-
# Diese Methode hier sollte auch für relation properties funktionieren aber gerne auch eine dedizierte hier
|
389
434
|
async def set_property_value_by_name(self, property_name: str, value: Any) -> Any:
|
390
435
|
"""
|
391
436
|
Set the value of a specific property by its name.
|
@@ -509,6 +554,8 @@ class NotionPage(LoggingMixin):
|
|
509
554
|
title=title,
|
510
555
|
url=page_response.url,
|
511
556
|
emoji_icon=emoji,
|
557
|
+
archived=page_response.archived,
|
558
|
+
in_trash=page_response.in_trash,
|
512
559
|
properties=page_response.properties,
|
513
560
|
parent_database=parent_database,
|
514
561
|
token=token,
|