notionary 0.3.1__py3-none-any.whl → 0.4.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.
Files changed (201) hide show
  1. notionary/__init__.py +49 -1
  2. notionary/blocks/client.py +37 -11
  3. notionary/blocks/enums.py +0 -6
  4. notionary/blocks/rich_text/markdown_rich_text_converter.py +49 -15
  5. notionary/blocks/rich_text/models.py +13 -4
  6. notionary/blocks/rich_text/name_id_resolver/data_source.py +9 -3
  7. notionary/blocks/rich_text/name_id_resolver/person.py +6 -2
  8. notionary/blocks/rich_text/rich_text_markdown_converter.py +10 -3
  9. notionary/blocks/schemas.py +33 -78
  10. notionary/comments/client.py +19 -6
  11. notionary/comments/factory.py +10 -3
  12. notionary/comments/schemas.py +10 -31
  13. notionary/comments/service.py +12 -4
  14. notionary/data_source/http/data_source_instance_client.py +59 -17
  15. notionary/data_source/properties/schemas.py +156 -115
  16. notionary/data_source/query/builder.py +67 -18
  17. notionary/data_source/query/resolver.py +16 -5
  18. notionary/data_source/query/schema.py +24 -6
  19. notionary/data_source/query/validator.py +18 -6
  20. notionary/data_source/schema/registry.py +31 -12
  21. notionary/data_source/schema/service.py +66 -20
  22. notionary/data_source/schemas.py +2 -2
  23. notionary/data_source/service.py +103 -43
  24. notionary/database/client.py +27 -9
  25. notionary/database/database_metadata_update_client.py +12 -4
  26. notionary/database/schemas.py +2 -2
  27. notionary/database/service.py +14 -9
  28. notionary/exceptions/__init__.py +20 -4
  29. notionary/exceptions/api.py +2 -2
  30. notionary/exceptions/base.py +1 -1
  31. notionary/exceptions/block_parsing.py +9 -5
  32. notionary/exceptions/data_source/builder.py +13 -7
  33. notionary/exceptions/data_source/properties.py +6 -4
  34. notionary/exceptions/file_upload.py +76 -0
  35. notionary/exceptions/properties.py +7 -5
  36. notionary/exceptions/search.py +10 -6
  37. notionary/file_upload/__init__.py +4 -0
  38. notionary/file_upload/client.py +128 -210
  39. notionary/file_upload/config/__init__.py +17 -0
  40. notionary/file_upload/config/config.py +39 -0
  41. notionary/file_upload/config/constants.py +16 -0
  42. notionary/file_upload/file/reader.py +28 -0
  43. notionary/file_upload/query/__init__.py +7 -0
  44. notionary/file_upload/query/builder.py +58 -0
  45. notionary/file_upload/query/models.py +37 -0
  46. notionary/file_upload/schemas.py +80 -0
  47. notionary/file_upload/service.py +182 -291
  48. notionary/file_upload/validation/factory.py +66 -0
  49. notionary/file_upload/validation/impl/file_name_length.py +25 -0
  50. notionary/file_upload/validation/models.py +134 -0
  51. notionary/file_upload/validation/port.py +7 -0
  52. notionary/file_upload/validation/service.py +17 -0
  53. notionary/file_upload/validation/validators/__init__.py +11 -0
  54. notionary/file_upload/validation/validators/file_exists.py +15 -0
  55. notionary/file_upload/validation/validators/file_extension.py +131 -0
  56. notionary/file_upload/validation/validators/file_name_length.py +21 -0
  57. notionary/file_upload/validation/validators/upload_limit.py +31 -0
  58. notionary/http/client.py +33 -30
  59. notionary/page/content/__init__.py +9 -0
  60. notionary/page/content/factory.py +21 -7
  61. notionary/page/content/markdown/builder.py +85 -23
  62. notionary/page/content/markdown/nodes/audio.py +8 -4
  63. notionary/page/content/markdown/nodes/base.py +3 -3
  64. notionary/page/content/markdown/nodes/bookmark.py +5 -3
  65. notionary/page/content/markdown/nodes/breadcrumb.py +2 -2
  66. notionary/page/content/markdown/nodes/bulleted_list.py +5 -3
  67. notionary/page/content/markdown/nodes/callout.py +2 -2
  68. notionary/page/content/markdown/nodes/code.py +5 -3
  69. notionary/page/content/markdown/nodes/columns.py +3 -3
  70. notionary/page/content/markdown/nodes/container.py +9 -5
  71. notionary/page/content/markdown/nodes/divider.py +2 -2
  72. notionary/page/content/markdown/nodes/embed.py +8 -4
  73. notionary/page/content/markdown/nodes/equation.py +4 -2
  74. notionary/page/content/markdown/nodes/file.py +8 -4
  75. notionary/page/content/markdown/nodes/heading.py +2 -2
  76. notionary/page/content/markdown/nodes/image.py +8 -4
  77. notionary/page/content/markdown/nodes/mixins/caption.py +5 -3
  78. notionary/page/content/markdown/nodes/numbered_list.py +5 -3
  79. notionary/page/content/markdown/nodes/paragraph.py +4 -2
  80. notionary/page/content/markdown/nodes/pdf.py +8 -4
  81. notionary/page/content/markdown/nodes/quote.py +2 -2
  82. notionary/page/content/markdown/nodes/space.py +2 -2
  83. notionary/page/content/markdown/nodes/table.py +8 -5
  84. notionary/page/content/markdown/nodes/table_of_contents.py +2 -2
  85. notionary/page/content/markdown/nodes/todo.py +15 -7
  86. notionary/page/content/markdown/nodes/toggle.py +2 -2
  87. notionary/page/content/markdown/nodes/video.py +8 -4
  88. notionary/page/content/markdown/structured_output/__init__.py +73 -0
  89. notionary/page/content/markdown/structured_output/models.py +391 -0
  90. notionary/page/content/markdown/structured_output/service.py +211 -0
  91. notionary/page/content/parser/context.py +1 -1
  92. notionary/page/content/parser/factory.py +26 -8
  93. notionary/page/content/parser/parsers/audio.py +12 -32
  94. notionary/page/content/parser/parsers/base.py +2 -2
  95. notionary/page/content/parser/parsers/bookmark.py +2 -2
  96. notionary/page/content/parser/parsers/breadcrumb.py +2 -2
  97. notionary/page/content/parser/parsers/bulleted_list.py +19 -6
  98. notionary/page/content/parser/parsers/callout.py +15 -5
  99. notionary/page/content/parser/parsers/caption.py +9 -3
  100. notionary/page/content/parser/parsers/code.py +21 -7
  101. notionary/page/content/parser/parsers/column.py +8 -4
  102. notionary/page/content/parser/parsers/column_list.py +19 -7
  103. notionary/page/content/parser/parsers/divider.py +2 -2
  104. notionary/page/content/parser/parsers/embed.py +2 -4
  105. notionary/page/content/parser/parsers/equation.py +8 -4
  106. notionary/page/content/parser/parsers/file.py +12 -34
  107. notionary/page/content/parser/parsers/file_like_block.py +109 -0
  108. notionary/page/content/parser/parsers/heading.py +31 -10
  109. notionary/page/content/parser/parsers/image.py +12 -34
  110. notionary/page/content/parser/parsers/numbered_list.py +18 -6
  111. notionary/page/content/parser/parsers/paragraph.py +3 -1
  112. notionary/page/content/parser/parsers/pdf.py +12 -34
  113. notionary/page/content/parser/parsers/quote.py +28 -9
  114. notionary/page/content/parser/parsers/space.py +2 -2
  115. notionary/page/content/parser/parsers/table.py +31 -10
  116. notionary/page/content/parser/parsers/table_of_contents.py +7 -3
  117. notionary/page/content/parser/parsers/todo.py +15 -5
  118. notionary/page/content/parser/parsers/toggle.py +15 -5
  119. notionary/page/content/parser/parsers/video.py +12 -34
  120. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +8 -2
  121. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +8 -2
  122. notionary/page/content/parser/post_processing/service.py +3 -1
  123. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +21 -7
  124. notionary/page/content/parser/pre_processsing/handlers/indentation.py +11 -4
  125. notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +13 -6
  126. notionary/page/content/parser/service.py +4 -1
  127. notionary/page/content/renderer/context.py +15 -5
  128. notionary/page/content/renderer/factory.py +12 -6
  129. notionary/page/content/renderer/post_processing/handlers/numbered_list.py +19 -9
  130. notionary/page/content/renderer/renderers/audio.py +20 -23
  131. notionary/page/content/renderer/renderers/base.py +3 -3
  132. notionary/page/content/renderer/renderers/bookmark.py +3 -1
  133. notionary/page/content/renderer/renderers/bulleted_list.py +11 -5
  134. notionary/page/content/renderer/renderers/callout.py +19 -7
  135. notionary/page/content/renderer/renderers/captioned_block.py +11 -5
  136. notionary/page/content/renderer/renderers/code.py +6 -2
  137. notionary/page/content/renderer/renderers/column.py +3 -1
  138. notionary/page/content/renderer/renderers/column_list.py +3 -1
  139. notionary/page/content/renderer/renderers/embed.py +3 -1
  140. notionary/page/content/renderer/renderers/equation.py +3 -1
  141. notionary/page/content/renderer/renderers/file.py +20 -23
  142. notionary/page/content/renderer/renderers/file_like_block.py +47 -0
  143. notionary/page/content/renderer/renderers/heading.py +22 -8
  144. notionary/page/content/renderer/renderers/image.py +20 -23
  145. notionary/page/content/renderer/renderers/numbered_list.py +8 -3
  146. notionary/page/content/renderer/renderers/paragraph.py +12 -4
  147. notionary/page/content/renderer/renderers/pdf.py +20 -23
  148. notionary/page/content/renderer/renderers/quote.py +14 -6
  149. notionary/page/content/renderer/renderers/table.py +15 -5
  150. notionary/page/content/renderer/renderers/todo.py +16 -6
  151. notionary/page/content/renderer/renderers/toggle.py +8 -4
  152. notionary/page/content/renderer/renderers/video.py +20 -23
  153. notionary/page/content/renderer/service.py +9 -3
  154. notionary/page/content/service.py +21 -7
  155. notionary/page/content/syntax/definition/__init__.py +11 -0
  156. notionary/page/content/syntax/definition/models.py +57 -0
  157. notionary/page/content/syntax/definition/registry.py +371 -0
  158. notionary/page/content/syntax/prompts/__init__.py +4 -0
  159. notionary/page/content/syntax/prompts/models.py +11 -0
  160. notionary/page/content/syntax/prompts/registry.py +703 -0
  161. notionary/page/page_metadata_update_client.py +12 -4
  162. notionary/page/properties/client.py +46 -16
  163. notionary/page/properties/factory.py +6 -2
  164. notionary/page/properties/{models.py → schemas.py} +93 -107
  165. notionary/page/properties/service.py +111 -37
  166. notionary/page/schemas.py +3 -3
  167. notionary/page/service.py +21 -7
  168. notionary/shared/entity/client.py +6 -2
  169. notionary/shared/entity/dto_parsers.py +4 -37
  170. notionary/shared/entity/entity_metadata_update_client.py +25 -5
  171. notionary/shared/entity/schemas.py +6 -6
  172. notionary/shared/entity/service.py +89 -35
  173. notionary/shared/models/file.py +36 -6
  174. notionary/shared/models/icon.py +5 -12
  175. notionary/user/base.py +6 -2
  176. notionary/user/bot.py +22 -14
  177. notionary/user/client.py +3 -1
  178. notionary/user/person.py +3 -1
  179. notionary/user/schemas.py +3 -1
  180. notionary/user/service.py +6 -2
  181. notionary/utils/decorators.py +13 -9
  182. notionary/utils/fuzzy.py +6 -2
  183. notionary/utils/mixins/logging.py +3 -1
  184. notionary/utils/pagination.py +14 -4
  185. notionary/workspace/__init__.py +6 -2
  186. notionary/workspace/query/__init__.py +2 -1
  187. notionary/workspace/query/service.py +42 -13
  188. notionary/workspace/service.py +74 -46
  189. {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/METADATA +1 -1
  190. notionary-0.4.1.dist-info/RECORD +236 -0
  191. notionary/file_upload/models.py +0 -69
  192. notionary/page/blocks/client.py +0 -1
  193. notionary/page/content/syntax/__init__.py +0 -4
  194. notionary/page/content/syntax/models.py +0 -66
  195. notionary/page/content/syntax/registry.py +0 -393
  196. notionary/page/page_context.py +0 -50
  197. notionary/shared/models/cover.py +0 -20
  198. notionary-0.3.1.dist-info/RECORD +0 -211
  199. /notionary/page/content/syntax/{grammar.py → definition/grammar.py} +0 -0
  200. {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/WHEEL +0 -0
  201. {notionary-0.3.1.dist-info → notionary-0.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  from enum import StrEnum
2
- from typing import Annotated, Literal, TypeVar
2
+ from typing import Literal, TypeVar
3
3
 
4
- from pydantic import BaseModel, Field
4
+ from pydantic import BaseModel, ConfigDict, Field
5
5
 
6
6
  from notionary.shared.properties.type import PropertyType
7
7
  from notionary.shared.typings import JsonDict
@@ -126,109 +126,11 @@ class DataSourceStatusGroup(BaseModel):
126
126
  option_ids: list[str]
127
127
 
128
128
 
129
- # ============================================================================
130
- # Config Models
131
- # ============================================================================
132
-
133
-
134
129
  class DataSourceStatusConfig(BaseModel):
135
130
  options: list[DataSourcePropertyOption] = Field(default_factory=list)
136
131
  groups: list[DataSourceStatusGroup] = Field(default_factory=list)
137
132
 
138
133
 
139
- class DataSourceSelectConfig(BaseModel):
140
- options: list[DataSourcePropertyOption] = Field(default_factory=list)
141
-
142
-
143
- class DataSourceMultiSelectConfig(BaseModel):
144
- options: list[DataSourcePropertyOption] = Field(default_factory=list)
145
-
146
-
147
- class DataSourceRelationConfig(BaseModel):
148
- data_source_id: str
149
- type: RelationType = RelationType.SINGLE_PROPERTY
150
- single_property: JsonDict = Field(default_factory=dict)
151
-
152
-
153
- class DataSourceNumberConfig(BaseModel):
154
- format: NumberFormat
155
-
156
-
157
- class DataSourceFormulaConfig(BaseModel):
158
- expression: str
159
-
160
-
161
- class DataSourceUniqueIdConfig(BaseModel):
162
- prefix: str | None = None
163
-
164
-
165
- class DataSourceRollupConfig(BaseModel):
166
- function: RollupFunction
167
- relation_property_id: str
168
- relation_property_name: str
169
- rollup_property_id: str
170
- rollup_property_name: str
171
-
172
-
173
- class DataSourceDateConfig(BaseModel): ...
174
-
175
-
176
- class DataSourceCreatedTimeConfig(BaseModel): ...
177
-
178
-
179
- class DataSourceCreatedByConfig(BaseModel): ...
180
-
181
-
182
- class DataSourceLastEditedTimeConfig(BaseModel): ...
183
-
184
-
185
- class DataSourceLastEditedByConfig(BaseModel): ...
186
-
187
-
188
- class DataSourceLastVisitedTimeConfig(BaseModel): ...
189
-
190
-
191
- class DataSourceTitleConfig(BaseModel): ...
192
-
193
-
194
- class DataSourceRichTextConfig(BaseModel): ...
195
-
196
-
197
- class DataSourceURLConfig(BaseModel): ...
198
-
199
-
200
- class DataSourcePeopleConfig(BaseModel): ...
201
-
202
-
203
- class DataSourceCheckboxConfig(BaseModel): ...
204
-
205
-
206
- class DataSourceEmailConfig(BaseModel): ...
207
-
208
-
209
- class DataSourcePhoneNumberConfig(BaseModel): ...
210
-
211
-
212
- class DataSourceFilesConfig(BaseModel): ...
213
-
214
-
215
- class DataSourceButtonConfig(BaseModel): ...
216
-
217
-
218
- class DataSourceLocationConfig(BaseModel): ...
219
-
220
-
221
- class DataSourceVerificationConfig(BaseModel): ...
222
-
223
-
224
- class DataSourcePlaceConfig(BaseModel): ...
225
-
226
-
227
- # ============================================================================
228
- # Property Models
229
- # ============================================================================
230
-
231
-
232
134
  class DataSourceStatusProperty(DataSourceProperty):
233
135
  type: Literal[PropertyType.STATUS] = PropertyType.STATUS
234
136
  status: DataSourceStatusConfig = Field(default_factory=DataSourceStatusConfig)
@@ -242,6 +144,10 @@ class DataSourceStatusProperty(DataSourceProperty):
242
144
  return [group.name for group in self.status.groups]
243
145
 
244
146
 
147
+ class DataSourceSelectConfig(BaseModel):
148
+ options: list[DataSourcePropertyOption] = Field(default_factory=list)
149
+
150
+
245
151
  class DataSourceSelectProperty(DataSourceProperty):
246
152
  type: Literal[PropertyType.SELECT] = PropertyType.SELECT
247
153
  select: DataSourceSelectConfig = Field(default_factory=DataSourceSelectConfig)
@@ -251,15 +157,28 @@ class DataSourceSelectProperty(DataSourceProperty):
251
157
  return [option.name for option in self.select.options]
252
158
 
253
159
 
160
+ class DataSourceMultiSelectConfig(BaseModel):
161
+ options: list[DataSourcePropertyOption] = Field(default_factory=list)
162
+
163
+
254
164
  class DataSourceMultiSelectProperty(DataSourceProperty):
255
165
  type: Literal[PropertyType.MULTI_SELECT] = PropertyType.MULTI_SELECT
256
- multi_select: DataSourceMultiSelectConfig = Field(default_factory=DataSourceMultiSelectConfig)
166
+ multi_select: DataSourceMultiSelectConfig = Field(
167
+ default_factory=DataSourceMultiSelectConfig
168
+ )
257
169
 
258
170
  @property
259
171
  def option_names(self) -> list[str]:
260
172
  return [option.name for option in self.multi_select.options]
261
173
 
262
174
 
175
+ # https://developers.notion.com/reference/property-object
176
+ class DataSourceRelationConfig(BaseModel):
177
+ data_source_id: str | None = None
178
+ type: RelationType = RelationType.SINGLE_PROPERTY
179
+ single_property: JsonDict = Field(default_factory=dict)
180
+
181
+
263
182
  class DataSourceRelationProperty(DataSourceProperty):
264
183
  type: Literal[PropertyType.RELATION] = PropertyType.RELATION
265
184
  relation: DataSourceRelationConfig = Field(default_factory=DataSourceRelationConfig)
@@ -269,29 +188,65 @@ class DataSourceRelationProperty(DataSourceProperty):
269
188
  return self.relation.data_source_id
270
189
 
271
190
 
191
+ class DataSourceDateConfig(BaseModel): ...
192
+
193
+
272
194
  class DataSourceDateProperty(DataSourceProperty):
273
195
  type: Literal[PropertyType.DATE] = PropertyType.DATE
274
196
  date: DataSourceDateConfig = Field(default_factory=DataSourceDateConfig)
275
197
 
276
198
 
199
+ class DataSourceCreatedTimeConfig(BaseModel): ...
200
+
201
+
277
202
  class DataSourceCreatedTimeProperty(DataSourceProperty):
278
203
  type: Literal[PropertyType.CREATED_TIME] = PropertyType.CREATED_TIME
279
- created_time: DataSourceCreatedTimeConfig = Field(default_factory=DataSourceCreatedTimeConfig)
204
+ created_time: DataSourceCreatedTimeConfig = Field(
205
+ default_factory=DataSourceCreatedTimeConfig
206
+ )
207
+
208
+
209
+ class DataSourceCreatedByConfig(BaseModel): ...
280
210
 
281
211
 
282
212
  class DataSourceCreatedByProperty(DataSourceProperty):
283
213
  type: Literal[PropertyType.CREATED_BY] = PropertyType.CREATED_BY
284
- created_by: DataSourceCreatedByConfig = Field(default_factory=DataSourceCreatedByConfig)
214
+ created_by: DataSourceCreatedByConfig = Field(
215
+ default_factory=DataSourceCreatedByConfig
216
+ )
217
+
218
+
219
+ class DataSourceLastEditedTimeConfig(BaseModel): ...
285
220
 
286
221
 
287
222
  class DataSourceLastEditedTimeProperty(DataSourceProperty):
288
223
  type: Literal[PropertyType.LAST_EDITED_TIME] = PropertyType.LAST_EDITED_TIME
289
- last_edited_time: DataSourceLastEditedTimeConfig = Field(default_factory=DataSourceLastEditedTimeConfig)
224
+ last_edited_time: DataSourceLastEditedTimeConfig = Field(
225
+ default_factory=DataSourceLastEditedTimeConfig
226
+ )
227
+
228
+
229
+ class DataSourceLastEditedByConfig(BaseModel): ...
290
230
 
291
231
 
292
232
  class DataSourceLastEditedByProperty(DataSourceProperty):
293
233
  type: Literal[PropertyType.LAST_EDITED_BY] = PropertyType.LAST_EDITED_BY
294
- last_edited_by: DataSourceLastEditedByConfig = Field(default_factory=DataSourceLastEditedByConfig)
234
+ last_edited_by: DataSourceLastEditedByConfig = Field(
235
+ default_factory=DataSourceLastEditedByConfig
236
+ )
237
+
238
+
239
+ class DataSourceLastVisitedTimeConfig(BaseModel): ...
240
+
241
+
242
+ class DataSourceLastVisitedTimeProperty(DataSourceProperty):
243
+ type: Literal[PropertyType.LAST_VISITED_TIME] = PropertyType.LAST_VISITED_TIME
244
+ last_visited_time: DataSourceLastVisitedTimeConfig = Field(
245
+ default_factory=DataSourceLastVisitedTimeConfig
246
+ )
247
+
248
+
249
+ class DataSourceTitleConfig(BaseModel): ...
295
250
 
296
251
 
297
252
  class DataSourceTitleProperty(DataSourceProperty):
@@ -299,9 +254,17 @@ class DataSourceTitleProperty(DataSourceProperty):
299
254
  title: DataSourceTitleConfig = Field(default_factory=DataSourceTitleConfig)
300
255
 
301
256
 
257
+ class DataSourceRichTextConfig(BaseModel): ...
258
+
259
+
302
260
  class DataSourceRichTextProperty(DataSourceProperty):
303
261
  type: Literal[PropertyType.RICH_TEXT] = PropertyType.RICH_TEXT
304
- rich_text: DataSourceRichTextConfig = Field(default_factory=DataSourceRichTextConfig)
262
+ rich_text: DataSourceRichTextConfig = Field(
263
+ default_factory=DataSourceRichTextConfig
264
+ )
265
+
266
+
267
+ class DataSourceURLConfig(BaseModel): ...
305
268
 
306
269
 
307
270
  class DataSourceURLProperty(DataSourceProperty):
@@ -309,11 +272,18 @@ class DataSourceURLProperty(DataSourceProperty):
309
272
  url: DataSourceURLConfig = Field(default_factory=DataSourceURLConfig)
310
273
 
311
274
 
275
+ class DataSourcePeopleConfig(BaseModel): ...
276
+
277
+
312
278
  class DataSourcePeopleProperty(DataSourceProperty):
313
279
  type: Literal[PropertyType.PEOPLE] = PropertyType.PEOPLE
314
280
  people: DataSourcePeopleConfig = Field(default_factory=DataSourcePeopleConfig)
315
281
 
316
282
 
283
+ class DataSourceNumberConfig(BaseModel):
284
+ format: NumberFormat
285
+
286
+
317
287
  class DataSourceNumberProperty(DataSourceProperty):
318
288
  type: Literal[PropertyType.NUMBER] = PropertyType.NUMBER
319
289
  number: DataSourceNumberConfig
@@ -323,19 +293,33 @@ class DataSourceNumberProperty(DataSourceProperty):
323
293
  return self.number.format
324
294
 
325
295
 
296
+ class DataSourceCheckboxConfig(BaseModel): ...
297
+
298
+
326
299
  class DataSourceCheckboxProperty(DataSourceProperty):
327
300
  type: Literal[PropertyType.CHECKBOX] = PropertyType.CHECKBOX
328
301
  checkbox: DataSourceCheckboxConfig = Field(default_factory=DataSourceCheckboxConfig)
329
302
 
330
303
 
304
+ class DataSourceEmailConfig(BaseModel): ...
305
+
306
+
331
307
  class DataSourceEmailProperty(DataSourceProperty):
332
308
  type: Literal[PropertyType.EMAIL] = PropertyType.EMAIL
333
309
  email: DataSourceEmailConfig = Field(default_factory=DataSourceEmailConfig)
334
310
 
335
311
 
312
+ class DataSourcePhoneNumberConfig(BaseModel): ...
313
+
314
+
336
315
  class DataSourcePhoneNumberProperty(DataSourceProperty):
337
316
  type: Literal[PropertyType.PHONE_NUMBER] = PropertyType.PHONE_NUMBER
338
- phone_number: DataSourcePhoneNumberConfig = Field(default_factory=DataSourcePhoneNumberConfig)
317
+ phone_number: DataSourcePhoneNumberConfig = Field(
318
+ default_factory=DataSourcePhoneNumberConfig
319
+ )
320
+
321
+
322
+ class DataSourceFilesConfig(BaseModel): ...
339
323
 
340
324
 
341
325
  class DataSourceFilesProperty(DataSourceProperty):
@@ -343,6 +327,10 @@ class DataSourceFilesProperty(DataSourceProperty):
343
327
  files: DataSourceFilesConfig = Field(default_factory=DataSourceFilesConfig)
344
328
 
345
329
 
330
+ class DataSourceFormulaConfig(BaseModel):
331
+ expression: str
332
+
333
+
346
334
  class DataSourceFormulaProperty(DataSourceProperty):
347
335
  type: Literal[PropertyType.FORMULA] = PropertyType.FORMULA
348
336
  formula: DataSourceFormulaConfig
@@ -352,6 +340,14 @@ class DataSourceFormulaProperty(DataSourceProperty):
352
340
  return self.formula.expression
353
341
 
354
342
 
343
+ class DataSourceRollupConfig(BaseModel):
344
+ function: RollupFunction
345
+ relation_property_id: str
346
+ relation_property_name: str
347
+ rollup_property_id: str
348
+ rollup_property_name: str
349
+
350
+
355
351
  class DataSourceRollupProperty(DataSourceProperty):
356
352
  type: Literal[PropertyType.ROLLUP] = PropertyType.ROLLUP
357
353
  rollup: DataSourceRollupConfig
@@ -361,20 +357,60 @@ class DataSourceRollupProperty(DataSourceProperty):
361
357
  return self.rollup.function
362
358
 
363
359
 
360
+ class DataSourceUniqueIdConfig(BaseModel):
361
+ prefix: str | None = None
362
+
363
+
364
364
  class DataSourceUniqueIdProperty(DataSourceProperty):
365
365
  type: Literal[PropertyType.UNIQUE_ID] = PropertyType.UNIQUE_ID
366
- unique_id: DataSourceUniqueIdConfig = Field(default_factory=DataSourceUniqueIdConfig)
366
+ unique_id: DataSourceUniqueIdConfig = Field(
367
+ default_factory=DataSourceUniqueIdConfig
368
+ )
367
369
 
368
370
  @property
369
371
  def prefix(self) -> str | None:
370
372
  return self.unique_id.prefix
371
373
 
372
374
 
373
- # ============================================================================
374
- # Discriminated Union
375
- # ============================================================================
375
+ class DataSourceButtonConfig(BaseModel): ...
376
+
377
+
378
+ class DataSourceButtonProperty(DataSourceProperty):
379
+ type: Literal[PropertyType.BUTTON] = PropertyType.BUTTON
380
+ button: DataSourceButtonConfig = Field(default_factory=DataSourceButtonConfig)
381
+
382
+
383
+ class DataSourceLocationConfig(BaseModel): ...
384
+
385
+
386
+ class DataSourceLocationProperty(DataSourceProperty):
387
+ type: Literal[PropertyType.LOCATION] = PropertyType.LOCATION
388
+ location: DataSourceLocationConfig = Field(default_factory=DataSourceLocationConfig)
389
+
390
+
391
+ class DataSourcePlaceConfig(BaseModel): ...
392
+
393
+
394
+ class DataSourcePlaceProperty(DataSourceProperty):
395
+ type: Literal[PropertyType.PLACE] = PropertyType.PLACE
396
+ place: DataSourcePlaceConfig = Field(default_factory=DataSourcePlaceConfig)
397
+
398
+
399
+ class DataSourceVerificationConfig(BaseModel): ...
400
+
401
+
402
+ class DataSourceVerificationProperty(DataSourceProperty):
403
+ type: Literal[PropertyType.VERIFICATION] = PropertyType.VERIFICATION
404
+ verification: DataSourceVerificationConfig = Field(
405
+ default_factory=DataSourceVerificationConfig
406
+ )
407
+
408
+
409
+ class DataSourceUnknownProperty(BaseModel):
410
+ model_config = ConfigDict(extra="allow")
411
+
376
412
 
377
- DiscriminatedDataSourceProperty = Annotated[
413
+ type AnyDataSourceProperty = (
378
414
  DataSourceStatusProperty
379
415
  | DataSourceSelectProperty
380
416
  | DataSourceMultiSelectProperty
@@ -384,6 +420,7 @@ DiscriminatedDataSourceProperty = Annotated[
384
420
  | DataSourceCreatedByProperty
385
421
  | DataSourceLastEditedTimeProperty
386
422
  | DataSourceLastEditedByProperty
423
+ | DataSourceLastVisitedTimeProperty
387
424
  | DataSourceTitleProperty
388
425
  | DataSourceRichTextProperty
389
426
  | DataSourceURLProperty
@@ -395,8 +432,12 @@ DiscriminatedDataSourceProperty = Annotated[
395
432
  | DataSourceFilesProperty
396
433
  | DataSourceFormulaProperty
397
434
  | DataSourceRollupProperty
398
- | DataSourceUniqueIdProperty,
399
- Field(discriminator="type"),
400
- ]
435
+ | DataSourceUniqueIdProperty
436
+ | DataSourceButtonProperty
437
+ | DataSourceLocationProperty
438
+ | DataSourcePlaceProperty
439
+ | DataSourceVerificationProperty
440
+ | DataSourceUnknownProperty
441
+ )
401
442
 
402
443
  DataSourcePropertyT = TypeVar("DataSourcePropertyT", bound=DataSourceProperty)
@@ -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,