notionary 0.2.26__py3-none-any.whl → 0.2.28__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 (387) hide show
  1. notionary/__init__.py +5 -20
  2. notionary/blocks/client.py +87 -215
  3. notionary/blocks/enums.py +167 -0
  4. notionary/blocks/rich_text/markdown_rich_text_converter.py +266 -0
  5. notionary/blocks/rich_text/models.py +164 -0
  6. notionary/blocks/rich_text/name_id_resolver/__init__.py +11 -0
  7. notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
  8. notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
  9. notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
  10. notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
  11. notionary/blocks/rich_text/rich_text_markdown_converter.py +132 -0
  12. notionary/blocks/rich_text/rich_text_patterns.py +39 -0
  13. notionary/blocks/schemas.py +746 -0
  14. notionary/comments/client.py +52 -187
  15. notionary/comments/factory.py +40 -0
  16. notionary/comments/models.py +5 -127
  17. notionary/comments/schemas.py +240 -0
  18. notionary/comments/service.py +34 -0
  19. notionary/data_source/http/client.py +11 -0
  20. notionary/data_source/http/data_source_instance_client.py +94 -0
  21. notionary/data_source/properties/models.py +406 -0
  22. notionary/data_source/query/builder.py +429 -0
  23. notionary/data_source/query/resolver.py +114 -0
  24. notionary/data_source/query/schema.py +304 -0
  25. notionary/data_source/query/validator.py +73 -0
  26. notionary/data_source/schemas.py +27 -0
  27. notionary/data_source/service.py +353 -0
  28. notionary/database/client.py +30 -135
  29. notionary/database/database_metadata_update_client.py +19 -0
  30. notionary/database/schemas.py +29 -0
  31. notionary/database/service.py +169 -0
  32. notionary/exceptions/__init__.py +33 -0
  33. notionary/exceptions/api.py +41 -0
  34. notionary/exceptions/base.py +2 -0
  35. notionary/exceptions/block_parsing.py +16 -0
  36. notionary/exceptions/data_source/__init__.py +6 -0
  37. notionary/exceptions/data_source/builder.py +182 -0
  38. notionary/exceptions/data_source/properties.py +34 -0
  39. notionary/exceptions/properties.py +58 -0
  40. notionary/exceptions/search.py +33 -0
  41. notionary/file_upload/client.py +18 -30
  42. notionary/file_upload/models.py +7 -8
  43. notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
  44. notionary/http/client.py +205 -0
  45. notionary/http/models.py +49 -0
  46. notionary/page/blocks/client.py +1 -0
  47. notionary/page/content/factory.py +68 -0
  48. notionary/page/content/markdown/__init__.py +5 -0
  49. notionary/page/content/markdown/builder.py +304 -0
  50. notionary/page/content/markdown/nodes/__init__.py +54 -0
  51. notionary/page/content/markdown/nodes/audio.py +23 -0
  52. notionary/page/content/markdown/nodes/base.py +12 -0
  53. notionary/page/content/markdown/nodes/bookmark.py +25 -0
  54. notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
  55. notionary/page/content/markdown/nodes/bulleted_list.py +18 -0
  56. notionary/page/content/markdown/nodes/callout.py +32 -0
  57. notionary/page/content/markdown/nodes/code.py +30 -0
  58. notionary/page/content/markdown/nodes/columns.py +51 -0
  59. notionary/page/content/markdown/nodes/divider.py +14 -0
  60. notionary/page/content/markdown/nodes/embed.py +23 -0
  61. notionary/page/content/markdown/nodes/equation.py +19 -0
  62. notionary/page/content/markdown/nodes/file.py +23 -0
  63. notionary/page/content/markdown/nodes/heading.py +16 -0
  64. notionary/page/content/markdown/nodes/image.py +23 -0
  65. notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
  66. notionary/page/content/markdown/nodes/numbered_list.py +15 -0
  67. notionary/page/content/markdown/nodes/paragraph.py +14 -0
  68. notionary/page/content/markdown/nodes/pdf.py +23 -0
  69. notionary/page/content/markdown/nodes/quote.py +15 -0
  70. notionary/page/content/markdown/nodes/space.py +14 -0
  71. notionary/page/content/markdown/nodes/table.py +45 -0
  72. notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
  73. notionary/page/content/markdown/nodes/todo.py +22 -0
  74. notionary/page/content/markdown/nodes/toggle.py +28 -0
  75. notionary/page/content/markdown/nodes/toggleable_heading.py +35 -0
  76. notionary/page/content/markdown/nodes/video.py +23 -0
  77. notionary/page/content/parser/context.py +49 -0
  78. notionary/page/content/parser/factory.py +219 -0
  79. notionary/page/content/parser/parsers/__init__.py +60 -0
  80. notionary/page/content/parser/parsers/audio.py +40 -0
  81. notionary/page/content/parser/parsers/base.py +30 -0
  82. notionary/page/content/parser/parsers/bookmark.py +33 -0
  83. notionary/page/content/parser/parsers/breadcrumb.py +33 -0
  84. notionary/page/content/parser/parsers/bulleted_list.py +41 -0
  85. notionary/page/content/parser/parsers/callout.py +129 -0
  86. notionary/page/content/parser/parsers/caption.py +55 -0
  87. notionary/page/content/parser/parsers/code.py +81 -0
  88. notionary/page/content/parser/parsers/column.py +117 -0
  89. notionary/page/content/parser/parsers/column_list.py +81 -0
  90. notionary/page/content/parser/parsers/divider.py +33 -0
  91. notionary/page/content/parser/parsers/embed.py +33 -0
  92. notionary/page/content/parser/parsers/equation.py +65 -0
  93. notionary/page/content/parser/parsers/file.py +42 -0
  94. notionary/page/content/parser/parsers/heading.py +58 -0
  95. notionary/page/content/parser/parsers/image.py +42 -0
  96. notionary/page/content/parser/parsers/numbered_list.py +45 -0
  97. notionary/page/content/parser/parsers/paragraph.py +36 -0
  98. notionary/page/content/parser/parsers/pdf.py +42 -0
  99. notionary/page/content/parser/parsers/quote.py +65 -0
  100. notionary/page/content/parser/parsers/space.py +35 -0
  101. notionary/page/content/parser/parsers/table.py +144 -0
  102. notionary/page/content/parser/parsers/table_of_contents.py +32 -0
  103. notionary/page/content/parser/parsers/todo.py +58 -0
  104. notionary/page/content/parser/parsers/toggle.py +127 -0
  105. notionary/page/content/parser/parsers/toggleable_heading.py +150 -0
  106. notionary/page/content/parser/parsers/video.py +42 -0
  107. notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
  108. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +93 -0
  109. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +93 -0
  110. notionary/page/content/parser/post_processing/port.py +9 -0
  111. notionary/page/content/parser/post_processing/service.py +16 -0
  112. notionary/page/content/parser/pre_processsing/handlers/__init__.py +9 -0
  113. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +80 -0
  114. notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
  115. notionary/page/content/parser/pre_processsing/handlers/whitespace.py +68 -0
  116. notionary/page/content/parser/pre_processsing/service.py +15 -0
  117. notionary/page/content/parser/service.py +69 -0
  118. notionary/page/content/renderer/context.py +48 -0
  119. notionary/page/content/renderer/factory.py +240 -0
  120. notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
  121. notionary/page/content/renderer/post_processing/handlers/numbered_list_placeholdere.py +62 -0
  122. notionary/page/content/renderer/post_processing/port.py +7 -0
  123. notionary/page/content/renderer/post_processing/service.py +15 -0
  124. notionary/page/content/renderer/renderers/__init__.py +57 -0
  125. notionary/page/content/renderer/renderers/audio.py +31 -0
  126. notionary/page/content/renderer/renderers/base.py +31 -0
  127. notionary/page/content/renderer/renderers/bookmark.py +25 -0
  128. notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
  129. notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
  130. notionary/page/content/renderer/renderers/callout.py +65 -0
  131. notionary/page/content/renderer/renderers/captioned_block.py +58 -0
  132. notionary/page/content/renderer/renderers/code.py +34 -0
  133. notionary/page/content/renderer/renderers/column.py +44 -0
  134. notionary/page/content/renderer/renderers/column_list.py +31 -0
  135. notionary/page/content/renderer/renderers/divider.py +22 -0
  136. notionary/page/content/renderer/renderers/embed.py +25 -0
  137. notionary/page/content/renderer/renderers/equation.py +37 -0
  138. notionary/page/content/renderer/renderers/fallback.py +24 -0
  139. notionary/page/content/renderer/renderers/file.py +40 -0
  140. notionary/page/content/renderer/renderers/heading.py +69 -0
  141. notionary/page/content/renderer/renderers/image.py +31 -0
  142. notionary/page/content/renderer/renderers/numbered_list.py +41 -0
  143. notionary/page/content/renderer/renderers/paragraph.py +40 -0
  144. notionary/page/content/renderer/renderers/pdf.py +31 -0
  145. notionary/page/content/renderer/renderers/quote.py +49 -0
  146. notionary/page/content/renderer/renderers/table.py +115 -0
  147. notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
  148. notionary/page/content/renderer/renderers/table_row.py +17 -0
  149. notionary/page/content/renderer/renderers/todo.py +56 -0
  150. notionary/page/content/renderer/renderers/toggle.py +53 -0
  151. notionary/page/content/renderer/renderers/toggleable_heading.py +78 -0
  152. notionary/page/content/renderer/renderers/video.py +31 -0
  153. notionary/page/content/renderer/service.py +50 -0
  154. notionary/page/content/service.py +65 -0
  155. notionary/page/content/syntax/models.py +68 -0
  156. notionary/page/content/syntax/service.py +453 -0
  157. notionary/page/page_context.py +7 -16
  158. notionary/page/page_http_client.py +15 -0
  159. notionary/page/page_metadata_update_client.py +19 -0
  160. notionary/page/properties/client.py +144 -0
  161. notionary/page/properties/factory.py +26 -0
  162. notionary/page/properties/models.py +307 -0
  163. notionary/page/properties/service.py +257 -0
  164. notionary/page/schemas.py +13 -0
  165. notionary/page/service.py +222 -0
  166. notionary/shared/entity/client.py +29 -0
  167. notionary/shared/entity/dto_parsers.py +53 -0
  168. notionary/shared/entity/entity_metadata_update_client.py +41 -0
  169. notionary/shared/entity/schemas.py +45 -0
  170. notionary/shared/entity/service.py +171 -0
  171. notionary/shared/models/cover.py +20 -0
  172. notionary/shared/models/file.py +21 -0
  173. notionary/shared/models/icon.py +28 -0
  174. notionary/shared/models/parent.py +41 -0
  175. notionary/shared/properties/type.py +30 -0
  176. notionary/user/__init__.py +4 -8
  177. notionary/user/base.py +89 -0
  178. notionary/user/bot.py +70 -0
  179. notionary/user/client.py +22 -111
  180. notionary/user/person.py +41 -0
  181. notionary/user/schemas.py +67 -0
  182. notionary/user/service.py +65 -0
  183. notionary/utils/async_retry.py +39 -0
  184. notionary/utils/date.py +51 -0
  185. notionary/utils/fuzzy.py +56 -0
  186. notionary/{util/logging_mixin.py → utils/mixins/logging.py} +4 -16
  187. notionary/utils/pagination.py +50 -0
  188. notionary/utils/singleton.py +13 -0
  189. notionary/utils/uuid_utils.py +20 -0
  190. notionary/workspace/__init__.py +3 -0
  191. notionary/workspace/client.py +62 -0
  192. notionary/workspace/query/builder.py +60 -0
  193. notionary/workspace/query/models.py +60 -0
  194. notionary/workspace/query/service.py +93 -0
  195. notionary/workspace/schemas.py +21 -0
  196. notionary/workspace/service.py +116 -0
  197. {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info}/METADATA +54 -49
  198. notionary-0.2.28.dist-info/RECORD +200 -0
  199. {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info}/WHEEL +1 -1
  200. {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info/licenses}/LICENSE +9 -9
  201. notionary/base_notion_client.py +0 -219
  202. notionary/blocks/__init__.py +0 -5
  203. notionary/blocks/_bootstrap.py +0 -271
  204. notionary/blocks/audio/__init__.py +0 -11
  205. notionary/blocks/audio/audio_element.py +0 -158
  206. notionary/blocks/audio/audio_markdown_node.py +0 -24
  207. notionary/blocks/audio/audio_models.py +0 -10
  208. notionary/blocks/base_block_element.py +0 -42
  209. notionary/blocks/bookmark/__init__.py +0 -12
  210. notionary/blocks/bookmark/bookmark_element.py +0 -83
  211. notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
  212. notionary/blocks/bookmark/bookmark_models.py +0 -15
  213. notionary/blocks/breadcrumbs/__init__.py +0 -15
  214. notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
  215. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
  216. notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
  217. notionary/blocks/bulleted_list/__init__.py +0 -15
  218. notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
  219. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
  220. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
  221. notionary/blocks/callout/__init__.py +0 -12
  222. notionary/blocks/callout/callout_element.py +0 -99
  223. notionary/blocks/callout/callout_markdown_node.py +0 -19
  224. notionary/blocks/callout/callout_models.py +0 -33
  225. notionary/blocks/child_database/__init__.py +0 -14
  226. notionary/blocks/child_database/child_database_element.py +0 -59
  227. notionary/blocks/child_database/child_database_models.py +0 -12
  228. notionary/blocks/child_page/__init__.py +0 -9
  229. notionary/blocks/child_page/child_page_element.py +0 -94
  230. notionary/blocks/child_page/child_page_models.py +0 -12
  231. notionary/blocks/code/__init__.py +0 -11
  232. notionary/blocks/code/code_element.py +0 -149
  233. notionary/blocks/code/code_markdown_node.py +0 -80
  234. notionary/blocks/code/code_models.py +0 -94
  235. notionary/blocks/column/__init__.py +0 -25
  236. notionary/blocks/column/column_element.py +0 -65
  237. notionary/blocks/column/column_list_element.py +0 -52
  238. notionary/blocks/column/column_list_markdown_node.py +0 -34
  239. notionary/blocks/column/column_markdown_node.py +0 -42
  240. notionary/blocks/column/column_models.py +0 -26
  241. notionary/blocks/divider/__init__.py +0 -12
  242. notionary/blocks/divider/divider_element.py +0 -41
  243. notionary/blocks/divider/divider_markdown_node.py +0 -11
  244. notionary/blocks/divider/divider_models.py +0 -12
  245. notionary/blocks/embed/__init__.py +0 -12
  246. notionary/blocks/embed/embed_element.py +0 -98
  247. notionary/blocks/embed/embed_markdown_node.py +0 -19
  248. notionary/blocks/embed/embed_models.py +0 -14
  249. notionary/blocks/equation/__init__.py +0 -13
  250. notionary/blocks/equation/equation_element.py +0 -133
  251. notionary/blocks/equation/equation_element_markdown_node.py +0 -23
  252. notionary/blocks/equation/equation_models.py +0 -11
  253. notionary/blocks/file/__init__.py +0 -23
  254. notionary/blocks/file/file_element.py +0 -133
  255. notionary/blocks/file/file_element_markdown_node.py +0 -24
  256. notionary/blocks/file/file_element_models.py +0 -39
  257. notionary/blocks/heading/__init__.py +0 -19
  258. notionary/blocks/heading/heading_element.py +0 -112
  259. notionary/blocks/heading/heading_markdown_node.py +0 -16
  260. notionary/blocks/heading/heading_models.py +0 -29
  261. notionary/blocks/image_block/__init__.py +0 -11
  262. notionary/blocks/image_block/image_element.py +0 -130
  263. notionary/blocks/image_block/image_markdown_node.py +0 -25
  264. notionary/blocks/image_block/image_models.py +0 -10
  265. notionary/blocks/markdown/markdown_builder.py +0 -525
  266. notionary/blocks/markdown/markdown_document_model.py +0 -0
  267. notionary/blocks/markdown/markdown_node.py +0 -25
  268. notionary/blocks/mixins/captions/__init__.py +0 -4
  269. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
  270. notionary/blocks/mixins/captions/caption_mixin.py +0 -92
  271. notionary/blocks/mixins/file_upload/__init__.py +0 -3
  272. notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
  273. notionary/blocks/models.py +0 -174
  274. notionary/blocks/numbered_list/__init__.py +0 -16
  275. notionary/blocks/numbered_list/numbered_list_element.py +0 -65
  276. notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
  277. notionary/blocks/numbered_list/numbered_list_models.py +0 -17
  278. notionary/blocks/paragraph/__init__.py +0 -15
  279. notionary/blocks/paragraph/paragraph_element.py +0 -58
  280. notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
  281. notionary/blocks/paragraph/paragraph_models.py +0 -16
  282. notionary/blocks/pdf/__init__.py +0 -11
  283. notionary/blocks/pdf/pdf_element.py +0 -146
  284. notionary/blocks/pdf/pdf_markdown_node.py +0 -24
  285. notionary/blocks/pdf/pdf_models.py +0 -11
  286. notionary/blocks/quote/__init__.py +0 -14
  287. notionary/blocks/quote/quote_element.py +0 -75
  288. notionary/blocks/quote/quote_markdown_node.py +0 -16
  289. notionary/blocks/quote/quote_models.py +0 -18
  290. notionary/blocks/registry/__init__.py +0 -3
  291. notionary/blocks/registry/block_registry.py +0 -150
  292. notionary/blocks/rich_text/__init__.py +0 -33
  293. notionary/blocks/rich_text/rich_text_models.py +0 -221
  294. notionary/blocks/rich_text/text_inline_formatter.py +0 -456
  295. notionary/blocks/syntax_prompt_builder.py +0 -137
  296. notionary/blocks/table/__init__.py +0 -19
  297. notionary/blocks/table/table_element.py +0 -225
  298. notionary/blocks/table/table_markdown_node.py +0 -42
  299. notionary/blocks/table/table_models.py +0 -28
  300. notionary/blocks/table_of_contents/__init__.py +0 -17
  301. notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
  302. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
  303. notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
  304. notionary/blocks/todo/__init__.py +0 -12
  305. notionary/blocks/todo/todo_element.py +0 -81
  306. notionary/blocks/todo/todo_markdown_node.py +0 -21
  307. notionary/blocks/todo/todo_models.py +0 -18
  308. notionary/blocks/toggle/__init__.py +0 -12
  309. notionary/blocks/toggle/toggle_element.py +0 -112
  310. notionary/blocks/toggle/toggle_markdown_node.py +0 -31
  311. notionary/blocks/toggle/toggle_models.py +0 -17
  312. notionary/blocks/toggleable_heading/__init__.py +0 -11
  313. notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
  314. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
  315. notionary/blocks/types.py +0 -130
  316. notionary/blocks/video/__init__.py +0 -11
  317. notionary/blocks/video/video_element.py +0 -187
  318. notionary/blocks/video/video_element_models.py +0 -10
  319. notionary/blocks/video/video_markdown_node.py +0 -26
  320. notionary/comments/__init__.py +0 -26
  321. notionary/database/__init__.py +0 -4
  322. notionary/database/database.py +0 -480
  323. notionary/database/database_filter_builder.py +0 -173
  324. notionary/database/database_provider.py +0 -227
  325. notionary/database/exceptions.py +0 -13
  326. notionary/database/factory.py +0 -0
  327. notionary/database/models.py +0 -337
  328. notionary/database/notion_database.py +0 -487
  329. notionary/file_upload/__init__.py +0 -7
  330. notionary/page/client.py +0 -124
  331. notionary/page/markdown_whitespace_processor.py +0 -129
  332. notionary/page/models.py +0 -322
  333. notionary/page/notion_page.py +0 -674
  334. notionary/page/page_content_deleting_service.py +0 -117
  335. notionary/page/page_content_writer.py +0 -80
  336. notionary/page/property_formatter.py +0 -99
  337. notionary/page/reader/handler/__init__.py +0 -19
  338. notionary/page/reader/handler/base_block_renderer.py +0 -44
  339. notionary/page/reader/handler/block_processing_context.py +0 -35
  340. notionary/page/reader/handler/block_rendering_context.py +0 -48
  341. notionary/page/reader/handler/column_list_renderer.py +0 -51
  342. notionary/page/reader/handler/column_renderer.py +0 -60
  343. notionary/page/reader/handler/equation_renderer.py +0 -0
  344. notionary/page/reader/handler/line_renderer.py +0 -73
  345. notionary/page/reader/handler/numbered_list_renderer.py +0 -85
  346. notionary/page/reader/handler/toggle_renderer.py +0 -69
  347. notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
  348. notionary/page/reader/page_content_retriever.py +0 -81
  349. notionary/page/search_filter_builder.py +0 -132
  350. notionary/page/utils.py +0 -60
  351. notionary/page/writer/handler/__init__.py +0 -24
  352. notionary/page/writer/handler/code_handler.py +0 -72
  353. notionary/page/writer/handler/column_handler.py +0 -141
  354. notionary/page/writer/handler/column_list_handler.py +0 -139
  355. notionary/page/writer/handler/equation_handler.py +0 -74
  356. notionary/page/writer/handler/line_handler.py +0 -35
  357. notionary/page/writer/handler/line_processing_context.py +0 -54
  358. notionary/page/writer/handler/regular_line_handler.py +0 -86
  359. notionary/page/writer/handler/table_handler.py +0 -66
  360. notionary/page/writer/handler/toggle_handler.py +0 -159
  361. notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
  362. notionary/page/writer/markdown_to_notion_converter.py +0 -139
  363. notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
  364. notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
  365. notionary/page/writer/notion_text_length_processor.py +0 -150
  366. notionary/schemas/__init__.py +0 -3
  367. notionary/schemas/base.py +0 -73
  368. notionary/shared/__init__.py +0 -3
  369. notionary/shared/name_to_id_resolver.py +0 -203
  370. notionary/telemetry/__init__.py +0 -19
  371. notionary/telemetry/service.py +0 -136
  372. notionary/telemetry/views.py +0 -73
  373. notionary/user/base_notion_user.py +0 -53
  374. notionary/user/models.py +0 -84
  375. notionary/user/notion_bot_user.py +0 -226
  376. notionary/user/notion_user.py +0 -255
  377. notionary/user/notion_user_manager.py +0 -101
  378. notionary/util/__init__.py +0 -15
  379. notionary/util/concurrency_limiter.py +0 -0
  380. notionary/util/factory_decorator.py +0 -0
  381. notionary/util/factory_only.py +0 -37
  382. notionary/util/fuzzy.py +0 -75
  383. notionary/util/page_id_utils.py +0 -27
  384. notionary/util/singleton.py +0 -18
  385. notionary/util/singleton_metaclass.py +0 -22
  386. notionary/workspace.py +0 -105
  387. notionary-0.2.26.dist-info/RECORD +0 -202
@@ -0,0 +1,257 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import TYPE_CHECKING, Never
5
+
6
+ from notionary.blocks.rich_text.rich_text_markdown_converter import convert_rich_text_to_markdown
7
+ from notionary.exceptions.properties import (
8
+ AccessPagePropertyWithoutDataSourceError,
9
+ PagePropertyNotFoundError,
10
+ PagePropertyTypeError,
11
+ )
12
+ from notionary.page.properties.client import PagePropertyHttpClient
13
+ from notionary.page.properties.models import (
14
+ PageCheckboxProperty,
15
+ PageCreatedTimeProperty,
16
+ PageDateProperty,
17
+ PageEmailProperty,
18
+ PageMultiSelectProperty,
19
+ PageNumberProperty,
20
+ PagePeopleProperty,
21
+ PagePhoneNumberProperty,
22
+ PageProperty,
23
+ PagePropertyT,
24
+ PageRelationProperty,
25
+ PageRichTextProperty,
26
+ PageSelectProperty,
27
+ PageStatusProperty,
28
+ PageTitleProperty,
29
+ PageURLProperty,
30
+ )
31
+ from notionary.shared.models.parent import ParentType
32
+
33
+ if TYPE_CHECKING:
34
+ from notionary import NotionDataSource
35
+
36
+
37
+ class PagePropertyHandler:
38
+ def __init__(
39
+ self,
40
+ properties: dict[str, PageProperty],
41
+ parent_type: ParentType,
42
+ page_url: str,
43
+ page_property_http_client: PagePropertyHttpClient,
44
+ parent_data_source: str,
45
+ ) -> None:
46
+ self._properties = properties
47
+ self._parent_type = parent_type
48
+ self._page_url = page_url
49
+ self._property_http_client = page_property_http_client
50
+ self._parent_data_source_id = parent_data_source
51
+ self._parent_data_source: NotionDataSource | None = None
52
+ self._data_source_loaded = False
53
+
54
+ # =========================================================================
55
+ # Reader Methods
56
+ # =========================================================================
57
+
58
+ def get_value_of_status_property(self, name: str) -> str | None:
59
+ status_property = self._get_typed_property_or_raise(name, PageStatusProperty)
60
+ return status_property.status.name if status_property.status else None
61
+
62
+ def get_value_of_select_property(self, name: str) -> str | None:
63
+ select_property = self._get_typed_property_or_raise(name, PageSelectProperty)
64
+ return select_property.select.name if select_property.select else None
65
+
66
+ async def get_value_of_title_property(self, name: str) -> str:
67
+ title_property = self._get_typed_property_or_raise(name, PageTitleProperty)
68
+ return await convert_rich_text_to_markdown(title_property.title)
69
+
70
+ def get_values_of_people_property(self, property_name: str) -> list[str]:
71
+ people_prop = self._get_typed_property_or_raise(property_name, PagePeopleProperty)
72
+ return [person.name for person in people_prop.people if person.name]
73
+
74
+ def get_value_of_created_time_property(self, name: str) -> str | None:
75
+ created_time_property = self._get_typed_property_or_raise(name, PageCreatedTimeProperty)
76
+ return created_time_property.created_time
77
+
78
+ async def get_values_of_relation_property(self, name: str) -> list[str]:
79
+ from notionary import NotionPage
80
+
81
+ relation_property = self._get_typed_property_or_raise(name, PageRelationProperty)
82
+ relation_page_ids = [rel.id for rel in relation_property.relation]
83
+ notion_pages = [await NotionPage.from_id(page_id) for page_id in relation_page_ids]
84
+ return [page.title for page in notion_pages if page]
85
+
86
+ def get_values_of_multiselect_property(self, name: str) -> list[str]:
87
+ multiselect_property = self._get_typed_property_or_raise(name, PageMultiSelectProperty)
88
+ return [option.name for option in multiselect_property.multi_select]
89
+
90
+ def get_value_of_url_property(self, name: str) -> str | None:
91
+ url_property = self._get_typed_property_or_raise(name, PageURLProperty)
92
+ return url_property.url
93
+
94
+ def get_value_of_number_property(self, name: str) -> float | None:
95
+ number_property = self._get_typed_property_or_raise(name, PageNumberProperty)
96
+ return number_property.number
97
+
98
+ def get_value_of_checkbox_property(self, name: str) -> bool:
99
+ checkbox_property = self._get_typed_property_or_raise(name, PageCheckboxProperty)
100
+ return checkbox_property.checkbox
101
+
102
+ def get_value_of_date_property(self, name: str) -> str | None:
103
+ date_property = self._get_typed_property_or_raise(name, PageDateProperty)
104
+ return date_property.date.start if date_property.date else None
105
+
106
+ async def get_value_of_rich_text_property(self, name: str) -> str:
107
+ rich_text_property = self._get_typed_property_or_raise(name, PageRichTextProperty)
108
+ return await convert_rich_text_to_markdown(rich_text_property.rich_text)
109
+
110
+ def get_value_of_email_property(self, name: str) -> str | None:
111
+ email_property = self._get_typed_property_or_raise(name, PageEmailProperty)
112
+ return email_property.email
113
+
114
+ def get_value_of_phone_number_property(self, name: str) -> str | None:
115
+ phone_property = self._get_typed_property_or_raise(name, PagePhoneNumberProperty)
116
+ return phone_property.phone_number
117
+
118
+ # =========================================================================
119
+ # Options Getters
120
+ # =========================================================================
121
+
122
+ async def get_select_options_by_property_name(self, property_name: str) -> list[str]:
123
+ data_source = await self._get_parent_data_source_or_raise()
124
+ return data_source.get_select_options_by_property_name(property_name)
125
+
126
+ async def get_multi_select_options_by_property_name(self, property_name: str) -> list[str]:
127
+ data_source = await self._get_parent_data_source_or_raise()
128
+ return data_source.get_multi_select_options_by_property_name(property_name)
129
+
130
+ async def get_status_options_by_property_name(self, property_name: str) -> list[str]:
131
+ data_source = await self._get_parent_data_source_or_raise()
132
+ return data_source.get_status_options_by_property_name(property_name)
133
+
134
+ async def get_relation_options_by_property_name(self, property_name: str) -> list[str]:
135
+ data_source = await self._get_parent_data_source_or_raise()
136
+ return await data_source.get_relation_options_by_property_name(property_name)
137
+
138
+ async def get_options_for_property_by_name(self, property_name: str) -> list[str]:
139
+ data_source = await self._get_parent_data_source_or_raise()
140
+ return await data_source.get_options_for_property_by_name(property_name)
141
+
142
+ # =========================================================================
143
+ # Writer Methods
144
+ # =========================================================================
145
+
146
+ async def set_title_property(self, property_name: str, title: str) -> None:
147
+ self._get_typed_property_or_raise(property_name, PageTitleProperty)
148
+ updated_page = await self._property_http_client.patch_title(property_name, title)
149
+ self._properties = updated_page.properties
150
+
151
+ async def set_rich_text_property(self, property_name: str, text: str) -> None:
152
+ self._get_typed_property_or_raise(property_name, PageRichTextProperty)
153
+ updated_page = await self._property_http_client.patch_rich_text_property(property_name, text)
154
+ self._properties = updated_page.properties
155
+
156
+ async def set_url_property(self, property_name: str, url: str) -> None:
157
+ self._get_typed_property_or_raise(property_name, PageURLProperty)
158
+ updated_page = await self._property_http_client.patch_url_property(property_name, url)
159
+ self._properties = updated_page.properties
160
+
161
+ async def set_email_property(self, property_name: str, email: str) -> None:
162
+ self._get_typed_property_or_raise(property_name, PageEmailProperty)
163
+ updated_page = await self._property_http_client.patch_email_property(property_name, email)
164
+ self._properties = updated_page.properties
165
+
166
+ async def set_phone_number_property(self, property_name: str, phone_number: str) -> None:
167
+ self._get_typed_property_or_raise(property_name, PagePhoneNumberProperty)
168
+ updated_page = await self._property_http_client.patch_phone_property(property_name, phone_number)
169
+ self._properties = updated_page.properties
170
+
171
+ async def set_number_property(self, property_name: str, number: int | float) -> None:
172
+ self._get_typed_property_or_raise(property_name, PageNumberProperty)
173
+ updated_page = await self._property_http_client.patch_number_property(property_name, number)
174
+ self._properties = updated_page.properties
175
+
176
+ async def set_checkbox_property(self, property_name: str, checked: bool) -> None:
177
+ self._get_typed_property_or_raise(property_name, PageCheckboxProperty)
178
+ updated_page = await self._property_http_client.patch_checkbox_property(property_name, checked)
179
+ self._properties = updated_page.properties
180
+
181
+ async def set_date_property(self, property_name: str, date_value: str | dict) -> None:
182
+ self._get_typed_property_or_raise(property_name, PageDateProperty)
183
+ updated_page = await self._property_http_client.patch_date_property(property_name, date_value)
184
+ self._properties = updated_page.properties
185
+
186
+ async def set_select_property_by_option_name(self, property_name: str, option_name: str) -> None:
187
+ self._get_typed_property_or_raise(property_name, PageSelectProperty)
188
+ updated_page = await self._property_http_client.patch_select_property(property_name, option_name)
189
+ self._properties = updated_page.properties
190
+
191
+ async def set_multi_select_property_by_option_names(self, property_name: str, option_names: list[str]) -> None:
192
+ self._get_typed_property_or_raise(property_name, PageMultiSelectProperty)
193
+ updated_page = await self._property_http_client.patch_multi_select_property(property_name, option_names)
194
+ self._properties = updated_page.properties
195
+
196
+ async def set_status_property_by_option_name(self, property_name: str, status: str) -> None:
197
+ self._get_typed_property_or_raise(property_name, PageStatusProperty)
198
+ updated_page = await self._property_http_client.patch_status_property(property_name, status)
199
+ self._properties = updated_page.properties
200
+
201
+ async def set_relation_property_by_page_titles(self, property_name: str, page_titles: list[str]) -> None:
202
+ self._get_typed_property_or_raise(property_name, PageRelationProperty)
203
+ relation_ids = await self._convert_page_titles_to_ids(page_titles)
204
+ updated_page = await self._property_http_client.patch_relation_property(property_name, relation_ids)
205
+ self._properties = updated_page.properties
206
+
207
+ async def _ensure_data_source_loaded(self) -> None:
208
+ from notionary import NotionDataSource
209
+
210
+ if self._data_source_loaded:
211
+ return
212
+
213
+ self._parent_data_source = (
214
+ await NotionDataSource.from_id(self._parent_data_source_id) if self._parent_data_source_id else None
215
+ )
216
+ self._data_source_loaded = True
217
+
218
+ async def _get_parent_data_source_or_raise(self) -> NotionDataSource:
219
+ await self._ensure_data_source_loaded()
220
+
221
+ if not self._parent_data_source:
222
+ raise AccessPagePropertyWithoutDataSourceError(self._parent_type)
223
+ return self._parent_data_source
224
+
225
+ def _get_typed_property_or_raise(self, name: str, property_type: type[PagePropertyT]) -> PagePropertyT:
226
+ prop = self._properties.get(name)
227
+
228
+ if prop is None:
229
+ self._handle_prop_not_found(name)
230
+
231
+ if not isinstance(prop, property_type):
232
+ self._handle_incorrect_type(name, type(prop))
233
+
234
+ return prop
235
+
236
+ def _handle_prop_not_found(self, name: str) -> Never:
237
+ raise PagePropertyNotFoundError(
238
+ property_name=name,
239
+ page_url=self._page_url,
240
+ available_properties=list(self._properties.keys()),
241
+ )
242
+
243
+ def _handle_incorrect_type(self, property_name: str, actual_type: type) -> Never:
244
+ raise PagePropertyTypeError(
245
+ property_name=property_name,
246
+ actual_type=actual_type.__name__,
247
+ )
248
+
249
+ async def _convert_page_titles_to_ids(self, page_titles: list[str]) -> list[str]:
250
+ from notionary import NotionPage
251
+
252
+ if not page_titles:
253
+ return []
254
+
255
+ pages = await asyncio.gather(*[NotionPage.from_title(title=title) for title in page_titles])
256
+
257
+ return [page.id for page in pages if page]
@@ -0,0 +1,13 @@
1
+ from pydantic import BaseModel
2
+
3
+ from notionary.page.properties.models import DiscriminatedPageProperty
4
+ from notionary.shared.entity.schemas import EntityResponseDto
5
+
6
+
7
+ class NotionPageDto(EntityResponseDto):
8
+ archived: bool
9
+ properties: dict[str, DiscriminatedPageProperty]
10
+
11
+
12
+ class PgePropertiesUpdateDto(BaseModel):
13
+ properties: dict[str, DiscriminatedPageProperty]
@@ -0,0 +1,222 @@
1
+ from collections.abc import Callable
2
+ from typing import Self
3
+
4
+ from notionary.blocks.client import NotionBlockHttpClient
5
+ from notionary.blocks.rich_text.rich_text_markdown_converter import convert_rich_text_to_markdown
6
+ from notionary.comments.models import Comment
7
+ from notionary.comments.service import CommentService
8
+ from notionary.page.content.factory import PageContentServiceFactory
9
+ from notionary.page.content.markdown.builder import MarkdownBuilder
10
+ from notionary.page.content.service import PageContentService
11
+ from notionary.page.page_http_client import NotionPageHttpClient
12
+ from notionary.page.page_metadata_update_client import PageMetadataUpdateClient
13
+ from notionary.page.properties.factory import PagePropertyHandlerFactory
14
+ from notionary.page.properties.models import PageTitleProperty
15
+ from notionary.page.properties.service import PagePropertyHandler
16
+ from notionary.page.schemas import NotionPageDto
17
+ from notionary.shared.entity.dto_parsers import (
18
+ extract_cover_image_url_from_dto,
19
+ extract_emoji_icon_from_dto,
20
+ extract_external_icon_url_from_dto,
21
+ )
22
+ from notionary.shared.entity.service import Entity
23
+ from notionary.user.schemas import PartialUserDto
24
+ from notionary.workspace.query.service import WorkspaceQueryService
25
+
26
+
27
+ class NotionPage(Entity):
28
+ def __init__(
29
+ self,
30
+ id: str,
31
+ title: str,
32
+ created_time: str,
33
+ created_by: PartialUserDto,
34
+ last_edited_time: str,
35
+ last_edited_by: PartialUserDto,
36
+ url: str,
37
+ archived: bool,
38
+ in_trash: bool,
39
+ page_property_handler: PagePropertyHandler,
40
+ block_client: NotionBlockHttpClient,
41
+ comment_service: CommentService,
42
+ page_content_service: PageContentService,
43
+ metadata_update_client: PageMetadataUpdateClient,
44
+ public_url: str | None = None,
45
+ emoji_icon: str | None = None,
46
+ external_icon_url: str | None = None,
47
+ cover_image_url: str | None = None,
48
+ ) -> None:
49
+ super().__init__(
50
+ id=id,
51
+ created_time=created_time,
52
+ created_by=created_by,
53
+ last_edited_time=last_edited_time,
54
+ last_edited_by=last_edited_by,
55
+ in_trash=in_trash,
56
+ emoji_icon=emoji_icon,
57
+ external_icon_url=external_icon_url,
58
+ cover_image_url=cover_image_url,
59
+ )
60
+ self._title = title
61
+ self._archived = archived
62
+ self._url = url
63
+ self._public_url = public_url
64
+
65
+ self._block_client = block_client
66
+ self._comment_service = comment_service
67
+ self._page_content_service = page_content_service
68
+ self.properties = page_property_handler
69
+ self._metadata_update_client = metadata_update_client
70
+
71
+ @classmethod
72
+ async def from_id(
73
+ cls,
74
+ page_id: str,
75
+ page_property_handler_factory: PagePropertyHandlerFactory | None = None,
76
+ ) -> Self:
77
+ factory = page_property_handler_factory or PagePropertyHandlerFactory()
78
+ response = await cls._fetch_page_dto(page_id)
79
+ return await cls._create_from_dto(response, factory)
80
+
81
+ @classmethod
82
+ async def from_title(
83
+ cls,
84
+ page_title: str,
85
+ min_similarity: float = 0.6,
86
+ search_service: WorkspaceQueryService | None = None,
87
+ ) -> Self:
88
+ service = search_service or WorkspaceQueryService()
89
+ return await service.find_page(page_title, min_similarity=min_similarity)
90
+
91
+ @classmethod
92
+ async def _fetch_page_dto(cls, page_id: str) -> NotionPageDto:
93
+ async with NotionPageHttpClient(page_id=page_id) as client:
94
+ return await client.get_page()
95
+
96
+ @classmethod
97
+ async def _create_from_dto(
98
+ cls,
99
+ response: NotionPageDto,
100
+ page_property_handler_factory: PagePropertyHandlerFactory,
101
+ ) -> Self:
102
+ title_task = cls._extract_title_from_dto(response)
103
+ page_property_handler = page_property_handler_factory.create_from_page_response(response)
104
+
105
+ title = await title_task
106
+
107
+ return cls._create_with_dependencies(
108
+ id=response.id,
109
+ title=title,
110
+ created_time=response.created_time,
111
+ created_by=response.created_by,
112
+ last_edited_time=response.last_edited_time,
113
+ last_edited_by=response.last_edited_by,
114
+ archived=response.archived,
115
+ in_trash=response.in_trash,
116
+ url=response.url,
117
+ page_property_handler=page_property_handler,
118
+ public_url=response.public_url,
119
+ emoji_icon=extract_emoji_icon_from_dto(response),
120
+ external_icon_url=extract_external_icon_url_from_dto(response),
121
+ cover_image_url=extract_cover_image_url_from_dto(response),
122
+ )
123
+
124
+ @classmethod
125
+ def _create_with_dependencies(
126
+ cls,
127
+ id: str,
128
+ title: str,
129
+ created_time: str,
130
+ created_by: PartialUserDto,
131
+ last_edited_time: str,
132
+ last_edited_by: PartialUserDto,
133
+ url: str,
134
+ archived: bool,
135
+ in_trash: bool,
136
+ page_property_handler: PagePropertyHandler,
137
+ public_url: str | None = None,
138
+ emoji_icon: str | None = None,
139
+ external_icon_url: str | None = None,
140
+ cover_image_url: str | None = None,
141
+ ) -> Self:
142
+ block_client = NotionBlockHttpClient()
143
+ comment_service = CommentService()
144
+
145
+ page_content_service_factory = PageContentServiceFactory()
146
+ page_content_service = page_content_service_factory.create(page_id=id, block_client=block_client)
147
+
148
+ metadata_update_client = PageMetadataUpdateClient(page_id=id)
149
+
150
+ return cls(
151
+ id=id,
152
+ title=title,
153
+ created_time=created_time,
154
+ created_by=created_by,
155
+ last_edited_time=last_edited_time,
156
+ last_edited_by=last_edited_by,
157
+ url=url,
158
+ archived=archived,
159
+ in_trash=in_trash,
160
+ page_property_handler=page_property_handler,
161
+ block_client=block_client,
162
+ comment_service=comment_service,
163
+ page_content_service=page_content_service,
164
+ metadata_update_client=metadata_update_client,
165
+ public_url=public_url,
166
+ emoji_icon=emoji_icon,
167
+ external_icon_url=external_icon_url,
168
+ cover_image_url=cover_image_url,
169
+ )
170
+
171
+ @staticmethod
172
+ async def _extract_title_from_dto(response: NotionPageDto) -> str:
173
+ title_property = next(
174
+ (prop for prop in response.properties.values() if isinstance(prop, PageTitleProperty)),
175
+ None,
176
+ )
177
+ rich_text_title = title_property.title if title_property else []
178
+ return await convert_rich_text_to_markdown(rich_text_title)
179
+
180
+ @property
181
+ def _entity_metadata_update_client(self) -> PageMetadataUpdateClient:
182
+ return self._metadata_update_client
183
+
184
+ @property
185
+ def title(self) -> str:
186
+ return self._title
187
+
188
+ @property
189
+ def url(self) -> str:
190
+ return self._url
191
+
192
+ async def get_comments(self) -> list[Comment]:
193
+ return await self._comment_service.list_all_comments_for_page(page_id=self._id)
194
+
195
+ async def post_top_level_comment(self, comment: str) -> None:
196
+ await self._comment_service.create_comment_on_page(page_id=self._id, text=comment)
197
+
198
+ async def post_reply_to_discussion(self, discussion_id: str, comment: str) -> None:
199
+ await self._comment_service.reply_to_discussion_by_id(discussion_id=discussion_id, text=comment)
200
+
201
+ async def set_title(self, title: str) -> None:
202
+ await self.properties.set_title_property(title)
203
+ self._title = title
204
+
205
+ async def append_markdown(
206
+ self,
207
+ content: (str | Callable[[MarkdownBuilder], MarkdownBuilder]),
208
+ ) -> None:
209
+ await self._page_content_service.append_markdown(content=content)
210
+
211
+ async def replace_content(
212
+ self,
213
+ content: (str | Callable[[MarkdownBuilder], MarkdownBuilder]),
214
+ ) -> None:
215
+ await self._page_content_service.clear()
216
+ await self._page_content_service.append_markdown(content=content)
217
+
218
+ async def clear_page_content(self) -> None:
219
+ await self._page_content_service.clear()
220
+
221
+ async def get_markdown_content(self) -> str:
222
+ return await self._page_content_service.get_as_markdown()
@@ -0,0 +1,29 @@
1
+ from typing import TypeVar, override
2
+
3
+ from notionary.http.client import NotionHttpClient
4
+ from notionary.shared.entity.entity_metadata_update_client import EntityMetadataUpdateClient
5
+ from notionary.shared.entity.schemas import EntityResponseDto, NotionEntityUpdateDto
6
+
7
+ ResponseType = TypeVar("ResponseType", bound=EntityResponseDto)
8
+
9
+
10
+ class GenericEntityMetadataUpdateClient(NotionHttpClient, EntityMetadataUpdateClient):
11
+ def __init__(
12
+ self,
13
+ entity_id: str,
14
+ path_segment: str,
15
+ response_dto_class: type[ResponseType],
16
+ timeout: int = 30,
17
+ ) -> None:
18
+ super().__init__(timeout)
19
+ self._entity_id = entity_id
20
+ self._path_segment = path_segment
21
+ self._response_dto_class = response_dto_class
22
+
23
+ @override
24
+ async def patch_metadata(self, updated_data: NotionEntityUpdateDto) -> ResponseType:
25
+ updated_data_dict = updated_data.model_dump(exclude_unset=True, exclude_none=True)
26
+ url = f"{self._path_segment}/{self._entity_id}"
27
+
28
+ response_dict = await self.patch(url, data=updated_data_dict)
29
+ return self._response_dto_class.model_validate(response_dict)
@@ -0,0 +1,53 @@
1
+ from typing import cast
2
+
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
4
+ from notionary.shared.entity.schemas import Describable, EntityResponseDto, Titled
5
+ from notionary.shared.models.cover import CoverType
6
+ from notionary.shared.models.icon import IconType
7
+ from notionary.shared.models.parent import DatabaseParent, DataSourceParent, ParentType
8
+
9
+
10
+ def extract_emoji_icon_from_dto(entity_dto: EntityResponseDto) -> str | None:
11
+ if not entity_dto.icon or entity_dto.icon.type != IconType.EMOJI:
12
+ return None
13
+ return entity_dto.icon.emoji
14
+
15
+
16
+ def extract_external_icon_url_from_dto(entity_dto: EntityResponseDto) -> str | None:
17
+ if not entity_dto.icon or entity_dto.icon.type != IconType.EXTERNAL:
18
+ return None
19
+ return entity_dto.icon.external.url if entity_dto.icon.external else None
20
+
21
+
22
+ def extract_cover_image_url_from_dto(entity_dto: EntityResponseDto) -> str | None:
23
+ if not entity_dto.cover or entity_dto.cover.type != CoverType.EXTERNAL:
24
+ return None
25
+ return entity_dto.cover.external.url if entity_dto.cover.external else None
26
+
27
+
28
+ def extract_database_id(entity_dto: EntityResponseDto) -> str | None:
29
+ if entity_dto.parent.type == ParentType.DATA_SOURCE_ID:
30
+ data_source_parent = cast(DataSourceParent, entity_dto.parent)
31
+ return data_source_parent.database_id if data_source_parent else None
32
+
33
+ if entity_dto.parent.type == ParentType.DATABASE_ID:
34
+ database_parent = cast(DatabaseParent, entity_dto.parent)
35
+ return database_parent.database_id if database_parent else None
36
+
37
+ return None
38
+
39
+
40
+ async def extract_title(
41
+ entity: Titled,
42
+ rich_text_converter: RichTextToMarkdownConverter,
43
+ ) -> str:
44
+ return await rich_text_converter.to_markdown(entity.title)
45
+
46
+
47
+ async def extract_description(
48
+ entity: Describable,
49
+ rich_text_converter: RichTextToMarkdownConverter,
50
+ ) -> str | None:
51
+ if not entity.description:
52
+ return None
53
+ return await rich_text_converter.to_markdown(entity.description)
@@ -0,0 +1,41 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from notionary.shared.entity.schemas import EntityResponseDto, NotionEntityUpdateDto
4
+ from notionary.shared.models.cover import NotionCover
5
+ from notionary.shared.models.icon import EmojiIcon, ExternalIcon
6
+
7
+
8
+ class EntityMetadataUpdateClient(ABC):
9
+ @abstractmethod
10
+ async def patch_metadata(self, updated_data: NotionEntityUpdateDto) -> EntityResponseDto: ...
11
+
12
+ async def patch_emoji_icon(self, emoji: str) -> EntityResponseDto:
13
+ icon = EmojiIcon(emoji=emoji)
14
+ update_dto = NotionEntityUpdateDto(icon=icon)
15
+ return await self.patch_metadata(update_dto)
16
+
17
+ async def patch_external_icon(self, icon_url: str) -> EntityResponseDto:
18
+ icon = ExternalIcon.from_url(icon_url)
19
+ update_dto = NotionEntityUpdateDto(icon=icon)
20
+ return await self.patch_metadata(update_dto)
21
+
22
+ async def remove_icon(self) -> None:
23
+ update_dto = NotionEntityUpdateDto(icon=None)
24
+ return await self.patch_metadata(update_dto)
25
+
26
+ async def patch_external_cover(self, cover_url: str) -> EntityResponseDto:
27
+ cover = NotionCover.from_url(cover_url)
28
+ update_dto = NotionEntityUpdateDto(cover=cover)
29
+ return await self.patch_metadata(update_dto)
30
+
31
+ async def remove_cover(self) -> None:
32
+ update_dto = NotionEntityUpdateDto(cover=None)
33
+ return await self.patch_metadata(update_dto)
34
+
35
+ async def move_to_trash(self) -> EntityResponseDto:
36
+ update_dto = NotionEntityUpdateDto(in_trash=True)
37
+ return await self.patch_metadata(update_dto)
38
+
39
+ async def restore_from_trash(self) -> EntityResponseDto:
40
+ update_dto = NotionEntityUpdateDto(in_trash=False)
41
+ return await self.patch_metadata(update_dto)
@@ -0,0 +1,45 @@
1
+ from enum import StrEnum
2
+ from typing import Protocol
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from notionary.blocks.rich_text.models import RichText
7
+ from notionary.shared.models.cover import NotionCover
8
+ from notionary.shared.models.icon import Icon
9
+ from notionary.shared.models.parent import Parent
10
+ from notionary.user.schemas import PartialUserDto
11
+
12
+
13
+ class EntityWorkspaceQueryObjectType(StrEnum):
14
+ PAGE = "page"
15
+ DATA_SOURCE = "data_source"
16
+ DATABASE = "database"
17
+
18
+
19
+ class EntityResponseDto(BaseModel):
20
+ object: EntityWorkspaceQueryObjectType
21
+ id: str
22
+ created_time: str
23
+ created_by: PartialUserDto
24
+ last_edited_time: str
25
+ last_edited_by: PartialUserDto
26
+ cover: NotionCover | None = None
27
+ icon: Icon | None = None
28
+ parent: Parent
29
+ in_trash: bool
30
+ url: str
31
+ public_url: str | None = None
32
+
33
+
34
+ class NotionEntityUpdateDto(BaseModel):
35
+ icon: Icon | None = None
36
+ cover: NotionCover | None = None
37
+ in_trash: bool | None = None
38
+
39
+
40
+ class Titled(Protocol):
41
+ title: list[RichText]
42
+
43
+
44
+ class Describable(Protocol):
45
+ description: list[RichText] | None