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,50 @@
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 CalloutRenderer(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.CALLOUT
22
+
23
+ @override
24
+ async def _process(self, context: MarkdownRenderingContext) -> None:
25
+ content = await self._extract_callout_content(context.block)
26
+
27
+ if not content:
28
+ context.markdown_result = ""
29
+ return
30
+
31
+ icon = await self._extract_callout_icon(context.block)
32
+
33
+ callout_start_delimiter = self._syntax_registry.get_callout_syntax().start_delimiter
34
+
35
+ result = f'{callout_start_delimiter}({content} "{icon}")' if icon else f"{callout_start_delimiter}({content})"
36
+
37
+ if context.indent_level > 0:
38
+ result = context.indent_text(result)
39
+
40
+ context.markdown_result = result
41
+
42
+ async def _extract_callout_icon(self, block: Block) -> str:
43
+ if not block.callout or not block.callout.icon:
44
+ return ""
45
+ return block.callout.icon.emoji or ""
46
+
47
+ async def _extract_callout_content(self, block: Block) -> str:
48
+ if not block.callout or not block.callout.rich_text:
49
+ return ""
50
+ return await self._rich_text_markdown_converter.to_markdown(block.callout.rich_text)
@@ -0,0 +1,58 @@
1
+ from abc import abstractmethod
2
+ from typing import override
3
+
4
+ from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
5
+ from notionary.blocks.schemas import Block
6
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
7
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
8
+ from notionary.page.content.syntax import SyntaxRegistry
9
+
10
+
11
+ class CaptionedBlockRenderer(BlockRenderer):
12
+ def __init__(
13
+ self,
14
+ syntax_registry: SyntaxRegistry | None = None,
15
+ rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
16
+ ) -> None:
17
+ super().__init__(syntax_registry=syntax_registry)
18
+ self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
19
+
20
+ @abstractmethod
21
+ async def _render_main_content(self, block: Block) -> str:
22
+ raise NotImplementedError
23
+
24
+ @override
25
+ async def _process(self, context: MarkdownRenderingContext) -> None:
26
+ main_content = await self._render_main_content(context.block)
27
+
28
+ if not main_content:
29
+ context.markdown_result = ""
30
+ return
31
+
32
+ caption_markdown = await self._render_caption(context.block)
33
+
34
+ final_markdown = f"{main_content}{caption_markdown}"
35
+
36
+ if context.indent_level > 0:
37
+ final_markdown = context.indent_text(final_markdown)
38
+
39
+ children_markdown = await context.render_children_with_additional_indent(1)
40
+
41
+ if children_markdown:
42
+ context.markdown_result = f"{final_markdown}\n{children_markdown}"
43
+ else:
44
+ context.markdown_result = final_markdown
45
+
46
+ async def _render_caption(self, block: Block) -> str:
47
+ block_data_object = getattr(block, block.type.value, None)
48
+
49
+ if not block_data_object or not hasattr(block_data_object, "caption"):
50
+ return ""
51
+
52
+ caption_rich_text = getattr(block_data_object, "caption", [])
53
+ if not caption_rich_text:
54
+ return ""
55
+
56
+ caption_markdown = await self._rich_text_markdown_converter.to_markdown(caption_rich_text)
57
+
58
+ return f"\n[caption] {caption_markdown}"
@@ -0,0 +1,34 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.enums import BlockType
4
+ from notionary.blocks.schemas import Block
5
+ from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
6
+
7
+
8
+ class CodeRenderer(CaptionedBlockRenderer):
9
+ @override
10
+ def _can_handle(self, block: Block) -> bool:
11
+ return block.type == BlockType.CODE
12
+
13
+ @override
14
+ async def _render_main_content(self, block: Block) -> str:
15
+ language = self._extract_code_language(block)
16
+ code_content = await self._extract_code_content(block)
17
+
18
+ if not code_content:
19
+ return ""
20
+
21
+ syntax = self._syntax_registry.get_code_syntax()
22
+ code_start = f"{syntax.start_delimiter}{language}"
23
+ code_end = syntax.end_delimiter
24
+ return f"{code_start}\n{code_content}\n{code_end}"
25
+
26
+ def _extract_code_language(self, block: Block) -> str:
27
+ if not block.code or not block.code.language:
28
+ return ""
29
+ return block.code.language.value
30
+
31
+ async def _extract_code_content(self, block: Block) -> str:
32
+ if not block.code or not block.code.rich_text:
33
+ return ""
34
+ return await self._rich_text_markdown_converter.to_markdown(block.code.rich_text)
@@ -0,0 +1,53 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.enums import BlockType
4
+ from notionary.blocks.schemas import Block
5
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
6
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
7
+
8
+
9
+ class ColumnRenderer(BlockRenderer):
10
+ @override
11
+ def _can_handle(self, block: Block) -> bool:
12
+ return block.type == BlockType.COLUMN
13
+
14
+ @override
15
+ async def _process(self, context: MarkdownRenderingContext) -> None:
16
+ column_start = self._format_column_start(context.block, context.indent_level)
17
+ children_markdown = await self._render_children_with_indentation(context)
18
+
19
+ if children_markdown:
20
+ context.markdown_result = f"{column_start}\n{children_markdown}"
21
+ else:
22
+ context.markdown_result = column_start
23
+
24
+ def _format_column_start(self, block: Block, indent_level: int) -> str:
25
+ column_start = self._build_column_start_tag(block)
26
+
27
+ if indent_level > 0:
28
+ indent = " " * indent_level
29
+ column_start = f"{indent}{column_start}"
30
+
31
+ return column_start
32
+
33
+ def _build_column_start_tag(self, block: Block) -> str:
34
+ delimiter = self._syntax_registry.get_column_syntax().start_delimiter
35
+
36
+ if not block.column:
37
+ return delimiter
38
+
39
+ width_ratio = block.column.width_ratio
40
+ if width_ratio:
41
+ return f"{delimiter} {width_ratio}"
42
+
43
+ return delimiter
44
+
45
+ async def _render_children_with_indentation(self, context: MarkdownRenderingContext) -> str:
46
+ original_indent = context.indent_level
47
+ context.indent_level += 1
48
+
49
+ children_markdown = await context.render_children()
50
+
51
+ context.indent_level = original_indent
52
+
53
+ return children_markdown
@@ -0,0 +1,44 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.enums import BlockType
4
+ from notionary.blocks.schemas import Block
5
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
6
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
7
+
8
+
9
+ class ColumnListRenderer(BlockRenderer):
10
+ @override
11
+ def _can_handle(self, block: Block) -> bool:
12
+ return block.type == BlockType.COLUMN_LIST
13
+
14
+ @override
15
+ async def _process(self, context: MarkdownRenderingContext) -> None:
16
+ column_list_start = self._format_column_list_start(context.indent_level)
17
+ children_markdown = await self._render_children_with_indentation(context)
18
+
19
+ if children_markdown:
20
+ context.markdown_result = f"{column_list_start}\n{children_markdown}"
21
+ else:
22
+ context.markdown_result = column_list_start
23
+
24
+ def _format_column_list_start(self, indent_level: int) -> str:
25
+ delimiter = self._get_column_list_delimiter()
26
+
27
+ if indent_level > 0:
28
+ indent = " " * indent_level
29
+ return f"{indent}{delimiter}"
30
+
31
+ return delimiter
32
+
33
+ def _get_column_list_delimiter(self) -> str:
34
+ return self._syntax_registry.get_column_list_syntax().start_delimiter
35
+
36
+ async def _render_children_with_indentation(self, context: MarkdownRenderingContext) -> str:
37
+ original_indent = context.indent_level
38
+ context.indent_level += 1
39
+
40
+ children_markdown = await context.render_children()
41
+
42
+ context.indent_level = original_indent
43
+
44
+ return children_markdown
@@ -0,0 +1,22 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.enums import BlockType
4
+ from notionary.blocks.schemas import Block
5
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
6
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
7
+
8
+
9
+ class DividerRenderer(BlockRenderer):
10
+ @override
11
+ def _can_handle(self, block: Block) -> bool:
12
+ return block.type == BlockType.DIVIDER
13
+
14
+ @override
15
+ async def _process(self, context: MarkdownRenderingContext) -> None:
16
+ syntax = self._syntax_registry.get_divider_syntax()
17
+ divider_markdown = syntax.start_delimiter
18
+
19
+ if context.indent_level > 0:
20
+ divider_markdown = context.indent_text(divider_markdown)
21
+
22
+ context.markdown_result = divider_markdown
@@ -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 EmbedRenderer(CaptionedBlockRenderer):
8
+ @override
9
+ def _can_handle(self, block: Block) -> bool:
10
+ return block.type == BlockType.EMBED
11
+
12
+ @override
13
+ async def _render_main_content(self, block: Block) -> str:
14
+ url = self._extract_embed_url(block)
15
+
16
+ if not url:
17
+ return ""
18
+
19
+ syntax = self._syntax_registry.get_embed_syntax()
20
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+
22
+ def _extract_embed_url(self, block: Block) -> str:
23
+ if not block.embed:
24
+ return ""
25
+ return block.embed.url or ""
@@ -0,0 +1,37 @@
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 EquationRenderer(BlockRenderer):
9
+ @override
10
+ def _can_handle(self, block: Block) -> bool:
11
+ return block.type == BlockType.EQUATION
12
+
13
+ @override
14
+ async def _process(self, context: MarkdownRenderingContext) -> None:
15
+ expression = self._extract_equation_expression(context.block)
16
+
17
+ if not expression:
18
+ context.markdown_result = ""
19
+ return
20
+
21
+ syntax = self._syntax_registry.get_equation_syntax()
22
+ equation_markdown = f"{syntax.start_delimiter}{expression}{syntax.end_delimiter}"
23
+
24
+ if context.indent_level > 0:
25
+ equation_markdown = context.indent_text(equation_markdown)
26
+
27
+ children_markdown = await context.render_children_with_additional_indent(1)
28
+
29
+ if children_markdown:
30
+ context.markdown_result = f"{equation_markdown}\n{children_markdown}"
31
+ else:
32
+ context.markdown_result = equation_markdown
33
+
34
+ def _extract_equation_expression(self, block: Block) -> str:
35
+ if not block.equation:
36
+ return ""
37
+ return block.equation.expression or ""
@@ -0,0 +1,24 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.schemas import Block
4
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
5
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
6
+ from notionary.utils.mixins.logging import LoggingMixin
7
+
8
+
9
+ class FallbackRenderer(BlockRenderer, LoggingMixin):
10
+ @override
11
+ def _can_handle(self, block: Block) -> bool:
12
+ return True
13
+
14
+ @override
15
+ async def _process(self, context: MarkdownRenderingContext) -> None:
16
+ block_type = context.block.type.value if context.block.type else "unknown"
17
+ self.logger.warning(f"No handler found for block type: {block_type}")
18
+
19
+ fallback_message = f"[Unsupported block type: {block_type}]"
20
+
21
+ if context.indent_level > 0:
22
+ fallback_message = context.indent_text(fallback_message)
23
+
24
+ context.markdown_result = fallback_message
@@ -0,0 +1,40 @@
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 FileRenderer(CaptionedBlockRenderer):
8
+ @override
9
+ def _can_handle(self, block: Block) -> bool:
10
+ return block.type == BlockType.FILE
11
+
12
+ @override
13
+ async def _render_main_content(self, block: Block) -> str:
14
+ url = self._extract_file_url(block)
15
+
16
+ if not url:
17
+ return ""
18
+
19
+ syntax = self._syntax_registry.get_file_syntax()
20
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+
22
+ def _extract_file_url(self, block: Block) -> str:
23
+ if not block.file:
24
+ return ""
25
+
26
+ if block.file.external:
27
+ return block.file.external.url or ""
28
+ elif block.file.file:
29
+ return block.file.file.url or ""
30
+
31
+ return ""
32
+
33
+ def _extract_file_name(self, block: Block) -> str:
34
+ if not block.file:
35
+ return ""
36
+
37
+ if block.file.name:
38
+ return block.file.name or ""
39
+
40
+ return ""
@@ -0,0 +1,95 @@
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, HeadingData
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 HeadingRenderer(BlockRenderer):
11
+ MIN_HEADING_LEVEL = 1
12
+ MAX_HEADING_LEVEL = 3
13
+
14
+ def __init__(
15
+ self,
16
+ syntax_registry: SyntaxRegistry | None = None,
17
+ rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
18
+ ) -> None:
19
+ super().__init__(syntax_registry=syntax_registry)
20
+ self._syntax = self._syntax_registry.get_heading_syntax()
21
+ self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
22
+
23
+ @override
24
+ def _can_handle(self, block: Block) -> bool:
25
+ return block.type in (BlockType.HEADING_1, BlockType.HEADING_2, BlockType.HEADING_3)
26
+
27
+ @override
28
+ async def _process(self, context: MarkdownRenderingContext) -> None:
29
+ level = self._get_heading_level(context.block)
30
+ title = await self._get_heading_title(context.block)
31
+
32
+ if not self._is_valid_heading(level, title):
33
+ return
34
+
35
+ heading_markdown = self._format_heading(level, title, context.indent_level)
36
+
37
+ if self._is_toggleable(context.block):
38
+ context.markdown_result = await self._render_toggleable_heading(heading_markdown, context)
39
+ else:
40
+ context.markdown_result = heading_markdown
41
+
42
+ def _is_valid_heading(self, level: int, title: str) -> bool:
43
+ return self.MIN_HEADING_LEVEL <= level <= self.MAX_HEADING_LEVEL and bool(title)
44
+
45
+ def _format_heading(self, level: int, title: str, indent_level: int) -> str:
46
+ heading_prefix = self._syntax.start_delimiter * level
47
+ heading_markdown = f"{heading_prefix} {title}"
48
+
49
+ if indent_level > 0:
50
+ indent = " " * indent_level
51
+ heading_markdown = f"{indent}{heading_markdown}"
52
+
53
+ return heading_markdown
54
+
55
+ async def _render_toggleable_heading(self, heading_markdown: str, context: MarkdownRenderingContext) -> str:
56
+ original_indent = context.indent_level
57
+ context.indent_level += 1
58
+
59
+ children_markdown = await context.render_children()
60
+
61
+ context.indent_level = original_indent
62
+
63
+ if children_markdown:
64
+ return f"{heading_markdown}\n{children_markdown}"
65
+ return heading_markdown
66
+
67
+ def _get_heading_level(self, block: Block) -> int:
68
+ if block.type == BlockType.HEADING_1:
69
+ return 1
70
+ elif block.type == BlockType.HEADING_2:
71
+ return 2
72
+ elif block.type == BlockType.HEADING_3:
73
+ return 3
74
+ return 0
75
+
76
+ def _is_toggleable(self, block: Block) -> bool:
77
+ heading_data = self._get_heading_data(block)
78
+ return heading_data.is_toggleable if heading_data else False
79
+
80
+ async def _get_heading_title(self, block: Block) -> str:
81
+ heading_data = self._get_heading_data(block)
82
+
83
+ if not heading_data or not heading_data.rich_text:
84
+ return ""
85
+
86
+ return await self._rich_text_markdown_converter.to_markdown(heading_data.rich_text)
87
+
88
+ def _get_heading_data(self, block: Block) -> HeadingData | None:
89
+ if block.type == BlockType.HEADING_1:
90
+ return block.heading_1
91
+ elif block.type == BlockType.HEADING_2:
92
+ return block.heading_2
93
+ elif block.type == BlockType.HEADING_3:
94
+ return block.heading_3
95
+ return None
@@ -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 ImageRenderer(CaptionedBlockRenderer):
8
+ @override
9
+ def _can_handle(self, block: Block) -> bool:
10
+ return block.type == BlockType.IMAGE
11
+
12
+ @override
13
+ async def _render_main_content(self, block: Block) -> str:
14
+ url = self._extract_image_url(block)
15
+
16
+ if not url:
17
+ return ""
18
+
19
+ syntax = self._syntax_registry.get_image_syntax()
20
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+
22
+ def _extract_image_url(self, block: Block) -> str:
23
+ if not block.image:
24
+ return ""
25
+
26
+ if block.image.external:
27
+ return block.image.external.url or ""
28
+ elif block.image.file:
29
+ return block.image.file.url or ""
30
+
31
+ return ""
@@ -0,0 +1,42 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.rich_text.rich_text_markdown_converter import (
4
+ RichTextToMarkdownConverter,
5
+ )
6
+ from notionary.blocks.schemas import Block, BlockType
7
+ from notionary.page.content.renderer.context import MarkdownRenderingContext
8
+ from notionary.page.content.renderer.renderers.base import BlockRenderer
9
+ from notionary.page.content.syntax import MarkdownGrammar, SyntaxRegistry
10
+
11
+
12
+ class NumberedListRenderer(BlockRenderer):
13
+ def __init__(
14
+ self,
15
+ syntax_registry: SyntaxRegistry | None = None,
16
+ rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
17
+ markdown_grammar: MarkdownGrammar | None = None,
18
+ ) -> None:
19
+ super().__init__(syntax_registry=syntax_registry)
20
+ self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
21
+
22
+ markdown_grammar = markdown_grammar or MarkdownGrammar()
23
+ self._numbered_list_placeholder = markdown_grammar.numbered_list_placeholder
24
+
25
+ @override
26
+ def _can_handle(self, block: Block) -> bool:
27
+ return block.type == BlockType.NUMBERED_LIST_ITEM
28
+
29
+ @override
30
+ async def _process(self, context: MarkdownRenderingContext) -> None:
31
+ list_item_data = context.block.numbered_list_item
32
+ rich_text = list_item_data.rich_text if list_item_data else []
33
+ content = await self._rich_text_markdown_converter.to_markdown(rich_text)
34
+
35
+ item_line = context.indent_text(f"{self._numbered_list_placeholder}. {content}")
36
+
37
+ children_markdown = await context.render_children_with_additional_indent(1)
38
+
39
+ if children_markdown:
40
+ context.markdown_result = f"{item_line}\n{children_markdown}"
41
+ else:
42
+ context.markdown_result = item_line
@@ -0,0 +1,40 @@
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
+
8
+
9
+ class ParagraphRenderer(BlockRenderer):
10
+ def __init__(self, rich_text_markdown_converter: RichTextToMarkdownConverter | None = None) -> None:
11
+ super().__init__()
12
+ self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
13
+
14
+ @override
15
+ def _can_handle(self, block: Block) -> bool:
16
+ return block.type == BlockType.PARAGRAPH
17
+
18
+ @override
19
+ async def _process(self, context: MarkdownRenderingContext) -> None:
20
+ markdown = await self._convert_paragraph_to_markdown(context.block)
21
+
22
+ if not markdown:
23
+ context.markdown_result = ""
24
+ return
25
+
26
+ if context.indent_level > 0:
27
+ markdown = context.indent_text(markdown)
28
+
29
+ children_markdown = await context.render_children_with_additional_indent(1)
30
+
31
+ if children_markdown:
32
+ context.markdown_result = f"{markdown}\n{children_markdown}"
33
+ else:
34
+ context.markdown_result = markdown
35
+
36
+ async def _convert_paragraph_to_markdown(self, block: Block) -> str | None:
37
+ if not block.paragraph or not block.paragraph.rich_text:
38
+ return None
39
+
40
+ return await self._rich_text_markdown_converter.to_markdown(block.paragraph.rich_text)
@@ -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 PdfRenderer(CaptionedBlockRenderer):
8
+ @override
9
+ def _can_handle(self, block: Block) -> bool:
10
+ return block.type == BlockType.PDF
11
+
12
+ @override
13
+ async def _render_main_content(self, block: Block) -> str:
14
+ url = self._extract_pdf_url(block)
15
+
16
+ if not url:
17
+ return ""
18
+
19
+ syntax = self._syntax_registry.get_pdf_syntax()
20
+ return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
21
+
22
+ def _extract_pdf_url(self, block: Block) -> str:
23
+ if not block.pdf:
24
+ return ""
25
+
26
+ if block.pdf.external:
27
+ return block.pdf.external.url or ""
28
+ elif block.pdf.file:
29
+ return block.pdf.file.url or ""
30
+
31
+ return ""