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