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
@@ -1,674 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from ast import Dict
4
- import asyncio
5
- import random
6
- from typing import TYPE_CHECKING, Any, Callable, Optional, Union
7
-
8
- from yaml import Token
9
-
10
- from notionary.blocks.client import NotionBlockClient
11
- from notionary.comments import CommentClient, Comment
12
- from notionary.blocks.syntax_prompt_builder import SyntaxPromptBuilder
13
- from notionary.blocks.models import DatabaseParent, ParentObject
14
- from notionary.blocks.registry.block_registry import BlockRegistry
15
- from notionary.database.client import NotionDatabaseClient
16
- from notionary.file_upload.client import NotionFileUploadClient
17
- from notionary.blocks.markdown.markdown_builder import MarkdownBuilder
18
- from notionary.schemas import NotionContentSchema
19
- from notionary.page import page_context
20
- from notionary.page.client import NotionPageClient
21
- from notionary.page.models import NotionPageResponse
22
- from notionary.page.page_content_deleting_service import PageContentDeletingService
23
- from notionary.page.page_content_writer import PageContentWriter
24
- from notionary.page.page_context import PageContextProvider, page_context
25
- from notionary.page.property_formatter import NotionPropertyFormatter
26
- from notionary.page.reader.page_content_retriever import PageContentRetriever
27
- from notionary.page.utils import extract_property_value
28
- from notionary.util import LoggingMixin, extract_uuid, factory_only, format_uuid
29
- from notionary.util.fuzzy import find_best_match
30
-
31
- if TYPE_CHECKING:
32
- from notionary import NotionDatabase
33
-
34
-
35
- class NotionPage(LoggingMixin):
36
- """
37
- Managing content and metadata of a Notion page.
38
- """
39
-
40
- @factory_only("from_page_id", "from_page_name", "from_url")
41
- def __init__(
42
- self,
43
- page_id: str,
44
- title: str,
45
- url: str,
46
- archived: bool,
47
- in_trash: bool,
48
- emoji_icon: Optional[str] = None,
49
- properties: Optional[dict[str, Any]] = None,
50
- parent_database: Optional[NotionDatabase] = None,
51
- token: Optional[str] = None,
52
- ):
53
- """
54
- Initialize the page manager with all metadata.
55
- """
56
- self._page_id = page_id
57
- self._title = title
58
- self._url = url
59
- self._is_archived = archived
60
- self._is_in_trash = in_trash
61
- self._emoji_icon = emoji_icon
62
- self._properties = properties
63
- self._parent_database = parent_database
64
-
65
- self._client = NotionPageClient(token=token)
66
- self._block_client = NotionBlockClient(token=token)
67
- self._database_client = NotionDatabaseClient(token=token)
68
- self._comment_client = CommentClient(token=token)
69
- self._page_data = None
70
-
71
- self.block_element_registry = BlockRegistry.create_registry()
72
-
73
- self._page_content_writer = PageContentWriter(
74
- page_id=self._page_id,
75
- block_registry=self.block_element_registry,
76
- )
77
-
78
- self._page_content_deleting_service = PageContentDeletingService(
79
- page_id=self._page_id,
80
- block_registry=self.block_element_registry,
81
- )
82
-
83
- self._page_content_retriever = PageContentRetriever(
84
- block_registry=self.block_element_registry,
85
- )
86
-
87
- self.page_context_provider = self._setup_page_context_provider()
88
-
89
- @classmethod
90
- async def from_page_id(
91
- cls, page_id: str, token: Optional[str] = None
92
- ) -> NotionPage:
93
- """
94
- Create a NotionPage from a page ID.
95
- """
96
- formatted_id = format_uuid(page_id) or page_id
97
-
98
- async with NotionPageClient(token=token) as client:
99
- page_response = await client.get_page(formatted_id)
100
- return await cls._create_from_response(page_response, token)
101
-
102
- @classmethod
103
- async def from_page_name(
104
- cls, page_name: str, token: Optional[str] = None, min_similarity: float = 0.6
105
- ) -> NotionPage:
106
- """
107
- Create a NotionPage by finding a page with fuzzy matching on the title.
108
- Uses Notion's search API and fuzzy matching to find the best result.
109
- """
110
- from notionary.workspace import NotionWorkspace
111
-
112
- workspace = NotionWorkspace()
113
-
114
- try:
115
- search_results: list[NotionPage] = await workspace.search_pages(
116
- page_name, limit=5
117
- )
118
-
119
- if not search_results:
120
- cls.logger.warning("No pages found for name: %s", page_name)
121
- raise ValueError(f"No pages found for name: {page_name}")
122
-
123
- best_match = find_best_match(
124
- query=page_name,
125
- items=search_results,
126
- text_extractor=lambda page: page.title,
127
- min_similarity=min_similarity,
128
- )
129
-
130
- if not best_match:
131
- available_titles = [result.title for result in search_results[:5]]
132
- cls.logger.warning(
133
- "No sufficiently similar page found for '%s' (min: %.3f). Available: %s",
134
- page_name,
135
- min_similarity,
136
- available_titles,
137
- )
138
- raise ValueError(
139
- f"No sufficiently similar page found for '{page_name}'"
140
- )
141
-
142
- async with NotionPageClient(token=token) as client:
143
- page_response = await client.get_page(page_id=best_match.item.id)
144
- instance = await cls._create_from_response(
145
- page_response=page_response, token=token
146
- )
147
- return instance
148
-
149
- except Exception as e:
150
- cls.logger.error("Error finding page by name: %s", str(e))
151
- raise
152
-
153
- @classmethod
154
- async def from_url(cls, url: str, token: Optional[str] = None) -> NotionPage:
155
- """
156
- Create a NotionPage from a Notion page URL.
157
- """
158
- try:
159
- page_id = extract_uuid(url)
160
- if not page_id:
161
- raise ValueError(f"Could not extract page ID from URL: {url}")
162
-
163
- formatted_id = format_uuid(page_id) or page_id
164
-
165
- async with NotionPageClient(token=token) as client:
166
- page_response = await client.get_page(formatted_id)
167
- return await cls._create_from_response(page_response, token)
168
-
169
- except Exception as e:
170
- cls.logger.error("Error creating page from URL '%s': %s", url, str(e))
171
- raise
172
-
173
- @property
174
- def id(self) -> str:
175
- """
176
- Get the ID of the page.
177
- """
178
- return self._page_id
179
-
180
- @property
181
- def title(self) -> str:
182
- """
183
- Get the title of the page.
184
- """
185
- return self._title
186
-
187
- @property
188
- def url(self) -> str:
189
- """
190
- Get the URL of the page.
191
- If not set, generate it from the title and ID.
192
- """
193
- return self._url
194
-
195
- @property
196
- def emoji_icon(self) -> Optional[str]:
197
- """
198
- Get the emoji icon of the page.
199
- """
200
- return self._emoji_icon
201
-
202
- @property
203
- def properties(self) -> Optional[dict[str, Any]]:
204
- """
205
- Get the properties of the page.
206
- """
207
- return self._properties
208
-
209
- @property
210
- def is_archived(self) -> bool:
211
- return self._is_archived
212
-
213
- @property
214
- def is_in_trash(self) -> bool:
215
- return self._is_in_trash
216
-
217
- def get_prompt_information(self) -> str:
218
- markdown_syntax_builder = SyntaxPromptBuilder()
219
- return markdown_syntax_builder.build_concise_reference()
220
-
221
- async def get_comments(self) -> list[Comment]:
222
- return await self._comment_client.list_all_comments_for_page(
223
- page_id=self._page_id
224
- )
225
-
226
- async def post_comment(
227
- self,
228
- content: str,
229
- *,
230
- discussion_id: Optional[str] = None,
231
- rich_text: Optional[list[dict[str, Any]]] = None,
232
- ) -> Optional[Comment]:
233
- """
234
- Post a comment on this page.
235
-
236
- Args:
237
- content: The plain text content of the comment
238
- discussion_id: Optional discussion ID to reply to an existing discussion
239
- rich_text: Optional rich text formatting for the comment content
240
-
241
- Returns:
242
- Comment: The created comment object, or None if creation failed
243
- """
244
- try:
245
- # Use the comment client to create the comment
246
- comment = await self._comment_client.create_comment(
247
- page_id=self._page_id,
248
- content=content,
249
- discussion_id=discussion_id,
250
- rich_text=rich_text,
251
- )
252
- self.logger.info(f"Successfully posted comment on page '{self._title}'")
253
- return comment
254
- except Exception as e:
255
- self.logger.error(
256
- f"Failed to post comment on page '{self._title}': {str(e)}"
257
- )
258
- return None
259
-
260
- async def set_title(self, title: str) -> str:
261
- """
262
- Set the title of the page.
263
- """
264
- try:
265
- data = {
266
- "properties": {
267
- "title": {"title": [{"type": "text", "text": {"content": title}}]}
268
- }
269
- }
270
-
271
- await self._client.patch_page(self._page_id, data)
272
-
273
- self._title = title
274
- return title
275
-
276
- except Exception as e:
277
- self.logger.error("Error setting page title: %s", str(e))
278
-
279
- async def append_markdown(
280
- self,
281
- content: Union[
282
- str, Callable[[MarkdownBuilder], MarkdownBuilder], NotionContentSchema
283
- ],
284
- ) -> bool:
285
- """
286
- Append markdown content to the page using text, builder callback, MarkdownDocumentModel, or NotionContentSchema.
287
- """
288
- async with page_context(self.page_context_provider):
289
- result = await self._page_content_writer.append_markdown(
290
- content=content,
291
- )
292
- return result is not None
293
-
294
- async def replace_content(
295
- self,
296
- content: Union[
297
- str, Callable[[MarkdownBuilder], MarkdownBuilder], NotionContentSchema
298
- ],
299
- ) -> bool:
300
- """
301
- Replace the entire page content with new markdown content.
302
-
303
- Args:
304
- content: Either raw markdown text, a callback function that receives a MarkdownBuilder,
305
- a MarkdownDocumentModel, or a NotionContentSchema
306
-
307
- Returns:
308
- bool: True if successful, False otherwise
309
- """
310
- clear_result = await self._page_content_deleting_service.clear_page_content()
311
- if not clear_result:
312
- self.logger.error("Failed to clear page content before replacement")
313
-
314
- result = await self._page_content_writer.append_markdown(
315
- content=content,
316
- )
317
- return result is not None
318
-
319
- async def clear_page_content(self) -> str:
320
- """
321
- Clear all content from the page.
322
- """
323
- return await self._page_content_deleting_service.clear_page_content()
324
-
325
- async def get_text_content(self) -> str:
326
- """
327
- Get the text content of the page.
328
-
329
- Returns:
330
- str: The text content of the page.
331
- """
332
- blocks = await self._block_client.get_blocks_by_page_id_recursively(
333
- page_id=self._page_id
334
- )
335
- return await self._page_content_retriever.convert_to_markdown(blocks=blocks)
336
-
337
- async def set_emoji_icon(self, emoji: str) -> Optional[str]:
338
- """
339
- Sets the page icon to an emoji.
340
- """
341
- try:
342
- icon = {"type": "emoji", "emoji": emoji}
343
- page_response = await self._client.patch_page(
344
- page_id=self._page_id, data={"icon": icon}
345
- )
346
-
347
- self._emoji = page_response.icon.emoji
348
- return page_response.icon.emoji
349
- except Exception as e:
350
-
351
- self.logger.error(f"Error updating page emoji: {str(e)}")
352
- return None
353
-
354
- async def create_child_database(self, title: str) -> NotionDatabase:
355
- from notionary import NotionDatabase
356
-
357
- database_client = NotionDatabaseClient(token=self._client.token)
358
-
359
- create_database_response = await database_client.create_database(
360
- title=title,
361
- parent_page_id=self._page_id,
362
- )
363
-
364
- return await NotionDatabase.from_database_id(
365
- id=create_database_response.id, token=self._client.token
366
- )
367
-
368
- async def create_child_page(self, title: str) -> NotionPage:
369
- from notionary import NotionPage
370
-
371
- child_page_response = await self._client.create_page(
372
- parent_page_id=self._page_id,
373
- title=title,
374
- )
375
-
376
- return await NotionPage.from_page_id(
377
- page_id=child_page_response.id, token=self._client.token
378
- )
379
-
380
- async def set_external_icon(self, url: str) -> Optional[str]:
381
- """
382
- Sets the page icon to an external image.
383
- """
384
- try:
385
- icon = {"type": "external", "external": {"url": url}}
386
- page_response = await self._client.patch_page(
387
- page_id=self._page_id, data={"icon": icon}
388
- )
389
-
390
- # For external icons, we clear the emoji since we now have external icon
391
- self._emoji = None
392
- self.logger.info(f"Successfully updated page external icon to: {url}")
393
- return page_response.icon.external.url
394
-
395
- except Exception as e:
396
- self.logger.error(f"Error updating page external icon: {str(e)}")
397
- return None
398
-
399
- async def get_cover_url(self) -> Optional[str]:
400
- """
401
- Get the URL of the page cover image.
402
- """
403
- try:
404
- page_data = await self._client.get_page(self.id)
405
- if not page_data or not page_data.cover:
406
- return None
407
- if page_data.cover.type == "external":
408
- return page_data.cover.external.url
409
- except Exception as e:
410
- self.logger.error(f"Error fetching cover URL: {str(e)}")
411
- return None
412
-
413
- async def set_cover(self, external_url: str) -> Optional[str]:
414
- """
415
- Set the cover image for the page using an external URL.
416
- """
417
- data = {"cover": {"type": "external", "external": {"url": external_url}}}
418
- try:
419
- updated_page = await self._client.patch_page(self.id, data=data)
420
- return updated_page.cover.external.url
421
- except Exception as e:
422
- self.logger.error("Failed to set cover image: %s", str(e))
423
- return None
424
-
425
- async def set_random_gradient_cover(self) -> Optional[str]:
426
- """
427
- Set a random gradient as the page cover.
428
- """
429
- default_notion_covers = [
430
- f"https://www.notion.so/images/page-cover/gradients_{i}.png"
431
- for i in range(1, 10)
432
- ]
433
- random_cover_url = random.choice(default_notion_covers)
434
- return await self.set_cover(random_cover_url)
435
-
436
- async def get_property_value_by_name(self, property_name: str) -> Any:
437
- """
438
- Get the value of a specific property.
439
- """
440
- if property_name not in self._properties:
441
- self.logger.warning(
442
- "Property '%s' not found in page properties", property_name
443
- )
444
- return None
445
-
446
- property_schema: dict = self._properties.get(property_name)
447
-
448
- property_type = property_schema.get("type")
449
-
450
- if property_type == "relation":
451
- return await self._get_relation_property_values_by_name(property_name)
452
-
453
- return extract_property_value(property_schema)
454
-
455
- async def _get_relation_property_values_by_name(
456
- self, property_name: str
457
- ) -> list[str]:
458
- """
459
- Retrieve the titles of all related pages for a relation property.
460
- """
461
- page_property_schema = self._properties.get(property_name)
462
- relation_page_ids = [
463
- rel.get("id") for rel in page_property_schema.get("relation", [])
464
- ]
465
- notion_pages = [
466
- await NotionPage.from_page_id(page_id) for page_id in relation_page_ids
467
- ]
468
- return [page.title for page in notion_pages if page]
469
-
470
- async def get_options_for_property_by_name(self, property_name: str) -> list[str]:
471
- """
472
- Get the available options for a property (select, multi_select, status, relation).
473
- """
474
- if property_name not in self.properties:
475
- self.logger.warning(
476
- "Property '%s' not found in page properties", property_name
477
- )
478
- return []
479
-
480
- property_schema: dict = self.properties.get(property_name)
481
- property_type = property_schema.get("type")
482
-
483
- if property_type in ["select", "multi_select", "status"]:
484
- options = property_schema.get(property_type, {}).get("options", [])
485
- return [option.get("name", "") for option in options]
486
-
487
- if property_type == "relation" and self._parent_database:
488
- return await self._parent_database._get_relation_options(property_name)
489
-
490
- return []
491
-
492
- # Fix this for pages that do not ah
493
- async def set_property_value_by_name(self, property_name: str, value: Any) -> Any:
494
- """
495
- Set the value of a specific property by its name.
496
- """
497
- if not self._parent_database:
498
- return None
499
-
500
- property_type = self._parent_database.properties.get(property_name).get("type")
501
-
502
- if not property_type:
503
- return None
504
-
505
- if property_type == "relation":
506
- return await self.set_relation_property_values_by_name(
507
- property_name=property_name, page_titles=value
508
- )
509
-
510
- property_formatter = NotionPropertyFormatter()
511
- update_data = property_formatter.format_value(
512
- property_name=property_name, property_type=property_type, value=value
513
- )
514
-
515
- try:
516
- updated_page_response = await self._client.patch_page(
517
- page_id=self._page_id, data=update_data
518
- )
519
- self._properties = updated_page_response.properties
520
- return extract_property_value(self._properties.get(property_name))
521
- except Exception as e:
522
- self.logger.error(
523
- "Error setting property '%s' to value '%s': %s",
524
- property_name,
525
- value,
526
- str(e),
527
- )
528
- return None
529
-
530
- async def set_relation_property_values_by_name(
531
- self, property_name: str, page_titles: list[str]
532
- ) -> list[str]:
533
- """
534
- Add one or more relations to a relation property.
535
- """
536
- if not self._parent_database:
537
- return []
538
-
539
- property_type = self._parent_database.properties.get(property_name).get("type")
540
-
541
- # for direct calls
542
- if property_type != "relation":
543
- return []
544
-
545
- relation_pages = await asyncio.gather(
546
- *(
547
- NotionPage.from_page_name(page_name=page_title)
548
- for page_title in page_titles
549
- )
550
- )
551
-
552
- relation_page_ids = [page.id for page in relation_pages]
553
-
554
- property_formatter = NotionPropertyFormatter()
555
-
556
- update_data = property_formatter.format_value(
557
- property_name=property_name,
558
- property_type="relation",
559
- value=relation_page_ids,
560
- )
561
-
562
- try:
563
- updated_page_response = await self._client.patch_page(
564
- page_id=self._page_id, data=update_data
565
- )
566
- self._properties = updated_page_response.properties
567
- return page_titles
568
- except Exception as e:
569
- self.logger.error(
570
- "Error setting property '%s' to value '%s': %s",
571
- property_name,
572
- page_titles,
573
- str(e),
574
- )
575
- return []
576
-
577
- async def archive(self) -> bool:
578
- """
579
- Archive the page by moving it to the trash.
580
- """
581
- try:
582
- result = await self._client.patch_page(
583
- page_id=self._page_id, data={"archived": True}
584
- )
585
- return result is not None
586
- except Exception as e:
587
- self.logger.error("Error archiving page %s: %s", self._page_id, str(e))
588
- return False
589
-
590
- @classmethod
591
- async def _create_from_response(
592
- cls,
593
- page_response: NotionPageResponse,
594
- token: Optional[str],
595
- ) -> NotionPage:
596
- """
597
- Create NotionPage instance from API response.
598
- """
599
- from notionary.database.database import NotionDatabase
600
-
601
- title = cls._extract_title(page_response)
602
- emoji = cls._extract_emoji(page_response)
603
- parent_database_id = cls._extract_parent_database_id(page_response)
604
-
605
- parent_database = (
606
- await NotionDatabase.from_database_id(id=parent_database_id, token=token)
607
- if parent_database_id
608
- else None
609
- )
610
-
611
- instance = cls(
612
- page_id=page_response.id,
613
- title=title,
614
- url=page_response.url,
615
- emoji_icon=emoji,
616
- archived=page_response.archived,
617
- in_trash=page_response.in_trash,
618
- properties=page_response.properties,
619
- parent_database=parent_database,
620
- token=token,
621
- )
622
-
623
- cls.logger.info("Created page manager: '%s' (ID: %s)", title, page_response.id)
624
- return instance
625
-
626
- @staticmethod
627
- def _extract_title(page_response: NotionPageResponse) -> str:
628
- """Extract title from page response. Returns empty string if not found."""
629
-
630
- if not page_response.properties:
631
- return ""
632
-
633
- title_property = next(
634
- (
635
- prop
636
- for prop in page_response.properties.values()
637
- if isinstance(prop, dict) and prop.get("type") == "title"
638
- ),
639
- None,
640
- )
641
-
642
- if not title_property or "title" not in title_property:
643
- return ""
644
-
645
- try:
646
- title_parts = title_property["title"]
647
- return "".join(part.get("plain_text", "") for part in title_parts)
648
- except (KeyError, TypeError, AttributeError):
649
- return ""
650
-
651
- @staticmethod
652
- def _extract_emoji(page_response: NotionPageResponse) -> Optional[str]:
653
- """Extract emoji from database response."""
654
- if not page_response.icon:
655
- return None
656
-
657
- if page_response.icon.type == "emoji":
658
- return page_response.icon.emoji
659
-
660
- return None
661
-
662
- @staticmethod
663
- def _extract_parent_database_id(page_response: NotionPageResponse) -> Optional[str]:
664
- """Extract parent database ID from page response."""
665
- parent = page_response.parent
666
- if isinstance(parent, DatabaseParent):
667
- return parent.database_id
668
-
669
- def _setup_page_context_provider(self) -> PageContextProvider:
670
- return PageContextProvider(
671
- page_id=self._page_id,
672
- database_client=NotionDatabaseClient(token=self._client.token),
673
- file_upload_client=NotionFileUploadClient(),
674
- )