notionary 0.4.0__py3-none-any.whl → 0.4.2__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.
Files changed (178) hide show
  1. notionary/__init__.py +44 -1
  2. notionary/blocks/client.py +37 -11
  3. notionary/blocks/rich_text/markdown_rich_text_converter.py +49 -15
  4. notionary/blocks/rich_text/models.py +13 -4
  5. notionary/blocks/rich_text/name_id_resolver/data_source.py +9 -3
  6. notionary/blocks/rich_text/name_id_resolver/person.py +6 -2
  7. notionary/blocks/rich_text/rich_text_markdown_converter.py +10 -3
  8. notionary/blocks/schemas.py +2 -1
  9. notionary/comments/client.py +19 -6
  10. notionary/comments/factory.py +10 -3
  11. notionary/comments/schemas.py +9 -3
  12. notionary/comments/service.py +12 -4
  13. notionary/data_source/http/data_source_instance_client.py +59 -17
  14. notionary/data_source/properties/schemas.py +30 -10
  15. notionary/data_source/query/builder.py +67 -18
  16. notionary/data_source/query/resolver.py +16 -5
  17. notionary/data_source/query/schema.py +24 -6
  18. notionary/data_source/query/validator.py +18 -6
  19. notionary/data_source/schema/registry.py +31 -12
  20. notionary/data_source/schema/service.py +66 -20
  21. notionary/data_source/service.py +74 -23
  22. notionary/database/client.py +27 -9
  23. notionary/database/database_metadata_update_client.py +12 -4
  24. notionary/database/service.py +11 -4
  25. notionary/exceptions/__init__.py +15 -3
  26. notionary/exceptions/block_parsing.py +6 -2
  27. notionary/exceptions/data_source/builder.py +11 -5
  28. notionary/exceptions/data_source/properties.py +3 -1
  29. notionary/exceptions/file_upload.py +12 -3
  30. notionary/exceptions/properties.py +3 -1
  31. notionary/exceptions/search.py +6 -2
  32. notionary/file_upload/client.py +5 -1
  33. notionary/file_upload/config/config.py +10 -3
  34. notionary/file_upload/query/builder.py +6 -2
  35. notionary/file_upload/schemas.py +3 -1
  36. notionary/file_upload/service.py +42 -14
  37. notionary/file_upload/validation/factory.py +3 -1
  38. notionary/file_upload/validation/impl/file_name_length.py +3 -1
  39. notionary/file_upload/validation/models.py +15 -5
  40. notionary/file_upload/validation/validators/file_extension.py +12 -3
  41. notionary/http/client.py +27 -8
  42. notionary/page/content/__init__.py +9 -0
  43. notionary/page/content/factory.py +21 -7
  44. notionary/page/content/markdown/builder.py +85 -23
  45. notionary/page/content/markdown/nodes/audio.py +8 -4
  46. notionary/page/content/markdown/nodes/base.py +3 -3
  47. notionary/page/content/markdown/nodes/bookmark.py +5 -3
  48. notionary/page/content/markdown/nodes/breadcrumb.py +2 -2
  49. notionary/page/content/markdown/nodes/bulleted_list.py +5 -3
  50. notionary/page/content/markdown/nodes/callout.py +2 -2
  51. notionary/page/content/markdown/nodes/code.py +5 -3
  52. notionary/page/content/markdown/nodes/columns.py +3 -3
  53. notionary/page/content/markdown/nodes/container.py +9 -5
  54. notionary/page/content/markdown/nodes/divider.py +2 -2
  55. notionary/page/content/markdown/nodes/embed.py +8 -4
  56. notionary/page/content/markdown/nodes/equation.py +4 -2
  57. notionary/page/content/markdown/nodes/file.py +8 -4
  58. notionary/page/content/markdown/nodes/heading.py +2 -2
  59. notionary/page/content/markdown/nodes/image.py +8 -4
  60. notionary/page/content/markdown/nodes/mixins/caption.py +5 -3
  61. notionary/page/content/markdown/nodes/numbered_list.py +5 -3
  62. notionary/page/content/markdown/nodes/paragraph.py +4 -2
  63. notionary/page/content/markdown/nodes/pdf.py +8 -4
  64. notionary/page/content/markdown/nodes/quote.py +2 -2
  65. notionary/page/content/markdown/nodes/space.py +2 -2
  66. notionary/page/content/markdown/nodes/table.py +8 -5
  67. notionary/page/content/markdown/nodes/table_of_contents.py +2 -2
  68. notionary/page/content/markdown/nodes/todo.py +15 -7
  69. notionary/page/content/markdown/nodes/toggle.py +2 -2
  70. notionary/page/content/markdown/nodes/video.py +8 -4
  71. notionary/page/content/markdown/structured_output/__init__.py +73 -0
  72. notionary/page/content/markdown/structured_output/models.py +391 -0
  73. notionary/page/content/markdown/structured_output/service.py +211 -0
  74. notionary/page/content/parser/context.py +1 -1
  75. notionary/page/content/parser/factory.py +23 -8
  76. notionary/page/content/parser/parsers/audio.py +7 -2
  77. notionary/page/content/parser/parsers/base.py +2 -2
  78. notionary/page/content/parser/parsers/bookmark.py +2 -2
  79. notionary/page/content/parser/parsers/breadcrumb.py +2 -2
  80. notionary/page/content/parser/parsers/bulleted_list.py +19 -6
  81. notionary/page/content/parser/parsers/callout.py +15 -5
  82. notionary/page/content/parser/parsers/caption.py +9 -3
  83. notionary/page/content/parser/parsers/code.py +21 -7
  84. notionary/page/content/parser/parsers/column.py +8 -4
  85. notionary/page/content/parser/parsers/column_list.py +19 -7
  86. notionary/page/content/parser/parsers/divider.py +2 -2
  87. notionary/page/content/parser/parsers/embed.py +2 -2
  88. notionary/page/content/parser/parsers/equation.py +8 -4
  89. notionary/page/content/parser/parsers/file.py +7 -2
  90. notionary/page/content/parser/parsers/file_like_block.py +30 -10
  91. notionary/page/content/parser/parsers/heading.py +31 -10
  92. notionary/page/content/parser/parsers/image.py +7 -2
  93. notionary/page/content/parser/parsers/numbered_list.py +18 -6
  94. notionary/page/content/parser/parsers/paragraph.py +3 -1
  95. notionary/page/content/parser/parsers/pdf.py +7 -2
  96. notionary/page/content/parser/parsers/quote.py +28 -9
  97. notionary/page/content/parser/parsers/space.py +2 -2
  98. notionary/page/content/parser/parsers/table.py +31 -10
  99. notionary/page/content/parser/parsers/table_of_contents.py +7 -3
  100. notionary/page/content/parser/parsers/todo.py +15 -5
  101. notionary/page/content/parser/parsers/toggle.py +15 -5
  102. notionary/page/content/parser/parsers/video.py +7 -2
  103. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +8 -2
  104. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +8 -2
  105. notionary/page/content/parser/post_processing/service.py +3 -1
  106. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +21 -7
  107. notionary/page/content/parser/pre_processsing/handlers/indentation.py +11 -4
  108. notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +13 -6
  109. notionary/page/content/parser/service.py +4 -1
  110. notionary/page/content/renderer/context.py +15 -5
  111. notionary/page/content/renderer/factory.py +12 -6
  112. notionary/page/content/renderer/post_processing/handlers/numbered_list.py +19 -9
  113. notionary/page/content/renderer/renderers/audio.py +14 -5
  114. notionary/page/content/renderer/renderers/base.py +3 -3
  115. notionary/page/content/renderer/renderers/bookmark.py +3 -1
  116. notionary/page/content/renderer/renderers/bulleted_list.py +11 -5
  117. notionary/page/content/renderer/renderers/callout.py +19 -7
  118. notionary/page/content/renderer/renderers/captioned_block.py +11 -5
  119. notionary/page/content/renderer/renderers/code.py +6 -2
  120. notionary/page/content/renderer/renderers/column.py +3 -1
  121. notionary/page/content/renderer/renderers/column_list.py +3 -1
  122. notionary/page/content/renderer/renderers/embed.py +3 -1
  123. notionary/page/content/renderer/renderers/equation.py +3 -1
  124. notionary/page/content/renderer/renderers/file.py +14 -5
  125. notionary/page/content/renderer/renderers/file_like_block.py +8 -4
  126. notionary/page/content/renderer/renderers/heading.py +22 -8
  127. notionary/page/content/renderer/renderers/image.py +13 -4
  128. notionary/page/content/renderer/renderers/numbered_list.py +8 -3
  129. notionary/page/content/renderer/renderers/paragraph.py +12 -4
  130. notionary/page/content/renderer/renderers/pdf.py +14 -5
  131. notionary/page/content/renderer/renderers/quote.py +14 -6
  132. notionary/page/content/renderer/renderers/table.py +15 -5
  133. notionary/page/content/renderer/renderers/todo.py +16 -6
  134. notionary/page/content/renderer/renderers/toggle.py +8 -4
  135. notionary/page/content/renderer/renderers/video.py +14 -5
  136. notionary/page/content/renderer/service.py +9 -3
  137. notionary/page/content/service.py +21 -7
  138. notionary/page/content/syntax/definition/__init__.py +11 -0
  139. notionary/page/content/syntax/definition/models.py +57 -0
  140. notionary/page/content/syntax/definition/registry.py +371 -0
  141. notionary/page/content/syntax/prompts/__init__.py +4 -0
  142. notionary/page/content/syntax/prompts/models.py +11 -0
  143. notionary/page/content/syntax/prompts/registry.py +703 -0
  144. notionary/page/page_metadata_update_client.py +12 -4
  145. notionary/page/properties/client.py +45 -15
  146. notionary/page/properties/factory.py +6 -2
  147. notionary/page/properties/service.py +110 -36
  148. notionary/page/service.py +20 -6
  149. notionary/shared/entity/client.py +6 -2
  150. notionary/shared/entity/dto_parsers.py +3 -1
  151. notionary/shared/entity/entity_metadata_update_client.py +9 -3
  152. notionary/shared/entity/schemas.py +1 -1
  153. notionary/shared/entity/service.py +53 -22
  154. notionary/shared/models/file.py +3 -1
  155. notionary/shared/models/icon.py +6 -4
  156. notionary/user/base.py +6 -2
  157. notionary/user/bot.py +10 -2
  158. notionary/user/client.py +3 -1
  159. notionary/user/person.py +3 -1
  160. notionary/user/schemas.py +3 -1
  161. notionary/user/service.py +6 -2
  162. notionary/utils/decorators.py +6 -2
  163. notionary/utils/fuzzy.py +6 -2
  164. notionary/utils/mixins/logging.py +3 -1
  165. notionary/utils/pagination.py +14 -4
  166. notionary/workspace/__init__.py +5 -1
  167. notionary/workspace/query/service.py +59 -16
  168. notionary/workspace/service.py +39 -11
  169. {notionary-0.4.0.dist-info → notionary-0.4.2.dist-info}/METADATA +1 -1
  170. notionary-0.4.2.dist-info/RECORD +236 -0
  171. notionary/page/blocks/client.py +0 -1
  172. notionary/page/content/syntax/__init__.py +0 -5
  173. notionary/page/content/syntax/models.py +0 -66
  174. notionary/page/content/syntax/registry.py +0 -371
  175. notionary-0.4.0.dist-info/RECORD +0 -230
  176. /notionary/page/content/syntax/{grammar.py → definition/grammar.py} +0 -0
  177. {notionary-0.4.0.dist-info → notionary-0.4.2.dist-info}/WHEEL +0 -0
  178. {notionary-0.4.0.dist-info → notionary-0.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -3,14 +3,25 @@ from __future__ import annotations
3
3
  from collections.abc import AsyncIterator
4
4
  from typing import TYPE_CHECKING, Any, override
5
5
 
6
- from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
6
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
7
+ RichTextToMarkdownConverter,
8
+ )
7
9
  from notionary.data_source.query.schema import DataSourceQueryParams
8
- from notionary.data_source.schemas import DataSourceDto, QueryDataSourceResponse, UpdateDataSourceDto
10
+ from notionary.data_source.schemas import (
11
+ DataSourceDto,
12
+ QueryDataSourceResponse,
13
+ UpdateDataSourceDto,
14
+ )
9
15
  from notionary.http.client import NotionHttpClient
10
16
  from notionary.page.schemas import NotionPageDto
11
- from notionary.shared.entity.entity_metadata_update_client import EntityMetadataUpdateClient
17
+ from notionary.shared.entity.entity_metadata_update_client import (
18
+ EntityMetadataUpdateClient,
19
+ )
12
20
  from notionary.shared.typings import JsonDict
13
- from notionary.utils.pagination import paginate_notion_api, paginate_notion_api_generator
21
+ from notionary.utils.pagination import (
22
+ paginate_notion_api,
23
+ paginate_notion_api_generator,
24
+ )
14
25
 
15
26
  if TYPE_CHECKING:
16
27
  from notionary import NotionPage
@@ -22,9 +33,15 @@ class DataSourceInstanceClient(NotionHttpClient, EntityMetadataUpdateClient):
22
33
  self._data_source_id = data_source_id
23
34
 
24
35
  @override
25
- async def patch_metadata(self, update_data_source_dto: UpdateDataSourceDto) -> DataSourceDto:
26
- update_data_source_dto_dict = update_data_source_dto.model_dump(exclude_none=True)
27
- response = await self.patch(f"data_sources/{self._data_source_id}", data=update_data_source_dto_dict)
36
+ async def patch_metadata(
37
+ self, update_data_source_dto: UpdateDataSourceDto
38
+ ) -> DataSourceDto:
39
+ update_data_source_dto_dict = update_data_source_dto.model_dump(
40
+ exclude_none=True
41
+ )
42
+ response = await self.patch(
43
+ f"data_sources/{self._data_source_id}", data=update_data_source_dto_dict
44
+ )
28
45
  return DataSourceDto.model_validate(response)
29
46
 
30
47
  async def update_title(self, title: str) -> DataSourceDto:
@@ -40,28 +57,38 @@ class DataSourceInstanceClient(NotionHttpClient, EntityMetadataUpdateClient):
40
57
  await self.patch_metadata(update_data_source_dto)
41
58
 
42
59
  async def update_description(self, description: str) -> str:
43
- from notionary.blocks.rich_text.markdown_rich_text_converter import MarkdownRichTextConverter
60
+ from notionary.blocks.rich_text.markdown_rich_text_converter import (
61
+ MarkdownRichTextConverter,
62
+ )
44
63
 
45
64
  markdown_rich_text_converter = MarkdownRichTextConverter()
46
- rich_text_description = await markdown_rich_text_converter.to_rich_text(description)
65
+ rich_text_description = await markdown_rich_text_converter.to_rich_text(
66
+ description
67
+ )
47
68
  update_data_source_dto = UpdateDataSourceDto(description=rich_text_description)
48
69
 
49
70
  updated_data_source_dto = await self.patch_metadata(update_data_source_dto)
50
71
 
51
72
  markdown_rich_text_converter = RichTextToMarkdownConverter()
52
73
  updated_markdown_description = (
53
- await markdown_rich_text_converter.to_markdown(updated_data_source_dto.description)
74
+ await markdown_rich_text_converter.to_markdown(
75
+ updated_data_source_dto.description
76
+ )
54
77
  if updated_data_source_dto.description
55
78
  else None
56
79
  )
57
80
  return updated_markdown_description
58
81
 
59
- async def query(self, query_params: DataSourceQueryParams | None = None) -> QueryDataSourceResponse:
82
+ async def query(
83
+ self, query_params: DataSourceQueryParams | None = None
84
+ ) -> QueryDataSourceResponse:
60
85
  query_params_dict = query_params.to_api_params() if query_params else {}
61
86
  total_result_limit = query_params.total_results_limit if query_params else None
62
87
 
63
88
  all_results = await paginate_notion_api(
64
- self._make_query_request, query_data=query_params_dict or {}, total_result_limit=total_result_limit
89
+ self._make_query_request,
90
+ query_data=query_params_dict or {},
91
+ total_result_limit=total_result_limit,
65
92
  )
66
93
 
67
94
  return QueryDataSourceResponse(
@@ -70,17 +97,24 @@ class DataSourceInstanceClient(NotionHttpClient, EntityMetadataUpdateClient):
70
97
  has_more=False,
71
98
  )
72
99
 
73
- async def query_stream(self, query_params: DataSourceQueryParams | None = None) -> AsyncIterator[Any]:
100
+ async def query_stream(
101
+ self, query_params: DataSourceQueryParams | None = None
102
+ ) -> AsyncIterator[Any]:
74
103
  query_params_dict = query_params.model_dump() if query_params else {}
75
104
  total_result_limit = query_params.total_results_limit if query_params else None
76
105
 
77
106
  async for result in paginate_notion_api_generator(
78
- self._make_query_request, query_data=query_params_dict or {}, total_results_limit=total_result_limit
107
+ self._make_query_request,
108
+ query_data=query_params_dict or {},
109
+ total_results_limit=total_result_limit,
79
110
  ):
80
111
  yield result
81
112
 
82
113
  async def _make_query_request(
83
- self, query_data: JsonDict, start_cursor: str | None = None, page_size: int | None = None
114
+ self,
115
+ query_data: JsonDict,
116
+ start_cursor: str | None = None,
117
+ page_size: int | None = None,
84
118
  ) -> QueryDataSourceResponse:
85
119
  current_query_data = query_data.copy()
86
120
  if start_cursor:
@@ -88,13 +122,21 @@ class DataSourceInstanceClient(NotionHttpClient, EntityMetadataUpdateClient):
88
122
  if page_size:
89
123
  current_query_data["page_size"] = page_size
90
124
 
91
- response = await self.post(f"data_sources/{self._data_source_id}/query", data=current_query_data)
125
+ response = await self.post(
126
+ f"data_sources/{self._data_source_id}/query", data=current_query_data
127
+ )
92
128
  return QueryDataSourceResponse.model_validate(response)
93
129
 
94
130
  async def create_blank_page(self, title: str | None = None) -> NotionPage:
95
131
  from notionary import NotionPage
96
132
 
97
- data = {"parent": {"type": "data_source_id", "data_source_id": self._data_source_id}, "properties": {}}
133
+ data = {
134
+ "parent": {
135
+ "type": "data_source_id",
136
+ "data_source_id": self._data_source_id,
137
+ },
138
+ "properties": {},
139
+ }
98
140
 
99
141
  if title:
100
142
  data["properties"]["Name"] = {"title": [{"text": {"content": title}}]}
@@ -163,7 +163,9 @@ class DataSourceMultiSelectConfig(BaseModel):
163
163
 
164
164
  class DataSourceMultiSelectProperty(DataSourceProperty):
165
165
  type: Literal[PropertyType.MULTI_SELECT] = PropertyType.MULTI_SELECT
166
- multi_select: DataSourceMultiSelectConfig = Field(default_factory=DataSourceMultiSelectConfig)
166
+ multi_select: DataSourceMultiSelectConfig = Field(
167
+ default_factory=DataSourceMultiSelectConfig
168
+ )
167
169
 
168
170
  @property
169
171
  def option_names(self) -> list[str]:
@@ -199,7 +201,9 @@ class DataSourceCreatedTimeConfig(BaseModel): ...
199
201
 
200
202
  class DataSourceCreatedTimeProperty(DataSourceProperty):
201
203
  type: Literal[PropertyType.CREATED_TIME] = PropertyType.CREATED_TIME
202
- created_time: DataSourceCreatedTimeConfig = Field(default_factory=DataSourceCreatedTimeConfig)
204
+ created_time: DataSourceCreatedTimeConfig = Field(
205
+ default_factory=DataSourceCreatedTimeConfig
206
+ )
203
207
 
204
208
 
205
209
  class DataSourceCreatedByConfig(BaseModel): ...
@@ -207,7 +211,9 @@ class DataSourceCreatedByConfig(BaseModel): ...
207
211
 
208
212
  class DataSourceCreatedByProperty(DataSourceProperty):
209
213
  type: Literal[PropertyType.CREATED_BY] = PropertyType.CREATED_BY
210
- created_by: DataSourceCreatedByConfig = Field(default_factory=DataSourceCreatedByConfig)
214
+ created_by: DataSourceCreatedByConfig = Field(
215
+ default_factory=DataSourceCreatedByConfig
216
+ )
211
217
 
212
218
 
213
219
  class DataSourceLastEditedTimeConfig(BaseModel): ...
@@ -215,7 +221,9 @@ class DataSourceLastEditedTimeConfig(BaseModel): ...
215
221
 
216
222
  class DataSourceLastEditedTimeProperty(DataSourceProperty):
217
223
  type: Literal[PropertyType.LAST_EDITED_TIME] = PropertyType.LAST_EDITED_TIME
218
- last_edited_time: DataSourceLastEditedTimeConfig = Field(default_factory=DataSourceLastEditedTimeConfig)
224
+ last_edited_time: DataSourceLastEditedTimeConfig = Field(
225
+ default_factory=DataSourceLastEditedTimeConfig
226
+ )
219
227
 
220
228
 
221
229
  class DataSourceLastEditedByConfig(BaseModel): ...
@@ -223,7 +231,9 @@ class DataSourceLastEditedByConfig(BaseModel): ...
223
231
 
224
232
  class DataSourceLastEditedByProperty(DataSourceProperty):
225
233
  type: Literal[PropertyType.LAST_EDITED_BY] = PropertyType.LAST_EDITED_BY
226
- last_edited_by: DataSourceLastEditedByConfig = Field(default_factory=DataSourceLastEditedByConfig)
234
+ last_edited_by: DataSourceLastEditedByConfig = Field(
235
+ default_factory=DataSourceLastEditedByConfig
236
+ )
227
237
 
228
238
 
229
239
  class DataSourceLastVisitedTimeConfig(BaseModel): ...
@@ -231,7 +241,9 @@ class DataSourceLastVisitedTimeConfig(BaseModel): ...
231
241
 
232
242
  class DataSourceLastVisitedTimeProperty(DataSourceProperty):
233
243
  type: Literal[PropertyType.LAST_VISITED_TIME] = PropertyType.LAST_VISITED_TIME
234
- last_visited_time: DataSourceLastVisitedTimeConfig = Field(default_factory=DataSourceLastVisitedTimeConfig)
244
+ last_visited_time: DataSourceLastVisitedTimeConfig = Field(
245
+ default_factory=DataSourceLastVisitedTimeConfig
246
+ )
235
247
 
236
248
 
237
249
  class DataSourceTitleConfig(BaseModel): ...
@@ -247,7 +259,9 @@ class DataSourceRichTextConfig(BaseModel): ...
247
259
 
248
260
  class DataSourceRichTextProperty(DataSourceProperty):
249
261
  type: Literal[PropertyType.RICH_TEXT] = PropertyType.RICH_TEXT
250
- rich_text: DataSourceRichTextConfig = Field(default_factory=DataSourceRichTextConfig)
262
+ rich_text: DataSourceRichTextConfig = Field(
263
+ default_factory=DataSourceRichTextConfig
264
+ )
251
265
 
252
266
 
253
267
  class DataSourceURLConfig(BaseModel): ...
@@ -300,7 +314,9 @@ class DataSourcePhoneNumberConfig(BaseModel): ...
300
314
 
301
315
  class DataSourcePhoneNumberProperty(DataSourceProperty):
302
316
  type: Literal[PropertyType.PHONE_NUMBER] = PropertyType.PHONE_NUMBER
303
- phone_number: DataSourcePhoneNumberConfig = Field(default_factory=DataSourcePhoneNumberConfig)
317
+ phone_number: DataSourcePhoneNumberConfig = Field(
318
+ default_factory=DataSourcePhoneNumberConfig
319
+ )
304
320
 
305
321
 
306
322
  class DataSourceFilesConfig(BaseModel): ...
@@ -347,7 +363,9 @@ class DataSourceUniqueIdConfig(BaseModel):
347
363
 
348
364
  class DataSourceUniqueIdProperty(DataSourceProperty):
349
365
  type: Literal[PropertyType.UNIQUE_ID] = PropertyType.UNIQUE_ID
350
- unique_id: DataSourceUniqueIdConfig = Field(default_factory=DataSourceUniqueIdConfig)
366
+ unique_id: DataSourceUniqueIdConfig = Field(
367
+ default_factory=DataSourceUniqueIdConfig
368
+ )
351
369
 
352
370
  @property
353
371
  def prefix(self) -> str | None:
@@ -383,7 +401,9 @@ class DataSourceVerificationConfig(BaseModel): ...
383
401
 
384
402
  class DataSourceVerificationProperty(DataSourceProperty):
385
403
  type: Literal[PropertyType.VERIFICATION] = PropertyType.VERIFICATION
386
- verification: DataSourceVerificationConfig = Field(default_factory=DataSourceVerificationConfig)
404
+ verification: DataSourceVerificationConfig = Field(
405
+ default_factory=DataSourceVerificationConfig
406
+ )
387
407
 
388
408
 
389
409
  class DataSourceUnknownProperty(BaseModel):
@@ -160,7 +160,9 @@ class DataSourceQueryBuilder:
160
160
  def people_is_empty(self) -> Self:
161
161
  return self._add_filter(ArrayOperator.IS_EMPTY, None)
162
162
 
163
- def order_by(self, property_name: str, direction: SortDirection = SortDirection.ASCENDING) -> Self:
163
+ def order_by(
164
+ self, property_name: str, direction: SortDirection = SortDirection.ASCENDING
165
+ ) -> Self:
164
166
  self._ensure_property_exists(property_name)
165
167
  sort = PropertySort(property=property_name, direction=direction)
166
168
  self._sorts.append(sort)
@@ -178,7 +180,9 @@ class DataSourceQueryBuilder:
178
180
  def order_by_created_time_descending(self) -> Self:
179
181
  return self._order_by_created_time(SortDirection.DESCENDING)
180
182
 
181
- def _order_by_created_time(self, direction: SortDirection = SortDirection.DESCENDING) -> Self:
183
+ def _order_by_created_time(
184
+ self, direction: SortDirection = SortDirection.DESCENDING
185
+ ) -> Self:
182
186
  sort = TimestampSort(timestamp=TimestampType.CREATED_TIME, direction=direction)
183
187
  self._sorts.append(sort)
184
188
  return self
@@ -189,8 +193,12 @@ class DataSourceQueryBuilder:
189
193
  def order_by_last_edited_time_descending(self) -> Self:
190
194
  return self._order_by_last_edited_time(SortDirection.DESCENDING)
191
195
 
192
- def _order_by_last_edited_time(self, direction: SortDirection = SortDirection.DESCENDING) -> Self:
193
- sort = TimestampSort(timestamp=TimestampType.LAST_EDITED_TIME, direction=direction)
196
+ def _order_by_last_edited_time(
197
+ self, direction: SortDirection = SortDirection.DESCENDING
198
+ ) -> Self:
199
+ sort = TimestampSort(
200
+ timestamp=TimestampType.LAST_EDITED_TIME, direction=direction
201
+ )
194
202
  self._sorts.append(sort)
195
203
  return self
196
204
 
@@ -211,7 +219,10 @@ class DataSourceQueryBuilder:
211
219
  notion_filter = self._create_notion_filter_if_needed()
212
220
  sorts = self._create_sorts_if_needed()
213
221
  return DataSourceQueryParams(
214
- filter=notion_filter, sorts=sorts, page_size=self._page_size, total_results_limit=self._total_results_limit
222
+ filter=notion_filter,
223
+ sorts=sorts,
224
+ page_size=self._page_size,
225
+ total_results_limit=self._total_results_limit,
215
226
  )
216
227
 
217
228
  def _select_property_without_negation(self, property_name: str) -> None:
@@ -264,7 +275,9 @@ class DataSourceQueryBuilder:
264
275
  def _has_no_filters(self) -> bool:
265
276
  return not self._filters
266
277
 
267
- def _is_regular_filter_condition(self, filter_item: InternalFilterCondition) -> bool:
278
+ def _is_regular_filter_condition(
279
+ self, filter_item: InternalFilterCondition
280
+ ) -> bool:
268
281
  return isinstance(filter_item, FilterCondition)
269
282
 
270
283
  def _finalize_current_or_group(self) -> None:
@@ -284,7 +297,11 @@ class DataSourceQueryBuilder:
284
297
 
285
298
  def _add_filter(
286
299
  self,
287
- operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
300
+ operator: StringOperator
301
+ | NumberOperator
302
+ | BooleanOperator
303
+ | DateOperator
304
+ | ArrayOperator,
288
305
  value: str | int | float | list[str | int | float] | None,
289
306
  ) -> Self:
290
307
  self._ensure_property_is_selected()
@@ -301,7 +318,9 @@ class DataSourceQueryBuilder:
301
318
 
302
319
  property_obj = self._properties.get(self._current_property)
303
320
  if property_obj:
304
- self._query_validator.validate_operator_for_property(self._current_property, property_obj, operator)
321
+ self._query_validator.validate_operator_for_property(
322
+ self._current_property, property_obj, operator
323
+ )
305
324
  return self
306
325
 
307
326
  def _ensure_property_is_selected(self) -> None:
@@ -313,8 +332,14 @@ class DataSourceQueryBuilder:
313
332
 
314
333
  def _apply_negation_if_needed(
315
334
  self,
316
- operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
317
- ) -> StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator:
335
+ operator: StringOperator
336
+ | NumberOperator
337
+ | BooleanOperator
338
+ | DateOperator
339
+ | ArrayOperator,
340
+ ) -> (
341
+ StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator
342
+ ):
318
343
  if not self._negate_next:
319
344
  return operator
320
345
 
@@ -324,7 +349,11 @@ class DataSourceQueryBuilder:
324
349
 
325
350
  def _create_filter_condition(
326
351
  self,
327
- operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
352
+ operator: StringOperator
353
+ | NumberOperator
354
+ | BooleanOperator
355
+ | DateOperator
356
+ | ArrayOperator,
328
357
  value: str | int | float | list[str | int | float] | None,
329
358
  ) -> FilterCondition:
330
359
  field_type = self._determine_field_type_from_operator(operator)
@@ -372,13 +401,17 @@ class DataSourceQueryBuilder:
372
401
  property_filters = [self._build_filter(f) for f in self._filters]
373
402
  return CompoundFilter(operator=LogicalOperator.AND, filters=property_filters)
374
403
 
375
- def _build_filter(self, condition: InternalFilterCondition) -> PropertyFilter | CompoundFilter:
404
+ def _build_filter(
405
+ self, condition: InternalFilterCondition
406
+ ) -> PropertyFilter | CompoundFilter:
376
407
  if isinstance(condition, OrGroupMarker):
377
408
  return self._build_or_compound_filter(condition)
378
409
  return self._build_property_filter(condition)
379
410
 
380
411
  def _build_or_compound_filter(self, or_marker: OrGroupMarker) -> CompoundFilter:
381
- property_filters = [self._build_property_filter(c) for c in or_marker.conditions]
412
+ property_filters = [
413
+ self._build_property_filter(c) for c in or_marker.conditions
414
+ ]
382
415
  return CompoundFilter(operator=LogicalOperator.OR, filters=property_filters)
383
416
 
384
417
  def _build_property_filter(self, condition: FilterCondition) -> PropertyFilter:
@@ -398,8 +431,14 @@ class DataSourceQueryBuilder:
398
431
 
399
432
  def _negate_operator(
400
433
  self,
401
- operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
402
- ) -> StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator:
434
+ operator: StringOperator
435
+ | NumberOperator
436
+ | BooleanOperator
437
+ | DateOperator
438
+ | ArrayOperator,
439
+ ) -> (
440
+ StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator
441
+ ):
403
442
  negation_map = {
404
443
  StringOperator.EQUALS: StringOperator.DOES_NOT_EQUAL,
405
444
  StringOperator.DOES_NOT_EQUAL: StringOperator.EQUALS,
@@ -436,13 +475,23 @@ class DataSourceQueryBuilder:
436
475
 
437
476
  def _raise_operator_cannot_be_negated_error(
438
477
  self,
439
- operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
478
+ operator: StringOperator
479
+ | NumberOperator
480
+ | BooleanOperator
481
+ | DateOperator
482
+ | ArrayOperator,
440
483
  ) -> None:
441
- raise ValueError(f"Operator '{operator}' cannot be negated. This should not happen - please report this issue.")
484
+ raise ValueError(
485
+ f"Operator '{operator}' cannot be negated. This should not happen - please report this issue."
486
+ )
442
487
 
443
488
  def _determine_field_type_from_operator(
444
489
  self,
445
- operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
490
+ operator: StringOperator
491
+ | NumberOperator
492
+ | BooleanOperator
493
+ | DateOperator
494
+ | ArrayOperator,
446
495
  ) -> FieldType:
447
496
  if isinstance(operator, StringOperator):
448
497
  return FieldType.STRING
@@ -14,7 +14,10 @@ from notionary.utils.mixins.logging import LoggingMixin
14
14
 
15
15
 
16
16
  class QueryResolver(LoggingMixin):
17
- UUID_PATTERN = re.compile(r"^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$", re.IGNORECASE)
17
+ UUID_PATTERN = re.compile(
18
+ r"^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$",
19
+ re.IGNORECASE,
20
+ )
18
21
 
19
22
  def __init__(
20
23
  self,
@@ -24,7 +27,9 @@ class QueryResolver(LoggingMixin):
24
27
  self._user_resolver = user_resolver or PersonNameIdResolver()
25
28
  self._page_resolver = page_resolver or PageNameIdResolver()
26
29
 
27
- async def resolve_params(self, params: DataSourceQueryParams) -> DataSourceQueryParams:
30
+ async def resolve_params(
31
+ self, params: DataSourceQueryParams
32
+ ) -> DataSourceQueryParams:
28
33
  if not params.filter:
29
34
  return params
30
35
 
@@ -38,7 +43,9 @@ class QueryResolver(LoggingMixin):
38
43
  return await self._resolve_compound_filter(filter)
39
44
  return filter
40
45
 
41
- async def _resolve_compound_filter(self, compound: CompoundFilter) -> CompoundFilter:
46
+ async def _resolve_compound_filter(
47
+ self, compound: CompoundFilter
48
+ ) -> CompoundFilter:
42
49
  resolved_filters = []
43
50
  for filter in compound.filters:
44
51
  resolved = await self._resolve_filter(filter)
@@ -46,7 +53,9 @@ class QueryResolver(LoggingMixin):
46
53
 
47
54
  return CompoundFilter(operator=compound.operator, filters=resolved_filters)
48
55
 
49
- async def _resolve_property_filter(self, prop_filter: PropertyFilter) -> PropertyFilter:
56
+ async def _resolve_property_filter(
57
+ self, prop_filter: PropertyFilter
58
+ ) -> PropertyFilter:
50
59
  if not self._is_resolvable_property_type(prop_filter.property_type):
51
60
  return prop_filter
52
61
 
@@ -56,7 +65,9 @@ class QueryResolver(LoggingMixin):
56
65
  if self._is_uuid(prop_filter.value):
57
66
  return prop_filter
58
67
 
59
- resolved_value = await self._resolve_value(prop_filter.value, prop_filter.property_type)
68
+ resolved_value = await self._resolve_value(
69
+ prop_filter.value, prop_filter.property_type
70
+ )
60
71
 
61
72
  return PropertyFilter(
62
73
  property=prop_filter.property,
@@ -3,7 +3,13 @@ from __future__ import annotations
3
3
  from enum import StrEnum
4
4
  from typing import Self
5
5
 
6
- from pydantic import BaseModel, ValidationInfo, field_validator, model_serializer, model_validator
6
+ from pydantic import (
7
+ BaseModel,
8
+ ValidationInfo,
9
+ field_validator,
10
+ model_serializer,
11
+ model_validator,
12
+ )
7
13
 
8
14
  from notionary.shared.properties.type import PropertyType
9
15
  from notionary.shared.typings import JsonDict
@@ -93,7 +99,9 @@ class TimeUnit(StrEnum):
93
99
  YEARS = "years"
94
100
 
95
101
 
96
- type Operator = StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator
102
+ type Operator = (
103
+ StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator
104
+ )
97
105
  type FilterValue = str | int | float | bool | list[str | int | float]
98
106
 
99
107
 
@@ -156,7 +164,10 @@ class FilterCondition(BaseModel):
156
164
  self._ensure_value_is_number()
157
165
  elif self.field_type == FieldType.BOOLEAN:
158
166
  self._ensure_value_is_boolean()
159
- elif self.field_type in (FieldType.DATE, FieldType.DATETIME) or self.field_type in (
167
+ elif self.field_type in (
168
+ FieldType.DATE,
169
+ FieldType.DATETIME,
170
+ ) or self.field_type in (
160
171
  FieldType.ARRAY,
161
172
  FieldType.RELATION,
162
173
  FieldType.PEOPLE,
@@ -196,7 +207,9 @@ class FilterCondition(BaseModel):
196
207
  operator_value = value if isinstance(value, str) else value.value
197
208
 
198
209
  if not cls._is_operator_valid_for_field_type(operator_value, field_type):
199
- raise ValueError(f"Operator '{operator_value}' is not valid for field type '{field_type}'")
210
+ raise ValueError(
211
+ f"Operator '{operator_value}' is not valid for field type '{field_type}'"
212
+ )
200
213
 
201
214
  return value
202
215
 
@@ -234,7 +247,10 @@ class PropertyFilter(BaseModel):
234
247
  if self.value is None:
235
248
  return self
236
249
 
237
- if self.property_type in (PropertyType.PEOPLE, PropertyType.RELATION) and not isinstance(self.value, str):
250
+ if self.property_type in (
251
+ PropertyType.PEOPLE,
252
+ PropertyType.RELATION,
253
+ ) and not isinstance(self.value, str):
238
254
  raise ValueError(
239
255
  f"Value for property type '{self.property_type.value}' must be a string, "
240
256
  f"got {type(self.value).__name__}"
@@ -254,7 +270,9 @@ class PropertyFilter(BaseModel):
254
270
 
255
271
  return {
256
272
  "property": self.property,
257
- property_type_str: {operator_str: filter_value if filter_value is not None else True},
273
+ property_type_str: {
274
+ operator_str: filter_value if filter_value is not None else True
275
+ },
258
276
  }
259
277
 
260
278
 
@@ -39,7 +39,9 @@ class QueryValidator:
39
39
  self, property_name: str, property_obj: DataSourceProperty, operator: Operator
40
40
  ) -> None:
41
41
  if not self._is_operator_valid_for_property_type(property_obj.type, operator):
42
- valid_operators = self._get_valid_operators_for_property_type(property_obj.type)
42
+ valid_operators = self._get_valid_operators_for_property_type(
43
+ property_obj.type
44
+ )
43
45
  raise InvalidOperatorForPropertyType(
44
46
  property_name=property_name,
45
47
  property_type=property_obj.type,
@@ -47,23 +49,33 @@ class QueryValidator:
47
49
  valid_operators=valid_operators,
48
50
  )
49
51
 
50
- def _is_operator_valid_for_property_type(self, property_type: PropertyType, operator: Operator) -> bool:
52
+ def _is_operator_valid_for_property_type(
53
+ self, property_type: PropertyType, operator: Operator
54
+ ) -> bool:
51
55
  allowed_operator_types = self._PROPERTY_TYPE_OPERATORS.get(property_type, [])
52
- valid_operator_values = self._get_operator_values_from_types(allowed_operator_types)
56
+ valid_operator_values = self._get_operator_values_from_types(
57
+ allowed_operator_types
58
+ )
53
59
  return operator.value in valid_operator_values
54
60
 
55
- def _get_operator_values_from_types(self, operator_types: list[type[Operator]]) -> set[str]:
61
+ def _get_operator_values_from_types(
62
+ self, operator_types: list[type[Operator]]
63
+ ) -> set[str]:
56
64
  values: set[str] = set()
57
65
  for operator_type in operator_types:
58
66
  for operator in operator_type:
59
67
  values.add(operator.value)
60
68
  return values
61
69
 
62
- def _get_valid_operators_for_property_type(self, property_type: PropertyType) -> list[Operator]:
70
+ def _get_valid_operators_for_property_type(
71
+ self, property_type: PropertyType
72
+ ) -> list[Operator]:
63
73
  allowed_operator_types = self._PROPERTY_TYPE_OPERATORS.get(property_type, [])
64
74
  return self._collect_all_operators_from_types(allowed_operator_types)
65
75
 
66
- def _collect_all_operators_from_types(self, operator_types: list[type[Operator]]) -> list[Operator]:
76
+ def _collect_all_operators_from_types(
77
+ self, operator_types: list[type[Operator]]
78
+ ) -> list[Operator]:
67
79
  operators: list[Operator] = []
68
80
  for operator_type in operator_types:
69
81
  operators.extend(self._get_all_enum_values(operator_type))