notionary 0.3.0__py3-none-any.whl → 0.4.0__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 +14 -2
- notionary/blocks/enums.py +27 -6
- notionary/blocks/schemas.py +32 -78
- notionary/comments/client.py +6 -9
- notionary/comments/schemas.py +2 -29
- notionary/data_source/http/data_source_instance_client.py +4 -4
- notionary/data_source/properties/schemas.py +128 -107
- notionary/data_source/query/__init__.py +9 -0
- notionary/data_source/query/builder.py +12 -3
- notionary/data_source/query/schema.py +5 -0
- notionary/data_source/schemas.py +2 -2
- notionary/data_source/service.py +43 -132
- notionary/database/schemas.py +2 -2
- notionary/database/service.py +19 -63
- notionary/exceptions/__init__.py +10 -2
- notionary/exceptions/api.py +2 -2
- notionary/exceptions/base.py +1 -1
- notionary/exceptions/block_parsing.py +24 -3
- notionary/exceptions/data_source/builder.py +2 -2
- notionary/exceptions/data_source/properties.py +3 -3
- notionary/exceptions/file_upload.py +67 -0
- notionary/exceptions/properties.py +4 -4
- notionary/exceptions/search.py +4 -4
- notionary/file_upload/__init__.py +4 -0
- notionary/file_upload/client.py +124 -210
- notionary/file_upload/config/__init__.py +17 -0
- notionary/file_upload/config/config.py +32 -0
- notionary/file_upload/config/constants.py +16 -0
- notionary/file_upload/file/reader.py +28 -0
- notionary/file_upload/query/__init__.py +7 -0
- notionary/file_upload/query/builder.py +54 -0
- notionary/file_upload/query/models.py +37 -0
- notionary/file_upload/schemas.py +78 -0
- notionary/file_upload/service.py +152 -289
- notionary/file_upload/validation/factory.py +64 -0
- notionary/file_upload/validation/impl/file_name_length.py +23 -0
- notionary/file_upload/validation/models.py +124 -0
- notionary/file_upload/validation/port.py +7 -0
- notionary/file_upload/validation/service.py +17 -0
- notionary/file_upload/validation/validators/__init__.py +11 -0
- notionary/file_upload/validation/validators/file_exists.py +15 -0
- notionary/file_upload/validation/validators/file_extension.py +122 -0
- notionary/file_upload/validation/validators/file_name_length.py +21 -0
- notionary/file_upload/validation/validators/upload_limit.py +31 -0
- notionary/http/client.py +7 -23
- notionary/page/content/factory.py +2 -0
- notionary/page/content/parser/factory.py +8 -5
- notionary/page/content/parser/parsers/audio.py +8 -33
- notionary/page/content/parser/parsers/embed.py +0 -2
- notionary/page/content/parser/parsers/file.py +8 -35
- notionary/page/content/parser/parsers/file_like_block.py +89 -0
- notionary/page/content/parser/parsers/image.py +8 -35
- notionary/page/content/parser/parsers/pdf.py +8 -35
- notionary/page/content/parser/parsers/video.py +8 -35
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +2 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +12 -8
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +2 -0
- notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +66 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +2 -0
- notionary/page/content/renderer/renderers/audio.py +9 -21
- notionary/page/content/renderer/renderers/file.py +9 -21
- notionary/page/content/renderer/renderers/file_like_block.py +43 -0
- notionary/page/content/renderer/renderers/image.py +9 -21
- notionary/page/content/renderer/renderers/pdf.py +9 -21
- notionary/page/content/renderer/renderers/video.py +9 -21
- notionary/page/content/syntax/__init__.py +2 -1
- notionary/page/content/syntax/registry.py +38 -60
- notionary/page/properties/client.py +3 -3
- notionary/page/properties/{models.py → schemas.py} +93 -107
- notionary/page/properties/service.py +15 -4
- notionary/page/schemas.py +3 -3
- notionary/page/service.py +18 -79
- notionary/shared/entity/dto_parsers.py +1 -36
- notionary/shared/entity/entity_metadata_update_client.py +18 -4
- notionary/shared/entity/schemas.py +6 -6
- notionary/shared/entity/service.py +121 -40
- notionary/shared/models/file.py +34 -6
- notionary/shared/models/icon.py +5 -12
- notionary/user/bot.py +12 -12
- notionary/utils/decorators.py +8 -8
- notionary/utils/pagination.py +36 -32
- notionary/workspace/__init__.py +2 -2
- notionary/workspace/client.py +2 -0
- notionary/workspace/query/__init__.py +3 -2
- notionary/workspace/query/builder.py +25 -1
- notionary/workspace/query/models.py +9 -1
- notionary/workspace/query/service.py +15 -11
- notionary/workspace/service.py +46 -36
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/METADATA +9 -5
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/RECORD +92 -71
- notionary/file_upload/models.py +0 -69
- notionary/page/page_context.py +0 -50
- notionary/shared/models/cover.py +0 -20
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/WHEEL +0 -0
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,6 +4,8 @@ from notionary.page.content.syntax.grammar import MarkdownGrammar
|
|
|
4
4
|
from notionary.page.content.syntax.models import SyntaxDefinition, SyntaxRegistryKey
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
# TODO: Add support for file upload in blocks for file types (refactor file types aswell)
|
|
8
|
+
# differentiate between external and uploaded files (file like blocks)
|
|
7
9
|
class SyntaxRegistry:
|
|
8
10
|
def __init__(self, markdown_markdown_grammar: MarkdownGrammar | None = None) -> None:
|
|
9
11
|
self._markdown_grammar = markdown_markdown_grammar or MarkdownGrammar()
|
|
@@ -92,63 +94,79 @@ class SyntaxRegistry:
|
|
|
92
94
|
def get_heading_syntax(self) -> SyntaxDefinition:
|
|
93
95
|
return self._definitions[SyntaxRegistryKey.HEADING]
|
|
94
96
|
|
|
97
|
+
def _create_media_syntax(self, media_type: str, url_pattern: str | None = None) -> SyntaxDefinition:
|
|
98
|
+
url_pattern = url_pattern or "[^)]+"
|
|
99
|
+
return SyntaxDefinition(
|
|
100
|
+
start_delimiter=f"[{media_type}](",
|
|
101
|
+
end_delimiter=")",
|
|
102
|
+
regex_pattern=re.compile(rf"(?<!\!)\[{re.escape(media_type)}\]\(({url_pattern})\)"),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def _create_url_media_syntax(self, media_type: str) -> SyntaxDefinition:
|
|
106
|
+
return SyntaxDefinition(
|
|
107
|
+
start_delimiter=f"[{media_type}](",
|
|
108
|
+
end_delimiter=")",
|
|
109
|
+
regex_pattern=re.compile(rf"(?<!\!)\[{re.escape(media_type)}\]\((https?://[^\s)]+)\)"),
|
|
110
|
+
)
|
|
111
|
+
|
|
95
112
|
def _register_defaults(self) -> None:
|
|
96
113
|
self._register_audio_syntax()
|
|
97
|
-
self._register_bookmark_syntax()
|
|
98
|
-
self._register_image_syntax()
|
|
99
114
|
self._register_video_syntax()
|
|
115
|
+
self._register_image_syntax()
|
|
100
116
|
self._register_file_syntax()
|
|
101
117
|
self._register_pdf_syntax()
|
|
118
|
+
self._register_bookmark_syntax()
|
|
119
|
+
self._register_embed_syntax()
|
|
102
120
|
|
|
103
|
-
# List blocks
|
|
104
121
|
self._register_bulleted_list_syntax()
|
|
105
122
|
self._register_numbered_list_syntax()
|
|
106
123
|
self._register_todo_syntax()
|
|
107
124
|
self._register_todo_done_syntax()
|
|
108
125
|
|
|
109
|
-
# Container blocks
|
|
110
126
|
self._register_toggle_syntax()
|
|
111
127
|
self._register_toggleable_heading_syntax()
|
|
112
128
|
self._register_callout_syntax()
|
|
113
129
|
self._register_quote_syntax()
|
|
114
130
|
self._register_code_syntax()
|
|
115
131
|
|
|
116
|
-
# Column layout blocks
|
|
117
132
|
self._register_column_list_syntax()
|
|
118
133
|
self._register_column_syntax()
|
|
119
134
|
|
|
120
135
|
self._register_heading_1_syntax()
|
|
121
136
|
self._register_heading_2_syntax()
|
|
122
137
|
self._register_heading_3_syntax()
|
|
123
|
-
self._register_heading_syntax()
|
|
138
|
+
self._register_heading_syntax()
|
|
124
139
|
|
|
125
140
|
self._register_divider_syntax()
|
|
126
141
|
self._register_breadcrumb_syntax()
|
|
127
142
|
self._register_table_of_contents_syntax()
|
|
128
143
|
self._register_equation_syntax()
|
|
129
|
-
self._register_embed_syntax()
|
|
130
144
|
self._register_table_syntax()
|
|
131
145
|
self._register_table_row_syntax()
|
|
132
146
|
|
|
133
|
-
# Post-processing and utility blocks
|
|
134
147
|
self._register_caption_syntax()
|
|
135
148
|
self._register_space_syntax()
|
|
136
149
|
|
|
137
150
|
def _register_audio_syntax(self) -> None:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
151
|
+
self._definitions[SyntaxRegistryKey.AUDIO] = self._create_media_syntax("audio")
|
|
152
|
+
|
|
153
|
+
def _register_video_syntax(self) -> None:
|
|
154
|
+
self._definitions[SyntaxRegistryKey.VIDEO] = self._create_media_syntax("video")
|
|
155
|
+
|
|
156
|
+
def _register_image_syntax(self) -> None:
|
|
157
|
+
self._definitions[SyntaxRegistryKey.IMAGE] = self._create_media_syntax("image")
|
|
158
|
+
|
|
159
|
+
def _register_file_syntax(self) -> None:
|
|
160
|
+
self._definitions[SyntaxRegistryKey.FILE] = self._create_media_syntax("file")
|
|
161
|
+
|
|
162
|
+
def _register_pdf_syntax(self) -> None:
|
|
163
|
+
self._definitions[SyntaxRegistryKey.PDF] = self._create_media_syntax("pdf")
|
|
144
164
|
|
|
145
165
|
def _register_bookmark_syntax(self) -> None:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
self._definitions[SyntaxRegistryKey.BOOKMARK] = definition
|
|
166
|
+
self._definitions[SyntaxRegistryKey.BOOKMARK] = self._create_url_media_syntax("bookmark")
|
|
167
|
+
|
|
168
|
+
def _register_embed_syntax(self) -> None:
|
|
169
|
+
self._definitions[SyntaxRegistryKey.EMBED] = self._create_url_media_syntax("embed")
|
|
152
170
|
|
|
153
171
|
def _register_breadcrumb_syntax(self) -> None:
|
|
154
172
|
definition = SyntaxDefinition(
|
|
@@ -217,14 +235,6 @@ class SyntaxRegistry:
|
|
|
217
235
|
)
|
|
218
236
|
self._definitions[SyntaxRegistryKey.DIVIDER] = definition
|
|
219
237
|
|
|
220
|
-
def _register_embed_syntax(self) -> None:
|
|
221
|
-
definition = SyntaxDefinition(
|
|
222
|
-
start_delimiter="[embed](",
|
|
223
|
-
end_delimiter=")",
|
|
224
|
-
regex_pattern=re.compile(r"\[embed\]\((https?://[^\s)]+)\)"),
|
|
225
|
-
)
|
|
226
|
-
self._definitions[SyntaxRegistryKey.EMBED] = definition
|
|
227
|
-
|
|
228
238
|
def _register_equation_syntax(self) -> None:
|
|
229
239
|
definition = SyntaxDefinition(
|
|
230
240
|
start_delimiter="$$",
|
|
@@ -233,14 +243,6 @@ class SyntaxRegistry:
|
|
|
233
243
|
)
|
|
234
244
|
self._definitions[SyntaxRegistryKey.EQUATION] = definition
|
|
235
245
|
|
|
236
|
-
def _register_file_syntax(self) -> None:
|
|
237
|
-
definition = SyntaxDefinition(
|
|
238
|
-
start_delimiter="[file](",
|
|
239
|
-
end_delimiter=")",
|
|
240
|
-
regex_pattern=re.compile(r"\[file\]\(([^)]+)\)"),
|
|
241
|
-
)
|
|
242
|
-
self._definitions[SyntaxRegistryKey.FILE] = definition
|
|
243
|
-
|
|
244
246
|
def _register_heading_1_syntax(self) -> None:
|
|
245
247
|
definition = SyntaxDefinition(
|
|
246
248
|
start_delimiter="# ",
|
|
@@ -265,14 +267,6 @@ class SyntaxRegistry:
|
|
|
265
267
|
)
|
|
266
268
|
self._definitions[SyntaxRegistryKey.HEADING_3] = definition
|
|
267
269
|
|
|
268
|
-
def _register_image_syntax(self) -> None:
|
|
269
|
-
definition = SyntaxDefinition(
|
|
270
|
-
start_delimiter="[image](",
|
|
271
|
-
end_delimiter=")",
|
|
272
|
-
regex_pattern=re.compile(r"(?<!!)\[image\]\(([^)]+)\)"),
|
|
273
|
-
)
|
|
274
|
-
self._definitions[SyntaxRegistryKey.IMAGE] = definition
|
|
275
|
-
|
|
276
270
|
def _register_numbered_list_syntax(self) -> None:
|
|
277
271
|
definition = SyntaxDefinition(
|
|
278
272
|
start_delimiter="1. ",
|
|
@@ -281,14 +275,6 @@ class SyntaxRegistry:
|
|
|
281
275
|
)
|
|
282
276
|
self._definitions[SyntaxRegistryKey.NUMBERED_LIST] = definition
|
|
283
277
|
|
|
284
|
-
def _register_pdf_syntax(self) -> None:
|
|
285
|
-
definition = SyntaxDefinition(
|
|
286
|
-
start_delimiter="[pdf](",
|
|
287
|
-
end_delimiter=")",
|
|
288
|
-
regex_pattern=re.compile(r"\[pdf\]\(([^)]+)\)"),
|
|
289
|
-
)
|
|
290
|
-
self._definitions[SyntaxRegistryKey.PDF] = definition
|
|
291
|
-
|
|
292
278
|
def _register_quote_syntax(self) -> None:
|
|
293
279
|
definition = SyntaxDefinition(
|
|
294
280
|
start_delimiter="> ",
|
|
@@ -360,14 +346,6 @@ class SyntaxRegistry:
|
|
|
360
346
|
)
|
|
361
347
|
self._definitions[SyntaxRegistryKey.TOGGLEABLE_HEADING] = definition
|
|
362
348
|
|
|
363
|
-
def _register_video_syntax(self) -> None:
|
|
364
|
-
definition = SyntaxDefinition(
|
|
365
|
-
start_delimiter="[video](",
|
|
366
|
-
end_delimiter=")",
|
|
367
|
-
regex_pattern=re.compile(r"\[video\]\(([^)]+)\)"),
|
|
368
|
-
)
|
|
369
|
-
self._definitions[SyntaxRegistryKey.VIDEO] = definition
|
|
370
|
-
|
|
371
349
|
def _register_caption_syntax(self) -> None:
|
|
372
350
|
definition = SyntaxDefinition(
|
|
373
351
|
start_delimiter="[caption]",
|
|
@@ -4,7 +4,7 @@ from pydantic import BaseModel
|
|
|
4
4
|
|
|
5
5
|
from notionary.blocks.rich_text.models import RichText
|
|
6
6
|
from notionary.http.client import NotionHttpClient
|
|
7
|
-
from notionary.page.properties.
|
|
7
|
+
from notionary.page.properties.schemas import (
|
|
8
8
|
DateValue,
|
|
9
9
|
PageCheckboxProperty,
|
|
10
10
|
PageDateProperty,
|
|
@@ -51,8 +51,8 @@ class PagePropertyHttpClient(NotionHttpClient):
|
|
|
51
51
|
|
|
52
52
|
return await self.patch_page(update_dto)
|
|
53
53
|
|
|
54
|
-
async def patch_title(self, title: str) -> NotionPageDto:
|
|
55
|
-
return await self._patch_property(
|
|
54
|
+
async def patch_title(self, property_name: str, title: str) -> NotionPageDto:
|
|
55
|
+
return await self._patch_property(property_name, title, PageTitleProperty)
|
|
56
56
|
|
|
57
57
|
async def patch_rich_text_property(self, property_name: str, text: str) -> NotionPageDto:
|
|
58
58
|
return await self._patch_property(property_name, text, PageRichTextProperty)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from enum import StrEnum
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Any, Literal, TypeVar
|
|
3
3
|
|
|
4
|
-
from pydantic import BaseModel, Field
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
5
5
|
|
|
6
6
|
from notionary.blocks.rich_text.models import RichText
|
|
7
|
+
from notionary.shared.models.file import File
|
|
7
8
|
from notionary.shared.properties.type import PropertyType
|
|
8
9
|
from notionary.shared.typings import JsonDict
|
|
9
10
|
from notionary.user.schemas import PersonUserResponseDto, UserResponseDto
|
|
@@ -38,38 +39,6 @@ class DateValue(BaseModel):
|
|
|
38
39
|
time_zone: str | None = None
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
# ============================================================================
|
|
42
|
-
# File Models
|
|
43
|
-
# ============================================================================
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class FileType(StrEnum):
|
|
47
|
-
EXTERNAL = "external"
|
|
48
|
-
FILE = "file"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class ExternalFile(BaseModel):
|
|
52
|
-
"""External file hosted outside of Notion."""
|
|
53
|
-
|
|
54
|
-
url: str
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class NotionFile(BaseModel):
|
|
58
|
-
"""File uploaded to Notion with expiration."""
|
|
59
|
-
|
|
60
|
-
url: str
|
|
61
|
-
expiry_time: str
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class FileObject(BaseModel):
|
|
65
|
-
"""File object can be external or uploaded to Notion."""
|
|
66
|
-
|
|
67
|
-
name: str
|
|
68
|
-
type: FileType
|
|
69
|
-
external: ExternalFile | None = None
|
|
70
|
-
file: NotionFile | None = None
|
|
71
|
-
|
|
72
|
-
|
|
73
42
|
# ============================================================================
|
|
74
43
|
# Formula Models
|
|
75
44
|
# ============================================================================
|
|
@@ -142,25 +111,9 @@ class VerificationValue(BaseModel):
|
|
|
142
111
|
# ============================================================================
|
|
143
112
|
|
|
144
113
|
|
|
145
|
-
class
|
|
146
|
-
type: Literal[PropertyType.
|
|
147
|
-
|
|
148
|
-
options: list[StatusOption] = Field(default_factory=list)
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def option_names(self) -> list[str]:
|
|
152
|
-
return [option.name for option in self.options]
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class PageRelationProperty(PageProperty):
|
|
156
|
-
type: Literal[PropertyType.RELATION] = PropertyType.RELATION
|
|
157
|
-
relation: list[RelationItem] = Field(default_factory=list)
|
|
158
|
-
has_more: bool = False
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
class PageURLProperty(PageProperty):
|
|
162
|
-
type: Literal[PropertyType.URL] = PropertyType.URL
|
|
163
|
-
url: str | None = None
|
|
114
|
+
class PageTitleProperty(PageProperty):
|
|
115
|
+
type: Literal[PropertyType.TITLE] = PropertyType.TITLE
|
|
116
|
+
title: list[RichText] = Field(default_factory=list)
|
|
164
117
|
|
|
165
118
|
|
|
166
119
|
class PageRichTextProperty(PageProperty):
|
|
@@ -168,6 +121,16 @@ class PageRichTextProperty(PageProperty):
|
|
|
168
121
|
rich_text: list[RichText] = Field(default_factory=list)
|
|
169
122
|
|
|
170
123
|
|
|
124
|
+
class PageSelectProperty(PageProperty):
|
|
125
|
+
type: Literal[PropertyType.SELECT] = PropertyType.SELECT
|
|
126
|
+
select: SelectOption | None = None
|
|
127
|
+
options: list[SelectOption] = Field(default_factory=list)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def option_names(self) -> list[str]:
|
|
131
|
+
return [option.name for option in self.options]
|
|
132
|
+
|
|
133
|
+
|
|
171
134
|
class PageMultiSelectProperty(PageProperty):
|
|
172
135
|
type: Literal[PropertyType.MULTI_SELECT] = PropertyType.MULTI_SELECT
|
|
173
136
|
multi_select: list[SelectOption] = Field(default_factory=list)
|
|
@@ -178,19 +141,19 @@ class PageMultiSelectProperty(PageProperty):
|
|
|
178
141
|
return [option.name for option in self.options]
|
|
179
142
|
|
|
180
143
|
|
|
181
|
-
class
|
|
182
|
-
type: Literal[PropertyType.
|
|
183
|
-
|
|
184
|
-
options: list[
|
|
144
|
+
class PageStatusProperty(PageProperty):
|
|
145
|
+
type: Literal[PropertyType.STATUS] = PropertyType.STATUS
|
|
146
|
+
status: StatusOption | None = None
|
|
147
|
+
options: list[StatusOption] = Field(default_factory=list)
|
|
185
148
|
|
|
186
149
|
@property
|
|
187
150
|
def option_names(self) -> list[str]:
|
|
188
151
|
return [option.name for option in self.options]
|
|
189
152
|
|
|
190
153
|
|
|
191
|
-
class
|
|
192
|
-
type: Literal[PropertyType.
|
|
193
|
-
|
|
154
|
+
class PageNumberProperty(PageProperty):
|
|
155
|
+
type: Literal[PropertyType.NUMBER] = PropertyType.NUMBER
|
|
156
|
+
number: float | None = None
|
|
194
157
|
|
|
195
158
|
|
|
196
159
|
class PageDateProperty(PageProperty):
|
|
@@ -198,21 +161,16 @@ class PageDateProperty(PageProperty):
|
|
|
198
161
|
date: DateValue | None = None
|
|
199
162
|
|
|
200
163
|
|
|
201
|
-
class PageTitleProperty(PageProperty):
|
|
202
|
-
type: Literal[PropertyType.TITLE] = PropertyType.TITLE
|
|
203
|
-
title: list[RichText] = Field(default_factory=list)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
class PageNumberProperty(PageProperty):
|
|
207
|
-
type: Literal[PropertyType.NUMBER] = PropertyType.NUMBER
|
|
208
|
-
number: float | None = None
|
|
209
|
-
|
|
210
|
-
|
|
211
164
|
class PageCheckboxProperty(PageProperty):
|
|
212
165
|
type: Literal[PropertyType.CHECKBOX] = PropertyType.CHECKBOX
|
|
213
166
|
checkbox: bool = False
|
|
214
167
|
|
|
215
168
|
|
|
169
|
+
class PageURLProperty(PageProperty):
|
|
170
|
+
type: Literal[PropertyType.URL] = PropertyType.URL
|
|
171
|
+
url: str | None = None
|
|
172
|
+
|
|
173
|
+
|
|
216
174
|
class PageEmailProperty(PageProperty):
|
|
217
175
|
type: Literal[PropertyType.EMAIL] = PropertyType.EMAIL
|
|
218
176
|
email: str | None = None
|
|
@@ -223,29 +181,34 @@ class PagePhoneNumberProperty(PageProperty):
|
|
|
223
181
|
phone_number: str | None = None
|
|
224
182
|
|
|
225
183
|
|
|
226
|
-
class
|
|
227
|
-
type: Literal[PropertyType.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
class PageLastEditedTimeProperty(PageProperty):
|
|
232
|
-
type: Literal[PropertyType.LAST_EDITED_TIME] = PropertyType.LAST_EDITED_TIME
|
|
233
|
-
last_edited_time: str # ISO 8601 datetime - read-only
|
|
184
|
+
class PagePeopleProperty(PageProperty):
|
|
185
|
+
type: Literal[PropertyType.PEOPLE] = PropertyType.PEOPLE
|
|
186
|
+
people: list[PersonUserResponseDto] = Field(default_factory=list)
|
|
234
187
|
|
|
235
188
|
|
|
236
189
|
class PageCreatedByProperty(PageProperty):
|
|
237
190
|
type: Literal[PropertyType.CREATED_BY] = PropertyType.CREATED_BY
|
|
238
|
-
created_by: UserResponseDto
|
|
191
|
+
created_by: UserResponseDto
|
|
239
192
|
|
|
240
193
|
|
|
241
194
|
class PageLastEditedByProperty(PageProperty):
|
|
242
195
|
type: Literal[PropertyType.LAST_EDITED_BY] = PropertyType.LAST_EDITED_BY
|
|
243
|
-
last_edited_by: UserResponseDto
|
|
196
|
+
last_edited_by: UserResponseDto
|
|
244
197
|
|
|
245
198
|
|
|
246
|
-
class
|
|
247
|
-
type: Literal[PropertyType.
|
|
248
|
-
|
|
199
|
+
class PageCreatedTimeProperty(PageProperty):
|
|
200
|
+
type: Literal[PropertyType.CREATED_TIME] = PropertyType.CREATED_TIME
|
|
201
|
+
created_time: str
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class PageLastEditedTimeProperty(PageProperty):
|
|
205
|
+
type: Literal[PropertyType.LAST_EDITED_TIME] = PropertyType.LAST_EDITED_TIME
|
|
206
|
+
last_edited_time: str
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class PageLastVisitedTimeProperty(PageProperty):
|
|
210
|
+
type: Literal[PropertyType.LAST_VISITED_TIME] = PropertyType.LAST_VISITED_TIME
|
|
211
|
+
last_visited_time: str | None = None
|
|
249
212
|
|
|
250
213
|
|
|
251
214
|
class PageFormulaProperty(PageProperty):
|
|
@@ -258,14 +221,15 @@ class PageRollupProperty(PageProperty):
|
|
|
258
221
|
rollup: RollupValue
|
|
259
222
|
|
|
260
223
|
|
|
261
|
-
class
|
|
262
|
-
type: Literal[PropertyType.
|
|
263
|
-
|
|
224
|
+
class PageFilesProperty(PageProperty):
|
|
225
|
+
type: Literal[PropertyType.FILES] = PropertyType.FILES
|
|
226
|
+
files: list[File] = Field(default_factory=list)
|
|
264
227
|
|
|
265
228
|
|
|
266
|
-
class
|
|
267
|
-
type: Literal[PropertyType.
|
|
268
|
-
|
|
229
|
+
class PageRelationProperty(PageProperty):
|
|
230
|
+
type: Literal[PropertyType.RELATION] = PropertyType.RELATION
|
|
231
|
+
relation: list[RelationItem] = Field(default_factory=list)
|
|
232
|
+
has_more: bool = False
|
|
269
233
|
|
|
270
234
|
|
|
271
235
|
class PageButtonProperty(PageProperty):
|
|
@@ -273,36 +237,58 @@ class PageButtonProperty(PageProperty):
|
|
|
273
237
|
button: JsonDict = Field(default_factory=dict)
|
|
274
238
|
|
|
275
239
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
240
|
+
class PageLocationProperty(PageProperty):
|
|
241
|
+
type: Literal[PropertyType.LOCATION] = PropertyType.LOCATION
|
|
242
|
+
location: JsonDict | None = None
|
|
279
243
|
|
|
280
244
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
|
284
|
-
|
|
245
|
+
class PagePlaceProperty(PageProperty):
|
|
246
|
+
type: Literal[PropertyType.PLACE] = PropertyType.PLACE
|
|
247
|
+
place: JsonDict | None = None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
class PageVerificationProperty(PageProperty):
|
|
251
|
+
type: Literal[PropertyType.VERIFICATION] = PropertyType.VERIFICATION
|
|
252
|
+
verification: VerificationValue
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class PageUniqueIdProperty(PageProperty):
|
|
256
|
+
type: Literal[PropertyType.UNIQUE_ID] = PropertyType.UNIQUE_ID
|
|
257
|
+
unique_id: UniqueIdValue
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class PageUnknownProperty(PageProperty):
|
|
261
|
+
model_config = ConfigDict(extra="allow")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
type AnyPageProperty = (
|
|
265
|
+
PageTitleProperty
|
|
285
266
|
| PageRichTextProperty
|
|
286
|
-
| PageMultiSelectProperty
|
|
287
267
|
| PageSelectProperty
|
|
288
|
-
|
|
|
289
|
-
|
|
|
290
|
-
| PageTitleProperty
|
|
268
|
+
| PageMultiSelectProperty
|
|
269
|
+
| PageStatusProperty
|
|
291
270
|
| PageNumberProperty
|
|
271
|
+
| PageDateProperty
|
|
292
272
|
| PageCheckboxProperty
|
|
273
|
+
| PageURLProperty
|
|
293
274
|
| PageEmailProperty
|
|
294
275
|
| PagePhoneNumberProperty
|
|
295
|
-
|
|
|
296
|
-
| PageLastEditedTimeProperty
|
|
276
|
+
| PagePeopleProperty
|
|
297
277
|
| PageCreatedByProperty
|
|
298
278
|
| PageLastEditedByProperty
|
|
299
|
-
|
|
|
279
|
+
| PageCreatedTimeProperty
|
|
280
|
+
| PageLastEditedTimeProperty
|
|
281
|
+
| PageLastVisitedTimeProperty
|
|
300
282
|
| PageFormulaProperty
|
|
301
283
|
| PageRollupProperty
|
|
302
|
-
|
|
|
284
|
+
| PageFilesProperty
|
|
285
|
+
| PageRelationProperty
|
|
286
|
+
| PageButtonProperty
|
|
287
|
+
| PageLocationProperty
|
|
288
|
+
| PagePlaceProperty
|
|
303
289
|
| PageVerificationProperty
|
|
304
|
-
|
|
|
305
|
-
|
|
306
|
-
|
|
290
|
+
| PageUniqueIdProperty
|
|
291
|
+
| PageUnknownProperty
|
|
292
|
+
)
|
|
307
293
|
|
|
308
294
|
PagePropertyT = TypeVar("PagePropertyT", bound=PageProperty)
|
|
@@ -10,7 +10,7 @@ from notionary.exceptions.properties import (
|
|
|
10
10
|
PagePropertyTypeError,
|
|
11
11
|
)
|
|
12
12
|
from notionary.page.properties.client import PagePropertyHttpClient
|
|
13
|
-
from notionary.page.properties.
|
|
13
|
+
from notionary.page.properties.schemas import (
|
|
14
14
|
PageCheckboxProperty,
|
|
15
15
|
PageCreatedTimeProperty,
|
|
16
16
|
PageDateProperty,
|
|
@@ -147,11 +147,22 @@ class PagePropertyHandler:
|
|
|
147
147
|
# Writer Methods
|
|
148
148
|
# =========================================================================
|
|
149
149
|
|
|
150
|
-
async def set_title_property(self,
|
|
151
|
-
self.
|
|
152
|
-
|
|
150
|
+
async def set_title_property(self, title: str) -> None:
|
|
151
|
+
title_property_name = self._extract_title_property_name()
|
|
152
|
+
|
|
153
|
+
self._get_typed_property_or_raise(title_property_name, PageTitleProperty)
|
|
154
|
+
updated_page = await self._property_http_client.patch_title(title_property_name, title)
|
|
153
155
|
self._properties = updated_page.properties
|
|
154
156
|
|
|
157
|
+
def _extract_title_property_name(self) -> str | None:
|
|
158
|
+
if not self._properties:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
return next(
|
|
162
|
+
(key for key, prop in self._properties.items() if isinstance(prop, PageTitleProperty)),
|
|
163
|
+
None,
|
|
164
|
+
)
|
|
165
|
+
|
|
155
166
|
async def set_rich_text_property(self, property_name: str, text: str) -> None:
|
|
156
167
|
self._get_typed_property_or_raise(property_name, PageRichTextProperty)
|
|
157
168
|
updated_page = await self._property_http_client.patch_rich_text_property(property_name, text)
|
notionary/page/schemas.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from pydantic import BaseModel
|
|
2
2
|
|
|
3
|
-
from notionary.page.properties.
|
|
3
|
+
from notionary.page.properties.schemas import AnyPageProperty
|
|
4
4
|
from notionary.shared.entity.schemas import EntityResponseDto
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class NotionPageDto(EntityResponseDto):
|
|
8
8
|
archived: bool
|
|
9
|
-
properties: dict[str,
|
|
9
|
+
properties: dict[str, AnyPageProperty]
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class PgePropertiesUpdateDto(BaseModel):
|
|
13
|
-
properties: dict[str,
|
|
13
|
+
properties: dict[str, AnyPageProperty]
|