notionary 0.2.28__tar.gz → 0.3.1__tar.gz

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 (250) hide show
  1. {notionary-0.2.28 → notionary-0.3.1}/PKG-INFO +35 -105
  2. notionary-0.3.1/README.md +190 -0
  3. notionary-0.3.1/notionary/__init__.py +14 -0
  4. notionary-0.3.1/notionary/blocks/__init__.py +5 -0
  5. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/client.py +6 -4
  6. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/enums.py +28 -1
  7. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/markdown_rich_text_converter.py +14 -0
  8. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/models.py +14 -0
  9. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/name_id_resolver/__init__.py +2 -0
  10. notionary-0.3.1/notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
  11. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/rich_text_markdown_converter.py +12 -0
  12. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/rich_text_patterns.py +3 -0
  13. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/schemas.py +42 -10
  14. notionary-0.3.1/notionary/comments/__init__.py +5 -0
  15. {notionary-0.2.28 → notionary-0.3.1}/notionary/comments/client.py +7 -10
  16. {notionary-0.2.28 → notionary-0.3.1}/notionary/comments/factory.py +4 -6
  17. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/http/data_source_instance_client.py +14 -4
  18. notionary-0.2.28/notionary/data_source/properties/models.py → notionary-0.3.1/notionary/data_source/properties/schemas.py +4 -8
  19. notionary-0.3.1/notionary/data_source/query/__init__.py +9 -0
  20. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/query/builder.py +38 -10
  21. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/query/schema.py +13 -10
  22. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/query/validator.py +11 -11
  23. notionary-0.3.1/notionary/data_source/schema/registry.py +104 -0
  24. notionary-0.3.1/notionary/data_source/schema/service.py +136 -0
  25. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/schemas.py +1 -1
  26. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/service.py +29 -103
  27. {notionary-0.2.28 → notionary-0.3.1}/notionary/database/service.py +17 -60
  28. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/__init__.py +5 -1
  29. notionary-0.3.1/notionary/exceptions/block_parsing.py +37 -0
  30. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/search.py +24 -0
  31. {notionary-0.2.28 → notionary-0.3.1}/notionary/http/client.py +9 -10
  32. {notionary-0.2.28 → notionary-0.3.1}/notionary/http/models.py +5 -4
  33. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/factory.py +10 -3
  34. notionary-0.3.1/notionary/page/content/markdown/builder.py +226 -0
  35. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/__init__.py +0 -2
  36. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/audio.py +1 -1
  37. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/base.py +1 -1
  38. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/bookmark.py +1 -1
  39. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/breadcrumb.py +1 -1
  40. notionary-0.3.1/notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
  41. notionary-0.3.1/notionary/page/content/markdown/nodes/callout.py +34 -0
  42. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/code.py +3 -5
  43. notionary-0.3.1/notionary/page/content/markdown/nodes/columns.py +69 -0
  44. notionary-0.3.1/notionary/page/content/markdown/nodes/container.py +64 -0
  45. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/divider.py +1 -1
  46. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/embed.py +1 -1
  47. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/equation.py +1 -1
  48. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/file.py +1 -1
  49. notionary-0.3.1/notionary/page/content/markdown/nodes/heading.py +36 -0
  50. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/image.py +1 -1
  51. notionary-0.3.1/notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
  52. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/mixins/caption.py +1 -1
  53. notionary-0.3.1/notionary/page/content/markdown/nodes/numbered_list.py +38 -0
  54. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/paragraph.py +1 -1
  55. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/pdf.py +1 -1
  56. notionary-0.3.1/notionary/page/content/markdown/nodes/quote.py +27 -0
  57. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/space.py +1 -1
  58. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/table.py +1 -1
  59. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/table_of_contents.py +1 -1
  60. notionary-0.3.1/notionary/page/content/markdown/nodes/todo.py +38 -0
  61. notionary-0.3.1/notionary/page/content/markdown/nodes/toggle.py +27 -0
  62. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/nodes/video.py +1 -1
  63. notionary-0.3.1/notionary/page/content/parser/context.py +126 -0
  64. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/factory.py +1 -10
  65. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/__init__.py +0 -2
  66. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/audio.py +1 -1
  67. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/base.py +1 -1
  68. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/bookmark.py +1 -1
  69. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/breadcrumb.py +1 -1
  70. notionary-0.3.1/notionary/page/content/parser/parsers/bulleted_list.py +85 -0
  71. notionary-0.3.1/notionary/page/content/parser/parsers/callout.py +100 -0
  72. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/caption.py +1 -1
  73. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/code.py +5 -5
  74. notionary-0.3.1/notionary/page/content/parser/parsers/column.py +76 -0
  75. notionary-0.3.1/notionary/page/content/parser/parsers/column_list.py +81 -0
  76. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/divider.py +1 -1
  77. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/embed.py +1 -1
  78. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/equation.py +1 -1
  79. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/file.py +1 -1
  80. notionary-0.3.1/notionary/page/content/parser/parsers/heading.py +115 -0
  81. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/image.py +1 -1
  82. notionary-0.3.1/notionary/page/content/parser/parsers/numbered_list.py +89 -0
  83. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/paragraph.py +3 -2
  84. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/pdf.py +1 -1
  85. notionary-0.3.1/notionary/page/content/parser/parsers/quote.py +125 -0
  86. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/space.py +14 -8
  87. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/table.py +1 -1
  88. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/table_of_contents.py +1 -1
  89. notionary-0.3.1/notionary/page/content/parser/parsers/todo.py +96 -0
  90. notionary-0.3.1/notionary/page/content/parser/parsers/toggle.py +70 -0
  91. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/parsers/video.py +1 -1
  92. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/post_processing/handlers/rich_text_length.py +6 -4
  93. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +43 -22
  94. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/pre_processsing/handlers/__init__.py +4 -0
  95. notionary-0.3.1/notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +134 -0
  96. notionary-0.3.1/notionary/page/content/parser/pre_processsing/handlers/indentation.py +86 -0
  97. notionary-0.3.1/notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +66 -0
  98. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/pre_processsing/handlers/whitespace.py +14 -7
  99. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/service.py +9 -0
  100. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/context.py +5 -2
  101. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/factory.py +2 -11
  102. notionary-0.3.1/notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
  103. notionary-0.3.1/notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
  104. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/__init__.py +0 -2
  105. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/base.py +1 -1
  106. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/bulleted_list.py +1 -1
  107. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/callout.py +6 -21
  108. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/captioned_block.py +1 -1
  109. notionary-0.3.1/notionary/page/content/renderer/renderers/column.py +53 -0
  110. notionary-0.3.1/notionary/page/content/renderer/renderers/column_list.py +44 -0
  111. notionary-0.3.1/notionary/page/content/renderer/renderers/heading.py +95 -0
  112. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/numbered_list.py +6 -5
  113. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/quote.py +1 -1
  114. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/todo.py +1 -1
  115. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/toggle.py +6 -7
  116. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/service.py +4 -1
  117. notionary-0.3.1/notionary/page/content/syntax/__init__.py +4 -0
  118. notionary-0.3.1/notionary/page/content/syntax/grammar.py +10 -0
  119. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/syntax/models.py +0 -2
  120. notionary-0.2.28/notionary/page/content/syntax/service.py → notionary-0.3.1/notionary/page/content/syntax/registry.py +31 -91
  121. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/properties/client.py +3 -3
  122. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/properties/models.py +3 -2
  123. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/properties/service.py +18 -3
  124. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/service.py +22 -80
  125. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/entity/service.py +94 -36
  126. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/models/cover.py +1 -1
  127. notionary-0.3.1/notionary/shared/typings.py +3 -0
  128. notionary-0.3.1/notionary/user/base.py +138 -0
  129. notionary-0.3.1/notionary/user/factory.py +0 -0
  130. notionary-0.3.1/notionary/utils/decorators.py +122 -0
  131. {notionary-0.2.28 → notionary-0.3.1}/notionary/utils/fuzzy.py +18 -6
  132. notionary-0.3.1/notionary/utils/mixins/logging.py +58 -0
  133. notionary-0.3.1/notionary/utils/pagination.py +104 -0
  134. notionary-0.3.1/notionary/workspace/__init__.py +4 -0
  135. {notionary-0.2.28 → notionary-0.3.1}/notionary/workspace/client.py +4 -2
  136. notionary-0.3.1/notionary/workspace/query/__init__.py +3 -0
  137. {notionary-0.2.28 → notionary-0.3.1}/notionary/workspace/query/builder.py +25 -1
  138. {notionary-0.2.28 → notionary-0.3.1}/notionary/workspace/query/models.py +12 -3
  139. notionary-0.3.1/notionary/workspace/query/service.py +118 -0
  140. {notionary-0.2.28 → notionary-0.3.1}/notionary/workspace/service.py +31 -21
  141. {notionary-0.2.28 → notionary-0.3.1}/pyproject.toml +2 -2
  142. notionary-0.2.28/README.md +0 -260
  143. notionary-0.2.28/notionary/__init__.py +0 -7
  144. notionary-0.2.28/notionary/exceptions/block_parsing.py +0 -16
  145. notionary-0.2.28/notionary/page/content/markdown/builder.py +0 -304
  146. notionary-0.2.28/notionary/page/content/markdown/nodes/bulleted_list.py +0 -18
  147. notionary-0.2.28/notionary/page/content/markdown/nodes/callout.py +0 -32
  148. notionary-0.2.28/notionary/page/content/markdown/nodes/columns.py +0 -51
  149. notionary-0.2.28/notionary/page/content/markdown/nodes/heading.py +0 -16
  150. notionary-0.2.28/notionary/page/content/markdown/nodes/numbered_list.py +0 -15
  151. notionary-0.2.28/notionary/page/content/markdown/nodes/quote.py +0 -15
  152. notionary-0.2.28/notionary/page/content/markdown/nodes/todo.py +0 -22
  153. notionary-0.2.28/notionary/page/content/markdown/nodes/toggle.py +0 -28
  154. notionary-0.2.28/notionary/page/content/markdown/nodes/toggleable_heading.py +0 -35
  155. notionary-0.2.28/notionary/page/content/parser/context.py +0 -49
  156. notionary-0.2.28/notionary/page/content/parser/parsers/bulleted_list.py +0 -41
  157. notionary-0.2.28/notionary/page/content/parser/parsers/callout.py +0 -129
  158. notionary-0.2.28/notionary/page/content/parser/parsers/column.py +0 -117
  159. notionary-0.2.28/notionary/page/content/parser/parsers/column_list.py +0 -81
  160. notionary-0.2.28/notionary/page/content/parser/parsers/heading.py +0 -58
  161. notionary-0.2.28/notionary/page/content/parser/parsers/numbered_list.py +0 -45
  162. notionary-0.2.28/notionary/page/content/parser/parsers/quote.py +0 -65
  163. notionary-0.2.28/notionary/page/content/parser/parsers/todo.py +0 -58
  164. notionary-0.2.28/notionary/page/content/parser/parsers/toggle.py +0 -127
  165. notionary-0.2.28/notionary/page/content/parser/parsers/toggleable_heading.py +0 -150
  166. notionary-0.2.28/notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +0 -80
  167. notionary-0.2.28/notionary/page/content/renderer/post_processing/handlers/__init__.py +0 -5
  168. notionary-0.2.28/notionary/page/content/renderer/post_processing/handlers/numbered_list_placeholdere.py +0 -62
  169. notionary-0.2.28/notionary/page/content/renderer/renderers/column.py +0 -44
  170. notionary-0.2.28/notionary/page/content/renderer/renderers/column_list.py +0 -31
  171. notionary-0.2.28/notionary/page/content/renderer/renderers/heading.py +0 -69
  172. notionary-0.2.28/notionary/page/content/renderer/renderers/toggleable_heading.py +0 -78
  173. notionary-0.2.28/notionary/user/base.py +0 -89
  174. notionary-0.2.28/notionary/utils/async_retry.py +0 -39
  175. notionary-0.2.28/notionary/utils/mixins/logging.py +0 -47
  176. notionary-0.2.28/notionary/utils/pagination.py +0 -50
  177. notionary-0.2.28/notionary/utils/singleton.py +0 -13
  178. notionary-0.2.28/notionary/workspace/__init__.py +0 -3
  179. notionary-0.2.28/notionary/workspace/query/service.py +0 -93
  180. {notionary-0.2.28 → notionary-0.3.1}/.gitignore +0 -0
  181. {notionary-0.2.28 → notionary-0.3.1}/LICENSE +0 -0
  182. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/name_id_resolver/database.py +0 -0
  183. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/name_id_resolver/page.py +0 -0
  184. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/name_id_resolver/person.py +0 -0
  185. {notionary-0.2.28 → notionary-0.3.1}/notionary/blocks/rich_text/name_id_resolver/port.py +0 -0
  186. {notionary-0.2.28 → notionary-0.3.1}/notionary/comments/models.py +0 -0
  187. {notionary-0.2.28 → notionary-0.3.1}/notionary/comments/schemas.py +0 -0
  188. {notionary-0.2.28 → notionary-0.3.1}/notionary/comments/service.py +0 -0
  189. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/http/client.py +0 -0
  190. {notionary-0.2.28 → notionary-0.3.1}/notionary/data_source/query/resolver.py +0 -0
  191. {notionary-0.2.28 → notionary-0.3.1}/notionary/database/client.py +0 -0
  192. {notionary-0.2.28 → notionary-0.3.1}/notionary/database/database_metadata_update_client.py +0 -0
  193. {notionary-0.2.28 → notionary-0.3.1}/notionary/database/schemas.py +0 -0
  194. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/api.py +0 -0
  195. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/base.py +0 -0
  196. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/data_source/__init__.py +0 -0
  197. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/data_source/builder.py +0 -0
  198. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/data_source/properties.py +0 -0
  199. {notionary-0.2.28 → notionary-0.3.1}/notionary/exceptions/properties.py +0 -0
  200. {notionary-0.2.28 → notionary-0.3.1}/notionary/file_upload/client.py +0 -0
  201. {notionary-0.2.28 → notionary-0.3.1}/notionary/file_upload/models.py +0 -0
  202. {notionary-0.2.28 → notionary-0.3.1}/notionary/file_upload/service.py +0 -0
  203. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/blocks/client.py +0 -0
  204. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/markdown/__init__.py +0 -0
  205. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/post_processing/handlers/__init__.py +0 -0
  206. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/post_processing/port.py +0 -0
  207. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/post_processing/service.py +0 -0
  208. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/pre_processsing/handlers/port.py +0 -0
  209. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/parser/pre_processsing/service.py +0 -0
  210. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/post_processing/port.py +0 -0
  211. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/post_processing/service.py +0 -0
  212. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/audio.py +0 -0
  213. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/bookmark.py +0 -0
  214. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/breadcrumb.py +0 -0
  215. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/code.py +0 -0
  216. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/divider.py +0 -0
  217. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/embed.py +0 -0
  218. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/equation.py +0 -0
  219. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/fallback.py +0 -0
  220. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/file.py +0 -0
  221. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/image.py +0 -0
  222. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/paragraph.py +0 -0
  223. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/pdf.py +0 -0
  224. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/table.py +0 -0
  225. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/table_of_contents.py +0 -0
  226. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/table_row.py +0 -0
  227. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/renderers/video.py +0 -0
  228. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/content/renderer/service.py +0 -0
  229. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/page_context.py +0 -0
  230. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/page_http_client.py +0 -0
  231. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/page_metadata_update_client.py +0 -0
  232. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/properties/factory.py +0 -0
  233. {notionary-0.2.28 → notionary-0.3.1}/notionary/page/schemas.py +0 -0
  234. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/entity/client.py +0 -0
  235. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/entity/dto_parsers.py +0 -0
  236. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/entity/entity_metadata_update_client.py +0 -0
  237. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/entity/schemas.py +0 -0
  238. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/models/file.py +0 -0
  239. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/models/icon.py +0 -0
  240. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/models/parent.py +0 -0
  241. {notionary-0.2.28 → notionary-0.3.1}/notionary/shared/properties/type.py +0 -0
  242. {notionary-0.2.28 → notionary-0.3.1}/notionary/user/__init__.py +0 -0
  243. {notionary-0.2.28 → notionary-0.3.1}/notionary/user/bot.py +0 -0
  244. {notionary-0.2.28 → notionary-0.3.1}/notionary/user/client.py +0 -0
  245. {notionary-0.2.28 → notionary-0.3.1}/notionary/user/person.py +0 -0
  246. {notionary-0.2.28 → notionary-0.3.1}/notionary/user/schemas.py +0 -0
  247. {notionary-0.2.28 → notionary-0.3.1}/notionary/user/service.py +0 -0
  248. {notionary-0.2.28 → notionary-0.3.1}/notionary/utils/date.py +0 -0
  249. {notionary-0.2.28 → notionary-0.3.1}/notionary/utils/uuid_utils.py +0 -0
  250. {notionary-0.2.28 → notionary-0.3.1}/notionary/workspace/schemas.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notionary
3
- Version: 0.2.28
3
+ Version: 0.3.1
4
4
  Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
5
5
  Project-URL: Homepage, https://github.com/mathisarends/notionary
6
6
  Author-email: Mathis Arends <mathisarends27@gmail.com>
7
7
  License: MIT
8
8
  License-File: LICENSE
9
- Requires-Python: >=3.11
9
+ Requires-Python: >=3.12
10
10
  Requires-Dist: aiofiles<25.0.0,>=24.1.0
11
11
  Requires-Dist: httpx>=0.28.0
12
12
  Requires-Dist: pydantic>=2.11.4
@@ -23,9 +23,13 @@ Description-Content-Type: text/markdown
23
23
 
24
24
  <div align="center">
25
25
 
26
- [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/downloads/)
27
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
28
- [![Documentation](https://img.shields.io/badge/docs-mathisarends.github.io-blue.svg)](https://mathisarends.github.io/notionary/)
26
+ [![PyPI version](https://badge.fury.io/py/notionary.svg)](https://badge.fury.io/py/notionary)
27
+ [![Python Version](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/)
28
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
29
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/mathisarends/fc0568b66a20fbbaa5018205861c0da9/raw/notionary-coverage.json)](https://github.com/mathisarends/notionary)
30
+ [![Downloads](https://img.shields.io/pypi/dm/notionary?color=blue)](https://pypi.org/project/notionary/)
31
+ [![Documentation](https://img.shields.io/badge/docs-notionary-blue?style=flat&logo=readthedocs)](https://mathisarends.github.io/notionary/)
32
+ [![Notion API](https://img.shields.io/badge/Notion%20API-Official-000000?logo=notion&logoColor=white)](https://developers.notion.com/)
29
33
 
30
34
  **Transform complex Notion API interactions into simple, Pythonic code.**
31
35
  Perfect for developers building AI agents, automation workflows, and dynamic content systems.
@@ -61,17 +65,9 @@ export NOTION_SECRET=your_integration_key
61
65
 
62
66
  ## See It in Action
63
67
 
64
- ### Creating Rich Database Entries
65
-
66
68
  https://github.com/user-attachments/assets/da8b4691-bee4-4b0f-801e-dccacb630398
67
69
 
68
- _Create styled project pages with properties, content, and rich formatting_
69
-
70
- ### Local File Uploads (Videos & Images)
71
-
72
- https://github.com/user-attachments/assets/a079ec01-bb56-4c65-8260-7b1fca42ac68
73
-
74
- _Upload videos and images using simple markdown syntax - files are automatically uploaded to Notion_
70
+ _Create rich database entries with properties, content, and beautiful formatting_
75
71
 
76
72
  ---
77
73
 
@@ -80,50 +76,30 @@ _Upload videos and images using simple markdown syntax - files are automatically
80
76
  ### Find → Create → Update Flow
81
77
 
82
78
  ```python
83
- import asyncio
84
- from notionary import NotionPage, NotionDatabase
85
-
86
- async def main():
87
- # Find pages by name - fuzzy matching included!
88
- page = await NotionPage.from_title("Meeting Notes")
89
-
90
- # Option 1: Direct Extended Markdown
91
- await page.append_markdown("""
92
- ## Action Items
93
- - [x] Review project proposal
94
- - [ ] Schedule team meeting
95
- - [ ] Update documentation
96
-
97
- [callout](Meeting decisions require follow-up "💡")
98
-
99
- +++ Details
100
- Additional context and next steps...
101
- +++
102
- """)
103
-
104
- # Option 2: Type-Safe Builder (maps to same markdown internally)
105
- await page.append_markdown(lambda builder: (
106
- builder
107
- .h2("Project Status")
108
- .callout("Milestone reached!", "🎉")
109
- .columns(
110
- lambda col: col.h3("Completed").bulleted_list([
111
- "API design", "Database setup", "Authentication"
112
- ]),
113
- lambda col: col.h3("In Progress").bulleted_list([
114
- "Frontend UI", "Testing", "Documentation"
115
- ]),
116
- width_ratios=[0.6, 0.4]
117
- )
118
- .toggle("Budget Details", lambda t: t
119
- .table(["Item", "Cost", "Status"], [
120
- ["Development", "$15,000", "Paid"],
121
- ["Design", "$8,000", "Pending"]
122
- ])
123
- )
124
- ))
125
-
126
- asyncio.run(main())
79
+ from notionary import NotionPage
80
+
81
+ # Find pages by name with fuzzy matching
82
+ page = await NotionPage.from_title("Meeting Notes")
83
+
84
+ # Define rich content with extended markdown
85
+ content = """
86
+ ## Action Items
87
+ - [x] Review proposal
88
+ - [ ] Schedule meeting
89
+
90
+ [callout](Key decision made! "💡")
91
+
92
+ | Task | Owner | Deadline |
93
+ |------|-------|----------|
94
+ | Design Review | Alice | 2024-03-15 |
95
+ | Implementation | Bob | 2024-03-22 |
96
+
97
+ +++ Budget Details
98
+ See attached spreadsheet...
99
+ +++
100
+ """
101
+
102
+ await page.append_markdown(content)
127
103
  ```
128
104
 
129
105
  ### Complete Block Support
@@ -132,36 +108,13 @@ Every Notion block type with extended syntax:
132
108
 
133
109
  | Block Type | Markdown Syntax | Use Case |
134
110
  | ------------- | -------------------------------------------- | ---------------------------- |
135
- | **Callouts** | `[callout](Text "🔥")` | Highlighting key information |
136
111
  | **Toggles** | `+++ Title\nContent\n+++` | Collapsible sections |
137
112
  | **Columns** | `::: columns\n::: column\nContent\n:::\n:::` | Side-by-side layouts |
138
113
  | **Tables** | Standard markdown tables | Structured data |
139
114
  | **Media** | `[video](./file.mp4)(caption:Description)` | Auto-uploading files |
140
115
  | **Code** | Standard code fences with captions | Code snippets |
141
116
  | **Equations** | `$LaTeX$` | Mathematical expressions |
142
- | **TOC** | `[toc](blue_background)` | Auto-generated navigation |
143
-
144
- ---
145
-
146
- ## What You Can Build 💡
147
-
148
- ### **AI Content Systems**
149
-
150
- - **Report Generation**: AI agents that create structured reports, documentation, and analysis
151
- - **Content Pipelines**: Automated workflows that process data and generate Notion pages
152
- - **Knowledge Management**: AI-powered documentation systems with smart categorization
153
-
154
- ### **Workflow Automation**
155
-
156
- - **Project Management**: Sync project status, update timelines, generate progress reports
157
- - **Data Integration**: Connect external APIs and databases to Notion workspaces
158
- - **Template Systems**: Dynamic page generation from templates and data sources
159
-
160
- ### **Content Management**
161
-
162
- - **Bulk Operations**: Mass page updates, content migration, and database management
163
- - **Media Handling**: Automated image/video uploads with proper organization
164
- - **Cross-Platform**: Sync content between Notion and other platforms
117
+ | **TOC** | `[toc]` | Auto-generated navigation |
165
118
 
166
119
  ---
167
120
 
@@ -222,29 +175,6 @@ Every Notion block type with extended syntax:
222
175
 
223
176
  [**mathisarends.github.io/notionary**](https://mathisarends.github.io/notionary/) - Complete API reference, guides, and tutorials
224
177
 
225
- ### Quick Links
226
-
227
- - [**Getting Started**](https://mathisarends.github.io/notionary/get-started/) - Setup and first steps
228
- - [**Page Management**](https://mathisarends.github.io/notionary/page/) - Content and properties
229
- - [**Database Operations**](https://mathisarends.github.io/notionary/database/) - Queries and management
230
- - [**Block Types Reference**](https://mathisarends.github.io/notionary/blocks/) - Complete syntax guide
231
-
232
- ### Hands-On Examples
233
-
234
- **Core Functionality:**
235
-
236
- - [Page Management](examples/page_example.py) - Create, update, and manage pages
237
- - [Database Operations](examples/database.py) - Connect and query databases
238
- - [Workspace Discovery](examples/workspace_discovery.py) - Explore your workspace
239
-
240
- **Extended Markdown:**
241
-
242
- - [Basic Formatting](examples/markdown/basic.py) - Text, lists, and links
243
- - [Callouts & Highlights](examples/markdown/callout.py) - Information boxes
244
- - [Toggle Sections](examples/markdown/toggle.py) - Collapsible content
245
- - [Multi-Column Layouts](examples/markdown/columns.py) - Side-by-side design
246
- - [Tables & Data](examples/markdown/table.py) - Structured presentations
247
-
248
178
  ---
249
179
 
250
180
  ## Contributing
@@ -0,0 +1,190 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
3
+ <source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
4
+ <img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
5
+ </picture>
6
+
7
+ <h1 align="center">The Modern Notion API for Python & AI Agents</h1>
8
+
9
+ <div align="center">
10
+
11
+ [![PyPI version](https://badge.fury.io/py/notionary.svg)](https://badge.fury.io/py/notionary)
12
+ [![Python Version](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/)
13
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/mathisarends/fc0568b66a20fbbaa5018205861c0da9/raw/notionary-coverage.json)](https://github.com/mathisarends/notionary)
15
+ [![Downloads](https://img.shields.io/pypi/dm/notionary?color=blue)](https://pypi.org/project/notionary/)
16
+ [![Documentation](https://img.shields.io/badge/docs-notionary-blue?style=flat&logo=readthedocs)](https://mathisarends.github.io/notionary/)
17
+ [![Notion API](https://img.shields.io/badge/Notion%20API-Official-000000?logo=notion&logoColor=white)](https://developers.notion.com/)
18
+
19
+ **Transform complex Notion API interactions into simple, Pythonic code.**
20
+ Perfect for developers building AI agents, automation workflows, and dynamic content systems.
21
+
22
+ </div>
23
+
24
+ ---
25
+
26
+ ## Why Notionary?
27
+
28
+ - **AI-Native Design** - Built specifically for AI agents with schema-driven markdown syntax
29
+ - **Smart Discovery** - Find pages and databases by name with fuzzy matching—no more hunting for IDs
30
+ - **Extended Markdown** - Rich syntax for toggles, columns, callouts, and media uploads
31
+ - **Async-First** - Modern Python with full async/await support and high performance
32
+ - **Round-Trip** - Read existing content, modify it, and write it back while preserving formatting
33
+ - **Complete Coverage** - Every Notion block type supported with type safety
34
+
35
+ ---
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install notionary
41
+ ```
42
+
43
+ Set up your [Notion integration](https://www.notion.so/profile/integrations) and configure your token:
44
+
45
+ ```bash
46
+ export NOTION_SECRET=your_integration_key
47
+ ```
48
+
49
+ ---
50
+
51
+ ## See It in Action
52
+
53
+ https://github.com/user-attachments/assets/da8b4691-bee4-4b0f-801e-dccacb630398
54
+
55
+ _Create rich database entries with properties, content, and beautiful formatting_
56
+
57
+ ---
58
+
59
+ ## Quick Start
60
+
61
+ ### Find → Create → Update Flow
62
+
63
+ ```python
64
+ from notionary import NotionPage
65
+
66
+ # Find pages by name with fuzzy matching
67
+ page = await NotionPage.from_title("Meeting Notes")
68
+
69
+ # Define rich content with extended markdown
70
+ content = """
71
+ ## Action Items
72
+ - [x] Review proposal
73
+ - [ ] Schedule meeting
74
+
75
+ [callout](Key decision made! "💡")
76
+
77
+ | Task | Owner | Deadline |
78
+ |------|-------|----------|
79
+ | Design Review | Alice | 2024-03-15 |
80
+ | Implementation | Bob | 2024-03-22 |
81
+
82
+ +++ Budget Details
83
+ See attached spreadsheet...
84
+ +++
85
+ """
86
+
87
+ await page.append_markdown(content)
88
+ ```
89
+
90
+ ### Complete Block Support
91
+
92
+ Every Notion block type with extended syntax:
93
+
94
+ | Block Type | Markdown Syntax | Use Case |
95
+ | ------------- | -------------------------------------------- | ---------------------------- |
96
+ | **Toggles** | `+++ Title\nContent\n+++` | Collapsible sections |
97
+ | **Columns** | `::: columns\n::: column\nContent\n:::\n:::` | Side-by-side layouts |
98
+ | **Tables** | Standard markdown tables | Structured data |
99
+ | **Media** | `[video](./file.mp4)(caption:Description)` | Auto-uploading files |
100
+ | **Code** | Standard code fences with captions | Code snippets |
101
+ | **Equations** | `$LaTeX$` | Mathematical expressions |
102
+ | **TOC** | `[toc]` | Auto-generated navigation |
103
+
104
+ ---
105
+
106
+ ## Key Features
107
+
108
+ <table>
109
+ <tr>
110
+ <td width="50%">
111
+
112
+ ### Smart Discovery
113
+
114
+ - Find pages/databases by name
115
+ - Fuzzy matching for approximate searches
116
+ - No more hunting for IDs or URLs
117
+
118
+ ### Extended Markdown
119
+
120
+ - Rich syntax beyond standard markdown
121
+ - Callouts, toggles, columns, media uploads
122
+ - Schema provided for AI agent integration
123
+
124
+ ### Modern Python
125
+
126
+ - Full async/await support
127
+ - Type hints throughout
128
+ - High-performance batch operations
129
+
130
+ </td>
131
+ <td width="50%">
132
+
133
+ ### Round-Trip Editing
134
+
135
+ - Read existing content as markdown
136
+ - Edit and modify preserving formatting
137
+ - Write back to Notion seamlessly
138
+
139
+ ### AI-Ready Architecture
140
+
141
+ - Schema-driven syntax for LLM prompts
142
+ - Perfect for AI content generation
143
+ - Handles complex nested structures
144
+
145
+ ### Complete Coverage
146
+
147
+ - Every Notion block type supported
148
+ - File uploads with automatic handling
149
+ - Database operations and properties
150
+
151
+ </td>
152
+ </tr>
153
+ </table>
154
+
155
+ ---
156
+
157
+ ## Examples & Documentation
158
+
159
+ ### Full Documentation
160
+
161
+ [**mathisarends.github.io/notionary**](https://mathisarends.github.io/notionary/) - Complete API reference, guides, and tutorials
162
+
163
+ ---
164
+
165
+ ## Contributing
166
+
167
+ We welcome contributions from the community! Whether you're:
168
+
169
+ - **Fixing bugs** - Help improve stability and reliability
170
+ - **Adding features** - Extend functionality for new use cases
171
+ - **Improving docs** - Make the library more accessible
172
+ - **Sharing examples** - Show creative applications and patterns
173
+
174
+ Check our [**Contributing Guide**](https://mathisarends.github.io/notionary/contributing/) to get started.
175
+
176
+ ---
177
+
178
+ <div align="center">
179
+
180
+ **Ready to revolutionize your Notion workflows?**
181
+
182
+ [📖 **Read the Docs**](https://mathisarends.github.io/notionary/) • [🚀 **Getting Started**](https://mathisarends.github.io/notionary/get-started/) • [💻 **Browse Examples**](examples/)
183
+
184
+ _Built with ❤️ for Python developers and AI agents_
185
+
186
+ ---
187
+
188
+ **Transform complex Notion API interactions into simple, powerful code.**
189
+
190
+ </div>
@@ -0,0 +1,14 @@
1
+ from .data_source.service import NotionDataSource
2
+ from .database.service import NotionDatabase
3
+ from .page.content.markdown.builder import MarkdownBuilder
4
+ from .page.service import NotionPage
5
+ from .workspace import NotionWorkspace, NotionWorkspaceQueryConfigBuilder
6
+
7
+ __all__ = [
8
+ "MarkdownBuilder",
9
+ "NotionDataSource",
10
+ "NotionDatabase",
11
+ "NotionPage",
12
+ "NotionWorkspace",
13
+ "NotionWorkspaceQueryConfigBuilder",
14
+ ]
@@ -0,0 +1,5 @@
1
+ from .enums import CodingLanguage
2
+
3
+ # Import from schemas aswell
4
+
5
+ __all__ = ["CodingLanguage"]
@@ -1,7 +1,7 @@
1
- from typing import Any
2
-
3
1
  from notionary.blocks.schemas import Block, BlockChildrenResponse, BlockCreatePayload
4
2
  from notionary.http.client import NotionHttpClient
3
+ from notionary.shared.typings import JsonDict
4
+ from notionary.utils.decorators import time_execution_async
5
5
  from notionary.utils.pagination import paginate_notion_api
6
6
 
7
7
 
@@ -16,6 +16,7 @@ class NotionBlockHttpClient(NotionHttpClient):
16
16
  self.logger.debug("Deleting block: %s", block_id)
17
17
  await self.delete(f"blocks/{block_id}")
18
18
 
19
+ @time_execution_async()
19
20
  async def get_block_tree(self, parent_block_id: str) -> list[Block]:
20
21
  blocks_at_this_level = await self.get_all_block_children(parent_block_id)
21
22
 
@@ -26,6 +27,7 @@ class NotionBlockHttpClient(NotionHttpClient):
26
27
 
27
28
  return blocks_at_this_level
28
29
 
30
+ @time_execution_async()
29
31
  async def get_all_block_children(self, parent_block_id: str) -> list[Block]:
30
32
  self.logger.debug("Retrieving all children for block: %s", parent_block_id)
31
33
 
@@ -73,11 +75,11 @@ class NotionBlockHttpClient(NotionHttpClient):
73
75
  batches.append(batch)
74
76
  return batches
75
77
 
76
- def _serialize_blocks(self, blocks: list[BlockCreatePayload]) -> list[dict[str, Any]]:
78
+ def _serialize_blocks(self, blocks: list[BlockCreatePayload]) -> list[JsonDict]:
77
79
  return [block.model_dump(exclude_none=True) for block in blocks]
78
80
 
79
81
  async def _send_append_request(
80
- self, block_id: str, children: list[dict[str, Any]], after_block_id: str | None = None
82
+ self, block_id: str, children: list[JsonDict], after_block_id: str | None = None
81
83
  ) -> BlockChildrenResponse:
82
84
  payload = {"children": children}
83
85
  if after_block_id:
@@ -68,7 +68,7 @@ class FileType(StrEnum):
68
68
  FILE_UPLOAD = "file_upload"
69
69
 
70
70
 
71
- class CodeLanguage(StrEnum):
71
+ class CodingLanguage(StrEnum):
72
72
  ABAP = "abap"
73
73
  ARDUINO = "arduino"
74
74
  BASH = "bash"
@@ -165,3 +165,30 @@ class CodeLanguage(StrEnum):
165
165
  return member
166
166
 
167
167
  return default if default is not None else cls.PLAIN_TEXT
168
+
169
+
170
+ class VideoFileType(StrEnum):
171
+ AMV = ".amv"
172
+ ASF = ".asf"
173
+ AVI = ".avi"
174
+ F4V = ".f4v"
175
+ FLV = ".flv"
176
+ GIFV = ".gifv"
177
+ MKV = ".mkv"
178
+ MOV = ".mov"
179
+ MPG = ".mpg"
180
+ MPEG = ".mpeg"
181
+ MPV = ".mpv"
182
+ MP4 = ".mp4"
183
+ M4V = ".m4v"
184
+ QT = ".qt"
185
+ WMV = ".wmv"
186
+
187
+ @classmethod
188
+ def get_all_extensions(cls) -> set[str]:
189
+ return {ext.value for ext in cls}
190
+
191
+ @classmethod
192
+ def is_valid_extension(cls, filename: str) -> bool:
193
+ lower_filename = filename.lower()
194
+ return any(lower_filename.endswith(ext.value) for ext in cls)
@@ -7,6 +7,7 @@ from typing import ClassVar
7
7
  from notionary.blocks.rich_text.models import MentionType, RichText, RichTextType, TextAnnotations
8
8
  from notionary.blocks.rich_text.name_id_resolver import (
9
9
  DatabaseNameIdResolver,
10
+ DataSourceNameIdResolver,
10
11
  NameIdResolver,
11
12
  PageNameIdResolver,
12
13
  PersonNameIdResolver,
@@ -44,10 +45,12 @@ class MarkdownRichTextConverter:
44
45
  *,
45
46
  page_resolver: NameIdResolver | None = None,
46
47
  database_resolver: NameIdResolver | None = None,
48
+ data_source_resolver: NameIdResolver | None = None,
47
49
  person_resolver: NameIdResolver | None = None,
48
50
  ):
49
51
  self.page_resolver = page_resolver or PageNameIdResolver()
50
52
  self.database_resolver = database_resolver or DatabaseNameIdResolver()
53
+ self.data_source_resolver = data_source_resolver or DataSourceNameIdResolver()
51
54
  self.person_resolver = person_resolver or PersonNameIdResolver()
52
55
  self.format_handlers = self._setup_format_handlers()
53
56
 
@@ -64,6 +67,7 @@ class MarkdownRichTextConverter:
64
67
  PatternHandler(RichTextPatterns.COLOR, self._handle_color_pattern),
65
68
  PatternHandler(RichTextPatterns.PAGE_MENTION, self._handle_page_mention_pattern),
66
69
  PatternHandler(RichTextPatterns.DATABASE_MENTION, self._handle_database_mention_pattern),
70
+ PatternHandler(RichTextPatterns.DATASOURCE_MENTION, self._handle_data_source_mention_pattern),
67
71
  PatternHandler(RichTextPatterns.USER_MENTION, self._handle_user_mention_pattern),
68
72
  ]
69
73
 
@@ -119,6 +123,7 @@ class MarkdownRichTextConverter:
119
123
  async_handlers = {
120
124
  self._handle_page_mention_pattern,
121
125
  self._handle_database_mention_pattern,
126
+ self._handle_data_source_mention_pattern,
122
127
  self._handle_color_pattern, # Color pattern needs async for recursive parsing
123
128
  self._handle_user_mention_pattern,
124
129
  }
@@ -210,6 +215,15 @@ class MarkdownRichTextConverter:
210
215
  mention_type=MentionType.DATABASE,
211
216
  )
212
217
 
218
+ async def _handle_data_source_mention_pattern(self, match: Match) -> RichText:
219
+ identifier = match.group(1)
220
+ return await self._create_mention_or_fallback(
221
+ identifier=identifier,
222
+ resolve_func=self.data_source_resolver.resolve_name_to_id,
223
+ create_mention_func=RichText.mention_data_source,
224
+ mention_type=MentionType.DATASOURCE,
225
+ )
226
+
213
227
  async def _handle_user_mention_pattern(self, match: Match) -> RichText:
214
228
  identifier = match.group(1)
215
229
  return await self._create_mention_or_fallback(
@@ -16,6 +16,7 @@ class MentionType(StrEnum):
16
16
  USER = "user"
17
17
  PAGE = "page"
18
18
  DATABASE = "database"
19
+ DATASOURCE = "data_source"
19
20
  DATE = "date"
20
21
  LINK_PREVIEW = "link_preview"
21
22
  TEMPLATE_MENTION = "template_mention"
@@ -60,6 +61,10 @@ class MentionDatabaseRef(BaseModel):
60
61
  id: str
61
62
 
62
63
 
64
+ class MentionDataSourceRef(BaseModel):
65
+ id: str
66
+
67
+
63
68
  class MentionLinkPreview(BaseModel):
64
69
  url: str
65
70
 
@@ -81,6 +86,7 @@ class MentionObject(BaseModel):
81
86
  user: MentionUserRef | None = None
82
87
  page: MentionPageRef | None = None
83
88
  database: MentionDatabaseRef | None = None
89
+ data_source: MentionDataSourceRef | None = None
84
90
  date: MentionDate | None = None
85
91
  link_preview: MentionLinkPreview | None = None
86
92
  template_mention: MentionTemplateMention | None = None
@@ -154,6 +160,14 @@ class RichText(BaseModel):
154
160
  annotations=TextAnnotations(),
155
161
  )
156
162
 
163
+ @classmethod
164
+ def mention_data_source(cls, data_source_id: str) -> Self:
165
+ return cls(
166
+ type=RichTextType.MENTION,
167
+ mention=MentionObject(type=MentionType.DATASOURCE, data_source=MentionDataSourceRef(id=data_source_id)),
168
+ annotations=TextAnnotations(),
169
+ )
170
+
157
171
  @classmethod
158
172
  def equation_inline(cls, expression: str) -> Self:
159
173
  return cls(
@@ -1,9 +1,11 @@
1
+ from .data_source import DataSourceNameIdResolver
1
2
  from .database import DatabaseNameIdResolver
2
3
  from .page import PageNameIdResolver
3
4
  from .person import PersonNameIdResolver
4
5
  from .port import NameIdResolver
5
6
 
6
7
  __all__ = [
8
+ "DataSourceNameIdResolver",
7
9
  "DatabaseNameIdResolver",
8
10
  "NameIdResolver",
9
11
  "PageNameIdResolver",
@@ -0,0 +1,32 @@
1
+ from typing import override
2
+
3
+ from notionary.blocks.rich_text.name_id_resolver.port import NameIdResolver
4
+ from notionary.workspace.query.service import WorkspaceQueryService
5
+
6
+
7
+ # !!! in the notion api mentions that reference datasources are not provided yet (it's a limiation of the API as of now)
8
+ class DataSourceNameIdResolver(NameIdResolver):
9
+ def __init__(self, workspace_query_service: WorkspaceQueryService | None = None) -> None:
10
+ self._workspace_query_service = workspace_query_service or WorkspaceQueryService()
11
+
12
+ @override
13
+ async def resolve_name_to_id(self, name: str) -> str | None:
14
+ if not name:
15
+ return None
16
+
17
+ cleaned_name = name.strip()
18
+ data_source = await self._workspace_query_service.find_data_source(query=cleaned_name)
19
+ return data_source.id if data_source else None
20
+
21
+ @override
22
+ async def resolve_id_to_name(self, data_source_id: str) -> str | None:
23
+ if not data_source_id:
24
+ return None
25
+
26
+ try:
27
+ from notionary import NotionDataSource
28
+
29
+ data_source = await NotionDataSource.from_id(data_source_id)
30
+ return data_source.title if data_source else None
31
+ except Exception:
32
+ return None
@@ -8,6 +8,7 @@ from notionary.blocks.rich_text.models import (
8
8
  )
9
9
  from notionary.blocks.rich_text.name_id_resolver import (
10
10
  DatabaseNameIdResolver,
11
+ DataSourceNameIdResolver,
11
12
  NameIdResolver,
12
13
  PageNameIdResolver,
13
14
  PersonNameIdResolver,
@@ -23,10 +24,12 @@ class RichTextToMarkdownConverter:
23
24
  *,
24
25
  page_resolver: NameIdResolver | None = None,
25
26
  database_resolver: NameIdResolver | None = None,
27
+ data_source_resolver: NameIdResolver | None = None,
26
28
  person_resolver: NameIdResolver | None = None,
27
29
  ) -> None:
28
30
  self.page_resolver = page_resolver or PageNameIdResolver()
29
31
  self.database_resolver = database_resolver or DatabaseNameIdResolver()
32
+ self.data_source_resolver = data_source_resolver or DataSourceNameIdResolver()
30
33
  self.person_resolver = person_resolver or PersonNameIdResolver()
31
34
 
32
35
  async def to_markdown(self, rich_text: list[RichText]) -> str:
@@ -65,6 +68,9 @@ class RichTextToMarkdownConverter:
65
68
  elif mention.type == MentionType.DATABASE and mention.database:
66
69
  return await self._extract_database_mention_markdown(mention.database.id)
67
70
 
71
+ elif mention.type == MentionType.DATASOURCE and mention.data_source:
72
+ return await self._extract_data_source_mention_markdown(mention.data_source.id)
73
+
68
74
  elif mention.type == MentionType.USER and mention.user:
69
75
  return await self._extract_user_mention_markdown(mention.user.id)
70
76
 
@@ -81,6 +87,10 @@ class RichTextToMarkdownConverter:
81
87
  database_name = await self.database_resolver.resolve_id_to_name(database_id)
82
88
  return f"@database[{database_name or database_id}]"
83
89
 
90
+ async def _extract_data_source_mention_markdown(self, data_source_id: str) -> str:
91
+ data_source_name = await self.data_source_resolver.resolve_id_to_name(data_source_id)
92
+ return f"@datasource[{data_source_name or data_source_id}]"
93
+
84
94
  async def _extract_user_mention_markdown(self, user_id: str) -> str:
85
95
  user_name = await self.person_resolver.resolve_id_to_name(user_id)
86
96
  return f"@user[{user_name or user_id}]"
@@ -122,11 +132,13 @@ async def convert_rich_text_to_markdown(
122
132
  *,
123
133
  page_resolver: NameIdResolver | None = None,
124
134
  database_resolver: NameIdResolver | None = None,
135
+ data_source_resolver: NameIdResolver | None = None,
125
136
  person_resolver: NameIdResolver | None = None,
126
137
  ) -> str:
127
138
  converter = RichTextToMarkdownConverter(
128
139
  page_resolver=page_resolver,
129
140
  database_resolver=database_resolver,
141
+ data_source_resolver=data_source_resolver,
130
142
  person_resolver=person_resolver,
131
143
  )
132
144
  return await converter.to_markdown(rich_text)