notionary 0.2.27__py3-none-any.whl → 0.3.0__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 (395) hide show
  1. notionary/__init__.py +5 -20
  2. notionary/blocks/__init__.py +4 -4
  3. notionary/blocks/client.py +90 -216
  4. notionary/blocks/enums.py +167 -0
  5. notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
  6. notionary/blocks/rich_text/models.py +178 -0
  7. notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
  8. notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
  9. notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
  10. notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
  11. notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
  12. notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
  13. notionary/blocks/rich_text/rich_text_markdown_converter.py +144 -0
  14. notionary/blocks/rich_text/rich_text_patterns.py +42 -0
  15. notionary/blocks/schemas.py +778 -0
  16. notionary/comments/__init__.py +1 -22
  17. notionary/comments/client.py +52 -187
  18. notionary/comments/factory.py +38 -0
  19. notionary/comments/models.py +5 -127
  20. notionary/comments/schemas.py +240 -0
  21. notionary/comments/service.py +34 -0
  22. notionary/data_source/http/client.py +11 -0
  23. notionary/data_source/http/data_source_instance_client.py +104 -0
  24. notionary/data_source/properties/schemas.py +402 -0
  25. notionary/data_source/query/builder.py +448 -0
  26. notionary/data_source/query/resolver.py +114 -0
  27. notionary/data_source/query/schema.py +302 -0
  28. notionary/data_source/query/validator.py +73 -0
  29. notionary/data_source/schema/registry.py +104 -0
  30. notionary/data_source/schema/service.py +136 -0
  31. notionary/data_source/schemas.py +27 -0
  32. notionary/data_source/service.py +377 -0
  33. notionary/database/client.py +30 -135
  34. notionary/database/database_metadata_update_client.py +19 -0
  35. notionary/database/schemas.py +29 -0
  36. notionary/database/service.py +168 -0
  37. notionary/exceptions/__init__.py +33 -0
  38. notionary/exceptions/api.py +41 -0
  39. notionary/exceptions/base.py +2 -0
  40. notionary/exceptions/block_parsing.py +16 -0
  41. notionary/exceptions/data_source/__init__.py +6 -0
  42. notionary/exceptions/data_source/builder.py +182 -0
  43. notionary/exceptions/data_source/properties.py +34 -0
  44. notionary/exceptions/properties.py +58 -0
  45. notionary/exceptions/search.py +57 -0
  46. notionary/file_upload/client.py +18 -30
  47. notionary/file_upload/models.py +7 -8
  48. notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
  49. notionary/http/client.py +204 -0
  50. notionary/http/models.py +50 -0
  51. notionary/page/blocks/client.py +1 -0
  52. notionary/page/content/factory.py +73 -0
  53. notionary/page/content/markdown/__init__.py +5 -0
  54. notionary/page/content/markdown/builder.py +226 -0
  55. notionary/page/content/markdown/nodes/__init__.py +52 -0
  56. notionary/page/content/markdown/nodes/audio.py +23 -0
  57. notionary/page/content/markdown/nodes/base.py +12 -0
  58. notionary/page/content/markdown/nodes/bookmark.py +25 -0
  59. notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
  60. notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
  61. notionary/page/content/markdown/nodes/callout.py +34 -0
  62. notionary/page/content/markdown/nodes/code.py +28 -0
  63. notionary/page/content/markdown/nodes/columns.py +69 -0
  64. notionary/page/content/markdown/nodes/container.py +64 -0
  65. notionary/page/content/markdown/nodes/divider.py +14 -0
  66. notionary/page/content/markdown/nodes/embed.py +23 -0
  67. notionary/page/content/markdown/nodes/equation.py +19 -0
  68. notionary/page/content/markdown/nodes/file.py +23 -0
  69. notionary/page/content/markdown/nodes/heading.py +36 -0
  70. notionary/page/content/markdown/nodes/image.py +23 -0
  71. notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
  72. notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
  73. notionary/page/content/markdown/nodes/numbered_list.py +38 -0
  74. notionary/page/content/markdown/nodes/paragraph.py +14 -0
  75. notionary/page/content/markdown/nodes/pdf.py +23 -0
  76. notionary/page/content/markdown/nodes/quote.py +27 -0
  77. notionary/page/content/markdown/nodes/space.py +14 -0
  78. notionary/page/content/markdown/nodes/table.py +45 -0
  79. notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
  80. notionary/page/content/markdown/nodes/todo.py +38 -0
  81. notionary/page/content/markdown/nodes/toggle.py +27 -0
  82. notionary/page/content/markdown/nodes/video.py +23 -0
  83. notionary/page/content/parser/context.py +126 -0
  84. notionary/page/content/parser/factory.py +210 -0
  85. notionary/page/content/parser/parsers/__init__.py +58 -0
  86. notionary/page/content/parser/parsers/audio.py +40 -0
  87. notionary/page/content/parser/parsers/base.py +30 -0
  88. notionary/page/content/parser/parsers/bookmark.py +33 -0
  89. notionary/page/content/parser/parsers/breadcrumb.py +33 -0
  90. notionary/page/content/parser/parsers/bulleted_list.py +85 -0
  91. notionary/page/content/parser/parsers/callout.py +100 -0
  92. notionary/page/content/parser/parsers/caption.py +55 -0
  93. notionary/page/content/parser/parsers/code.py +81 -0
  94. notionary/page/content/parser/parsers/column.py +76 -0
  95. notionary/page/content/parser/parsers/column_list.py +81 -0
  96. notionary/page/content/parser/parsers/divider.py +33 -0
  97. notionary/page/content/parser/parsers/embed.py +33 -0
  98. notionary/page/content/parser/parsers/equation.py +65 -0
  99. notionary/page/content/parser/parsers/file.py +42 -0
  100. notionary/page/content/parser/parsers/heading.py +115 -0
  101. notionary/page/content/parser/parsers/image.py +42 -0
  102. notionary/page/content/parser/parsers/numbered_list.py +89 -0
  103. notionary/page/content/parser/parsers/paragraph.py +37 -0
  104. notionary/page/content/parser/parsers/pdf.py +42 -0
  105. notionary/page/content/parser/parsers/quote.py +125 -0
  106. notionary/page/content/parser/parsers/space.py +41 -0
  107. notionary/page/content/parser/parsers/table.py +144 -0
  108. notionary/page/content/parser/parsers/table_of_contents.py +32 -0
  109. notionary/page/content/parser/parsers/todo.py +96 -0
  110. notionary/page/content/parser/parsers/toggle.py +70 -0
  111. notionary/page/content/parser/parsers/video.py +42 -0
  112. notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
  113. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +95 -0
  114. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -0
  115. notionary/page/content/parser/post_processing/port.py +9 -0
  116. notionary/page/content/parser/post_processing/service.py +16 -0
  117. notionary/page/content/parser/pre_processsing/handlers/__init__.py +11 -0
  118. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
  119. notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
  120. notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
  121. notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
  122. notionary/page/content/parser/pre_processsing/service.py +15 -0
  123. notionary/page/content/parser/service.py +78 -0
  124. notionary/page/content/renderer/context.py +51 -0
  125. notionary/page/content/renderer/factory.py +231 -0
  126. notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
  127. notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
  128. notionary/page/content/renderer/post_processing/port.py +7 -0
  129. notionary/page/content/renderer/post_processing/service.py +15 -0
  130. notionary/page/content/renderer/renderers/__init__.py +55 -0
  131. notionary/page/content/renderer/renderers/audio.py +31 -0
  132. notionary/page/content/renderer/renderers/base.py +31 -0
  133. notionary/page/content/renderer/renderers/bookmark.py +25 -0
  134. notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
  135. notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
  136. notionary/page/content/renderer/renderers/callout.py +50 -0
  137. notionary/page/content/renderer/renderers/captioned_block.py +58 -0
  138. notionary/page/content/renderer/renderers/code.py +34 -0
  139. notionary/page/content/renderer/renderers/column.py +53 -0
  140. notionary/page/content/renderer/renderers/column_list.py +44 -0
  141. notionary/page/content/renderer/renderers/divider.py +22 -0
  142. notionary/page/content/renderer/renderers/embed.py +25 -0
  143. notionary/page/content/renderer/renderers/equation.py +37 -0
  144. notionary/page/content/renderer/renderers/fallback.py +24 -0
  145. notionary/page/content/renderer/renderers/file.py +40 -0
  146. notionary/page/content/renderer/renderers/heading.py +95 -0
  147. notionary/page/content/renderer/renderers/image.py +31 -0
  148. notionary/page/content/renderer/renderers/numbered_list.py +42 -0
  149. notionary/page/content/renderer/renderers/paragraph.py +40 -0
  150. notionary/page/content/renderer/renderers/pdf.py +31 -0
  151. notionary/page/content/renderer/renderers/quote.py +49 -0
  152. notionary/page/content/renderer/renderers/table.py +115 -0
  153. notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
  154. notionary/page/content/renderer/renderers/table_row.py +17 -0
  155. notionary/page/content/renderer/renderers/todo.py +56 -0
  156. notionary/page/content/renderer/renderers/toggle.py +52 -0
  157. notionary/page/content/renderer/renderers/video.py +31 -0
  158. notionary/page/content/renderer/service.py +50 -0
  159. notionary/page/content/service.py +68 -0
  160. notionary/page/content/syntax/__init__.py +4 -0
  161. notionary/page/content/syntax/grammar.py +10 -0
  162. notionary/page/content/syntax/models.py +66 -0
  163. notionary/page/content/syntax/registry.py +393 -0
  164. notionary/page/page_context.py +7 -16
  165. notionary/page/page_http_client.py +15 -0
  166. notionary/page/page_metadata_update_client.py +19 -0
  167. notionary/page/properties/client.py +144 -0
  168. notionary/page/properties/factory.py +26 -0
  169. notionary/page/properties/models.py +308 -0
  170. notionary/page/properties/service.py +261 -0
  171. notionary/page/schemas.py +13 -0
  172. notionary/page/service.py +225 -0
  173. notionary/shared/entity/client.py +29 -0
  174. notionary/shared/entity/dto_parsers.py +53 -0
  175. notionary/shared/entity/entity_metadata_update_client.py +41 -0
  176. notionary/shared/entity/schemas.py +45 -0
  177. notionary/shared/entity/service.py +171 -0
  178. notionary/shared/models/cover.py +20 -0
  179. notionary/shared/models/file.py +21 -0
  180. notionary/shared/models/icon.py +28 -0
  181. notionary/shared/models/parent.py +41 -0
  182. notionary/shared/properties/type.py +30 -0
  183. notionary/shared/typings.py +3 -0
  184. notionary/user/__init__.py +4 -8
  185. notionary/user/base.py +138 -0
  186. notionary/user/bot.py +70 -0
  187. notionary/user/client.py +22 -111
  188. notionary/user/person.py +41 -0
  189. notionary/user/schemas.py +67 -0
  190. notionary/user/service.py +65 -0
  191. notionary/utils/date.py +51 -0
  192. notionary/utils/decorators.py +122 -0
  193. notionary/utils/fuzzy.py +68 -0
  194. notionary/utils/mixins/logging.py +58 -0
  195. notionary/utils/pagination.py +100 -0
  196. notionary/utils/uuid_utils.py +20 -0
  197. notionary/workspace/__init__.py +4 -0
  198. notionary/workspace/client.py +62 -0
  199. notionary/workspace/query/__init__.py +3 -0
  200. notionary/workspace/query/builder.py +60 -0
  201. notionary/workspace/query/models.py +61 -0
  202. notionary/workspace/query/service.py +100 -0
  203. notionary/workspace/schemas.py +21 -0
  204. notionary/workspace/service.py +116 -0
  205. notionary-0.3.0.dist-info/METADATA +201 -0
  206. notionary-0.3.0.dist-info/RECORD +209 -0
  207. {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
  208. {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
  209. notionary/base_notion_client.py +0 -219
  210. notionary/blocks/_bootstrap.py +0 -271
  211. notionary/blocks/audio/__init__.py +0 -11
  212. notionary/blocks/audio/audio_element.py +0 -158
  213. notionary/blocks/audio/audio_markdown_node.py +0 -24
  214. notionary/blocks/audio/audio_models.py +0 -10
  215. notionary/blocks/base_block_element.py +0 -42
  216. notionary/blocks/bookmark/__init__.py +0 -12
  217. notionary/blocks/bookmark/bookmark_element.py +0 -83
  218. notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
  219. notionary/blocks/bookmark/bookmark_models.py +0 -15
  220. notionary/blocks/breadcrumbs/__init__.py +0 -15
  221. notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
  222. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
  223. notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
  224. notionary/blocks/bulleted_list/__init__.py +0 -15
  225. notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
  226. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
  227. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
  228. notionary/blocks/callout/__init__.py +0 -12
  229. notionary/blocks/callout/callout_element.py +0 -99
  230. notionary/blocks/callout/callout_markdown_node.py +0 -19
  231. notionary/blocks/callout/callout_models.py +0 -33
  232. notionary/blocks/child_database/__init__.py +0 -14
  233. notionary/blocks/child_database/child_database_element.py +0 -59
  234. notionary/blocks/child_database/child_database_models.py +0 -12
  235. notionary/blocks/child_page/__init__.py +0 -9
  236. notionary/blocks/child_page/child_page_element.py +0 -94
  237. notionary/blocks/child_page/child_page_models.py +0 -12
  238. notionary/blocks/code/__init__.py +0 -11
  239. notionary/blocks/code/code_element.py +0 -149
  240. notionary/blocks/code/code_markdown_node.py +0 -80
  241. notionary/blocks/code/code_models.py +0 -94
  242. notionary/blocks/column/__init__.py +0 -25
  243. notionary/blocks/column/column_element.py +0 -65
  244. notionary/blocks/column/column_list_element.py +0 -52
  245. notionary/blocks/column/column_list_markdown_node.py +0 -34
  246. notionary/blocks/column/column_markdown_node.py +0 -42
  247. notionary/blocks/column/column_models.py +0 -26
  248. notionary/blocks/divider/__init__.py +0 -12
  249. notionary/blocks/divider/divider_element.py +0 -41
  250. notionary/blocks/divider/divider_markdown_node.py +0 -11
  251. notionary/blocks/divider/divider_models.py +0 -12
  252. notionary/blocks/embed/__init__.py +0 -12
  253. notionary/blocks/embed/embed_element.py +0 -98
  254. notionary/blocks/embed/embed_markdown_node.py +0 -19
  255. notionary/blocks/embed/embed_models.py +0 -14
  256. notionary/blocks/equation/__init__.py +0 -13
  257. notionary/blocks/equation/equation_element.py +0 -133
  258. notionary/blocks/equation/equation_element_markdown_node.py +0 -23
  259. notionary/blocks/equation/equation_models.py +0 -11
  260. notionary/blocks/file/__init__.py +0 -23
  261. notionary/blocks/file/file_element.py +0 -133
  262. notionary/blocks/file/file_element_markdown_node.py +0 -24
  263. notionary/blocks/file/file_element_models.py +0 -39
  264. notionary/blocks/heading/__init__.py +0 -19
  265. notionary/blocks/heading/heading_element.py +0 -112
  266. notionary/blocks/heading/heading_markdown_node.py +0 -16
  267. notionary/blocks/heading/heading_models.py +0 -29
  268. notionary/blocks/image_block/__init__.py +0 -11
  269. notionary/blocks/image_block/image_element.py +0 -130
  270. notionary/blocks/image_block/image_markdown_node.py +0 -25
  271. notionary/blocks/image_block/image_models.py +0 -10
  272. notionary/blocks/markdown/markdown_builder.py +0 -525
  273. notionary/blocks/markdown/markdown_document_model.py +0 -0
  274. notionary/blocks/markdown/markdown_node.py +0 -25
  275. notionary/blocks/mixins/captions/__init__.py +0 -4
  276. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
  277. notionary/blocks/mixins/captions/caption_mixin.py +0 -92
  278. notionary/blocks/mixins/file_upload/__init__.py +0 -3
  279. notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
  280. notionary/blocks/models.py +0 -174
  281. notionary/blocks/numbered_list/__init__.py +0 -16
  282. notionary/blocks/numbered_list/numbered_list_element.py +0 -65
  283. notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
  284. notionary/blocks/numbered_list/numbered_list_models.py +0 -17
  285. notionary/blocks/paragraph/__init__.py +0 -15
  286. notionary/blocks/paragraph/paragraph_element.py +0 -58
  287. notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
  288. notionary/blocks/paragraph/paragraph_models.py +0 -16
  289. notionary/blocks/pdf/__init__.py +0 -11
  290. notionary/blocks/pdf/pdf_element.py +0 -146
  291. notionary/blocks/pdf/pdf_markdown_node.py +0 -24
  292. notionary/blocks/pdf/pdf_models.py +0 -11
  293. notionary/blocks/quote/__init__.py +0 -14
  294. notionary/blocks/quote/quote_element.py +0 -75
  295. notionary/blocks/quote/quote_markdown_node.py +0 -16
  296. notionary/blocks/quote/quote_models.py +0 -18
  297. notionary/blocks/registry/__init__.py +0 -3
  298. notionary/blocks/registry/block_registry.py +0 -150
  299. notionary/blocks/rich_text/__init__.py +0 -33
  300. notionary/blocks/rich_text/rich_text_models.py +0 -221
  301. notionary/blocks/rich_text/text_inline_formatter.py +0 -456
  302. notionary/blocks/syntax_prompt_builder.py +0 -137
  303. notionary/blocks/table/__init__.py +0 -19
  304. notionary/blocks/table/table_element.py +0 -225
  305. notionary/blocks/table/table_markdown_node.py +0 -42
  306. notionary/blocks/table/table_models.py +0 -28
  307. notionary/blocks/table_of_contents/__init__.py +0 -17
  308. notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
  309. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
  310. notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
  311. notionary/blocks/todo/__init__.py +0 -12
  312. notionary/blocks/todo/todo_element.py +0 -81
  313. notionary/blocks/todo/todo_markdown_node.py +0 -21
  314. notionary/blocks/todo/todo_models.py +0 -18
  315. notionary/blocks/toggle/__init__.py +0 -12
  316. notionary/blocks/toggle/toggle_element.py +0 -112
  317. notionary/blocks/toggle/toggle_markdown_node.py +0 -31
  318. notionary/blocks/toggle/toggle_models.py +0 -17
  319. notionary/blocks/toggleable_heading/__init__.py +0 -11
  320. notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
  321. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
  322. notionary/blocks/types.py +0 -130
  323. notionary/blocks/video/__init__.py +0 -11
  324. notionary/blocks/video/video_element.py +0 -187
  325. notionary/blocks/video/video_element_models.py +0 -10
  326. notionary/blocks/video/video_markdown_node.py +0 -26
  327. notionary/database/__init__.py +0 -4
  328. notionary/database/database.py +0 -480
  329. notionary/database/database_filter_builder.py +0 -173
  330. notionary/database/database_provider.py +0 -227
  331. notionary/database/exceptions.py +0 -13
  332. notionary/database/models.py +0 -337
  333. notionary/database/notion_database.py +0 -487
  334. notionary/file_upload/__init__.py +0 -7
  335. notionary/page/client.py +0 -124
  336. notionary/page/markdown_whitespace_processor.py +0 -129
  337. notionary/page/models.py +0 -322
  338. notionary/page/notion_page.py +0 -712
  339. notionary/page/page_content_deleting_service.py +0 -117
  340. notionary/page/page_content_writer.py +0 -80
  341. notionary/page/property_formatter.py +0 -99
  342. notionary/page/reader/handler/__init__.py +0 -19
  343. notionary/page/reader/handler/base_block_renderer.py +0 -44
  344. notionary/page/reader/handler/block_processing_context.py +0 -35
  345. notionary/page/reader/handler/block_rendering_context.py +0 -48
  346. notionary/page/reader/handler/column_list_renderer.py +0 -51
  347. notionary/page/reader/handler/column_renderer.py +0 -60
  348. notionary/page/reader/handler/equation_renderer.py +0 -0
  349. notionary/page/reader/handler/line_renderer.py +0 -73
  350. notionary/page/reader/handler/numbered_list_renderer.py +0 -85
  351. notionary/page/reader/handler/toggle_renderer.py +0 -69
  352. notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
  353. notionary/page/reader/page_content_retriever.py +0 -81
  354. notionary/page/search_filter_builder.py +0 -132
  355. notionary/page/utils.py +0 -60
  356. notionary/page/writer/handler/__init__.py +0 -24
  357. notionary/page/writer/handler/code_handler.py +0 -72
  358. notionary/page/writer/handler/column_handler.py +0 -141
  359. notionary/page/writer/handler/column_list_handler.py +0 -139
  360. notionary/page/writer/handler/equation_handler.py +0 -74
  361. notionary/page/writer/handler/line_handler.py +0 -35
  362. notionary/page/writer/handler/line_processing_context.py +0 -54
  363. notionary/page/writer/handler/regular_line_handler.py +0 -86
  364. notionary/page/writer/handler/table_handler.py +0 -66
  365. notionary/page/writer/handler/toggle_handler.py +0 -159
  366. notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
  367. notionary/page/writer/markdown_to_notion_converter.py +0 -139
  368. notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
  369. notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
  370. notionary/page/writer/notion_text_length_processor.py +0 -150
  371. notionary/schemas/__init__.py +0 -3
  372. notionary/schemas/base.py +0 -73
  373. notionary/shared/__init__.py +0 -3
  374. notionary/shared/name_to_id_resolver.py +0 -203
  375. notionary/telemetry/__init__.py +0 -19
  376. notionary/telemetry/service.py +0 -136
  377. notionary/telemetry/views.py +0 -73
  378. notionary/user/base_notion_user.py +0 -53
  379. notionary/user/models.py +0 -84
  380. notionary/user/notion_bot_user.py +0 -226
  381. notionary/user/notion_user.py +0 -255
  382. notionary/user/notion_user_manager.py +0 -101
  383. notionary/util/__init__.py +0 -15
  384. notionary/util/concurrency_limiter.py +0 -0
  385. notionary/util/factory_decorator.py +0 -0
  386. notionary/util/factory_only.py +0 -37
  387. notionary/util/fuzzy.py +0 -75
  388. notionary/util/logging_mixin.py +0 -59
  389. notionary/util/page_id_utils.py +0 -27
  390. notionary/util/singleton.py +0 -18
  391. notionary/util/singleton_metaclass.py +0 -22
  392. notionary/workspace.py +0 -105
  393. notionary-0.2.27.dist-info/METADATA +0 -270
  394. notionary-0.2.27.dist-info/RECORD +0 -202
  395. /notionary/{database → user}/factory.py +0 -0
@@ -0,0 +1,73 @@
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 (
6
+ ColumnSyntaxPreProcessor,
7
+ IndentationNormalizer,
8
+ WhitespacePreProcessor,
9
+ )
10
+ from notionary.page.content.parser.pre_processsing.service import MarkdownPreProcessor
11
+ from notionary.page.content.parser.service import MarkdownToNotionConverter
12
+ from notionary.page.content.renderer.factory import RendererChainFactory
13
+ from notionary.page.content.renderer.post_processing.handlers import NumberedListPlaceholderReplacerPostProcessor
14
+ from notionary.page.content.renderer.post_processing.service import MarkdownRenderingPostProcessor
15
+ from notionary.page.content.renderer.service import NotionToMarkdownConverter
16
+ from notionary.page.content.service import PageContentService
17
+
18
+
19
+ class PageContentServiceFactory:
20
+ def __init__(
21
+ self,
22
+ converter_chain_factory: ConverterChainFactory | None = None,
23
+ renderer_chain_factory: RendererChainFactory | None = None,
24
+ ) -> None:
25
+ self._converter_chain_factory = converter_chain_factory or ConverterChainFactory()
26
+ self._renderer_chain_factory = renderer_chain_factory or RendererChainFactory()
27
+
28
+ def create(self, page_id: str, block_client: NotionBlockHttpClient) -> PageContentService:
29
+ markdown_converter = self._create_markdown_to_notion_converter()
30
+ notion_to_markdown_converter = self._create_notion_to_markdown_converter()
31
+
32
+ return PageContentService(
33
+ page_id=page_id,
34
+ block_client=block_client,
35
+ markdown_converter=markdown_converter,
36
+ notion_to_markdown_converter=notion_to_markdown_converter,
37
+ )
38
+
39
+ def _create_markdown_to_notion_converter(self) -> MarkdownToNotionConverter:
40
+ line_parser = self._converter_chain_factory.create()
41
+ markdown_pre_processor = self._create_markdown_preprocessor()
42
+ block_post_processor = self._create_post_processor()
43
+
44
+ return MarkdownToNotionConverter(
45
+ line_parser=line_parser,
46
+ pre_processor=markdown_pre_processor,
47
+ post_processor=block_post_processor,
48
+ )
49
+
50
+ def _create_notion_to_markdown_converter(self) -> NotionToMarkdownConverter:
51
+ renderer_chain = self._renderer_chain_factory.create()
52
+ markdown_rendering_post_processor = self._create_markdown_rendering_post_processor()
53
+ return NotionToMarkdownConverter(
54
+ renderer_chain=renderer_chain,
55
+ post_processor=markdown_rendering_post_processor,
56
+ )
57
+
58
+ def _create_markdown_preprocessor(self) -> MarkdownPreProcessor:
59
+ pre_processor = MarkdownPreProcessor()
60
+ pre_processor.register(ColumnSyntaxPreProcessor())
61
+ pre_processor.register(WhitespacePreProcessor())
62
+ pre_processor.register(IndentationNormalizer())
63
+ return pre_processor
64
+
65
+ def _create_post_processor(self) -> BlockPostProcessor:
66
+ post_processor = BlockPostProcessor()
67
+ post_processor.register(RichTextLengthTruncationPostProcessor())
68
+ return post_processor
69
+
70
+ def _create_markdown_rendering_post_processor(self) -> MarkdownRenderingPostProcessor:
71
+ post_processor = MarkdownRenderingPostProcessor()
72
+ post_processor.register(NumberedListPlaceholderReplacerPostProcessor())
73
+ return post_processor
@@ -0,0 +1,5 @@
1
+ from .builder import MarkdownBuilder
2
+
3
+ __all__ = [
4
+ "MarkdownBuilder",
5
+ ]
@@ -0,0 +1,226 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from typing import Self
5
+
6
+ from notionary.blocks.enums import CodingLanguage
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
+ ToggleMarkdownNode,
32
+ VideoMarkdownNode,
33
+ )
34
+ from notionary.page.content.markdown.nodes.container import flatten_children
35
+
36
+
37
+ class MarkdownBuilder:
38
+ def __init__(self) -> None:
39
+ self.children: list[MarkdownNode] = []
40
+
41
+ def h1(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
42
+ return self._add_heading(text, 1, builder_func)
43
+
44
+ def h2(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
45
+ return self._add_heading(text, 2, builder_func)
46
+
47
+ def h3(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
48
+ return self._add_heading(text, 3, builder_func)
49
+
50
+ def paragraph(self, text: str) -> Self:
51
+ self.children.append(ParagraphMarkdownNode(text=text))
52
+ return self
53
+
54
+ def space(self) -> Self:
55
+ self.children.append(SpaceMarkdownNode())
56
+ return self
57
+
58
+ def quote(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
59
+ children = self._build_children(builder_func)
60
+ self.children.append(QuoteMarkdownNode(text=text, children=children))
61
+ return self
62
+
63
+ def divider(self) -> Self:
64
+ self.children.append(DividerMarkdownNode())
65
+ return self
66
+
67
+ def numbered_list(self, items: list[str]) -> Self:
68
+ self.children.append(NumberedListMarkdownNode(texts=items))
69
+ return self
70
+
71
+ def numbered_list_item(
72
+ self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None
73
+ ) -> Self:
74
+ children = self._build_children(builder_func)
75
+ wrapped = flatten_children(children)
76
+ child_nodes = [wrapped] if wrapped else None
77
+ self.children.append(NumberedListMarkdownNode(texts=[text], children=child_nodes))
78
+ return self
79
+
80
+ def bulleted_list(self, items: list[str]) -> Self:
81
+ self.children.append(BulletedListMarkdownNode(texts=items))
82
+ return self
83
+
84
+ def bulleted_list_item(
85
+ self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None
86
+ ) -> Self:
87
+ children = self._build_children(builder_func)
88
+ wrapped = flatten_children(children)
89
+ child_nodes = [wrapped] if wrapped else None
90
+ self.children.append(BulletedListMarkdownNode(texts=[text], children=child_nodes))
91
+ return self
92
+
93
+ def todo(
94
+ self,
95
+ text: str,
96
+ checked: bool = False,
97
+ builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None,
98
+ ) -> Self:
99
+ children = self._build_children(builder_func)
100
+ self.children.append(TodoMarkdownNode(text=text, checked=checked, children=children))
101
+ return self
102
+
103
+ def checked_todo(self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None) -> Self:
104
+ return self.todo(text, checked=True, builder_func=builder_func)
105
+
106
+ def unchecked_todo(
107
+ self, text: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None
108
+ ) -> Self:
109
+ return self.todo(text, checked=False, builder_func=builder_func)
110
+
111
+ def todo_list(self, items: list[str], completed: list[bool] | None = None) -> Self:
112
+ completed = completed or [False] * len(items)
113
+ for i, item in enumerate(items):
114
+ is_done = completed[i] if i < len(completed) else False
115
+ self.children.append(TodoMarkdownNode(text=item, checked=is_done))
116
+ return self
117
+
118
+ def callout(self, text: str, emoji: str | None = None) -> Self:
119
+ self.children.append(CalloutMarkdownNode(text=text, emoji=emoji))
120
+ return self
121
+
122
+ def callout_with_children(
123
+ self,
124
+ text: str,
125
+ emoji: str | None = None,
126
+ builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None = None,
127
+ ) -> Self:
128
+ children = self._build_children(builder_func)
129
+ self.children.append(CalloutMarkdownNode(text=text, emoji=emoji, children=children))
130
+ return self
131
+
132
+ def toggle(self, title: str, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder]) -> Self:
133
+ children = self._build_children(builder_func)
134
+ self.children.append(ToggleMarkdownNode(title=title, children=children))
135
+ return self
136
+
137
+ def image(self, url: str, caption: str | None = None) -> Self:
138
+ self.children.append(ImageMarkdownNode(url=url, caption=caption))
139
+ return self
140
+
141
+ def video(self, url: str, caption: str | None = None) -> Self:
142
+ self.children.append(VideoMarkdownNode(url=url, caption=caption))
143
+ return self
144
+
145
+ def audio(self, url: str, caption: str | None = None) -> Self:
146
+ self.children.append(AudioMarkdownNode(url=url, caption=caption))
147
+ return self
148
+
149
+ def file(self, url: str, caption: str | None = None) -> Self:
150
+ self.children.append(FileMarkdownNode(url=url, caption=caption))
151
+ return self
152
+
153
+ def pdf(self, url: str, caption: str | None = None) -> Self:
154
+ self.children.append(PdfMarkdownNode(url=url, caption=caption))
155
+ return self
156
+
157
+ def bookmark(self, url: str, title: str | None = None, caption: str | None = None) -> Self:
158
+ self.children.append(BookmarkMarkdownNode(url=url, title=title, caption=caption))
159
+ return self
160
+
161
+ def embed(self, url: str, caption: str | None = None) -> Self:
162
+ self.children.append(EmbedMarkdownNode(url=url, caption=caption))
163
+ return self
164
+
165
+ def code(self, code: str, language: CodingLanguage | None = None, caption: str | None = None) -> Self:
166
+ self.children.append(CodeMarkdownNode(code=code, language=language, caption=caption))
167
+ return self
168
+
169
+ def mermaid(self, diagram: str, caption: str | None = None) -> Self:
170
+ self.children.append(CodeMarkdownNode(code=diagram, language=CodingLanguage.MERMAID.value, caption=caption))
171
+ return self
172
+
173
+ def table(self, headers: list[str], rows: list[list[str]]) -> Self:
174
+ self.children.append(TableMarkdownNode(headers=headers, rows=rows))
175
+ return self
176
+
177
+ def breadcrumb(self) -> Self:
178
+ self.children.append(BreadcrumbMarkdownNode())
179
+ return self
180
+
181
+ def equation(self, expression: str) -> Self:
182
+ self.children.append(EquationMarkdownNode(expression=expression))
183
+ return self
184
+
185
+ def table_of_contents(self) -> Self:
186
+ self.children.append(TableOfContentsMarkdownNode())
187
+ return self
188
+
189
+ def columns(
190
+ self,
191
+ *builder_funcs: Callable[[MarkdownBuilder], MarkdownBuilder],
192
+ width_ratios: list[float] | None = None,
193
+ ) -> Self:
194
+ columns = self._build_columns(builder_funcs, width_ratios)
195
+ self.children.append(ColumnListMarkdownNode(columns=columns))
196
+ return self
197
+
198
+ def build(self) -> str:
199
+ return "\n\n".join(child.to_markdown() for child in self.children if child is not None)
200
+
201
+ def _add_heading(
202
+ self, text: str, level: int, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None
203
+ ) -> Self:
204
+ children = self._build_children(builder_func)
205
+ self.children.append(HeadingMarkdownNode(text=text, level=level, children=children))
206
+ return self
207
+
208
+ def _build_children(self, builder_func: Callable[[MarkdownBuilder], MarkdownBuilder] | None) -> list[MarkdownNode]:
209
+ if builder_func is None:
210
+ return []
211
+
212
+ builder = MarkdownBuilder()
213
+ builder_func(builder)
214
+ return builder.children
215
+
216
+ def _build_columns(
217
+ self,
218
+ builder_funcs: tuple[Callable[[MarkdownBuilder], MarkdownBuilder], ...],
219
+ width_ratios: list[float] | None,
220
+ ) -> list[ColumnMarkdownNode]:
221
+ columns = []
222
+ for i, builder_func in enumerate(builder_funcs):
223
+ width_ratio = width_ratios[i] if width_ratios else None
224
+ children = self._build_children(builder_func)
225
+ columns.append(ColumnMarkdownNode(children=children, width_ratio=width_ratio))
226
+ return columns
@@ -0,0 +1,52 @@
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 .video import VideoMarkdownNode
25
+
26
+ __all__ = [
27
+ "AudioMarkdownNode",
28
+ "BookmarkMarkdownNode",
29
+ "BreadcrumbMarkdownNode",
30
+ "BulletedListMarkdownNode",
31
+ "CalloutMarkdownNode",
32
+ "CodeMarkdownNode",
33
+ "ColumnListMarkdownNode",
34
+ "ColumnMarkdownNode",
35
+ "DividerMarkdownNode",
36
+ "EmbedMarkdownNode",
37
+ "EquationMarkdownNode",
38
+ "FileMarkdownNode",
39
+ "HeadingMarkdownNode",
40
+ "ImageMarkdownNode",
41
+ "MarkdownNode",
42
+ "NumberedListMarkdownNode",
43
+ "ParagraphMarkdownNode",
44
+ "PdfMarkdownNode",
45
+ "QuoteMarkdownNode",
46
+ "SpaceMarkdownNode",
47
+ "TableMarkdownNode",
48
+ "TableOfContentsMarkdownNode",
49
+ "TodoMarkdownNode",
50
+ "ToggleMarkdownNode",
51
+ "VideoMarkdownNode",
52
+ ]
@@ -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 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 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 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 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,41 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.markdown.nodes.container import ContainerNode
5
+ from notionary.page.content.syntax import SyntaxRegistry
6
+
7
+
8
+ class BulletedListMarkdownNode(ContainerNode):
9
+ def __init__(
10
+ self,
11
+ texts: list[str],
12
+ children: list[MarkdownNode | None] | None = None,
13
+ syntax_registry: SyntaxRegistry | None = None,
14
+ ) -> None:
15
+ super().__init__(syntax_registry=syntax_registry)
16
+ self.texts = texts
17
+ self.children = children or []
18
+
19
+ @override
20
+ def to_markdown(self) -> str:
21
+ list_items = [self._render_list_item(index, text) for index, text in enumerate(self.texts)]
22
+ return "\n".join(list_items)
23
+
24
+ def _render_list_item(self, index: int, text: str) -> str:
25
+ delimiter = self._get_list_delimiter()
26
+ item_line = f"{delimiter}{text}"
27
+
28
+ child = self._get_child_for_item(index)
29
+ if child:
30
+ child_content = self.render_child(child)
31
+ return f"{item_line}\n{child_content}"
32
+
33
+ return item_line
34
+
35
+ def _get_list_delimiter(self) -> str:
36
+ return self._syntax_registry.get_bulleted_list_syntax().start_delimiter
37
+
38
+ def _get_child_for_item(self, index: int) -> MarkdownNode | None:
39
+ if not self.children or index >= len(self.children):
40
+ return None
41
+ return self.children[index]
@@ -0,0 +1,34 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.markdown.nodes.container import ContainerNode
5
+ from notionary.page.content.syntax import SyntaxRegistry
6
+
7
+
8
+ class CalloutMarkdownNode(ContainerNode):
9
+ def __init__(
10
+ self,
11
+ text: str,
12
+ emoji: str | None = None,
13
+ children: list[MarkdownNode] | None = None,
14
+ syntax_registry: SyntaxRegistry | None = None,
15
+ ):
16
+ super().__init__(syntax_registry=syntax_registry)
17
+ self.text = text
18
+ self.emoji = emoji
19
+ self.children = children or []
20
+
21
+ @override
22
+ def to_markdown(self) -> str:
23
+ callout_content = self._format_callout_content()
24
+ start_delimiter = self._syntax_registry.get_callout_syntax().start_delimiter
25
+ result = f"{start_delimiter}({callout_content})"
26
+
27
+ result += self.render_children()
28
+
29
+ return result
30
+
31
+ def _format_callout_content(self) -> str:
32
+ if self.emoji:
33
+ return f'{self.text} "{self.emoji}"'
34
+ return self.text
@@ -0,0 +1,28 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.enums import CodingLanguage
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 import SyntaxRegistry
7
+
8
+
9
+ class CodeMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
10
+ def __init__(
11
+ self,
12
+ code: str,
13
+ language: CodingLanguage | None = None,
14
+ caption: str | None = None,
15
+ syntax_registry: SyntaxRegistry | None = None,
16
+ ) -> None:
17
+ super().__init__(syntax_registry=syntax_registry)
18
+ self.code = code
19
+ self.language = language
20
+ self.caption = caption
21
+
22
+ @override
23
+ def to_markdown(self) -> str:
24
+ code_syntax = self._syntax_registry.get_code_syntax()
25
+ lang = self.language or ""
26
+
27
+ base_markdown = f"{code_syntax.start_delimiter}{lang}\n{self.code}\n{code_syntax.end_delimiter}"
28
+ return self._append_caption_to_markdown(base_markdown, self.caption)
@@ -0,0 +1,69 @@
1
+ from typing import override
2
+
3
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
4
+ from notionary.page.content.markdown.nodes.container import ContainerNode
5
+ from notionary.page.content.syntax import SyntaxRegistry
6
+
7
+
8
+ class ColumnMarkdownNode(ContainerNode):
9
+ def __init__(
10
+ self,
11
+ children: list[MarkdownNode] | None = None,
12
+ width_ratio: float | None = None,
13
+ syntax_registry: SyntaxRegistry | None = None,
14
+ ):
15
+ super().__init__(syntax_registry=syntax_registry)
16
+ self.children = children or []
17
+ self.width_ratio = width_ratio
18
+
19
+ @override
20
+ def to_markdown(self) -> str:
21
+ start_tag = self._format_column_start_tag()
22
+ result = start_tag + self.render_children()
23
+ return result
24
+
25
+ def _format_column_start_tag(self) -> str:
26
+ delimiter = self._syntax_registry.get_column_syntax().start_delimiter
27
+
28
+ if self.width_ratio is not None:
29
+ return f"{delimiter} {self.width_ratio}"
30
+ return delimiter
31
+
32
+
33
+ class ColumnListMarkdownNode(MarkdownNode):
34
+ def __init__(
35
+ self,
36
+ columns: list[ColumnMarkdownNode] | None = None,
37
+ syntax_registry: SyntaxRegistry | None = None,
38
+ ):
39
+ super().__init__(syntax_registry=syntax_registry)
40
+ self.columns = columns or []
41
+
42
+ @override
43
+ def to_markdown(self) -> str:
44
+ start_delimiter = self._get_column_list_delimiter()
45
+
46
+ if not self.columns:
47
+ return start_delimiter
48
+
49
+ result = start_delimiter + self._render_columns()
50
+ return result
51
+
52
+ def _get_column_list_delimiter(self) -> str:
53
+ return self._syntax_registry.get_column_list_syntax().start_delimiter
54
+
55
+ def _render_columns(self) -> str:
56
+ rendered_columns = []
57
+
58
+ for column in self.columns:
59
+ column_markdown = column.to_markdown()
60
+ if column_markdown:
61
+ indented = self._indent_column(column_markdown)
62
+ rendered_columns.append(indented)
63
+
64
+ return "\n" + "\n".join(rendered_columns) if rendered_columns else ""
65
+
66
+ @staticmethod
67
+ def _indent_column(text: str, indent: str = " ") -> str:
68
+ lines = text.split("\n")
69
+ return "\n".join(f"{indent}{line}" if line.strip() else line for line in lines)
@@ -0,0 +1,64 @@
1
+ from notionary.page.content.markdown.nodes.base import MarkdownNode
2
+ from notionary.page.content.syntax import SyntaxRegistry
3
+ from notionary.page.content.syntax.grammar import MarkdownGrammar
4
+
5
+
6
+ def flatten_children(children: list[MarkdownNode]) -> MarkdownNode | None:
7
+ """Convert a list of child nodes to a single node for container compatibility."""
8
+ if not children:
9
+ return None
10
+ if len(children) == 1:
11
+ return children[0]
12
+ return _MultiChildWrapper(children)
13
+
14
+
15
+ class _MultiChildWrapper(MarkdownNode):
16
+ def __init__(self, children: list[MarkdownNode]) -> None:
17
+ super().__init__()
18
+ self.children = children
19
+
20
+ def to_markdown(self) -> str:
21
+ return "\n".join(child.to_markdown() for child in self.children if child)
22
+
23
+
24
+ class ContainerNode(MarkdownNode):
25
+ children: list[MarkdownNode]
26
+
27
+ def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
28
+ super().__init__(syntax_registry=syntax_registry)
29
+ grammar = self._get_grammar(syntax_registry)
30
+ self._spaces_per_nesting_level = grammar.spaces_per_nesting_level
31
+
32
+ def render_children(self, indent_level: int = 1) -> str:
33
+ if not self.children:
34
+ return ""
35
+
36
+ indent = self._calculate_indent(indent_level)
37
+ rendered = [self._indent_child(child, indent) for child in self.children]
38
+ rendered = [text for text in rendered if text]
39
+
40
+ return f"\n{'\n'.join(rendered)}" if rendered else ""
41
+
42
+ def render_child(self, child: MarkdownNode, indent_level: int = 1) -> str:
43
+ child_markdown = child.to_markdown()
44
+ if not child_markdown:
45
+ return ""
46
+
47
+ indent = self._calculate_indent(indent_level)
48
+ return self._indent_text(child_markdown, indent)
49
+
50
+ def _calculate_indent(self, level: int) -> str:
51
+ return " " * (self._spaces_per_nesting_level * level)
52
+
53
+ def _indent_child(self, child: MarkdownNode, indent: str) -> str:
54
+ child_markdown = child.to_markdown()
55
+ return self._indent_text(child_markdown, indent) if child_markdown else ""
56
+
57
+ @staticmethod
58
+ def _indent_text(text: str, indent: str) -> str:
59
+ lines = text.split("\n")
60
+ return "\n".join(f"{indent}{line}" if line.strip() else line for line in lines)
61
+
62
+ @staticmethod
63
+ def _get_grammar(syntax_registry: SyntaxRegistry | None) -> MarkdownGrammar:
64
+ return syntax_registry._markdown_grammar if syntax_registry else MarkdownGrammar()