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,51 @@
1
+ from collections.abc import Awaitable, Callable
2
+
3
+ from notionary.blocks.schemas import Block
4
+ from notionary.page.content.syntax.grammar import MarkdownGrammar
5
+
6
+ ConvertChildrenCallback = Callable[[list[Block], int], Awaitable[str]]
7
+
8
+
9
+ class MarkdownRenderingContext:
10
+ def __init__(
11
+ self,
12
+ block: Block,
13
+ indent_level: int,
14
+ convert_children_callback: ConvertChildrenCallback | None = None,
15
+ markdown_grammar: MarkdownGrammar | None = None,
16
+ ) -> None:
17
+ self.block = block
18
+ self.indent_level = indent_level
19
+ self.convert_children_callback = convert_children_callback
20
+ markdown_grammar = markdown_grammar or MarkdownGrammar()
21
+ self._spaces_per_nesting_level = markdown_grammar.spaces_per_nesting_level
22
+
23
+ self.markdown_result: str | None = None
24
+
25
+ async def render_children(self) -> str:
26
+ return await self._convert_children_to_markdown(self.indent_level)
27
+
28
+ async def render_children_with_additional_indent(self, additional_indent: int) -> str:
29
+ return await self._convert_children_to_markdown(self.indent_level + additional_indent)
30
+
31
+ async def _convert_children_to_markdown(self, indent_level: int) -> str:
32
+ if not self._has_children() or not self.convert_children_callback:
33
+ return ""
34
+
35
+ return await self.convert_children_callback(self._get_children_blocks(), indent_level)
36
+
37
+ def _get_children_blocks(self) -> list[Block]:
38
+ if self._has_children():
39
+ return self.block.children
40
+ return []
41
+
42
+ def _has_children(self) -> bool:
43
+ return self.block.has_children and self.block.children and len(self.block.children) > 0
44
+
45
+ def indent_text(self, text: str) -> str:
46
+ if not text:
47
+ return text
48
+
49
+ spaces = " " * self._spaces_per_nesting_level * self.indent_level
50
+ lines = text.split("\n")
51
+ return "\n".join(f"{spaces}{line}" if line.strip() else line for line in lines)
@@ -0,0 +1,231 @@
1
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
2
+ RichTextToMarkdownConverter,
3
+ )
4
+ from notionary.page.content.renderer.renderers import (
5
+ AudioRenderer,
6
+ BlockRenderer,
7
+ BookmarkRenderer,
8
+ BreadcrumbRenderer,
9
+ BulletedListRenderer,
10
+ CalloutRenderer,
11
+ CodeRenderer,
12
+ ColumnListRenderer,
13
+ ColumnRenderer,
14
+ DividerRenderer,
15
+ EmbedRenderer,
16
+ EquationRenderer,
17
+ FallbackRenderer,
18
+ FileRenderer,
19
+ HeadingRenderer,
20
+ ImageRenderer,
21
+ NumberedListRenderer,
22
+ ParagraphRenderer,
23
+ PdfRenderer,
24
+ QuoteRenderer,
25
+ TableOfContentsRenderer,
26
+ TableRenderer,
27
+ TableRowHandler,
28
+ TodoRenderer,
29
+ ToggleRenderer,
30
+ VideoRenderer,
31
+ )
32
+ from notionary.page.content.syntax import SyntaxRegistry
33
+
34
+
35
+ class RendererChainFactory:
36
+ def __init__(
37
+ self,
38
+ rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
39
+ syntax_registry: SyntaxRegistry | None = None,
40
+ ) -> None:
41
+ self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
42
+ self._syntax_registry = syntax_registry or SyntaxRegistry()
43
+
44
+ def create(self) -> BlockRenderer:
45
+ # Strukturelle Blocks
46
+ toggle_handler = self._create_toggle_renderer()
47
+ heading_handler = self._create_heading_renderer()
48
+
49
+ # Content Blocks
50
+ callout_handler = self._create_callout_renderer()
51
+ code_handler = self._create_code_renderer()
52
+ quote_handler = self._create_quote_renderer()
53
+ todo_handler = self._create_todo_renderer()
54
+ bulleted_list_handler = self._create_bulleted_list_renderer()
55
+
56
+ # Layout Blocks
57
+ divider_handler = self._create_divider_renderer()
58
+ column_list_handler = self._create_column_list_renderer()
59
+ column_handler = self._create_column_renderer()
60
+ numbered_list_handler = self._create_numbered_list_renderer()
61
+
62
+ # Media Blocks
63
+ bookmark_handler = self._create_bookmark_renderer()
64
+ image_handler = self._create_image_renderer()
65
+ video_handler = self._create_video_renderer()
66
+ audio_handler = self._create_audio_renderer()
67
+ file_handler = self._create_file_renderer()
68
+ pdf_handler = self._create_pdf_renderer()
69
+ embed_handler = self._create_embed_renderer()
70
+
71
+ # Special Blocks
72
+ equation_handler = self._create_equation_renderer()
73
+ table_of_contents_handler = self._create_table_of_contents_renderer()
74
+ breadcrumb_handler = self._create_breadcrumb_renderer()
75
+ table_handler = self._create_table_renderer()
76
+ table_row_handler = self._create_table_row_handler()
77
+
78
+ # Standard & Fallback
79
+ paragraph_handler = self._create_paragraph_renderer()
80
+ fallback_handler = self._create_fallback_renderer()
81
+
82
+ # Chain verketten - most specific first, fallback last
83
+ (
84
+ toggle_handler.set_next(heading_handler)
85
+ .set_next(callout_handler)
86
+ .set_next(code_handler)
87
+ .set_next(quote_handler)
88
+ .set_next(todo_handler)
89
+ .set_next(bulleted_list_handler)
90
+ .set_next(divider_handler)
91
+ .set_next(column_list_handler)
92
+ .set_next(column_handler)
93
+ .set_next(numbered_list_handler)
94
+ .set_next(bookmark_handler)
95
+ .set_next(image_handler)
96
+ .set_next(video_handler)
97
+ .set_next(audio_handler)
98
+ .set_next(file_handler)
99
+ .set_next(pdf_handler)
100
+ .set_next(embed_handler)
101
+ .set_next(equation_handler)
102
+ .set_next(table_of_contents_handler)
103
+ .set_next(breadcrumb_handler)
104
+ .set_next(table_handler)
105
+ .set_next(table_row_handler)
106
+ .set_next(paragraph_handler)
107
+ .set_next(fallback_handler)
108
+ )
109
+
110
+ return toggle_handler
111
+
112
+ # Renderer Creation Methods
113
+ def _create_toggle_renderer(self) -> ToggleRenderer:
114
+ return ToggleRenderer(
115
+ syntax_registry=self._syntax_registry,
116
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
117
+ )
118
+
119
+ def _create_heading_renderer(self) -> HeadingRenderer:
120
+ return HeadingRenderer(
121
+ syntax_registry=self._syntax_registry,
122
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
123
+ )
124
+
125
+ def _create_callout_renderer(self) -> CalloutRenderer:
126
+ return CalloutRenderer(
127
+ syntax_registry=self._syntax_registry,
128
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
129
+ )
130
+
131
+ def _create_code_renderer(self) -> CodeRenderer:
132
+ return CodeRenderer(
133
+ syntax_registry=self._syntax_registry,
134
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
135
+ )
136
+
137
+ def _create_quote_renderer(self) -> QuoteRenderer:
138
+ return QuoteRenderer(
139
+ syntax_registry=self._syntax_registry,
140
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
141
+ )
142
+
143
+ def _create_todo_renderer(self) -> TodoRenderer:
144
+ return TodoRenderer(
145
+ syntax_registry=self._syntax_registry,
146
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
147
+ )
148
+
149
+ def _create_bulleted_list_renderer(self) -> BulletedListRenderer:
150
+ return BulletedListRenderer(
151
+ syntax_registry=self._syntax_registry,
152
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
153
+ )
154
+
155
+ def _create_divider_renderer(self) -> DividerRenderer:
156
+ return DividerRenderer(syntax_registry=self._syntax_registry)
157
+
158
+ def _create_column_list_renderer(self) -> ColumnListRenderer:
159
+ return ColumnListRenderer(syntax_registry=self._syntax_registry)
160
+
161
+ def _create_column_renderer(self) -> ColumnRenderer:
162
+ return ColumnRenderer(syntax_registry=self._syntax_registry)
163
+
164
+ def _create_numbered_list_renderer(self) -> NumberedListRenderer:
165
+ return NumberedListRenderer(
166
+ syntax_registry=self._syntax_registry,
167
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
168
+ )
169
+
170
+ def _create_bookmark_renderer(self) -> BookmarkRenderer:
171
+ return BookmarkRenderer(
172
+ syntax_registry=self._syntax_registry,
173
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
174
+ )
175
+
176
+ def _create_image_renderer(self) -> ImageRenderer:
177
+ return ImageRenderer(
178
+ syntax_registry=self._syntax_registry,
179
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
180
+ )
181
+
182
+ def _create_video_renderer(self) -> VideoRenderer:
183
+ return VideoRenderer(
184
+ syntax_registry=self._syntax_registry,
185
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
186
+ )
187
+
188
+ def _create_audio_renderer(self) -> AudioRenderer:
189
+ return AudioRenderer(
190
+ syntax_registry=self._syntax_registry,
191
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
192
+ )
193
+
194
+ def _create_file_renderer(self) -> FileRenderer:
195
+ return FileRenderer(
196
+ syntax_registry=self._syntax_registry,
197
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
198
+ )
199
+
200
+ def _create_pdf_renderer(self) -> PdfRenderer:
201
+ return PdfRenderer(
202
+ syntax_registry=self._syntax_registry,
203
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
204
+ )
205
+
206
+ def _create_embed_renderer(self) -> EmbedRenderer:
207
+ return EmbedRenderer(
208
+ syntax_registry=self._syntax_registry,
209
+ rich_text_markdown_converter=self._rich_text_markdown_converter,
210
+ )
211
+
212
+ def _create_equation_renderer(self) -> EquationRenderer:
213
+ return EquationRenderer(syntax_registry=self._syntax_registry)
214
+
215
+ def _create_table_of_contents_renderer(self) -> TableOfContentsRenderer:
216
+ return TableOfContentsRenderer(syntax_registry=self._syntax_registry)
217
+
218
+ def _create_breadcrumb_renderer(self) -> BreadcrumbRenderer:
219
+ return BreadcrumbRenderer(syntax_registry=self._syntax_registry)
220
+
221
+ def _create_table_renderer(self) -> TableRenderer:
222
+ return TableRenderer(rich_text_markdown_converter=self._rich_text_markdown_converter)
223
+
224
+ def _create_table_row_handler(self) -> TableRowHandler:
225
+ return TableRowHandler()
226
+
227
+ def _create_paragraph_renderer(self) -> ParagraphRenderer:
228
+ return ParagraphRenderer(rich_text_markdown_converter=self._rich_text_markdown_converter)
229
+
230
+ def _create_fallback_renderer(self) -> FallbackRenderer:
231
+ return FallbackRenderer()
@@ -0,0 +1,5 @@
1
+ from .numbered_list import NumberedListPlaceholderReplacerPostProcessor
2
+
3
+ __all__ = [
4
+ "NumberedListPlaceholderReplacerPostProcessor",
5
+ ]
@@ -0,0 +1,156 @@
1
+ import re
2
+ from enum import IntEnum
3
+ from typing import override
4
+
5
+ from notionary.page.content.renderer.post_processing.port import PostProcessor
6
+ from notionary.page.content.syntax.grammar import MarkdownGrammar
7
+
8
+
9
+ class _NumberingStyle(IntEnum):
10
+ NUMERIC = 0
11
+ ALPHABETIC = 1
12
+ ROMAN = 2
13
+
14
+
15
+ class _ListNumberingState:
16
+ def __init__(self):
17
+ self._counters_by_level: dict[int, int] = {}
18
+ self._current_level = -1
19
+
20
+ def advance_to_level(self, new_level: int) -> None:
21
+ self._forget_deeper_levels_than(new_level)
22
+ self._increment_counter_at_level(new_level)
23
+ self._current_level = new_level
24
+
25
+ def get_number_for_current_level(self) -> str:
26
+ counter = self._counters_by_level[self._current_level]
27
+ style = self._determine_numbering_style(self._current_level)
28
+ return self._format_number(counter, style)
29
+
30
+ def reset(self) -> None:
31
+ self._counters_by_level.clear()
32
+ self._current_level = -1
33
+
34
+ def _forget_deeper_levels_than(self, level: int) -> None:
35
+ self._counters_by_level = {
36
+ existing_level: count
37
+ for existing_level, count in self._counters_by_level.items()
38
+ if existing_level <= level
39
+ }
40
+
41
+ def _increment_counter_at_level(self, level: int) -> None:
42
+ self._counters_by_level[level] = self._counters_by_level.get(level, 0) + 1
43
+
44
+ def _determine_numbering_style(self, nesting_level: int) -> _NumberingStyle:
45
+ return _NumberingStyle(nesting_level % 3)
46
+
47
+ def _format_number(self, counter: int, style: _NumberingStyle) -> str:
48
+ if style == _NumberingStyle.NUMERIC:
49
+ return str(counter)
50
+ elif style == _NumberingStyle.ALPHABETIC:
51
+ return self._to_alphabetic(counter)
52
+ else:
53
+ return self._to_roman(counter)
54
+
55
+ def _to_alphabetic(self, number: int) -> str:
56
+ result = ""
57
+ number -= 1
58
+ while number >= 0:
59
+ result = chr(ord("a") + (number % 26)) + result
60
+ number = number // 26 - 1
61
+ return result
62
+
63
+ def _to_roman(self, number: int) -> str:
64
+ conversions = [
65
+ (1000, "m"),
66
+ (900, "cm"),
67
+ (500, "d"),
68
+ (400, "cd"),
69
+ (100, "c"),
70
+ (90, "xc"),
71
+ (50, "l"),
72
+ (40, "xl"),
73
+ (10, "x"),
74
+ (9, "ix"),
75
+ (5, "v"),
76
+ (4, "iv"),
77
+ (1, "i"),
78
+ ]
79
+
80
+ result = ""
81
+ for arabic, roman in conversions:
82
+ while number >= arabic:
83
+ result += roman
84
+ number -= arabic
85
+ return result
86
+
87
+
88
+ class NumberedListPlaceholderReplacerPostProcessor(PostProcessor):
89
+ def __init__(self, markdown_grammar: MarkdownGrammar | None = None) -> None:
90
+ self._markdown_grammar = markdown_grammar or MarkdownGrammar()
91
+ self._spaces_per_nesting_level = self._markdown_grammar.spaces_per_nesting_level
92
+ self._numbered_list_placeholder = self._markdown_grammar.numbered_list_placeholder
93
+
94
+ @override
95
+ def process(self, markdown_text: str) -> str:
96
+ lines = markdown_text.splitlines()
97
+ return self._convert_placeholder_lists_to_numbered_lists(lines)
98
+
99
+ def _convert_placeholder_lists_to_numbered_lists(self, lines: list[str]) -> str:
100
+ result = []
101
+ list_state = _ListNumberingState()
102
+
103
+ for line_index, line in enumerate(lines):
104
+ if self._is_placeholder_list_item(line):
105
+ numbered_line = self._convert_to_numbered_item(line, list_state)
106
+ result.append(numbered_line)
107
+ elif self._is_blank_between_list_items(lines, line_index, result):
108
+ continue
109
+ else:
110
+ list_state.reset()
111
+ result.append(line)
112
+
113
+ return "\n".join(result)
114
+
115
+ def _convert_to_numbered_item(self, line: str, state: _ListNumberingState) -> str:
116
+ indentation = self._extract_indentation(line)
117
+ content = self._extract_content(line)
118
+ nesting_level = self._calculate_nesting_level(indentation)
119
+
120
+ state.advance_to_level(nesting_level)
121
+ number = state.get_number_for_current_level()
122
+
123
+ return f"{indentation}{number}. {content}"
124
+
125
+ def _calculate_nesting_level(self, indentation: str) -> int:
126
+ return len(indentation) // self._spaces_per_nesting_level
127
+
128
+ def _extract_indentation(self, line: str) -> str:
129
+ match = re.match(rf"^(\s*){re.escape(self._numbered_list_placeholder)}\.", line)
130
+ return match.group(1) if match else ""
131
+
132
+ def _extract_content(self, line: str) -> str:
133
+ match = re.match(rf"^\s*{re.escape(self._numbered_list_placeholder)}\.\s*(.*)", line)
134
+ return match.group(1) if match else ""
135
+
136
+ def _is_placeholder_list_item(self, line: str) -> bool:
137
+ return bool(re.match(rf"^\s*{re.escape(self._numbered_list_placeholder)}\.", line))
138
+
139
+ def _is_blank_between_list_items(self, lines: list[str], current_index: int, processed_lines: list[str]) -> bool:
140
+ if not self._is_blank(lines[current_index]):
141
+ return False
142
+
143
+ previous_line_was_list_item = processed_lines and self._looks_like_numbered_list_item(processed_lines[-1])
144
+ if not previous_line_was_list_item:
145
+ return False
146
+
147
+ next_line_is_list_item = current_index + 1 < len(lines) and self._is_placeholder_list_item(
148
+ lines[current_index + 1]
149
+ )
150
+ return next_line_is_list_item
151
+
152
+ def _is_blank(self, line: str) -> bool:
153
+ return not line.strip()
154
+
155
+ def _looks_like_numbered_list_item(self, line: str) -> bool:
156
+ return bool(re.match(r"^\s*(\d+|[a-z]+|[ivxlcdm]+)\.\s+", line))
@@ -0,0 +1,7 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class PostProcessor(ABC):
5
+ @abstractmethod
6
+ def process(self, markdown_text: str) -> str:
7
+ pass
@@ -0,0 +1,15 @@
1
+ from notionary.page.content.parser.post_processing.port import PostProcessor
2
+
3
+
4
+ class MarkdownRenderingPostProcessor:
5
+ def __init__(self) -> None:
6
+ self._processors: list[PostProcessor] = []
7
+
8
+ def register(self, processor: PostProcessor) -> None:
9
+ self._processors.append(processor)
10
+
11
+ def process(self, markdown_text: str) -> str:
12
+ result = markdown_text
13
+ for processor in self._processors:
14
+ result = processor.process(result)
15
+ return result
@@ -0,0 +1,55 @@
1
+ from .audio import AudioRenderer
2
+ from .base import BlockRenderer
3
+ from .bookmark import BookmarkRenderer
4
+ from .breadcrumb import BreadcrumbRenderer
5
+ from .bulleted_list import BulletedListRenderer
6
+ from .callout import CalloutRenderer
7
+ from .code import CodeRenderer
8
+ from .column import ColumnRenderer
9
+ from .column_list import ColumnListRenderer
10
+ from .divider import DividerRenderer
11
+ from .embed import EmbedRenderer
12
+ from .equation import EquationRenderer
13
+ from .fallback import FallbackRenderer
14
+ from .file import FileRenderer
15
+ from .heading import HeadingRenderer
16
+ from .image import ImageRenderer
17
+ from .numbered_list import NumberedListRenderer
18
+ from .paragraph import ParagraphRenderer
19
+ from .pdf import PdfRenderer
20
+ from .quote import QuoteRenderer
21
+ from .table import TableRenderer
22
+ from .table_of_contents import TableOfContentsRenderer
23
+ from .table_row import TableRowHandler
24
+ from .todo import TodoRenderer
25
+ from .toggle import ToggleRenderer
26
+ from .video import VideoRenderer
27
+
28
+ __all__ = [
29
+ "AudioRenderer",
30
+ "BlockRenderer",
31
+ "BookmarkRenderer",
32
+ "BreadcrumbRenderer",
33
+ "BulletedListRenderer",
34
+ "CalloutRenderer",
35
+ "CodeRenderer",
36
+ "ColumnListRenderer",
37
+ "ColumnRenderer",
38
+ "DividerRenderer",
39
+ "EmbedRenderer",
40
+ "EquationRenderer",
41
+ "FallbackRenderer",
42
+ "FileRenderer",
43
+ "HeadingRenderer",
44
+ "ImageRenderer",
45
+ "NumberedListRenderer",
46
+ "ParagraphRenderer",
47
+ "PdfRenderer",
48
+ "QuoteRenderer",
49
+ "TableOfContentsRenderer",
50
+ "TableRenderer",
51
+ "TableRowHandler",
52
+ "TodoRenderer",
53
+ "ToggleRenderer",
54
+ "VideoRenderer",
55
+ ]
@@ -0,0 +1,31 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.schemas import Block, BlockType
4
+ from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
5
+
6
+
7
+ class AudioRenderer(CaptionedBlockRenderer):
8
+ @override
9
+ def _can_handle(self, block: Block) -> bool:
10
+ return block.type == BlockType.AUDIO
11
+
12
+ @override
13
+ async def _render_main_content(self, block: Block) -> str:
14
+ url = self._extract_audio_url(block)
15
+
16
+ if not url:
17
+ return ""
18
+
19
+ syntax = self._syntax_registry.get_audio_syntax()
20
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+
22
+ def _extract_audio_url(self, block: Block) -> str:
23
+ if not block.audio:
24
+ return ""
25
+
26
+ if block.audio.external:
27
+ return block.audio.external.url or ""
28
+ elif block.audio.file:
29
+ return block.audio.file.url or ""
30
+
31
+ return ""
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+ from notionary.blocks.schemas import Block
6
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
7
+ from notionary.page.content.syntax import SyntaxRegistry
8
+
9
+
10
+ class BlockRenderer(ABC):
11
+ def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
12
+ self._syntax_registry = syntax_registry or SyntaxRegistry()
13
+ self._next_handler: BlockRenderer | None = None
14
+
15
+ def set_next(self, handler: BlockRenderer) -> BlockRenderer:
16
+ self._next_handler = handler
17
+ return handler
18
+
19
+ async def handle(self, context: MarkdownRenderingContext) -> None:
20
+ if self._can_handle(context.block):
21
+ await self._process(context)
22
+ elif self._next_handler:
23
+ await self._next_handler.handle(context)
24
+
25
+ @abstractmethod
26
+ def _can_handle(self, block: Block) -> bool:
27
+ pass
28
+
29
+ @abstractmethod
30
+ async def _process(self, context: MarkdownRenderingContext) -> None:
31
+ pass
@@ -0,0 +1,25 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.schemas import Block, BlockType
4
+ from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
5
+
6
+
7
+ class BookmarkRenderer(CaptionedBlockRenderer):
8
+ @override
9
+ def _can_handle(self, block: Block) -> bool:
10
+ return block.type == BlockType.BOOKMARK
11
+
12
+ @override
13
+ async def _render_main_content(self, block: Block) -> str:
14
+ url = self._extract_bookmark_url(block)
15
+
16
+ if not url:
17
+ return ""
18
+
19
+ syntax = self._syntax_registry.get_bookmark_syntax()
20
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+
22
+ def _extract_bookmark_url(self, block: Block) -> str:
23
+ if not block.bookmark:
24
+ return ""
25
+ return block.bookmark.url or ""
@@ -0,0 +1,21 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.schemas import Block, BlockType
4
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
5
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
6
+
7
+
8
+ class BreadcrumbRenderer(BlockRenderer):
9
+ @override
10
+ def _can_handle(self, block: Block) -> bool:
11
+ return block.type == BlockType.BREADCRUMB
12
+
13
+ @override
14
+ async def _process(self, context: MarkdownRenderingContext) -> None:
15
+ syntax = self._syntax_registry.get_breadcrumb_syntax()
16
+ breadcrumb_markdown = syntax.start_delimiter
17
+
18
+ if context.indent_level > 0:
19
+ breadcrumb_markdown = context.indent_text(breadcrumb_markdown)
20
+
21
+ context.markdown_result = breadcrumb_markdown
@@ -0,0 +1,48 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
4
+ from notionary.blocks.schemas import Block, BlockType
5
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
6
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
7
+ from notionary.page.content.syntax import SyntaxRegistry
8
+
9
+
10
+ class BulletedListRenderer(BlockRenderer):
11
+ def __init__(
12
+ self,
13
+ syntax_registry: SyntaxRegistry | None = None,
14
+ rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
15
+ ) -> None:
16
+ super().__init__(syntax_registry=syntax_registry)
17
+ self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
18
+
19
+ @override
20
+ def _can_handle(self, block: Block) -> bool:
21
+ return block.type == BlockType.BULLETED_LIST_ITEM
22
+
23
+ @override
24
+ async def _process(self, context: MarkdownRenderingContext) -> None:
25
+ markdown = await self._convert_bulleted_list_to_markdown(context.block)
26
+
27
+ if not markdown:
28
+ context.markdown_result = ""
29
+ return
30
+
31
+ syntax = self._syntax_registry.get_bulleted_list_syntax()
32
+ list_item_markdown = f"{syntax.start_delimiter}{markdown}"
33
+
34
+ if context.indent_level > 0:
35
+ list_item_markdown = context.indent_text(list_item_markdown)
36
+
37
+ children_markdown = await context.render_children_with_additional_indent(1)
38
+
39
+ if children_markdown:
40
+ context.markdown_result = f"{list_item_markdown}\n{children_markdown}"
41
+ else:
42
+ context.markdown_result = list_item_markdown
43
+
44
+ async def _convert_bulleted_list_to_markdown(self, block: Block) -> str | None:
45
+ if not block.bulleted_list_item or not block.bulleted_list_item.rich_text:
46
+ return None
47
+
48
+ return await self._rich_text_markdown_converter.to_markdown(block.bulleted_list_item.rich_text)