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
@@ -0,0 +1,68 @@
1
+ from notionary.blocks.client import NotionBlockHttpClient
2
+ from notionary.page.content.parser.factory import ConverterChainFactory
3
+ from notionary.page.content.parser.post_processing.handlers import RichTextLengthTruncationPostProcessor
4
+ from notionary.page.content.parser.post_processing.service import BlockPostProcessor
5
+ from notionary.page.content.parser.pre_processsing.handlers import ColumnSyntaxPreProcessor, WhitespacePreProcessor
6
+ from notionary.page.content.parser.pre_processsing.service import MarkdownPreProcessor
7
+ from notionary.page.content.parser.service import MarkdownToNotionConverter
8
+ from notionary.page.content.renderer.factory import RendererChainFactory
9
+ from notionary.page.content.renderer.post_processing.handlers import NumberedListPlaceholderReplaceerPostProcessor
10
+ from notionary.page.content.renderer.post_processing.service import MarkdownRenderingPostProcessor
11
+ from notionary.page.content.renderer.service import NotionToMarkdownConverter
12
+ from notionary.page.content.service import PageContentService
13
+
14
+
15
+ class PageContentServiceFactory:
16
+ def __init__(
17
+ self,
18
+ converter_chain_factory: ConverterChainFactory | None = None,
19
+ renderer_chain_factory: RendererChainFactory | None = None,
20
+ ) -> None:
21
+ self._converter_chain_factory = converter_chain_factory or ConverterChainFactory()
22
+ self._renderer_chain_factory = renderer_chain_factory or RendererChainFactory()
23
+
24
+ def create(self, page_id: str, block_client: NotionBlockHttpClient) -> PageContentService:
25
+ markdown_converter = self._create_markdown_to_notion_converter()
26
+ notion_to_markdown_converter = self._create_notion_to_markdown_converter()
27
+
28
+ return PageContentService(
29
+ page_id=page_id,
30
+ block_client=block_client,
31
+ markdown_converter=markdown_converter,
32
+ notion_to_markdown_converter=notion_to_markdown_converter,
33
+ )
34
+
35
+ def _create_markdown_to_notion_converter(self) -> MarkdownToNotionConverter:
36
+ line_parser = self._converter_chain_factory.create()
37
+ markdown_pre_processor = self._create_markdown_preprocessor()
38
+ block_post_processor = self._create_post_processor()
39
+
40
+ return MarkdownToNotionConverter(
41
+ line_parser=line_parser,
42
+ pre_processor=markdown_pre_processor,
43
+ post_processor=block_post_processor,
44
+ )
45
+
46
+ def _create_notion_to_markdown_converter(self) -> NotionToMarkdownConverter:
47
+ renderer_chain = self._renderer_chain_factory.create()
48
+ markdown_rendering_post_processor = self._create_markdown_rendering_post_processor()
49
+ return NotionToMarkdownConverter(
50
+ renderer_chain=renderer_chain,
51
+ post_processor=markdown_rendering_post_processor,
52
+ )
53
+
54
+ def _create_markdown_preprocessor(self) -> MarkdownPreProcessor:
55
+ pre_processor = MarkdownPreProcessor()
56
+ pre_processor.register(ColumnSyntaxPreProcessor())
57
+ pre_processor.register(WhitespacePreProcessor())
58
+ return pre_processor
59
+
60
+ def _create_post_processor(self) -> BlockPostProcessor:
61
+ post_processor = BlockPostProcessor()
62
+ post_processor.register(RichTextLengthTruncationPostProcessor())
63
+ return post_processor
64
+
65
+ def _create_markdown_rendering_post_processor(self) -> MarkdownRenderingPostProcessor:
66
+ post_processor = MarkdownRenderingPostProcessor()
67
+ post_processor.register(NumberedListPlaceholderReplaceerPostProcessor())
68
+ return post_processor
@@ -0,0 +1,5 @@
1
+ from .builder import MarkdownBuilder
2
+
3
+ __all__ = [
4
+ "MarkdownBuilder",
5
+ ]
@@ -0,0 +1,304 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import Self
5
+
6
+ from notionary.blocks.enums import CodeLanguage
7
+ from notionary.page.content.markdown.nodes import (
8
+ AudioMarkdownNode,
9
+ BookmarkMarkdownNode,
10
+ BreadcrumbMarkdownNode,
11
+ BulletedListMarkdownNode,
12
+ CalloutMarkdownNode,
13
+ CodeMarkdownNode,
14
+ ColumnListMarkdownNode,
15
+ ColumnMarkdownNode,
16
+ DividerMarkdownNode,
17
+ EmbedMarkdownNode,
18
+ EquationMarkdownNode,
19
+ FileMarkdownNode,
20
+ HeadingMarkdownNode,
21
+ ImageMarkdownNode,
22
+ MarkdownNode,
23
+ NumberedListMarkdownNode,
24
+ ParagraphMarkdownNode,
25
+ PdfMarkdownNode,
26
+ QuoteMarkdownNode,
27
+ SpaceMarkdownNode,
28
+ TableMarkdownNode,
29
+ TableOfContentsMarkdownNode,
30
+ TodoMarkdownNode,
31
+ ToggleableHeadingMarkdownNode,
32
+ ToggleMarkdownNode,
33
+ VideoMarkdownNode,
34
+ )
35
+
36
+
37
+ class MarkdownBuilder:
38
+ def __init__(self) -> None:
39
+ self.children: list[MarkdownNode] = []
40
+
41
+ def h1(self, text: str) -> Self:
42
+ self.children.append(HeadingMarkdownNode(text=text, level=1))
43
+ return self
44
+
45
+ def h2(self, text: str) -> Self:
46
+ self.children.append(HeadingMarkdownNode(text=text, level=2))
47
+ return self
48
+
49
+ def h3(self, text: str) -> Self:
50
+ self.children.append(HeadingMarkdownNode(text=text, level=3))
51
+ return self
52
+
53
+ def paragraph(self, text: str) -> Self:
54
+ self.children.append(ParagraphMarkdownNode(text=text))
55
+ return self
56
+
57
+ def space(self) -> Self:
58
+ self.children.append(SpaceMarkdownNode())
59
+ return self
60
+
61
+ def quote(self, text: str) -> Self:
62
+ self.children.append(QuoteMarkdownNode(text=text))
63
+ return self
64
+
65
+ def divider(self) -> Self:
66
+ self.children.append(DividerMarkdownNode())
67
+ return self
68
+
69
+ def numbered_list(self, items: list[str]) -> Self:
70
+ self.children.append(NumberedListMarkdownNode(texts=items))
71
+ return self
72
+
73
+ def bulleted_list(self, items: list[str]) -> Self:
74
+ self.children.append(BulletedListMarkdownNode(texts=items))
75
+ return self
76
+
77
+ def todo(self, text: str, checked: bool = False) -> Self:
78
+ self.children.append(TodoMarkdownNode(text=text, checked=checked))
79
+ return self
80
+
81
+ def checked_todo(self, text: str) -> Self:
82
+ return self.todo(text, checked=True)
83
+
84
+ def unchecked_todo(self, text: str) -> Self:
85
+ return self.todo(text, checked=False)
86
+
87
+ def todo_list(self, items: list[str], completed: list[bool] | None = None) -> Self:
88
+ if completed is None:
89
+ completed = [False] * len(items)
90
+
91
+ for i, item in enumerate(items):
92
+ is_done = completed[i] if i < len(completed) else False
93
+ self.children.append(TodoMarkdownNode(text=item, checked=is_done))
94
+ return self
95
+
96
+ def callout(self, text: str, emoji: str | None = None) -> Self:
97
+ self.children.append(CalloutMarkdownNode(text=text, emoji=emoji))
98
+ return self
99
+
100
+ def callout_with_children(
101
+ self,
102
+ text: str,
103
+ emoji: str | None = None,
104
+ builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None,
105
+ ) -> Self:
106
+ """
107
+ Add a callout block with children built using the builder API.
108
+
109
+ Args:
110
+ text: The callout text content
111
+ emoji: Optional emoji for the callout icon
112
+ builder_func: Optional function that receives a MarkdownBuilder and returns it configured
113
+
114
+ Example:
115
+ builder.callout_with_children("Important note", "⚠️", lambda c:
116
+ c.paragraph("Additional details here")
117
+ .bulleted_list(["Point 1", "Point 2"])
118
+ )
119
+ """
120
+ if builder_func is None:
121
+ self.children.append(CalloutMarkdownNode(text=text, emoji=emoji))
122
+ return self
123
+
124
+ callout_builder = MarkdownBuilder()
125
+ builder_func(callout_builder)
126
+ self.children.append(CalloutMarkdownNode(text=text, emoji=emoji, children=callout_builder.children))
127
+ return self
128
+
129
+ def toggle(self, title: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder]) -> Self:
130
+ """
131
+ Add a toggle block with content built using the builder API.
132
+
133
+ Args:
134
+ title: The toggle title/header text
135
+ builder_func: Function that receives a MarkdownBuilder and returns it configured
136
+
137
+ Example:
138
+ builder.toggle("Advanced Settings", lambda t:
139
+ t.h3("Configuration")
140
+ .paragraph("Settings description")
141
+ .table(["Setting", "Value"], [["Debug", "True"]])
142
+ .callout("Important note", "⚠️")
143
+ )
144
+ """
145
+ toggle_builder = MarkdownBuilder()
146
+ builder_func(toggle_builder)
147
+ self.children.append(ToggleMarkdownNode(title=title, children=toggle_builder.children))
148
+ return self
149
+
150
+ def toggleable_heading(
151
+ self,
152
+ text: str,
153
+ level: int,
154
+ builder_func: Callable[[MarkdownBuilder], MarkdownBuilder],
155
+ ) -> Self:
156
+ """
157
+ Add a toggleable heading with content built using the builder API.
158
+
159
+ Args:
160
+ text: The heading text content
161
+ level: Heading level (1-3)
162
+ builder_func: Function that receives a MarkdownBuilder and returns it configured
163
+
164
+ Example:
165
+ builder.toggleable_heading("Advanced Section", 2, lambda t:
166
+ t.paragraph("Introduction to this section")
167
+ .numbered_list(["Step 1", "Step 2", "Step 3"])
168
+ .code("example_code()", "python")
169
+ .table(["Feature", "Status"], [["API", "Ready"]])
170
+ )
171
+ """
172
+ toggle_builder = MarkdownBuilder()
173
+ builder_func(toggle_builder)
174
+ self.children.append(ToggleableHeadingMarkdownNode(text=text, level=level, children=toggle_builder.children))
175
+ return self
176
+
177
+ def image(self, url: str, caption: str | None = None) -> Self:
178
+ self.children.append(ImageMarkdownNode(url=url, caption=caption))
179
+ return self
180
+
181
+ def video(self, url: str, caption: str | None = None) -> Self:
182
+ self.children.append(VideoMarkdownNode(url=url, caption=caption))
183
+ return self
184
+
185
+ def audio(self, url: str, caption: str | None = None) -> Self:
186
+ self.children.append(AudioMarkdownNode(url=url, caption=caption))
187
+ return self
188
+
189
+ def file(self, url: str, caption: str | None = None) -> Self:
190
+ self.children.append(FileMarkdownNode(url=url, caption=caption))
191
+ return self
192
+
193
+ def pdf(self, url: str, caption: str | None = None) -> Self:
194
+ self.children.append(PdfMarkdownNode(url=url, caption=caption))
195
+ return self
196
+
197
+ def bookmark(self, url: str, title: str | None = None, caption: str | None = None) -> Self:
198
+ self.children.append(BookmarkMarkdownNode(url=url, title=title, caption=caption))
199
+ return self
200
+
201
+ def embed(self, url: str, caption: str | None = None) -> Self:
202
+ self.children.append(EmbedMarkdownNode(url=url, caption=caption))
203
+ return self
204
+
205
+ def code(self, code: str, language: CodeLanguage | None = None, caption: str | None = None) -> Self:
206
+ self.children.append(CodeMarkdownNode(code=code, language=language, caption=caption))
207
+ return self
208
+
209
+ def mermaid(self, diagram: str, caption: str | None = None) -> Self:
210
+ self.children.append(CodeMarkdownNode(code=diagram, language=CodeLanguage.MERMAID.value, caption=caption))
211
+ return self
212
+
213
+ def table(self, headers: list[str], rows: list[list[str]]) -> Self:
214
+ self.children.append(TableMarkdownNode(headers=headers, rows=rows))
215
+ return self
216
+
217
+ def add_custom(self, node: MarkdownNode) -> Self:
218
+ self.children.append(node)
219
+ return self
220
+
221
+ def breadcrumb(self) -> Self:
222
+ self.children.append(BreadcrumbMarkdownNode())
223
+ return self
224
+
225
+ def equation(self, expression: str) -> Self:
226
+ self.children.append(EquationMarkdownNode(expression=expression))
227
+ return self
228
+
229
+ def table_of_contents(self) -> Self:
230
+ self.children.append(TableOfContentsMarkdownNode())
231
+ return self
232
+
233
+ def columns(
234
+ self,
235
+ *builder_funcs: Callable[[MarkdownBuilder], MarkdownBuilder],
236
+ width_ratios: list[float] | None = None,
237
+ ) -> Self:
238
+ """
239
+ Add multiple columns in a layout.
240
+
241
+ Args:
242
+ *builder_funcs: Multiple functions, each building one column
243
+ width_ratios: Optional list of width ratios (0.0 to 1.0).
244
+ If None, columns have equal width.
245
+ Length must match number of builder_funcs.
246
+
247
+ Examples:
248
+ # Equal width (original API unchanged):
249
+ builder.columns(
250
+ lambda col: col.h2("Left").paragraph("Left content"),
251
+ lambda col: col.h2("Right").paragraph("Right content")
252
+ )
253
+
254
+ # Custom ratios:
255
+ builder.columns(
256
+ lambda col: col.h2("Main").paragraph("70% width"),
257
+ lambda col: col.h2("Sidebar").paragraph("30% width"),
258
+ width_ratios=[0.7, 0.3]
259
+ )
260
+
261
+ # Three columns with custom ratios:
262
+ builder.columns(
263
+ lambda col: col.h3("Nav").paragraph("Navigation"),
264
+ lambda col: col.h2("Main").paragraph("Main content"),
265
+ lambda col: col.h3("Ads").paragraph("Advertisement"),
266
+ width_ratios=[0.2, 0.6, 0.2]
267
+ )
268
+ """
269
+ self._validate_columns_args(builder_funcs, width_ratios)
270
+
271
+ # Create all columns
272
+ columns = []
273
+ for i, builder_func in enumerate(builder_funcs):
274
+ width_ratio = width_ratios[i] if width_ratios else None
275
+
276
+ col_builder = MarkdownBuilder()
277
+ builder_func(col_builder)
278
+
279
+ column_node = ColumnMarkdownNode(children=col_builder.children, width_ratio=width_ratio)
280
+ columns.append(column_node)
281
+
282
+ self.children.append(ColumnListMarkdownNode(columns=columns))
283
+ return self
284
+
285
+ def _validate_columns_args(
286
+ self,
287
+ builder_funcs: tuple[Callable[[MarkdownBuilder], MarkdownBuilder], ...],
288
+ width_ratios: list[float] | None,
289
+ ) -> None:
290
+ if len(builder_funcs) < 2:
291
+ raise ValueError("Column layout requires at least 2 columns")
292
+
293
+ if width_ratios is not None:
294
+ if len(width_ratios) != len(builder_funcs):
295
+ raise ValueError(
296
+ f"width_ratios length ({len(width_ratios)}) must match number of columns ({len(builder_funcs)})"
297
+ )
298
+
299
+ ratio_sum = sum(width_ratios)
300
+ if not (0.9 <= ratio_sum <= 1.1): # Allow small floating point errors
301
+ raise ValueError(f"width_ratios should sum to 1.0, got {ratio_sum}")
302
+
303
+ def build(self) -> str:
304
+ return "\n\n".join(child.to_markdown() for child in self.children if child is not None)
@@ -0,0 +1,54 @@
1
+ from .audio import AudioMarkdownNode
2
+ from .base import MarkdownNode
3
+ from .bookmark import BookmarkMarkdownNode
4
+ from .breadcrumb import BreadcrumbMarkdownNode
5
+ from .bulleted_list import BulletedListMarkdownNode
6
+ from .callout import CalloutMarkdownNode
7
+ from .code import CodeMarkdownNode
8
+ from .columns import ColumnListMarkdownNode, ColumnMarkdownNode
9
+ from .divider import DividerMarkdownNode
10
+ from .embed import EmbedMarkdownNode
11
+ from .equation import EquationMarkdownNode
12
+ from .file import FileMarkdownNode
13
+ from .heading import HeadingMarkdownNode
14
+ from .image import ImageMarkdownNode
15
+ from .numbered_list import NumberedListMarkdownNode
16
+ from .paragraph import ParagraphMarkdownNode
17
+ from .pdf import PdfMarkdownNode
18
+ from .quote import QuoteMarkdownNode
19
+ from .space import SpaceMarkdownNode
20
+ from .table import TableMarkdownNode
21
+ from .table_of_contents import TableOfContentsMarkdownNode
22
+ from .todo import TodoMarkdownNode
23
+ from .toggle import ToggleMarkdownNode
24
+ from .toggleable_heading import ToggleableHeadingMarkdownNode
25
+ from .video import VideoMarkdownNode
26
+
27
+ __all__ = [
28
+ "AudioMarkdownNode",
29
+ "BookmarkMarkdownNode",
30
+ "BreadcrumbMarkdownNode",
31
+ "BulletedListMarkdownNode",
32
+ "CalloutMarkdownNode",
33
+ "CodeMarkdownNode",
34
+ "ColumnListMarkdownNode",
35
+ "ColumnMarkdownNode",
36
+ "DividerMarkdownNode",
37
+ "EmbedMarkdownNode",
38
+ "EquationMarkdownNode",
39
+ "FileMarkdownNode",
40
+ "HeadingMarkdownNode",
41
+ "ImageMarkdownNode",
42
+ "MarkdownNode",
43
+ "NumberedListMarkdownNode",
44
+ "ParagraphMarkdownNode",
45
+ "PdfMarkdownNode",
46
+ "QuoteMarkdownNode",
47
+ "SpaceMarkdownNode",
48
+ "TableMarkdownNode",
49
+ "TableOfContentsMarkdownNode",
50
+ "TodoMarkdownNode",
51
+ "ToggleMarkdownNode",
52
+ "ToggleableHeadingMarkdownNode",
53
+ "VideoMarkdownNode",
54
+ ]
@@ -0,0 +1,23 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
5
+ from notionary.page.content.syntax.service import SyntaxRegistry
6
+
7
+
8
+ class AudioMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
9
+ def __init__(
10
+ self,
11
+ url: str,
12
+ caption: str | None = None,
13
+ syntax_registry: SyntaxRegistry | None = None,
14
+ ) -> None:
15
+ super().__init__(syntax_registry=syntax_registry)
16
+ self.url = url
17
+ self.caption = caption
18
+
19
+ @override
20
+ def to_markdown(self) -> str:
21
+ audio_syntax = self._syntax_registry.get_audio_syntax()
22
+ base_markdown = f"{audio_syntax.start_delimiter}{self.url}{audio_syntax.end_delimiter}"
23
+ return self._append_caption_to_markdown(base_markdown, self.caption)
@@ -0,0 +1,12 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from notionary.page.content.syntax.service import SyntaxRegistry
4
+
5
+
6
+ class MarkdownNode(ABC):
7
+ def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
8
+ self._syntax_registry = syntax_registry or SyntaxRegistry()
9
+
10
+ @abstractmethod
11
+ def to_markdown(self) -> str:
12
+ pass
@@ -0,0 +1,25 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
5
+ from notionary.page.content.syntax.service import SyntaxRegistry
6
+
7
+
8
+ class BookmarkMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
9
+ def __init__(
10
+ self,
11
+ url: str,
12
+ title: str | None = None,
13
+ caption: str | None = None,
14
+ syntax_registry: SyntaxRegistry | None = None,
15
+ ) -> None:
16
+ super().__init__(syntax_registry=syntax_registry)
17
+ self.url = url
18
+ self.title = title
19
+ self.caption = caption
20
+
21
+ @override
22
+ def to_markdown(self) -> str:
23
+ bookmark_syntax = self._syntax_registry.get_bookmark_syntax()
24
+ base_markdown = f"{bookmark_syntax.start_delimiter}{self.url}{bookmark_syntax.end_delimiter}"
25
+ return self._append_caption_to_markdown(base_markdown, self.caption)
@@ -0,0 +1,14 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.syntax.service import SyntaxRegistry
5
+
6
+
7
+ class BreadcrumbMarkdownNode(MarkdownNode):
8
+ def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
9
+ super().__init__(syntax_registry=syntax_registry)
10
+
11
+ @override
12
+ def to_markdown(self) -> str:
13
+ breadcrumb_syntax = self._syntax_registry.get_breadcrumb_syntax()
14
+ return breadcrumb_syntax.start_delimiter
@@ -0,0 +1,18 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.syntax.service import SyntaxRegistry
5
+
6
+
7
+ class BulletedListMarkdownNode(MarkdownNode):
8
+ def __init__(self, texts: list[str], syntax_registry: SyntaxRegistry | None = None) -> None:
9
+ super().__init__(syntax_registry=syntax_registry)
10
+ self.texts = texts
11
+
12
+ @override
13
+ def to_markdown(self) -> str:
14
+ bulleted_list_syntax = self._syntax_registry.get_bulleted_list_syntax()
15
+ result = []
16
+ for text in self.texts:
17
+ result.append(f"{bulleted_list_syntax.start_delimiter} {text}")
18
+ return "\n".join(result)
@@ -0,0 +1,32 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.syntax.service import SyntaxRegistry
5
+
6
+
7
+ class CalloutMarkdownNode(MarkdownNode):
8
+ def __init__(
9
+ self,
10
+ text: str,
11
+ emoji: str | None = None,
12
+ children: list[MarkdownNode] | None = None,
13
+ syntax_registry: SyntaxRegistry | None = None,
14
+ ):
15
+ super().__init__(syntax_registry=syntax_registry)
16
+ self.text = text
17
+ self.emoji = emoji
18
+ self.children = children or []
19
+
20
+ @override
21
+ def to_markdown(self) -> str:
22
+ callout_syntax = self._syntax_registry.get_callout_syntax()
23
+ start_tag = f"{callout_syntax.start_delimiter} {self.emoji}" if self.emoji else callout_syntax.start_delimiter
24
+
25
+ if not self.children:
26
+ return f"{start_tag}\n{self.text}\n{callout_syntax.end_delimiter}"
27
+
28
+ # Convert children to markdown
29
+ content_parts = [self.text] + [child.to_markdown() for child in self.children]
30
+ content_text = "\n\n".join(content_parts)
31
+
32
+ return f"{start_tag}\n{content_text}\n{callout_syntax.end_delimiter}"
@@ -0,0 +1,30 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.enums import CodeLanguage
4
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
5
+ from notionary.page.content.markdown.nodes.mixins.caption import CaptionMarkdownNodeMixin
6
+ from notionary.page.content.syntax.service import SyntaxRegistry
7
+
8
+
9
+ class CodeMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
10
+ """Code node for creating Notion-style code blocks."""
11
+
12
+ def __init__(
13
+ self,
14
+ code: str,
15
+ language: CodeLanguage | None = None,
16
+ caption: str | None = None,
17
+ syntax_registry: SyntaxRegistry | None = None,
18
+ ) -> None:
19
+ super().__init__(syntax_registry=syntax_registry)
20
+ self.code = code
21
+ self.language = language
22
+ self.caption = caption
23
+
24
+ @override
25
+ def to_markdown(self) -> str:
26
+ code_syntax = self._syntax_registry.get_code_syntax()
27
+ lang = self.language or ""
28
+
29
+ base_markdown = f"{code_syntax.start_delimiter}{lang}\n{self.code}\n{code_syntax.end_delimiter}"
30
+ return self._append_caption_to_markdown(base_markdown, self.caption)
@@ -0,0 +1,51 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.syntax.service import SyntaxRegistry
5
+
6
+
7
+ class ColumnMarkdownNode(MarkdownNode):
8
+ def __init__(
9
+ self,
10
+ children: list[MarkdownNode] | None = None,
11
+ width_ratio: float | None = None,
12
+ syntax_registry: SyntaxRegistry | None = None,
13
+ ):
14
+ super().__init__(syntax_registry=syntax_registry)
15
+ self.children = children or []
16
+ self.width_ratio = width_ratio
17
+
18
+ @override
19
+ def to_markdown(self) -> str:
20
+ column_syntax = self._syntax_registry.get_column_syntax()
21
+ start_tag = (
22
+ f"{column_syntax.start_delimiter} {self.width_ratio}"
23
+ if self.width_ratio is not None
24
+ else column_syntax.start_delimiter
25
+ )
26
+
27
+ if not self.children:
28
+ return f"{start_tag}\n{column_syntax.end_delimiter}"
29
+
30
+ # Convert children to markdown
31
+ content_parts = [child.to_markdown() for child in self.children]
32
+ content_text = "\n\n".join(content_parts)
33
+
34
+ return f"{start_tag}\n{content_text}\n{column_syntax.end_delimiter}"
35
+
36
+
37
+ class ColumnListMarkdownNode(MarkdownNode):
38
+ def __init__(self, columns: list[ColumnMarkdownNode] | None = None, syntax_registry: SyntaxRegistry | None = None):
39
+ super().__init__(syntax_registry=syntax_registry)
40
+ self.columns = columns or []
41
+
42
+ @override
43
+ def to_markdown(self) -> str:
44
+ column_list_syntax = self._syntax_registry.get_column_list_syntax()
45
+ if not self.columns:
46
+ return f"{column_list_syntax.start_delimiter}\n{column_list_syntax.end_delimiter}"
47
+
48
+ column_parts = [column.to_markdown() for column in self.columns]
49
+ columns_content = "\n\n".join(column_parts)
50
+
51
+ return f"{column_list_syntax.start_delimiter}\n{columns_content}\n{column_list_syntax.end_delimiter}"
@@ -0,0 +1,14 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.syntax.service import SyntaxRegistry
5
+
6
+
7
+ class DividerMarkdownNode(MarkdownNode):
8
+ def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
9
+ super().__init__(syntax_registry=syntax_registry)
10
+
11
+ @override
12
+ def to_markdown(self) -> str:
13
+ divider_syntax = self._syntax_registry.get_divider_syntax()
14
+ return divider_syntax.start_delimiter