notionary 0.2.28__py3-none-any.whl → 0.3.1__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 +9 -2
- notionary/blocks/__init__.py +5 -0
- notionary/blocks/client.py +6 -4
- notionary/blocks/enums.py +28 -1
- notionary/blocks/rich_text/markdown_rich_text_converter.py +14 -0
- notionary/blocks/rich_text/models.py +14 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +2 -0
- notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
- notionary/blocks/rich_text/rich_text_markdown_converter.py +12 -0
- notionary/blocks/rich_text/rich_text_patterns.py +3 -0
- notionary/blocks/schemas.py +42 -10
- notionary/comments/__init__.py +5 -0
- notionary/comments/client.py +7 -10
- notionary/comments/factory.py +4 -6
- notionary/data_source/http/data_source_instance_client.py +14 -4
- notionary/data_source/properties/{models.py → schemas.py} +4 -8
- notionary/data_source/query/__init__.py +9 -0
- notionary/data_source/query/builder.py +38 -10
- notionary/data_source/query/schema.py +13 -10
- notionary/data_source/query/validator.py +11 -11
- notionary/data_source/schema/registry.py +104 -0
- notionary/data_source/schema/service.py +136 -0
- notionary/data_source/schemas.py +1 -1
- notionary/data_source/service.py +29 -103
- notionary/database/service.py +17 -60
- notionary/exceptions/__init__.py +5 -1
- notionary/exceptions/block_parsing.py +21 -0
- notionary/exceptions/search.py +24 -0
- notionary/http/client.py +9 -10
- notionary/http/models.py +5 -4
- notionary/page/content/factory.py +10 -3
- notionary/page/content/markdown/builder.py +76 -154
- notionary/page/content/markdown/nodes/__init__.py +0 -2
- notionary/page/content/markdown/nodes/audio.py +1 -1
- notionary/page/content/markdown/nodes/base.py +1 -1
- notionary/page/content/markdown/nodes/bookmark.py +1 -1
- notionary/page/content/markdown/nodes/breadcrumb.py +1 -1
- notionary/page/content/markdown/nodes/bulleted_list.py +31 -8
- notionary/page/content/markdown/nodes/callout.py +12 -10
- notionary/page/content/markdown/nodes/code.py +3 -5
- notionary/page/content/markdown/nodes/columns.py +39 -21
- notionary/page/content/markdown/nodes/container.py +64 -0
- notionary/page/content/markdown/nodes/divider.py +1 -1
- notionary/page/content/markdown/nodes/embed.py +1 -1
- notionary/page/content/markdown/nodes/equation.py +1 -1
- notionary/page/content/markdown/nodes/file.py +1 -1
- notionary/page/content/markdown/nodes/heading.py +26 -6
- notionary/page/content/markdown/nodes/image.py +1 -1
- notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +1 -1
- notionary/page/content/markdown/nodes/numbered_list.py +28 -5
- notionary/page/content/markdown/nodes/paragraph.py +1 -1
- notionary/page/content/markdown/nodes/pdf.py +1 -1
- notionary/page/content/markdown/nodes/quote.py +17 -5
- notionary/page/content/markdown/nodes/space.py +1 -1
- notionary/page/content/markdown/nodes/table.py +1 -1
- notionary/page/content/markdown/nodes/table_of_contents.py +1 -1
- notionary/page/content/markdown/nodes/todo.py +23 -7
- notionary/page/content/markdown/nodes/toggle.py +13 -14
- notionary/page/content/markdown/nodes/video.py +1 -1
- notionary/page/content/parser/context.py +98 -21
- notionary/page/content/parser/factory.py +1 -10
- notionary/page/content/parser/parsers/__init__.py +0 -2
- notionary/page/content/parser/parsers/audio.py +1 -1
- notionary/page/content/parser/parsers/base.py +1 -1
- notionary/page/content/parser/parsers/bookmark.py +1 -1
- notionary/page/content/parser/parsers/breadcrumb.py +1 -1
- notionary/page/content/parser/parsers/bulleted_list.py +52 -8
- notionary/page/content/parser/parsers/callout.py +55 -84
- notionary/page/content/parser/parsers/caption.py +1 -1
- notionary/page/content/parser/parsers/code.py +5 -5
- notionary/page/content/parser/parsers/column.py +23 -64
- notionary/page/content/parser/parsers/column_list.py +45 -45
- notionary/page/content/parser/parsers/divider.py +1 -1
- notionary/page/content/parser/parsers/embed.py +1 -1
- notionary/page/content/parser/parsers/equation.py +1 -1
- notionary/page/content/parser/parsers/file.py +1 -1
- notionary/page/content/parser/parsers/heading.py +65 -8
- notionary/page/content/parser/parsers/image.py +1 -1
- notionary/page/content/parser/parsers/numbered_list.py +52 -8
- notionary/page/content/parser/parsers/paragraph.py +3 -2
- notionary/page/content/parser/parsers/pdf.py +1 -1
- notionary/page/content/parser/parsers/quote.py +75 -15
- notionary/page/content/parser/parsers/space.py +14 -8
- notionary/page/content/parser/parsers/table.py +1 -1
- notionary/page/content/parser/parsers/table_of_contents.py +1 -1
- notionary/page/content/parser/parsers/todo.py +57 -19
- notionary/page/content/parser/parsers/toggle.py +17 -74
- notionary/page/content/parser/parsers/video.py +1 -1
- notionary/page/content/parser/post_processing/handlers/rich_text_length.py +6 -4
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +43 -22
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +4 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +108 -54
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +86 -0
- notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +66 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +14 -7
- notionary/page/content/parser/service.py +9 -0
- notionary/page/content/renderer/context.py +5 -2
- notionary/page/content/renderer/factory.py +2 -11
- notionary/page/content/renderer/post_processing/handlers/__init__.py +2 -2
- notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
- notionary/page/content/renderer/renderers/__init__.py +0 -2
- notionary/page/content/renderer/renderers/base.py +1 -1
- notionary/page/content/renderer/renderers/bulleted_list.py +1 -1
- notionary/page/content/renderer/renderers/callout.py +6 -21
- notionary/page/content/renderer/renderers/captioned_block.py +1 -1
- notionary/page/content/renderer/renderers/column.py +28 -19
- notionary/page/content/renderer/renderers/column_list.py +24 -11
- notionary/page/content/renderer/renderers/heading.py +53 -27
- notionary/page/content/renderer/renderers/numbered_list.py +6 -5
- notionary/page/content/renderer/renderers/quote.py +1 -1
- notionary/page/content/renderer/renderers/todo.py +1 -1
- notionary/page/content/renderer/renderers/toggle.py +6 -7
- notionary/page/content/service.py +4 -1
- notionary/page/content/syntax/__init__.py +4 -0
- notionary/page/content/syntax/grammar.py +10 -0
- notionary/page/content/syntax/models.py +0 -2
- notionary/page/content/syntax/{service.py → registry.py} +31 -91
- notionary/page/properties/client.py +3 -3
- notionary/page/properties/models.py +3 -2
- notionary/page/properties/service.py +18 -3
- notionary/page/service.py +22 -80
- notionary/shared/entity/service.py +94 -36
- notionary/shared/models/cover.py +1 -1
- notionary/shared/typings.py +3 -0
- notionary/user/base.py +60 -11
- notionary/user/factory.py +0 -0
- notionary/utils/decorators.py +122 -0
- notionary/utils/fuzzy.py +18 -6
- notionary/utils/mixins/logging.py +38 -27
- notionary/utils/pagination.py +70 -16
- notionary/workspace/__init__.py +2 -1
- notionary/workspace/client.py +4 -2
- notionary/workspace/query/__init__.py +3 -0
- notionary/workspace/query/builder.py +25 -1
- notionary/workspace/query/models.py +12 -3
- notionary/workspace/query/service.py +57 -32
- notionary/workspace/service.py +31 -21
- {notionary-0.2.28.dist-info → notionary-0.3.1.dist-info}/METADATA +35 -105
- notionary-0.3.1.dist-info/RECORD +211 -0
- notionary/page/content/markdown/nodes/toggleable_heading.py +0 -35
- notionary/page/content/parser/parsers/toggleable_heading.py +0 -150
- notionary/page/content/renderer/post_processing/handlers/numbered_list_placeholdere.py +0 -62
- notionary/page/content/renderer/renderers/toggleable_heading.py +0 -78
- notionary/utils/async_retry.py +0 -39
- notionary/utils/singleton.py +0 -13
- notionary-0.2.28.dist-info/RECORD +0 -200
- {notionary-0.2.28.dist-info → notionary-0.3.1.dist-info}/WHEEL +0 -0
- {notionary-0.2.28.dist-info → notionary-0.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import re
|
|
2
2
|
|
|
3
|
+
from notionary.page.content.syntax.grammar import MarkdownGrammar
|
|
3
4
|
from notionary.page.content.syntax.models import SyntaxDefinition, SyntaxRegistryKey
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class SyntaxRegistry:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
TABLE_DELIMITER = "|"
|
|
8
|
+
def __init__(self, markdown_markdown_grammar: MarkdownGrammar | None = None) -> None:
|
|
9
|
+
self._markdown_grammar = markdown_markdown_grammar or MarkdownGrammar()
|
|
10
10
|
|
|
11
|
-
def __init__(self) -> None:
|
|
12
11
|
self._definitions: dict[SyntaxRegistryKey, SyntaxDefinition] = {}
|
|
13
12
|
self._register_defaults()
|
|
14
13
|
|
|
@@ -140,8 +139,6 @@ class SyntaxRegistry:
|
|
|
140
139
|
start_delimiter="[audio](",
|
|
141
140
|
end_delimiter=")",
|
|
142
141
|
regex_pattern=re.compile(r"\[audio\]\(([^)]+)\)"),
|
|
143
|
-
is_multiline_block=False,
|
|
144
|
-
is_inline=True,
|
|
145
142
|
)
|
|
146
143
|
self._definitions[SyntaxRegistryKey.AUDIO] = definition
|
|
147
144
|
|
|
@@ -150,8 +147,6 @@ class SyntaxRegistry:
|
|
|
150
147
|
start_delimiter="[bookmark](",
|
|
151
148
|
end_delimiter=")",
|
|
152
149
|
regex_pattern=re.compile(r"\[bookmark\]\((https?://[^\s\"]+)\)"),
|
|
153
|
-
is_multiline_block=False,
|
|
154
|
-
is_inline=True,
|
|
155
150
|
)
|
|
156
151
|
self._definitions[SyntaxRegistryKey.BOOKMARK] = definition
|
|
157
152
|
|
|
@@ -160,8 +155,6 @@ class SyntaxRegistry:
|
|
|
160
155
|
start_delimiter="[breadcrumb]",
|
|
161
156
|
end_delimiter="",
|
|
162
157
|
regex_pattern=re.compile(r"^\[breadcrumb\]\s*$", re.IGNORECASE),
|
|
163
|
-
is_multiline_block=False,
|
|
164
|
-
is_inline=False,
|
|
165
158
|
)
|
|
166
159
|
self._definitions[SyntaxRegistryKey.BREADCRUMB] = definition
|
|
167
160
|
|
|
@@ -170,21 +163,16 @@ class SyntaxRegistry:
|
|
|
170
163
|
start_delimiter="- ",
|
|
171
164
|
end_delimiter="",
|
|
172
165
|
regex_pattern=re.compile(r"^(\s*)-\s+(?!\[[ xX]\])(.+)$"),
|
|
173
|
-
is_multiline_block=False,
|
|
174
|
-
is_inline=False,
|
|
175
166
|
)
|
|
176
167
|
self._definitions[SyntaxRegistryKey.BULLETED_LIST] = definition
|
|
177
168
|
|
|
178
169
|
def _register_callout_syntax(self) -> None:
|
|
179
170
|
definition = SyntaxDefinition(
|
|
180
|
-
start_delimiter=
|
|
181
|
-
end_delimiter=
|
|
171
|
+
start_delimiter="[callout]",
|
|
172
|
+
end_delimiter=")",
|
|
182
173
|
regex_pattern=re.compile(
|
|
183
|
-
|
|
174
|
+
r'\[callout\](?:\(([^")]+?)(?:\s+"([^"]+)")?\)|(?:\s+([^"\n]+?)(?:\s+"([^"]+)")?)(?:\n|$))'
|
|
184
175
|
),
|
|
185
|
-
end_regex_pattern=re.compile(rf"^{re.escape(self.MULTI_LINE_BLOCK_DELIMITER)}\s*$"),
|
|
186
|
-
is_multiline_block=True,
|
|
187
|
-
is_inline=False,
|
|
188
176
|
)
|
|
189
177
|
self._definitions[SyntaxRegistryKey.CALLOUT] = definition
|
|
190
178
|
|
|
@@ -195,33 +183,29 @@ class SyntaxRegistry:
|
|
|
195
183
|
end_delimiter=code_delimiter,
|
|
196
184
|
regex_pattern=re.compile("^" + re.escape(code_delimiter) + r"(\w*)\s*$"),
|
|
197
185
|
end_regex_pattern=re.compile("^" + re.escape(code_delimiter) + r"\s*$"),
|
|
198
|
-
is_multiline_block=False,
|
|
199
|
-
is_inline=False,
|
|
200
186
|
)
|
|
201
187
|
self._definitions[SyntaxRegistryKey.CODE] = definition
|
|
202
188
|
|
|
203
189
|
def _register_column_syntax(self) -> None:
|
|
190
|
+
delimiter = self._markdown_grammar.column_delimiter
|
|
204
191
|
definition = SyntaxDefinition(
|
|
205
|
-
start_delimiter=f"{
|
|
206
|
-
end_delimiter=
|
|
192
|
+
start_delimiter=f"{delimiter} column",
|
|
193
|
+
end_delimiter=delimiter,
|
|
207
194
|
regex_pattern=re.compile(
|
|
208
|
-
rf"^{re.escape(
|
|
195
|
+
rf"^{re.escape(delimiter)}\s*column(?:\s+(0?\.\d+|1(?:\.0?)?))??\s*$",
|
|
209
196
|
re.IGNORECASE | re.MULTILINE,
|
|
210
197
|
),
|
|
211
|
-
end_regex_pattern=re.compile(rf"^{re.escape(
|
|
212
|
-
is_multiline_block=True,
|
|
213
|
-
is_inline=False,
|
|
198
|
+
end_regex_pattern=re.compile(rf"^{re.escape(delimiter)}\s*$", re.MULTILINE),
|
|
214
199
|
)
|
|
215
200
|
self._definitions[SyntaxRegistryKey.COLUMN] = definition
|
|
216
201
|
|
|
217
202
|
def _register_column_list_syntax(self) -> None:
|
|
203
|
+
delimiter = self._markdown_grammar.column_delimiter
|
|
218
204
|
definition = SyntaxDefinition(
|
|
219
|
-
start_delimiter=f"{
|
|
220
|
-
end_delimiter=
|
|
221
|
-
regex_pattern=re.compile(rf"^{re.escape(
|
|
222
|
-
end_regex_pattern=re.compile(rf"^{re.escape(
|
|
223
|
-
is_multiline_block=True,
|
|
224
|
-
is_inline=False,
|
|
205
|
+
start_delimiter=f"{delimiter} columns",
|
|
206
|
+
end_delimiter=delimiter,
|
|
207
|
+
regex_pattern=re.compile(rf"^{re.escape(delimiter)}\s*columns?\s*$", re.IGNORECASE),
|
|
208
|
+
end_regex_pattern=re.compile(rf"^{re.escape(delimiter)}\s*$"),
|
|
225
209
|
)
|
|
226
210
|
self._definitions[SyntaxRegistryKey.COLUMN_LIST] = definition
|
|
227
211
|
|
|
@@ -230,8 +214,6 @@ class SyntaxRegistry:
|
|
|
230
214
|
start_delimiter="---",
|
|
231
215
|
end_delimiter="",
|
|
232
216
|
regex_pattern=re.compile(r"^\s*-{3,}\s*$"),
|
|
233
|
-
is_multiline_block=False,
|
|
234
|
-
is_inline=False,
|
|
235
217
|
)
|
|
236
218
|
self._definitions[SyntaxRegistryKey.DIVIDER] = definition
|
|
237
219
|
|
|
@@ -240,8 +222,6 @@ class SyntaxRegistry:
|
|
|
240
222
|
start_delimiter="[embed](",
|
|
241
223
|
end_delimiter=")",
|
|
242
224
|
regex_pattern=re.compile(r"\[embed\]\((https?://[^\s)]+)\)"),
|
|
243
|
-
is_multiline_block=False,
|
|
244
|
-
is_inline=True,
|
|
245
225
|
)
|
|
246
226
|
self._definitions[SyntaxRegistryKey.EMBED] = definition
|
|
247
227
|
|
|
@@ -250,8 +230,6 @@ class SyntaxRegistry:
|
|
|
250
230
|
start_delimiter="$$",
|
|
251
231
|
end_delimiter="$$",
|
|
252
232
|
regex_pattern=re.compile(r"^\$\$\s*$"),
|
|
253
|
-
is_multiline_block=False,
|
|
254
|
-
is_inline=False,
|
|
255
233
|
)
|
|
256
234
|
self._definitions[SyntaxRegistryKey.EQUATION] = definition
|
|
257
235
|
|
|
@@ -260,8 +238,6 @@ class SyntaxRegistry:
|
|
|
260
238
|
start_delimiter="[file](",
|
|
261
239
|
end_delimiter=")",
|
|
262
240
|
regex_pattern=re.compile(r"\[file\]\(([^)]+)\)"),
|
|
263
|
-
is_multiline_block=False,
|
|
264
|
-
is_inline=True,
|
|
265
241
|
)
|
|
266
242
|
self._definitions[SyntaxRegistryKey.FILE] = definition
|
|
267
243
|
|
|
@@ -270,8 +246,6 @@ class SyntaxRegistry:
|
|
|
270
246
|
start_delimiter="# ",
|
|
271
247
|
end_delimiter="",
|
|
272
248
|
regex_pattern=re.compile(r"^#\s+(.+)$"),
|
|
273
|
-
is_multiline_block=False,
|
|
274
|
-
is_inline=False,
|
|
275
249
|
)
|
|
276
250
|
self._definitions[SyntaxRegistryKey.HEADING_1] = definition
|
|
277
251
|
|
|
@@ -280,8 +254,6 @@ class SyntaxRegistry:
|
|
|
280
254
|
start_delimiter="## ",
|
|
281
255
|
end_delimiter="",
|
|
282
256
|
regex_pattern=re.compile(r"^#{2}\s+(.+)$"),
|
|
283
|
-
is_multiline_block=False,
|
|
284
|
-
is_inline=False,
|
|
285
257
|
)
|
|
286
258
|
self._definitions[SyntaxRegistryKey.HEADING_2] = definition
|
|
287
259
|
|
|
@@ -290,8 +262,6 @@ class SyntaxRegistry:
|
|
|
290
262
|
start_delimiter="### ",
|
|
291
263
|
end_delimiter="",
|
|
292
264
|
regex_pattern=re.compile(r"^#{3}\s+(.+)$"),
|
|
293
|
-
is_multiline_block=False,
|
|
294
|
-
is_inline=False,
|
|
295
265
|
)
|
|
296
266
|
self._definitions[SyntaxRegistryKey.HEADING_3] = definition
|
|
297
267
|
|
|
@@ -300,8 +270,6 @@ class SyntaxRegistry:
|
|
|
300
270
|
start_delimiter="[image](",
|
|
301
271
|
end_delimiter=")",
|
|
302
272
|
regex_pattern=re.compile(r"(?<!!)\[image\]\(([^)]+)\)"),
|
|
303
|
-
is_multiline_block=False,
|
|
304
|
-
is_inline=True,
|
|
305
273
|
)
|
|
306
274
|
self._definitions[SyntaxRegistryKey.IMAGE] = definition
|
|
307
275
|
|
|
@@ -310,8 +278,6 @@ class SyntaxRegistry:
|
|
|
310
278
|
start_delimiter="1. ",
|
|
311
279
|
end_delimiter="",
|
|
312
280
|
regex_pattern=re.compile(r"^(\s*)(\d+)\.\s+(.+)$"),
|
|
313
|
-
is_multiline_block=False,
|
|
314
|
-
is_inline=False,
|
|
315
281
|
)
|
|
316
282
|
self._definitions[SyntaxRegistryKey.NUMBERED_LIST] = definition
|
|
317
283
|
|
|
@@ -320,8 +286,6 @@ class SyntaxRegistry:
|
|
|
320
286
|
start_delimiter="[pdf](",
|
|
321
287
|
end_delimiter=")",
|
|
322
288
|
regex_pattern=re.compile(r"\[pdf\]\(([^)]+)\)"),
|
|
323
|
-
is_multiline_block=False,
|
|
324
|
-
is_inline=True,
|
|
325
289
|
)
|
|
326
290
|
self._definitions[SyntaxRegistryKey.PDF] = definition
|
|
327
291
|
|
|
@@ -330,32 +294,24 @@ class SyntaxRegistry:
|
|
|
330
294
|
start_delimiter="> ",
|
|
331
295
|
end_delimiter="",
|
|
332
296
|
regex_pattern=re.compile(r"^>(?!>)\s*(.+)$"),
|
|
333
|
-
is_multiline_block=False,
|
|
334
|
-
is_inline=False,
|
|
335
297
|
)
|
|
336
298
|
self._definitions[SyntaxRegistryKey.QUOTE] = definition
|
|
337
299
|
|
|
338
300
|
def _register_table_syntax(self) -> None:
|
|
301
|
+
delimiter = self._markdown_grammar.table_delimiter
|
|
339
302
|
definition = SyntaxDefinition(
|
|
340
|
-
start_delimiter=
|
|
303
|
+
start_delimiter=delimiter,
|
|
341
304
|
end_delimiter="",
|
|
342
|
-
regex_pattern=re.compile(
|
|
343
|
-
rf"^\s*{re.escape(self.TABLE_DELIMITER)}(.+){re.escape(self.TABLE_DELIMITER)}\s*$"
|
|
344
|
-
),
|
|
345
|
-
is_multiline_block=False,
|
|
346
|
-
is_inline=False,
|
|
305
|
+
regex_pattern=re.compile(rf"^\s*{re.escape(delimiter)}(.+){re.escape(delimiter)}\s*$"),
|
|
347
306
|
)
|
|
348
307
|
self._definitions[SyntaxRegistryKey.TABLE] = definition
|
|
349
308
|
|
|
350
309
|
def _register_table_row_syntax(self) -> None:
|
|
310
|
+
delimiter = self._markdown_grammar.table_delimiter
|
|
351
311
|
definition = SyntaxDefinition(
|
|
352
|
-
start_delimiter=
|
|
312
|
+
start_delimiter=delimiter,
|
|
353
313
|
end_delimiter="",
|
|
354
|
-
regex_pattern=re.compile(
|
|
355
|
-
rf"^\s*{re.escape(self.TABLE_DELIMITER)}([\s\-:|]+){re.escape(self.TABLE_DELIMITER)}\s*$"
|
|
356
|
-
),
|
|
357
|
-
is_multiline_block=False,
|
|
358
|
-
is_inline=False,
|
|
314
|
+
regex_pattern=re.compile(rf"^\s*{re.escape(delimiter)}([\s\-:|]+){re.escape(delimiter)}\s*$"),
|
|
359
315
|
)
|
|
360
316
|
self._definitions[SyntaxRegistryKey.TABLE_ROW] = definition
|
|
361
317
|
|
|
@@ -364,8 +320,6 @@ class SyntaxRegistry:
|
|
|
364
320
|
start_delimiter="[toc]",
|
|
365
321
|
end_delimiter="",
|
|
366
322
|
regex_pattern=re.compile(r"^\[toc\]$", re.IGNORECASE),
|
|
367
|
-
is_multiline_block=False,
|
|
368
|
-
is_inline=False,
|
|
369
323
|
)
|
|
370
324
|
self._definitions[SyntaxRegistryKey.TABLE_OF_CONTENTS] = definition
|
|
371
325
|
|
|
@@ -374,8 +328,6 @@ class SyntaxRegistry:
|
|
|
374
328
|
start_delimiter="- [ ]",
|
|
375
329
|
end_delimiter="",
|
|
376
330
|
regex_pattern=re.compile(r"^\s*-\s+\[ \]\s+(.+)$"),
|
|
377
|
-
is_multiline_block=False,
|
|
378
|
-
is_inline=False,
|
|
379
331
|
)
|
|
380
332
|
self._definitions[SyntaxRegistryKey.TO_DO] = definition
|
|
381
333
|
|
|
@@ -384,31 +336,27 @@ class SyntaxRegistry:
|
|
|
384
336
|
start_delimiter="- [x]",
|
|
385
337
|
end_delimiter="",
|
|
386
338
|
regex_pattern=re.compile(r"^\s*-\s+\[x\]\s+(.+)$", re.IGNORECASE),
|
|
387
|
-
is_multiline_block=False,
|
|
388
|
-
is_inline=False,
|
|
389
339
|
)
|
|
390
340
|
self._definitions[SyntaxRegistryKey.TO_DO_DONE] = definition
|
|
391
341
|
|
|
392
342
|
def _register_toggle_syntax(self) -> None:
|
|
343
|
+
delimiter = self._markdown_grammar.toggle_delimiter
|
|
393
344
|
definition = SyntaxDefinition(
|
|
394
|
-
start_delimiter=
|
|
395
|
-
end_delimiter=
|
|
396
|
-
regex_pattern=re.compile(rf"^{re.escape(
|
|
397
|
-
end_regex_pattern=re.compile(rf"^{re.escape(
|
|
398
|
-
is_multiline_block=True,
|
|
399
|
-
is_inline=False,
|
|
345
|
+
start_delimiter=delimiter,
|
|
346
|
+
end_delimiter=delimiter,
|
|
347
|
+
regex_pattern=re.compile(rf"^{re.escape(delimiter)}\s+(.+)$"),
|
|
348
|
+
end_regex_pattern=re.compile(rf"^{re.escape(delimiter)}\s*$"),
|
|
400
349
|
)
|
|
401
350
|
self._definitions[SyntaxRegistryKey.TOGGLE] = definition
|
|
402
351
|
|
|
403
352
|
def _register_toggleable_heading_syntax(self) -> None:
|
|
404
|
-
|
|
353
|
+
delimiter = self._markdown_grammar.toggle_delimiter
|
|
354
|
+
escaped_delimiter = re.escape(delimiter)
|
|
405
355
|
definition = SyntaxDefinition(
|
|
406
|
-
start_delimiter=f"{
|
|
407
|
-
end_delimiter=
|
|
356
|
+
start_delimiter=f"{delimiter} #",
|
|
357
|
+
end_delimiter=delimiter,
|
|
408
358
|
regex_pattern=re.compile(rf"^{escaped_delimiter}\s*(?P<level>#{{1,3}})(?!#)\s*(.+)$", re.IGNORECASE),
|
|
409
359
|
end_regex_pattern=re.compile(rf"^{escaped_delimiter}\s*$"),
|
|
410
|
-
is_multiline_block=True,
|
|
411
|
-
is_inline=False,
|
|
412
360
|
)
|
|
413
361
|
self._definitions[SyntaxRegistryKey.TOGGLEABLE_HEADING] = definition
|
|
414
362
|
|
|
@@ -417,8 +365,6 @@ class SyntaxRegistry:
|
|
|
417
365
|
start_delimiter="[video](",
|
|
418
366
|
end_delimiter=")",
|
|
419
367
|
regex_pattern=re.compile(r"\[video\]\(([^)]+)\)"),
|
|
420
|
-
is_multiline_block=False,
|
|
421
|
-
is_inline=True,
|
|
422
368
|
)
|
|
423
369
|
self._definitions[SyntaxRegistryKey.VIDEO] = definition
|
|
424
370
|
|
|
@@ -427,8 +373,6 @@ class SyntaxRegistry:
|
|
|
427
373
|
start_delimiter="[caption]",
|
|
428
374
|
end_delimiter="",
|
|
429
375
|
regex_pattern=re.compile(r"^\[caption\]\s+(\S.*)$"),
|
|
430
|
-
is_multiline_block=False,
|
|
431
|
-
is_inline=False,
|
|
432
376
|
)
|
|
433
377
|
self._definitions[SyntaxRegistryKey.CAPTION] = definition
|
|
434
378
|
|
|
@@ -437,8 +381,6 @@ class SyntaxRegistry:
|
|
|
437
381
|
start_delimiter="[space]",
|
|
438
382
|
end_delimiter="",
|
|
439
383
|
regex_pattern=re.compile(r"^\[space\]\s*$"),
|
|
440
|
-
is_multiline_block=False,
|
|
441
|
-
is_inline=False,
|
|
442
384
|
)
|
|
443
385
|
self._definitions[SyntaxRegistryKey.SPACE] = definition
|
|
444
386
|
|
|
@@ -447,7 +389,5 @@ class SyntaxRegistry:
|
|
|
447
389
|
start_delimiter="#",
|
|
448
390
|
end_delimiter="",
|
|
449
391
|
regex_pattern=re.compile(r"^(#{1,3})[ \t]+(.+)$"),
|
|
450
|
-
is_multiline_block=False,
|
|
451
|
-
is_inline=False,
|
|
452
392
|
)
|
|
453
393
|
self._definitions[SyntaxRegistryKey.HEADING] = definition
|
|
@@ -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)
|
|
@@ -123,7 +123,7 @@ class PagePropertyHttpClient(NotionHttpClient):
|
|
|
123
123
|
elif property_type == PageCheckboxProperty:
|
|
124
124
|
return PageCheckboxProperty(id=property_id, checkbox=bool(value))
|
|
125
125
|
elif property_type == PageSelectProperty:
|
|
126
|
-
select_option = SelectOption(
|
|
126
|
+
select_option = SelectOption(name=str(value))
|
|
127
127
|
return PageSelectProperty(id=property_id, select=select_option)
|
|
128
128
|
elif property_type == PageMultiSelectProperty:
|
|
129
129
|
multi_select_options = [SelectOption(id="", name=str(item)) for item in value]
|
|
@@ -5,6 +5,7 @@ from pydantic import BaseModel, Field
|
|
|
5
5
|
|
|
6
6
|
from notionary.blocks.rich_text.models import RichText
|
|
7
7
|
from notionary.shared.properties.type import PropertyType
|
|
8
|
+
from notionary.shared.typings import JsonDict
|
|
8
9
|
from notionary.user.schemas import PersonUserResponseDto, UserResponseDto
|
|
9
10
|
|
|
10
11
|
# ============================================================================
|
|
@@ -23,7 +24,7 @@ class StatusOption(BaseModel):
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class SelectOption(BaseModel):
|
|
26
|
-
id: str
|
|
27
|
+
id: str | None = None
|
|
27
28
|
name: str
|
|
28
29
|
|
|
29
30
|
|
|
@@ -269,7 +270,7 @@ class PageVerificationProperty(PageProperty):
|
|
|
269
270
|
|
|
270
271
|
class PageButtonProperty(PageProperty):
|
|
271
272
|
type: Literal[PropertyType.BUTTON] = PropertyType.BUTTON
|
|
272
|
-
button:
|
|
273
|
+
button: JsonDict = Field(default_factory=dict)
|
|
273
274
|
|
|
274
275
|
|
|
275
276
|
# ============================================================================
|
|
@@ -139,15 +139,30 @@ class PagePropertyHandler:
|
|
|
139
139
|
data_source = await self._get_parent_data_source_or_raise()
|
|
140
140
|
return await data_source.get_options_for_property_by_name(property_name)
|
|
141
141
|
|
|
142
|
+
async def get_schema_description(self, property_name: str) -> str:
|
|
143
|
+
data_source = await self._get_parent_data_source_or_raise()
|
|
144
|
+
return await data_source.get_schema_description(property_name)
|
|
145
|
+
|
|
142
146
|
# =========================================================================
|
|
143
147
|
# Writer Methods
|
|
144
148
|
# =========================================================================
|
|
145
149
|
|
|
146
|
-
async def set_title_property(self,
|
|
147
|
-
self.
|
|
148
|
-
|
|
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)
|
|
149
155
|
self._properties = updated_page.properties
|
|
150
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
|
+
|
|
151
166
|
async def set_rich_text_property(self, property_name: str, text: str) -> None:
|
|
152
167
|
self._get_typed_property_or_raise(property_name, PageRichTextProperty)
|
|
153
168
|
updated_page = await self._property_http_client.patch_rich_text_property(property_name, text)
|
notionary/page/service.py
CHANGED
|
@@ -14,59 +14,31 @@ from notionary.page.properties.factory import PagePropertyHandlerFactory
|
|
|
14
14
|
from notionary.page.properties.models import PageTitleProperty
|
|
15
15
|
from notionary.page.properties.service import PagePropertyHandler
|
|
16
16
|
from notionary.page.schemas import NotionPageDto
|
|
17
|
-
from notionary.shared.entity.dto_parsers import (
|
|
18
|
-
extract_cover_image_url_from_dto,
|
|
19
|
-
extract_emoji_icon_from_dto,
|
|
20
|
-
extract_external_icon_url_from_dto,
|
|
21
|
-
)
|
|
22
17
|
from notionary.shared.entity.service import Entity
|
|
23
|
-
from notionary.user.schemas import PartialUserDto
|
|
24
18
|
from notionary.workspace.query.service import WorkspaceQueryService
|
|
25
19
|
|
|
26
20
|
|
|
27
21
|
class NotionPage(Entity):
|
|
28
22
|
def __init__(
|
|
29
23
|
self,
|
|
30
|
-
|
|
24
|
+
dto: NotionPageDto,
|
|
31
25
|
title: str,
|
|
32
|
-
created_time: str,
|
|
33
|
-
created_by: PartialUserDto,
|
|
34
|
-
last_edited_time: str,
|
|
35
|
-
last_edited_by: PartialUserDto,
|
|
36
|
-
url: str,
|
|
37
|
-
archived: bool,
|
|
38
|
-
in_trash: bool,
|
|
39
26
|
page_property_handler: PagePropertyHandler,
|
|
40
27
|
block_client: NotionBlockHttpClient,
|
|
41
28
|
comment_service: CommentService,
|
|
42
29
|
page_content_service: PageContentService,
|
|
43
30
|
metadata_update_client: PageMetadataUpdateClient,
|
|
44
|
-
public_url: str | None = None,
|
|
45
|
-
emoji_icon: str | None = None,
|
|
46
|
-
external_icon_url: str | None = None,
|
|
47
|
-
cover_image_url: str | None = None,
|
|
48
31
|
) -> None:
|
|
49
|
-
super().__init__(
|
|
50
|
-
|
|
51
|
-
created_time=created_time,
|
|
52
|
-
created_by=created_by,
|
|
53
|
-
last_edited_time=last_edited_time,
|
|
54
|
-
last_edited_by=last_edited_by,
|
|
55
|
-
in_trash=in_trash,
|
|
56
|
-
emoji_icon=emoji_icon,
|
|
57
|
-
external_icon_url=external_icon_url,
|
|
58
|
-
cover_image_url=cover_image_url,
|
|
59
|
-
)
|
|
32
|
+
super().__init__(dto=dto)
|
|
33
|
+
|
|
60
34
|
self._title = title
|
|
61
|
-
self._archived = archived
|
|
62
|
-
self._url = url
|
|
63
|
-
self._public_url = public_url
|
|
35
|
+
self._archived = dto.archived
|
|
64
36
|
|
|
65
37
|
self._block_client = block_client
|
|
66
38
|
self._comment_service = comment_service
|
|
67
39
|
self._page_content_service = page_content_service
|
|
68
|
-
self.properties = page_property_handler
|
|
69
40
|
self._metadata_update_client = metadata_update_client
|
|
41
|
+
self.properties = page_property_handler
|
|
70
42
|
|
|
71
43
|
@classmethod
|
|
72
44
|
async def from_id(
|
|
@@ -75,18 +47,17 @@ class NotionPage(Entity):
|
|
|
75
47
|
page_property_handler_factory: PagePropertyHandlerFactory | None = None,
|
|
76
48
|
) -> Self:
|
|
77
49
|
factory = page_property_handler_factory or PagePropertyHandlerFactory()
|
|
78
|
-
|
|
79
|
-
return await cls._create_from_dto(
|
|
50
|
+
dto = await cls._fetch_page_dto(page_id)
|
|
51
|
+
return await cls._create_from_dto(dto, factory)
|
|
80
52
|
|
|
81
53
|
@classmethod
|
|
82
54
|
async def from_title(
|
|
83
55
|
cls,
|
|
84
56
|
page_title: str,
|
|
85
|
-
min_similarity: float = 0.6,
|
|
86
57
|
search_service: WorkspaceQueryService | None = None,
|
|
87
58
|
) -> Self:
|
|
88
59
|
service = search_service or WorkspaceQueryService()
|
|
89
|
-
return await service.find_page(page_title
|
|
60
|
+
return await service.find_page(page_title)
|
|
90
61
|
|
|
91
62
|
@classmethod
|
|
92
63
|
async def _fetch_page_dto(cls, page_id: str) -> NotionPageDto:
|
|
@@ -96,76 +67,43 @@ class NotionPage(Entity):
|
|
|
96
67
|
@classmethod
|
|
97
68
|
async def _create_from_dto(
|
|
98
69
|
cls,
|
|
99
|
-
|
|
70
|
+
dto: NotionPageDto,
|
|
100
71
|
page_property_handler_factory: PagePropertyHandlerFactory,
|
|
101
72
|
) -> Self:
|
|
102
|
-
title_task = cls._extract_title_from_dto(
|
|
103
|
-
page_property_handler = page_property_handler_factory.create_from_page_response(
|
|
73
|
+
title_task = cls._extract_title_from_dto(dto)
|
|
74
|
+
page_property_handler = page_property_handler_factory.create_from_page_response(dto)
|
|
104
75
|
|
|
105
76
|
title = await title_task
|
|
106
77
|
|
|
107
78
|
return cls._create_with_dependencies(
|
|
108
|
-
|
|
79
|
+
dto=dto,
|
|
109
80
|
title=title,
|
|
110
|
-
created_time=response.created_time,
|
|
111
|
-
created_by=response.created_by,
|
|
112
|
-
last_edited_time=response.last_edited_time,
|
|
113
|
-
last_edited_by=response.last_edited_by,
|
|
114
|
-
archived=response.archived,
|
|
115
|
-
in_trash=response.in_trash,
|
|
116
|
-
url=response.url,
|
|
117
81
|
page_property_handler=page_property_handler,
|
|
118
|
-
public_url=response.public_url,
|
|
119
|
-
emoji_icon=extract_emoji_icon_from_dto(response),
|
|
120
|
-
external_icon_url=extract_external_icon_url_from_dto(response),
|
|
121
|
-
cover_image_url=extract_cover_image_url_from_dto(response),
|
|
122
82
|
)
|
|
123
83
|
|
|
124
84
|
@classmethod
|
|
125
85
|
def _create_with_dependencies(
|
|
126
86
|
cls,
|
|
127
|
-
|
|
87
|
+
dto: NotionPageDto,
|
|
128
88
|
title: str,
|
|
129
|
-
created_time: str,
|
|
130
|
-
created_by: PartialUserDto,
|
|
131
|
-
last_edited_time: str,
|
|
132
|
-
last_edited_by: PartialUserDto,
|
|
133
|
-
url: str,
|
|
134
|
-
archived: bool,
|
|
135
|
-
in_trash: bool,
|
|
136
89
|
page_property_handler: PagePropertyHandler,
|
|
137
|
-
public_url: str | None = None,
|
|
138
|
-
emoji_icon: str | None = None,
|
|
139
|
-
external_icon_url: str | None = None,
|
|
140
|
-
cover_image_url: str | None = None,
|
|
141
90
|
) -> Self:
|
|
142
91
|
block_client = NotionBlockHttpClient()
|
|
143
92
|
comment_service = CommentService()
|
|
144
93
|
|
|
145
94
|
page_content_service_factory = PageContentServiceFactory()
|
|
146
|
-
page_content_service = page_content_service_factory.create(page_id=id, block_client=block_client)
|
|
95
|
+
page_content_service = page_content_service_factory.create(page_id=dto.id, block_client=block_client)
|
|
147
96
|
|
|
148
|
-
metadata_update_client = PageMetadataUpdateClient(page_id=id)
|
|
97
|
+
metadata_update_client = PageMetadataUpdateClient(page_id=dto.id)
|
|
149
98
|
|
|
150
99
|
return cls(
|
|
151
|
-
|
|
100
|
+
dto=dto,
|
|
152
101
|
title=title,
|
|
153
|
-
created_time=created_time,
|
|
154
|
-
created_by=created_by,
|
|
155
|
-
last_edited_time=last_edited_time,
|
|
156
|
-
last_edited_by=last_edited_by,
|
|
157
|
-
url=url,
|
|
158
|
-
archived=archived,
|
|
159
|
-
in_trash=in_trash,
|
|
160
102
|
page_property_handler=page_property_handler,
|
|
161
103
|
block_client=block_client,
|
|
162
104
|
comment_service=comment_service,
|
|
163
105
|
page_content_service=page_content_service,
|
|
164
106
|
metadata_update_client=metadata_update_client,
|
|
165
|
-
public_url=public_url,
|
|
166
|
-
emoji_icon=emoji_icon,
|
|
167
|
-
external_icon_url=external_icon_url,
|
|
168
|
-
cover_image_url=cover_image_url,
|
|
169
107
|
)
|
|
170
108
|
|
|
171
109
|
@staticmethod
|
|
@@ -186,8 +124,12 @@ class NotionPage(Entity):
|
|
|
186
124
|
return self._title
|
|
187
125
|
|
|
188
126
|
@property
|
|
189
|
-
def
|
|
190
|
-
return self.
|
|
127
|
+
def archived(self) -> bool:
|
|
128
|
+
return self._archived
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def markdown_builder() -> MarkdownBuilder:
|
|
132
|
+
return MarkdownBuilder()
|
|
191
133
|
|
|
192
134
|
async def get_comments(self) -> list[Comment]:
|
|
193
135
|
return await self._comment_service.list_all_comments_for_page(page_id=self._id)
|