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,98 +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.embed.embed_models import CreateEmbedBlock, EmbedBlock
8
- from notionary.blocks.file.file_element_models import (
9
- ExternalFile,
10
- FileUploadFile,
11
- NotionHostedFile,
12
- )
13
- from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
14
- from notionary.blocks.models import Block, BlockCreateResult, BlockType
15
- from notionary.blocks.rich_text.rich_text_models import RichTextObject
16
- from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
17
-
18
-
19
- class EmbedElement(BaseBlockElement):
20
- """
21
- Handles conversion between Markdown embeds and Notion embed blocks.
22
-
23
- Markdown embed syntax:
24
- - [embed](https://example.com) - URL only
25
- - [embed](https://example.com "Caption") - URL + caption
26
- """
27
-
28
- PATTERN = re.compile(
29
- r"^\[embed\]\(" # prefix
30
- r"(https?://[^\s\"]+)" # URL
31
- r"(?:\s+\"([^\"]+)\")?" # optional caption
32
- r"\)$"
33
- )
34
-
35
- @classmethod
36
- def match_notion(cls, block: Block) -> bool:
37
- return block.type == BlockType.EMBED and block.embed
38
-
39
- @classmethod
40
- async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
41
- """Convert markdown embed syntax to Notion EmbedBlock."""
42
- match = cls.PATTERN.match(text.strip())
43
- if not match:
44
- return None
45
-
46
- url, rich_text = match.group(1), match.group(2) or ""
47
-
48
- # Build EmbedBlock
49
- embed_block = EmbedBlock(url=url, caption=[])
50
- if rich_text.strip():
51
- rich_text_obj = RichTextObject.from_plain_text(rich_text.strip())
52
- embed_block.caption = [rich_text_obj]
53
-
54
- return CreateEmbedBlock(embed=embed_block)
55
-
56
- @classmethod
57
- async def notion_to_markdown(cls, block: Block) -> Optional[str]:
58
- if block.type != BlockType.EMBED or not block.embed:
59
- return None
60
-
61
- fo = block.embed
62
-
63
- if isinstance(fo, (ExternalFile, NotionHostedFile)):
64
- url = fo.url
65
- elif isinstance(fo, FileUploadFile):
66
- return None
67
- else:
68
- return None
69
-
70
- if not fo.caption:
71
- return f"[embed]({url})"
72
-
73
- text_parts = []
74
- for rt in fo.caption:
75
- if rt.plain_text:
76
- text_parts.append(rt.plain_text)
77
- else:
78
- formatted_text = await TextInlineFormatter.extract_text_with_formatting(
79
- [rt]
80
- )
81
- text_parts.append(formatted_text)
82
- text = "".join(text_parts)
83
-
84
- return f'[embed]({url} "{text}")'
85
-
86
- @classmethod
87
- def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
88
- """Get system prompt information for embed blocks."""
89
- return BlockElementMarkdownInformation(
90
- block_type=cls.__name__,
91
- description="Embed blocks display interactive content from external URLs like videos, maps, or widgets",
92
- syntax_examples=[
93
- "[embed](https://youtube.com/watch?v=123)",
94
- '[embed](https://maps.google.com/location "Map Location")',
95
- '[embed](https://codepen.io/pen/123 "Interactive Demo")',
96
- ],
97
- usage_guidelines="Use for embedding interactive content that supports iframe embedding. URL must be from a supported platform. Caption describes the embedded content.",
98
- )
@@ -1,19 +0,0 @@
1
- from typing import Optional
2
-
3
- from notionary.blocks.markdown.markdown_node import MarkdownNode
4
-
5
-
6
- class EmbedMarkdownNode(MarkdownNode):
7
- """
8
- Enhanced Embed node with Pydantic integration.
9
- Programmatic interface for creating Notion-style Markdown embed blocks.
10
- Example: [embed](https://example.com "Optional caption")
11
- """
12
-
13
- url: str
14
- caption: Optional[str] = None
15
-
16
- def to_markdown(self) -> str:
17
- if self.caption:
18
- return f'[embed]({self.url} "{self.caption}")'
19
- return f"[embed]({self.url})"
@@ -1,14 +0,0 @@
1
- from pydantic import BaseModel, Field
2
- from typing_extensions import Literal
3
-
4
- from notionary.blocks.rich_text.rich_text_models import RichTextObject
5
-
6
-
7
- class EmbedBlock(BaseModel):
8
- url: str
9
- caption: list[RichTextObject] = Field(default_factory=list)
10
-
11
-
12
- class CreateEmbedBlock(BaseModel):
13
- type: Literal["embed"] = "embed"
14
- embed: EmbedBlock
@@ -1,13 +0,0 @@
1
- from notionary.blocks.equation.equation_element import EquationElement
2
- from notionary.blocks.equation.equation_element_markdown_node import (
3
- EquationMarkdownNode,
4
- )
5
- from notionary.blocks.equation.equation_models import CreateEquationBlock, EquationBlock
6
-
7
- __all__ = [
8
- "EquationElement",
9
- "EquationBlock",
10
- "CreateEquationBlock",
11
- "EquationMarkdownNode",
12
- "EquationMarkdownBlockParams",
13
- ]
@@ -1,133 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- import textwrap
5
- from typing import Optional
6
-
7
- from notionary.blocks.base_block_element import BaseBlockElement
8
- from notionary.blocks.equation.equation_models import CreateEquationBlock, EquationBlock
9
- from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
10
- from notionary.blocks.models import Block, BlockCreateResult
11
- from notionary.blocks.types import BlockType
12
-
13
-
14
- class EquationElement(BaseBlockElement):
15
- """
16
- Supports standard Markdown equation syntax:
17
-
18
- - $$E = mc^2$$ # simple equations
19
- - $$E = mc^2 + \\frac{a}{b}$$ # complex equations with LaTeX
20
-
21
- Uses $$...$$ parsing for block equations.
22
- """
23
-
24
- _EQUATION_PATTERN = re.compile(
25
- r"^\$\$\s*(?P<expression>.*?)\s*\$\$$",
26
- re.DOTALL,
27
- )
28
-
29
- @classmethod
30
- def match_notion(cls, block: Block) -> bool:
31
- return block.type == BlockType.EQUATION and block.equation
32
-
33
- @classmethod
34
- async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
35
- input_text = text.strip()
36
-
37
- equation_match = cls._EQUATION_PATTERN.match(input_text)
38
- if not equation_match:
39
- return None
40
-
41
- expression = equation_match.group("expression").strip()
42
- if not expression:
43
- return None
44
-
45
- return CreateEquationBlock(equation=EquationBlock(expression=expression))
46
-
47
- @classmethod
48
- def create_from_markdown_block(
49
- cls, opening_line: str, equation_lines: list[str]
50
- ) -> BlockCreateResult:
51
- """
52
- Create a complete equation block from markdown components.
53
- Handles multiline equations like:
54
- $$
55
- some
56
- inline formula here
57
- $$
58
-
59
- Automatically handles:
60
- - Indentation removal from multiline strings
61
- - Single backslash conversion to double backslash for LaTeX line breaks
62
- """
63
- # Check if opening line is just $$
64
- if opening_line.strip() != "$$":
65
- return None
66
-
67
- # Process equation lines if any exist
68
- if equation_lines:
69
- # Remove common indentation from all lines
70
- raw_content = "\n".join(equation_lines)
71
- dedented_content = textwrap.dedent(raw_content)
72
-
73
- # Fix single backslashes at line ends for LaTeX line breaks
74
- fixed_lines = cls._fix_latex_line_breaks(dedented_content.splitlines())
75
- expression = "\n".join(fixed_lines).strip()
76
-
77
- if expression:
78
- return CreateEquationBlock(
79
- equation=EquationBlock(expression=expression)
80
- )
81
-
82
- return None
83
-
84
- @classmethod
85
- def _fix_latex_line_breaks(cls, lines: list[str]) -> list[str]:
86
- """
87
- Fix lines that end with single backslashes by converting them to double backslashes.
88
- This makes LaTeX line breaks work correctly when users write single backslashes.
89
-
90
- Examples:
91
- - "a = b + c \" -> "a = b + c \\"
92
- - "a = b + c \\\\" -> "a = b + c \\\\" (unchanged)
93
- """
94
- fixed_lines = []
95
-
96
- for line in lines:
97
- # Check if line ends with backslashes
98
- backslash_match = re.search(r"(\\+)$", line)
99
- if backslash_match:
100
- backslashes = backslash_match.group(1)
101
- # If odd number of backslashes, the last one needs to be doubled
102
- if len(backslashes) % 2 == 1:
103
- line = line + "\\"
104
-
105
- fixed_lines.append(line)
106
-
107
- return fixed_lines
108
-
109
- @classmethod
110
- async def notion_to_markdown(cls, block: Block) -> Optional[str]:
111
- if block.type != BlockType.EQUATION or not block.equation:
112
- return None
113
-
114
- expression = (block.equation.expression or "").strip()
115
- if not expression:
116
- return None
117
-
118
- return f"$${expression}$$"
119
-
120
- @classmethod
121
- def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
122
- """Get system prompt information for equation blocks."""
123
- return BlockElementMarkdownInformation(
124
- block_type=cls.__name__,
125
- description="Mathematical equations using standard Markdown LaTeX syntax",
126
- syntax_examples=[
127
- "$$E = mc^2$$",
128
- "$$\\frac{a}{b} + \\sqrt{c}$$",
129
- "$$\\int_0^\\infty e^{-x} dx = 1$$",
130
- "$$\\sum_{i=1}^n i = \\frac{n(n+1)}{2}$$",
131
- ],
132
- usage_guidelines="Use for mathematical expressions and formulas. Supports LaTeX syntax. Wrap equations in double dollar signs ($$).",
133
- )
@@ -1,23 +0,0 @@
1
- from notionary.blocks.markdown.markdown_node import MarkdownNode
2
-
3
-
4
- class EquationMarkdownNode(MarkdownNode):
5
- """
6
- Enhanced Equation node with Pydantic integration.
7
- Programmatic interface for creating Markdown equation blocks.
8
- Uses standard Markdown equation syntax with double dollar signs.
9
-
10
- Examples:
11
- $$E = mc^2$$
12
- $$\\frac{a}{b} + \\sqrt{c}$$
13
- $$\\int_0^\\infty e^{-x} dx = 1$$
14
- """
15
-
16
- expression: str
17
-
18
- def to_markdown(self) -> str:
19
- expr = self.expression.strip()
20
- if not expr:
21
- return "$$$$"
22
-
23
- return f"$${expr}$$"
@@ -1,11 +0,0 @@
1
- from pydantic import BaseModel
2
- from typing_extensions import Literal
3
-
4
-
5
- class EquationBlock(BaseModel):
6
- expression: str
7
-
8
-
9
- class CreateEquationBlock(BaseModel):
10
- type: Literal["equation"] = "equation"
11
- equation: EquationBlock
@@ -1,23 +0,0 @@
1
- from notionary.blocks.file.file_element import FileElement
2
- from notionary.blocks.file.file_element_markdown_node import (
3
- FileMarkdownNode,
4
- )
5
- from notionary.blocks.file.file_element_models import (
6
- CreateFileBlock,
7
- ExternalFile,
8
- FileBlock,
9
- FileType,
10
- FileUploadFile,
11
- NotionHostedFile,
12
- )
13
-
14
- __all__ = [
15
- "FileElement",
16
- "FileType",
17
- "ExternalFile",
18
- "NotionHostedFile",
19
- "FileUploadFile",
20
- "FileBlock",
21
- "CreateFileBlock",
22
- "FileMarkdownNode",
23
- ]
@@ -1,133 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- from pathlib import Path
5
- from typing import Optional
6
-
7
- from notionary.blocks.base_block_element import BaseBlockElement
8
- from notionary.blocks.file.file_element_models import (
9
- CreateFileBlock,
10
- ExternalFile,
11
- FileBlock,
12
- FileType,
13
- FileUploadFile,
14
- )
15
- from notionary.blocks.mixins.captions import CaptionMixin
16
- from notionary.blocks.mixins.file_upload.file_upload_mixin import FileUploadMixin
17
- from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
18
- from notionary.blocks.models import Block, BlockCreateResult, BlockType
19
-
20
-
21
- class FileElement(BaseBlockElement, CaptionMixin, FileUploadMixin):
22
- r"""
23
- Handles conversion between Markdown file embeds and Notion file blocks.
24
-
25
- Supports both external URLs and local file uploads.
26
-
27
- Markdown file syntax:
28
- - [file](https://example.com/document.pdf) - External URL
29
- - [file](./local/document.pdf) - Local file (will be uploaded)
30
- - [file](C:\Documents\report.pdf) - Absolute local path (will be uploaded)
31
- - [file](https://example.com/document.pdf)(caption:Annual Report) - With caption
32
- - (caption:Important document)[file](./doc.pdf) - Caption before URL
33
- """
34
-
35
- FILE_PATTERN = re.compile(r"\[file\]\(([^)]+)\)")
36
-
37
- @classmethod
38
- def match_notion(cls, block: Block) -> bool:
39
- return bool(block.type == BlockType.FILE and block.file)
40
-
41
- @classmethod
42
- async def markdown_to_notion(cls, text: str) -> Optional[BlockCreateResult]:
43
- """Convert markdown file link to Notion FileBlock."""
44
- file_path = cls._extract_file_path(text.strip())
45
- if not file_path:
46
- return None
47
-
48
- cls.logger.info(f"Processing file: {file_path}")
49
-
50
- # Extract caption
51
- caption_text = cls.extract_caption(text.strip())
52
- caption_rich_text = cls.build_caption_rich_text(caption_text or "")
53
-
54
- # Determine if it's a local file or external URL
55
- if cls._is_local_file_path(file_path):
56
- cls.logger.debug(f"Detected local file: {file_path}")
57
-
58
- # Upload the local file using mixin method
59
- file_upload_id = await cls._upload_local_file(file_path, "file")
60
- if not file_upload_id:
61
- cls.logger.error(f"Failed to upload file: {file_path}")
62
- return None
63
-
64
- # Create FILE_UPLOAD block
65
- file_block = FileBlock(
66
- type=FileType.FILE_UPLOAD,
67
- file_upload=FileUploadFile(id=file_upload_id),
68
- caption=caption_rich_text,
69
- name=Path(file_path).name,
70
- )
71
-
72
- else:
73
- cls.logger.debug(f"Using external URL: {file_path}")
74
-
75
- file_block = FileBlock(
76
- type=FileType.EXTERNAL,
77
- external=ExternalFile(url=file_path),
78
- caption=caption_rich_text,
79
- )
80
-
81
- return CreateFileBlock(file=file_block)
82
-
83
- @classmethod
84
- async def notion_to_markdown(cls, block: Block) -> Optional[str]:
85
- if block.type != BlockType.FILE or not block.file:
86
- return None
87
-
88
- fb: FileBlock = block.file
89
-
90
- # Determine the source for markdown
91
- if fb.type == FileType.EXTERNAL and fb.external:
92
- source = fb.external.url
93
- elif fb.type == FileType.FILE and fb.file:
94
- source = fb.file.url
95
- else:
96
- return None
97
-
98
- result = f"[file]({source})"
99
-
100
- # Add caption if present
101
- caption_markdown = await cls.format_caption_for_markdown(fb.caption or [])
102
- if caption_markdown:
103
- result += caption_markdown
104
-
105
- return result
106
-
107
- @classmethod
108
- def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
109
- """Get system prompt information for file blocks."""
110
- return BlockElementMarkdownInformation(
111
- block_type=cls.__name__,
112
- description="File blocks embed files from external URLs or upload local files with optional captions",
113
- syntax_examples=[
114
- "[file](https://example.com/document.pdf)",
115
- "[file](./local/document.pdf)",
116
- "[file](C:\\Documents\\report.xlsx)",
117
- "[file](https://example.com/document.pdf)(caption:Annual Report)",
118
- "(caption:Q1 Data)[file](./spreadsheet.xlsx)",
119
- "[file](./manual.docx)(caption:**User** manual)",
120
- ],
121
- usage_guidelines="Use for both external URLs and local files. Local files will be automatically uploaded to Notion. Supports various file formats including PDFs, documents, spreadsheets, images. Caption supports rich text formatting and should describe the file content or purpose.",
122
- )
123
-
124
- @classmethod
125
- def _extract_file_path(cls, text: str) -> Optional[str]:
126
- """Extract file path/URL from text, handling caption patterns."""
127
- clean_text = cls.remove_caption(text)
128
-
129
- match = cls.FILE_PATTERN.search(clean_text)
130
- if match:
131
- return match.group(1).strip()
132
-
133
- return None
@@ -1,24 +0,0 @@
1
- from typing import Optional
2
-
3
- from notionary.blocks.markdown.markdown_node import MarkdownNode
4
- from notionary.blocks.mixins.captions import CaptionMarkdownNodeMixin
5
-
6
-
7
- class FileMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
8
- """
9
- Enhanced File node with Pydantic integration.
10
- Programmatic interface for creating Notion-style Markdown file embeds.
11
- """
12
-
13
- url: str
14
- caption: Optional[str] = None
15
-
16
- def to_markdown(self) -> str:
17
- """Return the Markdown representation.
18
-
19
- Examples:
20
- - [file](https://example.com/document.pdf)
21
- - [file](https://example.com/document.pdf)(caption:User manual)
22
- """
23
- base_markdown = f"[file]({self.url})"
24
- return self.append_caption_to_markdown(base_markdown, self.caption)
@@ -1,39 +0,0 @@
1
- from enum import Enum
2
- from typing import Literal, Optional
3
-
4
- from pydantic import BaseModel, Field
5
-
6
- from notionary.blocks.rich_text.rich_text_models import RichTextObject
7
-
8
-
9
- class FileType(str, Enum):
10
- EXTERNAL = "external"
11
- FILE = "file"
12
- FILE_UPLOAD = "file_upload"
13
-
14
-
15
- class ExternalFile(BaseModel):
16
- url: str
17
-
18
-
19
- class NotionHostedFile(BaseModel):
20
- url: str
21
- expiry_time: str
22
-
23
-
24
- class FileUploadFile(BaseModel):
25
- id: str
26
-
27
-
28
- class FileBlock(BaseModel):
29
- caption: list[RichTextObject] = Field(default_factory=list)
30
- type: FileType
31
- external: Optional[ExternalFile] = None
32
- file: Optional[NotionHostedFile] = None
33
- file_upload: Optional[FileUploadFile] = None
34
- name: Optional[str] = None
35
-
36
-
37
- class CreateFileBlock(BaseModel):
38
- type: Literal["file"] = "file"
39
- file: FileBlock
@@ -1,19 +0,0 @@
1
- from notionary.blocks.heading.heading_element import HeadingElement
2
- from notionary.blocks.heading.heading_markdown_node import (
3
- HeadingMarkdownNode,
4
- )
5
- from notionary.blocks.heading.heading_models import (
6
- CreateHeading1Block,
7
- CreateHeading2Block,
8
- CreateHeading3Block,
9
- HeadingBlock,
10
- )
11
-
12
- __all__ = [
13
- "HeadingElement",
14
- "HeadingBlock",
15
- "CreateHeading1Block",
16
- "CreateHeading2Block",
17
- "CreateHeading3Block",
18
- "HeadingMarkdownNode",
19
- ]
@@ -1,112 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- from typing import Optional, cast
5
-
6
- from notionary.blocks.base_block_element import BaseBlockElement
7
- from notionary.blocks.heading.heading_models import (
8
- CreateHeading1Block,
9
- CreateHeading2Block,
10
- CreateHeading3Block,
11
- HeadingBlock,
12
- )
13
- from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
14
- from notionary.blocks.models import Block, BlockCreateResult, BlockType
15
- from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
16
- from notionary.blocks.types import BlockColor
17
-
18
-
19
- class HeadingElement(BaseBlockElement):
20
- """Handles conversion between Markdown headings and Notion heading blocks."""
21
-
22
- PATTERN = re.compile(r"^(#{1,3})[ \t]+(.+)$")
23
-
24
- @classmethod
25
- def match_notion(cls, block: Block) -> bool:
26
- return (
27
- block.type
28
- in (
29
- BlockType.HEADING_1,
30
- BlockType.HEADING_2,
31
- BlockType.HEADING_3,
32
- )
33
- and getattr(block, block.type.value) is not None
34
- )
35
-
36
- @classmethod
37
- async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
38
- """Convert markdown headings (#, ##, ###) to Notion HeadingBlock."""
39
- match = cls.PATTERN.match(text.strip())
40
- if not match:
41
- return None
42
-
43
- level = len(match.group(1))
44
- if level < 1 or level > 3:
45
- return None
46
-
47
- content = match.group(2).strip()
48
- if not content:
49
- return None
50
-
51
- rich_text = await TextInlineFormatter.parse_inline_formatting(content)
52
- heading_content = HeadingBlock(
53
- rich_text=rich_text, color=BlockColor.DEFAULT, is_toggleable=False
54
- )
55
-
56
- if level == 1:
57
- return CreateHeading1Block(heading_1=heading_content)
58
- elif level == 2:
59
- return CreateHeading2Block(heading_2=heading_content)
60
- else:
61
- return CreateHeading3Block(heading_3=heading_content)
62
-
63
- @classmethod
64
- async def notion_to_markdown(cls, block: Block) -> Optional[str]:
65
- # Only handle heading blocks via BlockType enum
66
- if block.type not in (
67
- BlockType.HEADING_1,
68
- BlockType.HEADING_2,
69
- BlockType.HEADING_3,
70
- ):
71
- return None
72
-
73
- # Determine heading level from enum
74
- if block.type == BlockType.HEADING_1:
75
- level = 1
76
- elif block.type == BlockType.HEADING_2:
77
- level = 2
78
- else:
79
- level = 3
80
-
81
- heading_obj = getattr(block, block.type.value)
82
- if not heading_obj:
83
- return None
84
-
85
- heading_data = cast(HeadingBlock, heading_obj)
86
- if not heading_data.rich_text:
87
- return None
88
-
89
- text = await TextInlineFormatter.extract_text_with_formatting(
90
- heading_data.rich_text
91
- )
92
- if not text:
93
- return None
94
-
95
- # Use hash-style for all heading levels
96
- return f"{('#' * level)} {text}"
97
-
98
- @classmethod
99
- def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
100
- """Get system prompt information for heading blocks."""
101
- return BlockElementMarkdownInformation(
102
- block_type=cls.__name__,
103
- description="Heading blocks create hierarchical document structure with different levels",
104
- syntax_examples=[
105
- "# Heading Level 1",
106
- "## Heading Level 2",
107
- "### Heading Level 3",
108
- "# Heading with **bold text**",
109
- "## Heading with *italic text*",
110
- ],
111
- usage_guidelines="Use # for main titles, ## for sections, ### for subsections. Supports inline formatting. Only levels 1-3 are supported in Notion.",
112
- )
@@ -1,16 +0,0 @@
1
- from pydantic import Field
2
- from notionary.blocks.markdown.markdown_node import MarkdownNode
3
-
4
-
5
- class HeadingMarkdownNode(MarkdownNode):
6
- """
7
- Enhanced Heading node with Pydantic integration.
8
- Programmatic interface for creating Markdown headings (H1-H3).
9
- Example: # Heading 1, ## Heading 2, ### Heading 3
10
- """
11
-
12
- text: str
13
- level: int = Field(default=1, ge=1, le=3)
14
-
15
- def to_markdown(self) -> str:
16
- return f"{'#' * self.level} {self.text}"