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,320 +0,0 @@
1
- from urllib.parse import urlparse
2
- from pathlib import Path
3
- from typing import Optional
4
- from notionary.file_upload import NotionFileUploadClient
5
- from notionary.file_upload.models import UploadMode
6
- from notionary.page.page_context import get_page_context
7
- from notionary.util.logging_mixin import LoggingMixin
8
-
9
-
10
- # TOOD: Hier überlegen wirklich nur was common ist hier in den mixin ansonstne dediziert handeln.
11
- class FileUploadMixin(LoggingMixin):
12
- """
13
- Mixin to add file upload functionality to all media block elements.
14
-
15
- Supports uploading local files for:
16
- - file blocks
17
- - image blocks
18
- - pdf blocks
19
- - audio blocks
20
- - video blocks
21
- """
22
-
23
- @classmethod
24
- def _get_file_upload_client(cls) -> NotionFileUploadClient:
25
- """Get the file upload client from the current page context."""
26
- context = get_page_context()
27
- return context.file_upload_client
28
-
29
- @classmethod
30
- def _is_local_file_path(cls, path: str) -> bool:
31
- """Determine if the path is a local file rather than a URL."""
32
- if path.startswith(("http://", "https://", "ftp://")):
33
- return False
34
-
35
- return (
36
- "/" in path
37
- or "\\" in path
38
- or path.startswith("./")
39
- or path.startswith("../")
40
- or ":" in path[:3]
41
- ) # Windows drive letters like C:
42
-
43
- @classmethod
44
- def _should_upload_file(cls, path: str, expected_category: str = "file") -> bool:
45
- """
46
- Determine if a path should be uploaded vs used as external URL.
47
-
48
- Args:
49
- path: File path or URL
50
- expected_category: Expected file category
51
-
52
- Returns:
53
- True if file should be uploaded
54
- """
55
- if not cls._is_local_file_path(path):
56
- return False
57
-
58
- file_path = Path(path)
59
- if not file_path.exists():
60
- return False
61
-
62
- return True
63
-
64
- @classmethod
65
- def _get_content_type(cls, file_path: Path) -> str:
66
- """Get MIME type based on file extension."""
67
- extension_map = {
68
- # Documents
69
- ".pdf": "application/pdf",
70
- ".doc": "application/msword",
71
- ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
72
- ".xls": "application/vnd.ms-excel",
73
- ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
74
- ".ppt": "application/vnd.ms-powerpoint",
75
- ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
76
- ".txt": "text/plain",
77
- ".csv": "text/csv",
78
- ".json": "application/json",
79
- ".xml": "application/xml",
80
- ".zip": "application/zip",
81
- ".rar": "application/vnd.rar",
82
- ".7z": "application/x-7z-compressed",
83
- # Images
84
- ".png": "image/png",
85
- ".jpg": "image/jpeg",
86
- ".jpeg": "image/jpeg",
87
- ".gif": "image/gif",
88
- ".webp": "image/webp",
89
- ".bmp": "image/bmp",
90
- ".tiff": "image/tiff",
91
- ".tif": "image/tiff",
92
- ".svg": "image/svg+xml",
93
- ".ico": "image/x-icon",
94
- ".heic": "image/heic",
95
- ".heif": "image/heif",
96
- # Audio
97
- ".mp3": "audio/mpeg",
98
- ".wav": "audio/wav",
99
- ".ogg": "audio/ogg",
100
- ".m4a": "audio/mp4",
101
- ".aac": "audio/aac",
102
- ".flac": "audio/flac",
103
- ".wma": "audio/x-ms-wma",
104
- ".opus": "audio/opus",
105
- # Video
106
- ".mp4": "video/mp4",
107
- ".avi": "video/x-msvideo",
108
- ".mov": "video/quicktime",
109
- ".wmv": "video/x-ms-wmv",
110
- ".flv": "video/x-flv",
111
- ".webm": "video/webm",
112
- ".mkv": "video/x-matroska",
113
- ".m4v": "video/mp4",
114
- ".3gp": "video/3gpp",
115
- }
116
-
117
- suffix = file_path.suffix.lower()
118
- return extension_map.get(suffix, "application/octet-stream")
119
-
120
- @classmethod
121
- def _get_file_category(cls, file_path: Path) -> str:
122
- """
123
- Determine the category of file based on extension.
124
-
125
- Returns:
126
- One of: 'image', 'audio', 'video', 'pdf', 'document', 'archive', 'other'
127
- """
128
- suffix = file_path.suffix.lower()
129
-
130
- # Define extension sets for each category
131
- extension_categories = {
132
- "image": {
133
- ".png",
134
- ".jpg",
135
- ".jpeg",
136
- ".gif",
137
- ".webp",
138
- ".bmp",
139
- ".tiff",
140
- ".tif",
141
- ".svg",
142
- ".ico",
143
- ".heic",
144
- ".heif",
145
- },
146
- "audio": {".mp3", ".wav", ".ogg", ".m4a", ".aac", ".flac", ".wma", ".opus"},
147
- "video": {
148
- ".mp4",
149
- ".avi",
150
- ".mov",
151
- ".wmv",
152
- ".flv",
153
- ".webm",
154
- ".mkv",
155
- ".m4v",
156
- ".3gp",
157
- },
158
- "pdf": {".pdf"},
159
- "document": {
160
- ".doc",
161
- ".docx",
162
- ".xls",
163
- ".xlsx",
164
- ".ppt",
165
- ".pptx",
166
- ".txt",
167
- ".csv",
168
- ".json",
169
- ".xml",
170
- },
171
- "archive": {".zip", ".rar", ".7z"},
172
- }
173
-
174
- # Find matching category
175
- for category, extensions in extension_categories.items():
176
- if suffix in extensions:
177
- return category
178
-
179
- return "other"
180
-
181
- @classmethod
182
- def _is_supported_file_type(cls, file_path: Path, expected_category: str) -> bool:
183
- """
184
- Check if the file type matches the expected category.
185
-
186
- Args:
187
- file_path: Path to the file
188
- expected_category: Expected category ('image', 'audio', 'video', 'pdf', 'file')
189
-
190
- Returns:
191
- True if file type matches expected category
192
- """
193
- # 'file' category accepts any file type
194
- if expected_category == "file":
195
- return True
196
-
197
- actual_category = cls._get_file_category(file_path)
198
- return actual_category == expected_category
199
-
200
- @classmethod
201
- async def _upload_local_file(
202
- cls, file_path_str: str, expected_category: str = "file"
203
- ) -> Optional[str]:
204
- """
205
- Upload a local file and return the file upload ID.
206
-
207
- Args:
208
- file_path_str: String path to the local file
209
- expected_category: Expected file category for validation
210
-
211
- Returns:
212
- File upload ID if successful, None otherwise
213
- """
214
- try:
215
- file_upload_client = cls._get_file_upload_client()
216
- file_path = Path(file_path_str)
217
-
218
- # Pre-upload validation
219
- if not cls._validate_file_for_upload(file_path, expected_category):
220
- return None
221
-
222
- # Get file metadata
223
- file_size = file_path.stat().st_size
224
- content_type = cls._get_content_type(file_path)
225
-
226
- cls.logger.info(
227
- f"Uploading {expected_category} file: {file_path.name} "
228
- f"({file_size} bytes, {content_type})"
229
- )
230
-
231
- # Create and execute upload
232
- upload_id = await cls._execute_upload(
233
- file_upload_client, file_path, content_type, file_size
234
- )
235
-
236
- if upload_id:
237
- cls.logger.info(
238
- f"File upload completed: {upload_id} ({file_path.name})"
239
- )
240
-
241
- return upload_id
242
-
243
- except Exception as e:
244
- cls.logger.error(
245
- f"Error uploading {expected_category} file {file_path_str}: {e}"
246
- )
247
- cls.logger.debug("Upload error traceback:", exc_info=True)
248
- return None
249
-
250
- @classmethod
251
- def _validate_file_for_upload(cls, file_path: Path, expected_category: str) -> bool:
252
- """Validate file exists and type matches expected category."""
253
- # Check if file exists
254
- if not file_path.exists():
255
- cls.logger.error(f"File not found: {file_path}")
256
- return False
257
-
258
- # Validate file type if needed
259
- if not cls._is_supported_file_type(file_path, expected_category):
260
- actual_category = cls._get_file_category(file_path)
261
- cls.logger.warning(
262
- f"File type mismatch: expected {expected_category}, "
263
- f"got {actual_category} for {file_path} - proceeding anyway"
264
- )
265
-
266
- return True
267
-
268
- @classmethod
269
- async def _execute_upload(
270
- cls,
271
- file_upload_client: NotionFileUploadClient,
272
- file_path: Path,
273
- content_type: str,
274
- file_size: int,
275
- ) -> Optional[str]:
276
- """Execute the actual file upload process."""
277
- # Step 1: Create file upload
278
- upload_response = await file_upload_client.create_file_upload(
279
- filename=file_path.name,
280
- content_type=content_type,
281
- content_length=file_size,
282
- mode=UploadMode.SINGLE_PART,
283
- )
284
-
285
- if not upload_response:
286
- cls.logger.error(f"Failed to create file upload for {file_path.name}")
287
- return None
288
-
289
- cls.logger.debug(f"Created file upload with ID: {upload_response.id}")
290
-
291
- # Step 2: Send file content
292
- success = await file_upload_client.send_file_from_path(
293
- file_upload_id=upload_response.id, file_path=file_path
294
- )
295
-
296
- if not success:
297
- cls.logger.error(f"Failed to send file content for {file_path.name}")
298
- return None
299
-
300
- cls.logger.debug(f"File content sent successfully for {file_path.name}")
301
- return upload_response.id
302
-
303
- @classmethod
304
- def _get_upload_error_message(
305
- cls, file_path_str: str, expected_category: str
306
- ) -> str:
307
- """Get a user-friendly error message for upload failures."""
308
- file_path = Path(file_path_str)
309
-
310
- if not file_path.exists():
311
- return f"File not found: {file_path_str}"
312
-
313
- actual_category = cls._get_file_category(file_path)
314
- if actual_category != expected_category and expected_category != "file":
315
- return (
316
- f"Invalid file type for {expected_category} block: "
317
- f"{file_path.suffix} (detected as {actual_category})"
318
- )
319
-
320
- return f"Failed to upload {expected_category} file: {file_path_str}"
@@ -1,174 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import TYPE_CHECKING, Any, Literal, Optional, Union
4
-
5
- from pydantic import BaseModel
6
-
7
- from notionary.blocks.types import BlockType
8
-
9
- if TYPE_CHECKING:
10
- from notionary.blocks.bookmark import BookmarkBlock, CreateBookmarkBlock
11
- from notionary.blocks.breadcrumbs import BreadcrumbBlock, CreateBreadcrumbBlock
12
- from notionary.blocks.bulleted_list import (
13
- BulletedListItemBlock,
14
- CreateBulletedListItemBlock,
15
- )
16
- from notionary.blocks.callout import CalloutBlock, CreateCalloutBlock
17
- from notionary.blocks.child_page import ChildPageBlock, CreateChildPageBlock
18
- from notionary.blocks.code import CodeBlock, CreateCodeBlock
19
- from notionary.blocks.column import (
20
- ColumnBlock,
21
- ColumnListBlock,
22
- CreateColumnBlock,
23
- CreateColumnListBlock,
24
- )
25
- from notionary.blocks.divider import CreateDividerBlock, DividerBlock
26
- from notionary.blocks.embed import CreateEmbedBlock, EmbedBlock
27
- from notionary.blocks.equation import CreateEquationBlock, EquationBlock
28
- from notionary.blocks.file import CreateFileBlock, FileBlock
29
- from notionary.blocks.heading import (
30
- CreateHeading1Block,
31
- CreateHeading2Block,
32
- CreateHeading3Block,
33
- HeadingBlock,
34
- )
35
- from notionary.blocks.image_block import CreateImageBlock
36
- from notionary.blocks.numbered_list import (
37
- CreateNumberedListItemBlock,
38
- NumberedListItemBlock,
39
- )
40
- from notionary.blocks.paragraph import CreateParagraphBlock, ParagraphBlock
41
- from notionary.blocks.pdf import CreatePdfBlock
42
- from notionary.blocks.quote import CreateQuoteBlock, QuoteBlock
43
- from notionary.blocks.table import CreateTableBlock, TableBlock, TableRowBlock
44
- from notionary.blocks.table_of_contents import (
45
- CreateTableOfContentsBlock,
46
- TableOfContentsBlock,
47
- )
48
- from notionary.blocks.todo import CreateToDoBlock, ToDoBlock
49
- from notionary.blocks.toggle import CreateToggleBlock, ToggleBlock
50
- from notionary.blocks.video import CreateVideoBlock
51
- from notionary.blocks.child_database import ChildDatabaseBlock
52
-
53
-
54
- class BlockChildrenResponse(BaseModel):
55
- object: Literal["list"]
56
- results: list["Block"]
57
- next_cursor: Optional[str] = None
58
- has_more: bool
59
- type: Literal["block"]
60
- block: dict = {}
61
- request_id: str
62
-
63
-
64
- class PageParent(BaseModel):
65
- type: Literal["page_id"]
66
- page_id: str
67
-
68
-
69
- class DatabaseParent(BaseModel):
70
- type: Literal["database_id"]
71
- database_id: str
72
-
73
-
74
- class BlockParent(BaseModel):
75
- type: Literal["block_id"]
76
- block_id: str
77
-
78
-
79
- class WorkspaceParent(BaseModel):
80
- type: Literal["workspace"]
81
- workspace: bool = True
82
-
83
-
84
- ParentObject = Union[PageParent, DatabaseParent, BlockParent, WorkspaceParent]
85
-
86
-
87
- class PartialUser(BaseModel):
88
- object: Literal["user"]
89
- id: str
90
-
91
-
92
- class Block(BaseModel):
93
- object: Literal["block"]
94
- id: str
95
- parent: Optional[ParentObject] = None
96
- type: BlockType
97
- created_time: str
98
- last_edited_time: str
99
- created_by: PartialUser
100
- last_edited_by: PartialUser
101
- archived: bool = False
102
- in_trash: bool = False
103
- has_children: bool = False
104
-
105
- children: Optional[list[Block]] = None
106
-
107
- # Block type-specific content (only one will be populated based on type)
108
- audio: Optional[FileBlock] = None
109
- bookmark: Optional[BookmarkBlock] = None
110
- breadcrumb: Optional[BreadcrumbBlock] = None
111
- bulleted_list_item: Optional[BulletedListItemBlock] = None
112
- callout: Optional[CalloutBlock] = None
113
- child_page: Optional[ChildPageBlock] = None
114
- code: Optional[CodeBlock] = None
115
- column_list: Optional[ColumnListBlock] = None
116
- column: Optional[ColumnBlock] = None
117
- divider: Optional[DividerBlock] = None
118
- embed: Optional[EmbedBlock] = None
119
- equation: Optional[EquationBlock] = None
120
- file: Optional[FileBlock] = None
121
- heading_1: Optional[HeadingBlock] = None
122
- heading_2: Optional[HeadingBlock] = None
123
- heading_3: Optional[HeadingBlock] = None
124
- image: Optional[FileBlock] = None
125
- numbered_list_item: Optional[NumberedListItemBlock] = None
126
- paragraph: Optional[ParagraphBlock] = None
127
- quote: Optional[QuoteBlock] = None
128
- table: Optional[TableBlock] = None
129
- table_row: Optional[TableRowBlock] = None
130
- to_do: Optional[ToDoBlock] = None
131
- toggle: Optional[ToggleBlock] = None
132
- video: Optional[FileBlock] = None
133
- pdf: Optional[FileBlock] = None
134
- table_of_contents: Optional[TableOfContentsBlock] = None
135
- child_database: Optional[ChildDatabaseBlock] = None
136
-
137
- def get_block_content(self) -> Optional[Any]:
138
- """Get the content object for this block based on its type."""
139
- return getattr(self, self.type, None)
140
-
141
-
142
- if TYPE_CHECKING:
143
- BlockCreateRequest = Union[
144
- CreateBookmarkBlock,
145
- CreateBreadcrumbBlock,
146
- CreateBulletedListItemBlock,
147
- CreateCalloutBlock,
148
- CreateChildPageBlock,
149
- CreateCodeBlock,
150
- CreateColumnListBlock,
151
- CreateColumnBlock,
152
- CreateDividerBlock,
153
- CreateEmbedBlock,
154
- CreateEquationBlock,
155
- CreateFileBlock,
156
- CreateHeading1Block,
157
- CreateHeading2Block,
158
- CreateHeading3Block,
159
- CreateImageBlock,
160
- CreateNumberedListItemBlock,
161
- CreateParagraphBlock,
162
- CreateQuoteBlock,
163
- CreateToDoBlock,
164
- CreateToggleBlock,
165
- CreateVideoBlock,
166
- CreateTableOfContentsBlock,
167
- CreatePdfBlock,
168
- CreateTableBlock,
169
- ]
170
- BlockCreateResult = Union[BlockCreateRequest]
171
- else:
172
- # at runtime there are no typings anyway
173
- BlockCreateRequest = Any
174
- BlockCreateResult = Any
@@ -1,16 +0,0 @@
1
- from notionary.blocks.numbered_list.numbered_list_element import NumberedListElement
2
- from notionary.blocks.numbered_list.numbered_list_markdown_node import (
3
- NumberedListMarkdownNode,
4
- )
5
- from notionary.blocks.numbered_list.numbered_list_models import (
6
- CreateNumberedListItemBlock,
7
- NumberedListItemBlock,
8
- )
9
-
10
- __all__ = [
11
- "NumberedListElement",
12
- "NumberedListItemBlock",
13
- "CreateNumberedListItemBlock",
14
- "NumberedListMarkdownNode",
15
- "NumberedListMarkdownBlockParams",
16
- ]
@@ -1,65 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- from typing import Optional
5
-
6
- from notionary.blocks.base_block_element import BaseBlockElement
7
- from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
8
- from notionary.blocks.models import Block, BlockCreateResult, BlockType
9
- from notionary.blocks.numbered_list.numbered_list_models import (
10
- CreateNumberedListItemBlock,
11
- NumberedListItemBlock,
12
- )
13
- from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
14
- from notionary.blocks.types import BlockColor
15
-
16
-
17
- class NumberedListElement(BaseBlockElement):
18
- """Converts between Markdown numbered lists and Notion numbered list items."""
19
-
20
- PATTERN = re.compile(r"^\s*(\d+)\.\s+(.+)$")
21
-
22
- @classmethod
23
- def match_notion(cls, block: Block) -> bool:
24
- return block.type == BlockType.NUMBERED_LIST_ITEM and block.numbered_list_item
25
-
26
- @classmethod
27
- async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
28
- """Convert markdown numbered list item to Notion NumberedListItemBlock."""
29
- match = cls.PATTERN.match(text.strip())
30
- if not match:
31
- return None
32
-
33
- content = match.group(2)
34
- rich_text = await TextInlineFormatter.parse_inline_formatting(content)
35
-
36
- numbered_list_content = NumberedListItemBlock(
37
- rich_text=rich_text, color=BlockColor.DEFAULT
38
- )
39
- return CreateNumberedListItemBlock(numbered_list_item=numbered_list_content)
40
-
41
- # FIX: Roundtrip conversions will never work this way here
42
- @classmethod
43
- async def notion_to_markdown(cls, block: Block) -> Optional[str]:
44
- if block.type != BlockType.NUMBERED_LIST_ITEM or not block.numbered_list_item:
45
- return None
46
-
47
- rich = block.numbered_list_item.rich_text
48
- content = await TextInlineFormatter.extract_text_with_formatting(rich)
49
- return f"1. {content}"
50
-
51
- @classmethod
52
- def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
53
- """Get system prompt information for numbered list blocks."""
54
- return BlockElementMarkdownInformation(
55
- block_type=cls.__name__,
56
- description="Numbered list items create ordered lists with sequential numbering",
57
- syntax_examples=[
58
- "1. First item",
59
- "2. Second item",
60
- "3. Third item",
61
- "1. Item with **bold text**",
62
- "1. Item with *italic text*",
63
- ],
64
- usage_guidelines="Use numbers followed by periods to create ordered lists. Supports inline formatting like bold, italic, and links. Numbering is automatically handled by Notion.",
65
- )
@@ -1,17 +0,0 @@
1
- from notionary.blocks.markdown.markdown_node import MarkdownNode
2
-
3
-
4
- class NumberedListMarkdownNode(MarkdownNode):
5
- """
6
- Enhanced NumberedList node with Pydantic integration.
7
- Programmatic interface for creating Markdown numbered list items.
8
- Example:
9
- 1. First step
10
- 2. Second step
11
- 3. Third step
12
- """
13
-
14
- texts: list[str]
15
-
16
- def to_markdown(self) -> str:
17
- return "\n".join(f"{i + 1}. {text}" for i, text in enumerate(self.texts))
@@ -1,17 +0,0 @@
1
- from pydantic import BaseModel
2
- from typing import Literal, Optional
3
-
4
- from notionary.blocks.models import Block
5
- from notionary.blocks.rich_text.rich_text_models import RichTextObject
6
- from notionary.blocks.types import BlockColor
7
-
8
-
9
- class NumberedListItemBlock(BaseModel):
10
- rich_text: list[RichTextObject]
11
- color: BlockColor = BlockColor.DEFAULT
12
- children: Optional[list[Block]] = None
13
-
14
-
15
- class CreateNumberedListItemBlock(BaseModel):
16
- type: Literal["numbered_list_item"] = "numbered_list_item"
17
- numbered_list_item: NumberedListItemBlock
@@ -1,15 +0,0 @@
1
- from notionary.blocks.paragraph.paragraph_element import ParagraphElement
2
- from notionary.blocks.paragraph.paragraph_markdown_node import (
3
- ParagraphMarkdownNode,
4
- )
5
- from notionary.blocks.paragraph.paragraph_models import (
6
- CreateParagraphBlock,
7
- ParagraphBlock,
8
- )
9
-
10
- __all__ = [
11
- "ParagraphElement",
12
- "ParagraphBlock",
13
- "CreateParagraphBlock",
14
- "ParagraphMarkdownNode",
15
- ]
@@ -1,58 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Optional
4
-
5
- from notionary.blocks.base_block_element import BaseBlockElement
6
- from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
7
- from notionary.blocks.models import Block, BlockCreateResult
8
- from notionary.blocks.paragraph.paragraph_models import (
9
- CreateParagraphBlock,
10
- ParagraphBlock,
11
- )
12
- from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
13
- from notionary.blocks.types import BlockColor, BlockType
14
-
15
-
16
- class ParagraphElement(BaseBlockElement):
17
- """
18
- Handles conversion between Markdown paragraphs and Notion paragraph blocks.
19
- """
20
-
21
- @classmethod
22
- def match_notion(cls, block: Block) -> bool:
23
- return block.type == "paragraph" and block.paragraph
24
-
25
- @classmethod
26
- async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
27
- """Convert markdown text to a Notion ParagraphBlock."""
28
- if not text.strip():
29
- return None
30
-
31
- rich = await TextInlineFormatter.parse_inline_formatting(text)
32
-
33
- paragraph_content = ParagraphBlock(rich_text=rich, color=BlockColor.DEFAULT)
34
- return CreateParagraphBlock(paragraph=paragraph_content)
35
-
36
- @classmethod
37
- async def notion_to_markdown(cls, block: Block) -> Optional[str]:
38
- if block.type != BlockType.PARAGRAPH or not block.paragraph:
39
- return None
40
-
41
- rich_list = block.paragraph.rich_text
42
- markdown = await TextInlineFormatter.extract_text_with_formatting(rich_list)
43
- return markdown or None
44
-
45
- @classmethod
46
- def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
47
- """Get system prompt information for paragraph blocks."""
48
- return BlockElementMarkdownInformation(
49
- block_type=cls.__name__,
50
- description="Paragraph blocks contain regular text content with optional inline formatting",
51
- syntax_examples=[
52
- "This is a simple paragraph.",
53
- "Paragraph with **bold text** and *italic text*.",
54
- "Paragraph with [link](https://example.com) and `code`.",
55
- "Multiple sentences in one paragraph. Each sentence flows naturally.",
56
- ],
57
- usage_guidelines="Use for regular text content. Supports inline formatting: **bold**, *italic*, `code`, [links](url). Default block type for plain text.",
58
- )