notionary 0.2.27__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. notionary/__init__.py +5 -20
  2. notionary/blocks/__init__.py +4 -4
  3. notionary/blocks/client.py +90 -216
  4. notionary/blocks/enums.py +167 -0
  5. notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
  6. notionary/blocks/rich_text/models.py +178 -0
  7. notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
  8. notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
  9. notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
  10. notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
  11. notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
  12. notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
  13. notionary/blocks/rich_text/rich_text_markdown_converter.py +144 -0
  14. notionary/blocks/rich_text/rich_text_patterns.py +42 -0
  15. notionary/blocks/schemas.py +778 -0
  16. notionary/comments/__init__.py +1 -22
  17. notionary/comments/client.py +52 -187
  18. notionary/comments/factory.py +38 -0
  19. notionary/comments/models.py +5 -127
  20. notionary/comments/schemas.py +240 -0
  21. notionary/comments/service.py +34 -0
  22. notionary/data_source/http/client.py +11 -0
  23. notionary/data_source/http/data_source_instance_client.py +104 -0
  24. notionary/data_source/properties/schemas.py +402 -0
  25. notionary/data_source/query/builder.py +448 -0
  26. notionary/data_source/query/resolver.py +114 -0
  27. notionary/data_source/query/schema.py +302 -0
  28. notionary/data_source/query/validator.py +73 -0
  29. notionary/data_source/schema/registry.py +104 -0
  30. notionary/data_source/schema/service.py +136 -0
  31. notionary/data_source/schemas.py +27 -0
  32. notionary/data_source/service.py +377 -0
  33. notionary/database/client.py +30 -135
  34. notionary/database/database_metadata_update_client.py +19 -0
  35. notionary/database/schemas.py +29 -0
  36. notionary/database/service.py +168 -0
  37. notionary/exceptions/__init__.py +33 -0
  38. notionary/exceptions/api.py +41 -0
  39. notionary/exceptions/base.py +2 -0
  40. notionary/exceptions/block_parsing.py +16 -0
  41. notionary/exceptions/data_source/__init__.py +6 -0
  42. notionary/exceptions/data_source/builder.py +182 -0
  43. notionary/exceptions/data_source/properties.py +34 -0
  44. notionary/exceptions/properties.py +58 -0
  45. notionary/exceptions/search.py +57 -0
  46. notionary/file_upload/client.py +18 -30
  47. notionary/file_upload/models.py +7 -8
  48. notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
  49. notionary/http/client.py +204 -0
  50. notionary/http/models.py +50 -0
  51. notionary/page/blocks/client.py +1 -0
  52. notionary/page/content/factory.py +73 -0
  53. notionary/page/content/markdown/__init__.py +5 -0
  54. notionary/page/content/markdown/builder.py +226 -0
  55. notionary/page/content/markdown/nodes/__init__.py +52 -0
  56. notionary/page/content/markdown/nodes/audio.py +23 -0
  57. notionary/page/content/markdown/nodes/base.py +12 -0
  58. notionary/page/content/markdown/nodes/bookmark.py +25 -0
  59. notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
  60. notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
  61. notionary/page/content/markdown/nodes/callout.py +34 -0
  62. notionary/page/content/markdown/nodes/code.py +28 -0
  63. notionary/page/content/markdown/nodes/columns.py +69 -0
  64. notionary/page/content/markdown/nodes/container.py +64 -0
  65. notionary/page/content/markdown/nodes/divider.py +14 -0
  66. notionary/page/content/markdown/nodes/embed.py +23 -0
  67. notionary/page/content/markdown/nodes/equation.py +19 -0
  68. notionary/page/content/markdown/nodes/file.py +23 -0
  69. notionary/page/content/markdown/nodes/heading.py +36 -0
  70. notionary/page/content/markdown/nodes/image.py +23 -0
  71. notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
  72. notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
  73. notionary/page/content/markdown/nodes/numbered_list.py +38 -0
  74. notionary/page/content/markdown/nodes/paragraph.py +14 -0
  75. notionary/page/content/markdown/nodes/pdf.py +23 -0
  76. notionary/page/content/markdown/nodes/quote.py +27 -0
  77. notionary/page/content/markdown/nodes/space.py +14 -0
  78. notionary/page/content/markdown/nodes/table.py +45 -0
  79. notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
  80. notionary/page/content/markdown/nodes/todo.py +38 -0
  81. notionary/page/content/markdown/nodes/toggle.py +27 -0
  82. notionary/page/content/markdown/nodes/video.py +23 -0
  83. notionary/page/content/parser/context.py +126 -0
  84. notionary/page/content/parser/factory.py +210 -0
  85. notionary/page/content/parser/parsers/__init__.py +58 -0
  86. notionary/page/content/parser/parsers/audio.py +40 -0
  87. notionary/page/content/parser/parsers/base.py +30 -0
  88. notionary/page/content/parser/parsers/bookmark.py +33 -0
  89. notionary/page/content/parser/parsers/breadcrumb.py +33 -0
  90. notionary/page/content/parser/parsers/bulleted_list.py +85 -0
  91. notionary/page/content/parser/parsers/callout.py +100 -0
  92. notionary/page/content/parser/parsers/caption.py +55 -0
  93. notionary/page/content/parser/parsers/code.py +81 -0
  94. notionary/page/content/parser/parsers/column.py +76 -0
  95. notionary/page/content/parser/parsers/column_list.py +81 -0
  96. notionary/page/content/parser/parsers/divider.py +33 -0
  97. notionary/page/content/parser/parsers/embed.py +33 -0
  98. notionary/page/content/parser/parsers/equation.py +65 -0
  99. notionary/page/content/parser/parsers/file.py +42 -0
  100. notionary/page/content/parser/parsers/heading.py +115 -0
  101. notionary/page/content/parser/parsers/image.py +42 -0
  102. notionary/page/content/parser/parsers/numbered_list.py +89 -0
  103. notionary/page/content/parser/parsers/paragraph.py +37 -0
  104. notionary/page/content/parser/parsers/pdf.py +42 -0
  105. notionary/page/content/parser/parsers/quote.py +125 -0
  106. notionary/page/content/parser/parsers/space.py +41 -0
  107. notionary/page/content/parser/parsers/table.py +144 -0
  108. notionary/page/content/parser/parsers/table_of_contents.py +32 -0
  109. notionary/page/content/parser/parsers/todo.py +96 -0
  110. notionary/page/content/parser/parsers/toggle.py +70 -0
  111. notionary/page/content/parser/parsers/video.py +42 -0
  112. notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
  113. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +95 -0
  114. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -0
  115. notionary/page/content/parser/post_processing/port.py +9 -0
  116. notionary/page/content/parser/post_processing/service.py +16 -0
  117. notionary/page/content/parser/pre_processsing/handlers/__init__.py +11 -0
  118. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
  119. notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
  120. notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
  121. notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
  122. notionary/page/content/parser/pre_processsing/service.py +15 -0
  123. notionary/page/content/parser/service.py +78 -0
  124. notionary/page/content/renderer/context.py +51 -0
  125. notionary/page/content/renderer/factory.py +231 -0
  126. notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
  127. notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
  128. notionary/page/content/renderer/post_processing/port.py +7 -0
  129. notionary/page/content/renderer/post_processing/service.py +15 -0
  130. notionary/page/content/renderer/renderers/__init__.py +55 -0
  131. notionary/page/content/renderer/renderers/audio.py +31 -0
  132. notionary/page/content/renderer/renderers/base.py +31 -0
  133. notionary/page/content/renderer/renderers/bookmark.py +25 -0
  134. notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
  135. notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
  136. notionary/page/content/renderer/renderers/callout.py +50 -0
  137. notionary/page/content/renderer/renderers/captioned_block.py +58 -0
  138. notionary/page/content/renderer/renderers/code.py +34 -0
  139. notionary/page/content/renderer/renderers/column.py +53 -0
  140. notionary/page/content/renderer/renderers/column_list.py +44 -0
  141. notionary/page/content/renderer/renderers/divider.py +22 -0
  142. notionary/page/content/renderer/renderers/embed.py +25 -0
  143. notionary/page/content/renderer/renderers/equation.py +37 -0
  144. notionary/page/content/renderer/renderers/fallback.py +24 -0
  145. notionary/page/content/renderer/renderers/file.py +40 -0
  146. notionary/page/content/renderer/renderers/heading.py +95 -0
  147. notionary/page/content/renderer/renderers/image.py +31 -0
  148. notionary/page/content/renderer/renderers/numbered_list.py +42 -0
  149. notionary/page/content/renderer/renderers/paragraph.py +40 -0
  150. notionary/page/content/renderer/renderers/pdf.py +31 -0
  151. notionary/page/content/renderer/renderers/quote.py +49 -0
  152. notionary/page/content/renderer/renderers/table.py +115 -0
  153. notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
  154. notionary/page/content/renderer/renderers/table_row.py +17 -0
  155. notionary/page/content/renderer/renderers/todo.py +56 -0
  156. notionary/page/content/renderer/renderers/toggle.py +52 -0
  157. notionary/page/content/renderer/renderers/video.py +31 -0
  158. notionary/page/content/renderer/service.py +50 -0
  159. notionary/page/content/service.py +68 -0
  160. notionary/page/content/syntax/__init__.py +4 -0
  161. notionary/page/content/syntax/grammar.py +10 -0
  162. notionary/page/content/syntax/models.py +66 -0
  163. notionary/page/content/syntax/registry.py +393 -0
  164. notionary/page/page_context.py +7 -16
  165. notionary/page/page_http_client.py +15 -0
  166. notionary/page/page_metadata_update_client.py +19 -0
  167. notionary/page/properties/client.py +144 -0
  168. notionary/page/properties/factory.py +26 -0
  169. notionary/page/properties/models.py +308 -0
  170. notionary/page/properties/service.py +261 -0
  171. notionary/page/schemas.py +13 -0
  172. notionary/page/service.py +225 -0
  173. notionary/shared/entity/client.py +29 -0
  174. notionary/shared/entity/dto_parsers.py +53 -0
  175. notionary/shared/entity/entity_metadata_update_client.py +41 -0
  176. notionary/shared/entity/schemas.py +45 -0
  177. notionary/shared/entity/service.py +171 -0
  178. notionary/shared/models/cover.py +20 -0
  179. notionary/shared/models/file.py +21 -0
  180. notionary/shared/models/icon.py +28 -0
  181. notionary/shared/models/parent.py +41 -0
  182. notionary/shared/properties/type.py +30 -0
  183. notionary/shared/typings.py +3 -0
  184. notionary/user/__init__.py +4 -8
  185. notionary/user/base.py +138 -0
  186. notionary/user/bot.py +70 -0
  187. notionary/user/client.py +22 -111
  188. notionary/user/person.py +41 -0
  189. notionary/user/schemas.py +67 -0
  190. notionary/user/service.py +65 -0
  191. notionary/utils/date.py +51 -0
  192. notionary/utils/decorators.py +122 -0
  193. notionary/utils/fuzzy.py +68 -0
  194. notionary/utils/mixins/logging.py +58 -0
  195. notionary/utils/pagination.py +100 -0
  196. notionary/utils/uuid_utils.py +20 -0
  197. notionary/workspace/__init__.py +4 -0
  198. notionary/workspace/client.py +62 -0
  199. notionary/workspace/query/__init__.py +3 -0
  200. notionary/workspace/query/builder.py +60 -0
  201. notionary/workspace/query/models.py +61 -0
  202. notionary/workspace/query/service.py +100 -0
  203. notionary/workspace/schemas.py +21 -0
  204. notionary/workspace/service.py +116 -0
  205. notionary-0.3.0.dist-info/METADATA +201 -0
  206. notionary-0.3.0.dist-info/RECORD +209 -0
  207. {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
  208. {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
  209. notionary/base_notion_client.py +0 -219
  210. notionary/blocks/_bootstrap.py +0 -271
  211. notionary/blocks/audio/__init__.py +0 -11
  212. notionary/blocks/audio/audio_element.py +0 -158
  213. notionary/blocks/audio/audio_markdown_node.py +0 -24
  214. notionary/blocks/audio/audio_models.py +0 -10
  215. notionary/blocks/base_block_element.py +0 -42
  216. notionary/blocks/bookmark/__init__.py +0 -12
  217. notionary/blocks/bookmark/bookmark_element.py +0 -83
  218. notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
  219. notionary/blocks/bookmark/bookmark_models.py +0 -15
  220. notionary/blocks/breadcrumbs/__init__.py +0 -15
  221. notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
  222. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
  223. notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
  224. notionary/blocks/bulleted_list/__init__.py +0 -15
  225. notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
  226. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
  227. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
  228. notionary/blocks/callout/__init__.py +0 -12
  229. notionary/blocks/callout/callout_element.py +0 -99
  230. notionary/blocks/callout/callout_markdown_node.py +0 -19
  231. notionary/blocks/callout/callout_models.py +0 -33
  232. notionary/blocks/child_database/__init__.py +0 -14
  233. notionary/blocks/child_database/child_database_element.py +0 -59
  234. notionary/blocks/child_database/child_database_models.py +0 -12
  235. notionary/blocks/child_page/__init__.py +0 -9
  236. notionary/blocks/child_page/child_page_element.py +0 -94
  237. notionary/blocks/child_page/child_page_models.py +0 -12
  238. notionary/blocks/code/__init__.py +0 -11
  239. notionary/blocks/code/code_element.py +0 -149
  240. notionary/blocks/code/code_markdown_node.py +0 -80
  241. notionary/blocks/code/code_models.py +0 -94
  242. notionary/blocks/column/__init__.py +0 -25
  243. notionary/blocks/column/column_element.py +0 -65
  244. notionary/blocks/column/column_list_element.py +0 -52
  245. notionary/blocks/column/column_list_markdown_node.py +0 -34
  246. notionary/blocks/column/column_markdown_node.py +0 -42
  247. notionary/blocks/column/column_models.py +0 -26
  248. notionary/blocks/divider/__init__.py +0 -12
  249. notionary/blocks/divider/divider_element.py +0 -41
  250. notionary/blocks/divider/divider_markdown_node.py +0 -11
  251. notionary/blocks/divider/divider_models.py +0 -12
  252. notionary/blocks/embed/__init__.py +0 -12
  253. notionary/blocks/embed/embed_element.py +0 -98
  254. notionary/blocks/embed/embed_markdown_node.py +0 -19
  255. notionary/blocks/embed/embed_models.py +0 -14
  256. notionary/blocks/equation/__init__.py +0 -13
  257. notionary/blocks/equation/equation_element.py +0 -133
  258. notionary/blocks/equation/equation_element_markdown_node.py +0 -23
  259. notionary/blocks/equation/equation_models.py +0 -11
  260. notionary/blocks/file/__init__.py +0 -23
  261. notionary/blocks/file/file_element.py +0 -133
  262. notionary/blocks/file/file_element_markdown_node.py +0 -24
  263. notionary/blocks/file/file_element_models.py +0 -39
  264. notionary/blocks/heading/__init__.py +0 -19
  265. notionary/blocks/heading/heading_element.py +0 -112
  266. notionary/blocks/heading/heading_markdown_node.py +0 -16
  267. notionary/blocks/heading/heading_models.py +0 -29
  268. notionary/blocks/image_block/__init__.py +0 -11
  269. notionary/blocks/image_block/image_element.py +0 -130
  270. notionary/blocks/image_block/image_markdown_node.py +0 -25
  271. notionary/blocks/image_block/image_models.py +0 -10
  272. notionary/blocks/markdown/markdown_builder.py +0 -525
  273. notionary/blocks/markdown/markdown_document_model.py +0 -0
  274. notionary/blocks/markdown/markdown_node.py +0 -25
  275. notionary/blocks/mixins/captions/__init__.py +0 -4
  276. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
  277. notionary/blocks/mixins/captions/caption_mixin.py +0 -92
  278. notionary/blocks/mixins/file_upload/__init__.py +0 -3
  279. notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
  280. notionary/blocks/models.py +0 -174
  281. notionary/blocks/numbered_list/__init__.py +0 -16
  282. notionary/blocks/numbered_list/numbered_list_element.py +0 -65
  283. notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
  284. notionary/blocks/numbered_list/numbered_list_models.py +0 -17
  285. notionary/blocks/paragraph/__init__.py +0 -15
  286. notionary/blocks/paragraph/paragraph_element.py +0 -58
  287. notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
  288. notionary/blocks/paragraph/paragraph_models.py +0 -16
  289. notionary/blocks/pdf/__init__.py +0 -11
  290. notionary/blocks/pdf/pdf_element.py +0 -146
  291. notionary/blocks/pdf/pdf_markdown_node.py +0 -24
  292. notionary/blocks/pdf/pdf_models.py +0 -11
  293. notionary/blocks/quote/__init__.py +0 -14
  294. notionary/blocks/quote/quote_element.py +0 -75
  295. notionary/blocks/quote/quote_markdown_node.py +0 -16
  296. notionary/blocks/quote/quote_models.py +0 -18
  297. notionary/blocks/registry/__init__.py +0 -3
  298. notionary/blocks/registry/block_registry.py +0 -150
  299. notionary/blocks/rich_text/__init__.py +0 -33
  300. notionary/blocks/rich_text/rich_text_models.py +0 -221
  301. notionary/blocks/rich_text/text_inline_formatter.py +0 -456
  302. notionary/blocks/syntax_prompt_builder.py +0 -137
  303. notionary/blocks/table/__init__.py +0 -19
  304. notionary/blocks/table/table_element.py +0 -225
  305. notionary/blocks/table/table_markdown_node.py +0 -42
  306. notionary/blocks/table/table_models.py +0 -28
  307. notionary/blocks/table_of_contents/__init__.py +0 -17
  308. notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
  309. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
  310. notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
  311. notionary/blocks/todo/__init__.py +0 -12
  312. notionary/blocks/todo/todo_element.py +0 -81
  313. notionary/blocks/todo/todo_markdown_node.py +0 -21
  314. notionary/blocks/todo/todo_models.py +0 -18
  315. notionary/blocks/toggle/__init__.py +0 -12
  316. notionary/blocks/toggle/toggle_element.py +0 -112
  317. notionary/blocks/toggle/toggle_markdown_node.py +0 -31
  318. notionary/blocks/toggle/toggle_models.py +0 -17
  319. notionary/blocks/toggleable_heading/__init__.py +0 -11
  320. notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
  321. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
  322. notionary/blocks/types.py +0 -130
  323. notionary/blocks/video/__init__.py +0 -11
  324. notionary/blocks/video/video_element.py +0 -187
  325. notionary/blocks/video/video_element_models.py +0 -10
  326. notionary/blocks/video/video_markdown_node.py +0 -26
  327. notionary/database/__init__.py +0 -4
  328. notionary/database/database.py +0 -480
  329. notionary/database/database_filter_builder.py +0 -173
  330. notionary/database/database_provider.py +0 -227
  331. notionary/database/exceptions.py +0 -13
  332. notionary/database/models.py +0 -337
  333. notionary/database/notion_database.py +0 -487
  334. notionary/file_upload/__init__.py +0 -7
  335. notionary/page/client.py +0 -124
  336. notionary/page/markdown_whitespace_processor.py +0 -129
  337. notionary/page/models.py +0 -322
  338. notionary/page/notion_page.py +0 -712
  339. notionary/page/page_content_deleting_service.py +0 -117
  340. notionary/page/page_content_writer.py +0 -80
  341. notionary/page/property_formatter.py +0 -99
  342. notionary/page/reader/handler/__init__.py +0 -19
  343. notionary/page/reader/handler/base_block_renderer.py +0 -44
  344. notionary/page/reader/handler/block_processing_context.py +0 -35
  345. notionary/page/reader/handler/block_rendering_context.py +0 -48
  346. notionary/page/reader/handler/column_list_renderer.py +0 -51
  347. notionary/page/reader/handler/column_renderer.py +0 -60
  348. notionary/page/reader/handler/equation_renderer.py +0 -0
  349. notionary/page/reader/handler/line_renderer.py +0 -73
  350. notionary/page/reader/handler/numbered_list_renderer.py +0 -85
  351. notionary/page/reader/handler/toggle_renderer.py +0 -69
  352. notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
  353. notionary/page/reader/page_content_retriever.py +0 -81
  354. notionary/page/search_filter_builder.py +0 -132
  355. notionary/page/utils.py +0 -60
  356. notionary/page/writer/handler/__init__.py +0 -24
  357. notionary/page/writer/handler/code_handler.py +0 -72
  358. notionary/page/writer/handler/column_handler.py +0 -141
  359. notionary/page/writer/handler/column_list_handler.py +0 -139
  360. notionary/page/writer/handler/equation_handler.py +0 -74
  361. notionary/page/writer/handler/line_handler.py +0 -35
  362. notionary/page/writer/handler/line_processing_context.py +0 -54
  363. notionary/page/writer/handler/regular_line_handler.py +0 -86
  364. notionary/page/writer/handler/table_handler.py +0 -66
  365. notionary/page/writer/handler/toggle_handler.py +0 -159
  366. notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
  367. notionary/page/writer/markdown_to_notion_converter.py +0 -139
  368. notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
  369. notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
  370. notionary/page/writer/notion_text_length_processor.py +0 -150
  371. notionary/schemas/__init__.py +0 -3
  372. notionary/schemas/base.py +0 -73
  373. notionary/shared/__init__.py +0 -3
  374. notionary/shared/name_to_id_resolver.py +0 -203
  375. notionary/telemetry/__init__.py +0 -19
  376. notionary/telemetry/service.py +0 -136
  377. notionary/telemetry/views.py +0 -73
  378. notionary/user/base_notion_user.py +0 -53
  379. notionary/user/models.py +0 -84
  380. notionary/user/notion_bot_user.py +0 -226
  381. notionary/user/notion_user.py +0 -255
  382. notionary/user/notion_user_manager.py +0 -101
  383. notionary/util/__init__.py +0 -15
  384. notionary/util/concurrency_limiter.py +0 -0
  385. notionary/util/factory_decorator.py +0 -0
  386. notionary/util/factory_only.py +0 -37
  387. notionary/util/fuzzy.py +0 -75
  388. notionary/util/logging_mixin.py +0 -59
  389. notionary/util/page_id_utils.py +0 -27
  390. notionary/util/singleton.py +0 -18
  391. notionary/util/singleton_metaclass.py +0 -22
  392. notionary/workspace.py +0 -105
  393. notionary-0.2.27.dist-info/METADATA +0 -270
  394. notionary-0.2.27.dist-info/RECORD +0 -202
  395. /notionary/{database → user}/factory.py +0 -0
@@ -0,0 +1,448 @@
1
+ from collections.abc import Callable
2
+ from typing import Self
3
+
4
+ from notionary.data_source.properties.schemas import DataSourceProperty
5
+ from notionary.data_source.query.schema import (
6
+ ArrayOperator,
7
+ BooleanOperator,
8
+ CompoundFilter,
9
+ DataSourceQueryParams,
10
+ DateOperator,
11
+ FieldType,
12
+ FilterCondition,
13
+ InternalFilterCondition,
14
+ LogicalOperator,
15
+ NotionFilter,
16
+ NotionSort,
17
+ NumberOperator,
18
+ Operator,
19
+ OrGroupMarker,
20
+ PropertyFilter,
21
+ PropertySort,
22
+ SortDirection,
23
+ StringOperator,
24
+ TimestampSort,
25
+ TimestampType,
26
+ )
27
+ from notionary.data_source.query.validator import QueryValidator
28
+ from notionary.exceptions.data_source.properties import DataSourcePropertyNotFound
29
+ from notionary.utils.date import parse_date
30
+
31
+
32
+ class DataSourceQueryBuilder:
33
+ def __init__(
34
+ self,
35
+ properties: dict[str, DataSourceProperty],
36
+ query_validator: QueryValidator | None = None,
37
+ date_parser: Callable[[str], str] = parse_date,
38
+ ) -> None:
39
+ self._properties = properties
40
+ self._query_validator = query_validator or QueryValidator()
41
+ self._date_parser = date_parser
42
+
43
+ self._filters: list[InternalFilterCondition] = []
44
+ self._sorts: list[NotionSort] = []
45
+ self._current_property: str | None = None
46
+ self._negate_next = False
47
+ self._or_group: list[FilterCondition] | None = None
48
+ self._page_size: int | None = None
49
+
50
+ def where(self, property_name: str) -> Self:
51
+ self._finalize_current_or_group()
52
+ self._ensure_property_exists(property_name)
53
+ self._select_property_without_negation(property_name)
54
+ return self
55
+
56
+ def where_not(self, property_name: str) -> Self:
57
+ self._finalize_current_or_group()
58
+ self._ensure_property_exists(property_name)
59
+ self._select_property_with_negation(property_name)
60
+ return self
61
+
62
+ def and_where(self, property_name: str) -> Self:
63
+ return self.where(property_name)
64
+
65
+ def and_where_not(self, property_name: str) -> Self:
66
+ return self.where_not(property_name)
67
+
68
+ def or_where(self, property_name: str) -> Self:
69
+ self._ensure_or_group_exists()
70
+ self._ensure_property_exists(property_name)
71
+ self._select_property_without_negation(property_name)
72
+ return self
73
+
74
+ def or_where_not(self, property_name: str) -> Self:
75
+ self._ensure_or_group_exists()
76
+ self._ensure_property_exists(property_name)
77
+ self._select_property_with_negation(property_name)
78
+ return self
79
+
80
+ def equals(self, value: str | int | float) -> Self:
81
+ return self._add_filter(StringOperator.EQUALS, value)
82
+
83
+ def does_not_equal(self, value: str | int | float) -> Self:
84
+ return self._add_filter(StringOperator.DOES_NOT_EQUAL, value)
85
+
86
+ def contains(self, value: str) -> Self:
87
+ return self._add_filter(StringOperator.CONTAINS, value)
88
+
89
+ def does_not_contain(self, value: str) -> Self:
90
+ return self._add_filter(StringOperator.DOES_NOT_CONTAIN, value)
91
+
92
+ def starts_with(self, value: str) -> Self:
93
+ return self._add_filter(StringOperator.STARTS_WITH, value)
94
+
95
+ def ends_with(self, value: str) -> Self:
96
+ return self._add_filter(StringOperator.ENDS_WITH, value)
97
+
98
+ def is_empty(self) -> Self:
99
+ return self._add_filter(StringOperator.IS_EMPTY, None)
100
+
101
+ def is_not_empty(self) -> Self:
102
+ return self._add_filter(StringOperator.IS_NOT_EMPTY, None)
103
+
104
+ def greater_than(self, value: float | int) -> Self:
105
+ return self._add_filter(NumberOperator.GREATER_THAN, value)
106
+
107
+ def greater_than_or_equal_to(self, value: float | int) -> Self:
108
+ return self._add_filter(NumberOperator.GREATER_THAN_OR_EQUAL_TO, value)
109
+
110
+ def less_than(self, value: float | int) -> Self:
111
+ return self._add_filter(NumberOperator.LESS_THAN, value)
112
+
113
+ def less_than_or_equal_to(self, value: float | int) -> Self:
114
+ return self._add_filter(NumberOperator.LESS_THAN_OR_EQUAL_TO, value)
115
+
116
+ def is_true(self) -> Self:
117
+ return self._add_filter(BooleanOperator.IS_TRUE, None)
118
+
119
+ def is_false(self) -> Self:
120
+ return self._add_filter(BooleanOperator.IS_FALSE, None)
121
+
122
+ def before(self, date: str) -> Self:
123
+ parsed_date = self._date_parser(date)
124
+ return self._add_filter(DateOperator.BEFORE, parsed_date)
125
+
126
+ def after(self, date: str) -> Self:
127
+ parsed_date = self._date_parser(date)
128
+ return self._add_filter(DateOperator.AFTER, parsed_date)
129
+
130
+ def on_or_before(self, date: str) -> Self:
131
+ parsed_date = self._date_parser(date)
132
+ return self._add_filter(DateOperator.ON_OR_BEFORE, parsed_date)
133
+
134
+ def on_or_after(self, date: str) -> Self:
135
+ parsed_date = self._date_parser(date)
136
+ return self._add_filter(DateOperator.ON_OR_AFTER, parsed_date)
137
+
138
+ def array_contains(self, value: str) -> Self:
139
+ return self._add_filter(ArrayOperator.CONTAINS, value)
140
+
141
+ def array_does_not_contain(self, value: str) -> Self:
142
+ return self._add_filter(ArrayOperator.DOES_NOT_CONTAIN, value)
143
+
144
+ def array_is_empty(self) -> Self:
145
+ return self._add_filter(ArrayOperator.IS_EMPTY, None)
146
+
147
+ def array_is_not_empty(self) -> Self:
148
+ return self._add_filter(ArrayOperator.IS_NOT_EMPTY, None)
149
+
150
+ def relation_contains(self, uuid: str) -> Self:
151
+ return self._add_filter(ArrayOperator.CONTAINS, uuid)
152
+
153
+ def relation_is_empty(self) -> Self:
154
+ return self._add_filter(ArrayOperator.IS_EMPTY, None)
155
+
156
+ def people_contains(self, uuid: str) -> Self:
157
+ return self._add_filter(ArrayOperator.CONTAINS, uuid)
158
+
159
+ def people_is_empty(self) -> Self:
160
+ return self._add_filter(ArrayOperator.IS_EMPTY, None)
161
+
162
+ def order_by(self, property_name: str, direction: SortDirection = SortDirection.ASCENDING) -> Self:
163
+ self._ensure_property_exists(property_name)
164
+ sort = PropertySort(property=property_name, direction=direction)
165
+ self._sorts.append(sort)
166
+ return self
167
+
168
+ def order_by_property_name_ascending(self, property_name: str) -> Self:
169
+ return self.order_by(property_name, SortDirection.ASCENDING)
170
+
171
+ def order_by_property_name_descending(self, property_name: str) -> Self:
172
+ return self.order_by(property_name, SortDirection.DESCENDING)
173
+
174
+ def order_by_created_time_ascending(self) -> Self:
175
+ return self._order_by_created_time(SortDirection.ASCENDING)
176
+
177
+ def order_by_created_time_descending(self) -> Self:
178
+ return self._order_by_created_time(SortDirection.DESCENDING)
179
+
180
+ def _order_by_created_time(self, direction: SortDirection = SortDirection.DESCENDING) -> Self:
181
+ sort = TimestampSort(timestamp=TimestampType.CREATED_TIME, direction=direction)
182
+ self._sorts.append(sort)
183
+ return self
184
+
185
+ def order_by_last_edited_time_ascending(self) -> Self:
186
+ return self._order_by_last_edited_time(SortDirection.ASCENDING)
187
+
188
+ def order_by_last_edited_time_descending(self) -> Self:
189
+ return self._order_by_last_edited_time(SortDirection.DESCENDING)
190
+
191
+ def _order_by_last_edited_time(self, direction: SortDirection = SortDirection.DESCENDING) -> Self:
192
+ sort = TimestampSort(timestamp=TimestampType.LAST_EDITED_TIME, direction=direction)
193
+ self._sorts.append(sort)
194
+ return self
195
+
196
+ def limit(self, page_size: int) -> Self:
197
+ if page_size < 1:
198
+ raise ValueError("Limit must be at least 1")
199
+ self._page_size = page_size
200
+ return self
201
+
202
+ def build(self) -> DataSourceQueryParams:
203
+ self._finalize_current_or_group()
204
+ notion_filter = self._create_notion_filter_if_needed()
205
+ sorts = self._create_sorts_if_needed()
206
+ return DataSourceQueryParams(filter=notion_filter, sorts=sorts, page_size=self._page_size)
207
+
208
+ def _select_property_without_negation(self, property_name: str) -> None:
209
+ self._current_property = property_name
210
+ self._negate_next = False
211
+
212
+ def _select_property_with_negation(self, property_name: str) -> None:
213
+ self._current_property = property_name
214
+ self._negate_next = True
215
+
216
+ def _ensure_property_exists(self, property_name: str) -> None:
217
+ if self._has_no_properties():
218
+ return
219
+
220
+ if self._property_is_unknown(property_name):
221
+ self._raise_property_not_found_error(property_name)
222
+
223
+ def _has_no_properties(self) -> bool:
224
+ return not self._properties
225
+
226
+ def _property_is_unknown(self, property_name: str) -> bool:
227
+ return property_name not in self._properties
228
+
229
+ def _raise_property_not_found_error(self, property_name: str) -> None:
230
+ available_properties = list(self._properties.keys())
231
+ raise DataSourcePropertyNotFound(
232
+ property_name=property_name,
233
+ available_properties=available_properties,
234
+ )
235
+
236
+ def _ensure_or_group_exists(self) -> None:
237
+ if self._or_group_is_not_active():
238
+ self._start_new_or_group()
239
+
240
+ def _or_group_is_not_active(self) -> bool:
241
+ return self._or_group is None
242
+
243
+ def _start_new_or_group(self) -> None:
244
+ self._or_group = []
245
+ self._move_last_filter_to_or_group()
246
+
247
+ def _move_last_filter_to_or_group(self) -> None:
248
+ if self._has_no_filters():
249
+ return
250
+
251
+ last_filter = self._filters.pop()
252
+ if self._is_regular_filter_condition(last_filter):
253
+ self._or_group.append(last_filter)
254
+
255
+ def _has_no_filters(self) -> bool:
256
+ return not self._filters
257
+
258
+ def _is_regular_filter_condition(self, filter_item: InternalFilterCondition) -> bool:
259
+ return isinstance(filter_item, FilterCondition)
260
+
261
+ def _finalize_current_or_group(self) -> None:
262
+ if self._should_finalize_or_group():
263
+ self._convert_or_group_to_marker()
264
+ self._clear_or_group()
265
+
266
+ def _should_finalize_or_group(self) -> bool:
267
+ return self._or_group is not None and len(self._or_group) > 0
268
+
269
+ def _convert_or_group_to_marker(self) -> None:
270
+ or_marker = OrGroupMarker(conditions=self._or_group)
271
+ self._filters.append(or_marker)
272
+
273
+ def _clear_or_group(self) -> None:
274
+ self._or_group = None
275
+
276
+ def _add_filter(
277
+ self,
278
+ operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
279
+ value: str | int | float | list[str | int | float] | None,
280
+ ) -> Self:
281
+ self._ensure_property_is_selected()
282
+ self._validate_operator_for_current_property(operator)
283
+ final_operator = self._apply_negation_if_needed(operator)
284
+ filter_condition = self._create_filter_condition(final_operator, value)
285
+ self._store_filter_condition(filter_condition)
286
+ self._reset_current_property()
287
+ return self
288
+
289
+ def _validate_operator_for_current_property(self, operator: Operator) -> None:
290
+ if not self._current_property or not self._properties:
291
+ return
292
+
293
+ property_obj = self._properties.get(self._current_property)
294
+ if property_obj:
295
+ self._query_validator.validate_operator_for_property(self._current_property, property_obj, operator)
296
+ return self
297
+
298
+ def _ensure_property_is_selected(self) -> None:
299
+ if self._no_property_is_selected():
300
+ raise ValueError("No property selected. Use .where(property_name) first.")
301
+
302
+ def _no_property_is_selected(self) -> bool:
303
+ return self._current_property is None
304
+
305
+ def _apply_negation_if_needed(
306
+ self,
307
+ operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
308
+ ) -> StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator:
309
+ if not self._negate_next:
310
+ return operator
311
+
312
+ negated_operator = self._negate_operator(operator)
313
+ self._negate_next = False
314
+ return negated_operator
315
+
316
+ def _create_filter_condition(
317
+ self,
318
+ operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
319
+ value: str | int | float | list[str | int | float] | None,
320
+ ) -> FilterCondition:
321
+ field_type = self._determine_field_type_from_operator(operator)
322
+ return FilterCondition(
323
+ field=self._current_property,
324
+ field_type=field_type,
325
+ operator=operator,
326
+ value=value,
327
+ )
328
+
329
+ def _store_filter_condition(self, filter_condition: FilterCondition) -> None:
330
+ if self._or_group_is_active():
331
+ self._or_group.append(filter_condition)
332
+ else:
333
+ self._filters.append(filter_condition)
334
+
335
+ def _or_group_is_active(self) -> bool:
336
+ return self._or_group is not None
337
+
338
+ def _reset_current_property(self) -> None:
339
+ self._current_property = None
340
+
341
+ def _create_notion_filter_if_needed(self) -> NotionFilter | None:
342
+ if self._has_no_filters():
343
+ return None
344
+ return self._build_notion_filter()
345
+
346
+ def _create_sorts_if_needed(self) -> list[NotionSort] | None:
347
+ if not self._sorts:
348
+ return None
349
+ return self._sorts
350
+
351
+ def _build_notion_filter(self) -> NotionFilter:
352
+ if self._has_single_filter():
353
+ return self._build_single_filter()
354
+ return self._build_compound_and_filter()
355
+
356
+ def _has_single_filter(self) -> bool:
357
+ return len(self._filters) == 1
358
+
359
+ def _build_single_filter(self) -> NotionFilter:
360
+ return self._build_filter(self._filters[0])
361
+
362
+ def _build_compound_and_filter(self) -> CompoundFilter:
363
+ property_filters = [self._build_filter(f) for f in self._filters]
364
+ return CompoundFilter(operator=LogicalOperator.AND, filters=property_filters)
365
+
366
+ def _build_filter(self, condition: InternalFilterCondition) -> PropertyFilter | CompoundFilter:
367
+ if isinstance(condition, OrGroupMarker):
368
+ return self._build_or_compound_filter(condition)
369
+ return self._build_property_filter(condition)
370
+
371
+ def _build_or_compound_filter(self, or_marker: OrGroupMarker) -> CompoundFilter:
372
+ property_filters = [self._build_property_filter(c) for c in or_marker.conditions]
373
+ return CompoundFilter(operator=LogicalOperator.OR, filters=property_filters)
374
+
375
+ def _build_property_filter(self, condition: FilterCondition) -> PropertyFilter:
376
+ property_definition = self._get_property_definition(condition.field)
377
+ return PropertyFilter(
378
+ property=condition.field,
379
+ property_type=property_definition.type,
380
+ operator=condition.operator,
381
+ value=condition.value,
382
+ )
383
+
384
+ def _get_property_definition(self, property_name: str) -> DataSourceProperty:
385
+ property_definition = self._properties.get(property_name)
386
+ if property_definition is None:
387
+ self._raise_property_not_found_error(property_name)
388
+ return property_definition
389
+
390
+ def _negate_operator(
391
+ self,
392
+ operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
393
+ ) -> StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator:
394
+ negation_map = {
395
+ StringOperator.EQUALS: StringOperator.DOES_NOT_EQUAL,
396
+ StringOperator.DOES_NOT_EQUAL: StringOperator.EQUALS,
397
+ StringOperator.CONTAINS: StringOperator.DOES_NOT_CONTAIN,
398
+ StringOperator.DOES_NOT_CONTAIN: StringOperator.CONTAINS,
399
+ StringOperator.IS_EMPTY: StringOperator.IS_NOT_EMPTY,
400
+ StringOperator.IS_NOT_EMPTY: StringOperator.IS_EMPTY,
401
+ NumberOperator.EQUALS: NumberOperator.DOES_NOT_EQUAL,
402
+ NumberOperator.DOES_NOT_EQUAL: NumberOperator.EQUALS,
403
+ NumberOperator.GREATER_THAN: NumberOperator.LESS_THAN_OR_EQUAL_TO,
404
+ NumberOperator.GREATER_THAN_OR_EQUAL_TO: NumberOperator.LESS_THAN,
405
+ NumberOperator.LESS_THAN: NumberOperator.GREATER_THAN_OR_EQUAL_TO,
406
+ NumberOperator.LESS_THAN_OR_EQUAL_TO: NumberOperator.GREATER_THAN,
407
+ NumberOperator.IS_EMPTY: NumberOperator.IS_NOT_EMPTY,
408
+ NumberOperator.IS_NOT_EMPTY: NumberOperator.IS_EMPTY,
409
+ BooleanOperator.IS_TRUE: BooleanOperator.IS_FALSE,
410
+ BooleanOperator.IS_FALSE: BooleanOperator.IS_TRUE,
411
+ DateOperator.BEFORE: DateOperator.ON_OR_AFTER,
412
+ DateOperator.AFTER: DateOperator.ON_OR_BEFORE,
413
+ DateOperator.ON_OR_BEFORE: DateOperator.AFTER,
414
+ DateOperator.ON_OR_AFTER: DateOperator.BEFORE,
415
+ DateOperator.IS_EMPTY: DateOperator.IS_NOT_EMPTY,
416
+ DateOperator.IS_NOT_EMPTY: DateOperator.IS_EMPTY,
417
+ ArrayOperator.CONTAINS: ArrayOperator.DOES_NOT_CONTAIN,
418
+ ArrayOperator.DOES_NOT_CONTAIN: ArrayOperator.CONTAINS,
419
+ ArrayOperator.IS_EMPTY: ArrayOperator.IS_NOT_EMPTY,
420
+ ArrayOperator.IS_NOT_EMPTY: ArrayOperator.IS_EMPTY,
421
+ }
422
+
423
+ if operator not in negation_map:
424
+ self._raise_operator_cannot_be_negated_error(operator)
425
+
426
+ return negation_map[operator]
427
+
428
+ def _raise_operator_cannot_be_negated_error(
429
+ self,
430
+ operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
431
+ ) -> None:
432
+ raise ValueError(f"Operator '{operator}' cannot be negated. This should not happen - please report this issue.")
433
+
434
+ def _determine_field_type_from_operator(
435
+ self,
436
+ operator: StringOperator | NumberOperator | BooleanOperator | DateOperator | ArrayOperator,
437
+ ) -> FieldType:
438
+ if isinstance(operator, StringOperator):
439
+ return FieldType.STRING
440
+ if isinstance(operator, NumberOperator):
441
+ return FieldType.NUMBER
442
+ if isinstance(operator, BooleanOperator):
443
+ return FieldType.BOOLEAN
444
+ if isinstance(operator, DateOperator):
445
+ return FieldType.DATE
446
+ if isinstance(operator, ArrayOperator):
447
+ return FieldType.ARRAY
448
+ return FieldType.STRING
@@ -0,0 +1,114 @@
1
+ import re
2
+ from uuid import UUID
3
+
4
+ from notionary.blocks.rich_text.name_id_resolver.page import PageNameIdResolver
5
+ from notionary.blocks.rich_text.name_id_resolver.person import PersonNameIdResolver
6
+ from notionary.data_source.query.schema import (
7
+ CompoundFilter,
8
+ DataSourceQueryParams,
9
+ NotionFilter,
10
+ PropertyFilter,
11
+ )
12
+ from notionary.shared.properties.type import PropertyType
13
+ from notionary.utils.mixins.logging import LoggingMixin
14
+
15
+
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)
18
+
19
+ def __init__(
20
+ self,
21
+ user_resolver: PersonNameIdResolver | None = None,
22
+ page_resolver: PageNameIdResolver | None = None,
23
+ ):
24
+ self._user_resolver = user_resolver or PersonNameIdResolver()
25
+ self._page_resolver = page_resolver or PageNameIdResolver()
26
+
27
+ async def resolve_params(self, params: DataSourceQueryParams) -> DataSourceQueryParams:
28
+ if not params.filter:
29
+ return params
30
+
31
+ resolved_filter = await self._resolve_filter(params.filter)
32
+ return DataSourceQueryParams(filter=resolved_filter, sorts=params.sorts)
33
+
34
+ async def _resolve_filter(self, filter: NotionFilter) -> NotionFilter:
35
+ if isinstance(filter, PropertyFilter):
36
+ return await self._resolve_property_filter(filter)
37
+ elif isinstance(filter, CompoundFilter):
38
+ return await self._resolve_compound_filter(filter)
39
+ return filter
40
+
41
+ async def _resolve_compound_filter(self, compound: CompoundFilter) -> CompoundFilter:
42
+ resolved_filters = []
43
+ for filter in compound.filters:
44
+ resolved = await self._resolve_filter(filter)
45
+ resolved_filters.append(resolved)
46
+
47
+ return CompoundFilter(operator=compound.operator, filters=resolved_filters)
48
+
49
+ async def _resolve_property_filter(self, prop_filter: PropertyFilter) -> PropertyFilter:
50
+ if not self._is_resolvable_property_type(prop_filter.property_type):
51
+ return prop_filter
52
+
53
+ if prop_filter.value is None:
54
+ return prop_filter
55
+
56
+ if self._is_uuid(prop_filter.value):
57
+ return prop_filter
58
+
59
+ resolved_value = await self._resolve_value(prop_filter.value, prop_filter.property_type)
60
+
61
+ return PropertyFilter(
62
+ property=prop_filter.property,
63
+ property_type=prop_filter.property_type,
64
+ operator=prop_filter.operator,
65
+ value=resolved_value,
66
+ )
67
+
68
+ def _is_resolvable_property_type(self, property_type: PropertyType) -> bool:
69
+ return property_type in (PropertyType.PEOPLE, PropertyType.RELATION)
70
+
71
+ def _is_uuid(self, value: str | int | float | bool | list) -> bool:
72
+ if not isinstance(value, str):
73
+ return False
74
+
75
+ return self._is_standard_uuid(value) or self._is_notion_style_uuid(value)
76
+
77
+ def _is_standard_uuid(self, value: str) -> bool:
78
+ try:
79
+ UUID(value)
80
+ return True
81
+ except (ValueError, AttributeError):
82
+ return False
83
+
84
+ def _is_notion_style_uuid(self, value: str) -> bool:
85
+ return bool(self.UUID_PATTERN.match(value))
86
+
87
+ async def _resolve_value(self, value: str, property_type: PropertyType) -> str:
88
+ if property_type == PropertyType.PEOPLE:
89
+ return await self._resolve_user_name_to_id(value)
90
+
91
+ if property_type == PropertyType.RELATION:
92
+ return await self._resolve_page_name_to_id(value)
93
+
94
+ return value
95
+
96
+ def _ensure_value_is_string(self, value: str | int | float | bool | list) -> None:
97
+ if not isinstance(value, str):
98
+ raise ValueError(f"Cannot resolve non-string value: {value}")
99
+
100
+ async def _resolve_user_name_to_id(self, name: str) -> str:
101
+ resolved = await self._user_resolver.resolve_name_to_id(name)
102
+
103
+ if not resolved:
104
+ raise ValueError(f"Could not resolve user name '{name}' to ID")
105
+
106
+ return resolved
107
+
108
+ async def _resolve_page_name_to_id(self, name: str) -> str:
109
+ resolved = await self._page_resolver.resolve_name_to_id(name)
110
+
111
+ if not resolved:
112
+ raise ValueError(f"Could not resolve page name '{name}' to ID")
113
+
114
+ return resolved