notionary 0.2.27__py3-none-any.whl → 0.2.28__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. notionary/__init__.py +5 -20
  2. notionary/blocks/client.py +87 -215
  3. notionary/blocks/enums.py +167 -0
  4. notionary/blocks/rich_text/markdown_rich_text_converter.py +266 -0
  5. notionary/blocks/rich_text/models.py +164 -0
  6. notionary/blocks/rich_text/name_id_resolver/__init__.py +11 -0
  7. notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
  8. notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
  9. notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
  10. notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
  11. notionary/blocks/rich_text/rich_text_markdown_converter.py +132 -0
  12. notionary/blocks/rich_text/rich_text_patterns.py +39 -0
  13. notionary/blocks/schemas.py +746 -0
  14. notionary/comments/client.py +52 -187
  15. notionary/comments/factory.py +40 -0
  16. notionary/comments/models.py +5 -127
  17. notionary/comments/schemas.py +240 -0
  18. notionary/comments/service.py +34 -0
  19. notionary/data_source/http/client.py +11 -0
  20. notionary/data_source/http/data_source_instance_client.py +94 -0
  21. notionary/data_source/properties/models.py +406 -0
  22. notionary/data_source/query/builder.py +429 -0
  23. notionary/data_source/query/resolver.py +114 -0
  24. notionary/data_source/query/schema.py +304 -0
  25. notionary/data_source/query/validator.py +73 -0
  26. notionary/data_source/schemas.py +27 -0
  27. notionary/data_source/service.py +353 -0
  28. notionary/database/client.py +30 -135
  29. notionary/database/database_metadata_update_client.py +19 -0
  30. notionary/database/schemas.py +29 -0
  31. notionary/database/service.py +169 -0
  32. notionary/exceptions/__init__.py +33 -0
  33. notionary/exceptions/api.py +41 -0
  34. notionary/exceptions/base.py +2 -0
  35. notionary/exceptions/block_parsing.py +16 -0
  36. notionary/exceptions/data_source/__init__.py +6 -0
  37. notionary/exceptions/data_source/builder.py +182 -0
  38. notionary/exceptions/data_source/properties.py +34 -0
  39. notionary/exceptions/properties.py +58 -0
  40. notionary/exceptions/search.py +33 -0
  41. notionary/file_upload/client.py +18 -30
  42. notionary/file_upload/models.py +7 -8
  43. notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
  44. notionary/http/client.py +205 -0
  45. notionary/http/models.py +49 -0
  46. notionary/page/blocks/client.py +1 -0
  47. notionary/page/content/factory.py +68 -0
  48. notionary/page/content/markdown/__init__.py +5 -0
  49. notionary/page/content/markdown/builder.py +304 -0
  50. notionary/page/content/markdown/nodes/__init__.py +54 -0
  51. notionary/page/content/markdown/nodes/audio.py +23 -0
  52. notionary/page/content/markdown/nodes/base.py +12 -0
  53. notionary/page/content/markdown/nodes/bookmark.py +25 -0
  54. notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
  55. notionary/page/content/markdown/nodes/bulleted_list.py +18 -0
  56. notionary/page/content/markdown/nodes/callout.py +32 -0
  57. notionary/page/content/markdown/nodes/code.py +30 -0
  58. notionary/page/content/markdown/nodes/columns.py +51 -0
  59. notionary/page/content/markdown/nodes/divider.py +14 -0
  60. notionary/page/content/markdown/nodes/embed.py +23 -0
  61. notionary/page/content/markdown/nodes/equation.py +19 -0
  62. notionary/page/content/markdown/nodes/file.py +23 -0
  63. notionary/page/content/markdown/nodes/heading.py +16 -0
  64. notionary/page/content/markdown/nodes/image.py +23 -0
  65. notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
  66. notionary/page/content/markdown/nodes/numbered_list.py +15 -0
  67. notionary/page/content/markdown/nodes/paragraph.py +14 -0
  68. notionary/page/content/markdown/nodes/pdf.py +23 -0
  69. notionary/page/content/markdown/nodes/quote.py +15 -0
  70. notionary/page/content/markdown/nodes/space.py +14 -0
  71. notionary/page/content/markdown/nodes/table.py +45 -0
  72. notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
  73. notionary/page/content/markdown/nodes/todo.py +22 -0
  74. notionary/page/content/markdown/nodes/toggle.py +28 -0
  75. notionary/page/content/markdown/nodes/toggleable_heading.py +35 -0
  76. notionary/page/content/markdown/nodes/video.py +23 -0
  77. notionary/page/content/parser/context.py +49 -0
  78. notionary/page/content/parser/factory.py +219 -0
  79. notionary/page/content/parser/parsers/__init__.py +60 -0
  80. notionary/page/content/parser/parsers/audio.py +40 -0
  81. notionary/page/content/parser/parsers/base.py +30 -0
  82. notionary/page/content/parser/parsers/bookmark.py +33 -0
  83. notionary/page/content/parser/parsers/breadcrumb.py +33 -0
  84. notionary/page/content/parser/parsers/bulleted_list.py +41 -0
  85. notionary/page/content/parser/parsers/callout.py +129 -0
  86. notionary/page/content/parser/parsers/caption.py +55 -0
  87. notionary/page/content/parser/parsers/code.py +81 -0
  88. notionary/page/content/parser/parsers/column.py +117 -0
  89. notionary/page/content/parser/parsers/column_list.py +81 -0
  90. notionary/page/content/parser/parsers/divider.py +33 -0
  91. notionary/page/content/parser/parsers/embed.py +33 -0
  92. notionary/page/content/parser/parsers/equation.py +65 -0
  93. notionary/page/content/parser/parsers/file.py +42 -0
  94. notionary/page/content/parser/parsers/heading.py +58 -0
  95. notionary/page/content/parser/parsers/image.py +42 -0
  96. notionary/page/content/parser/parsers/numbered_list.py +45 -0
  97. notionary/page/content/parser/parsers/paragraph.py +36 -0
  98. notionary/page/content/parser/parsers/pdf.py +42 -0
  99. notionary/page/content/parser/parsers/quote.py +65 -0
  100. notionary/page/content/parser/parsers/space.py +35 -0
  101. notionary/page/content/parser/parsers/table.py +144 -0
  102. notionary/page/content/parser/parsers/table_of_contents.py +32 -0
  103. notionary/page/content/parser/parsers/todo.py +58 -0
  104. notionary/page/content/parser/parsers/toggle.py +127 -0
  105. notionary/page/content/parser/parsers/toggleable_heading.py +150 -0
  106. notionary/page/content/parser/parsers/video.py +42 -0
  107. notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
  108. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +93 -0
  109. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +93 -0
  110. notionary/page/content/parser/post_processing/port.py +9 -0
  111. notionary/page/content/parser/post_processing/service.py +16 -0
  112. notionary/page/content/parser/pre_processsing/handlers/__init__.py +9 -0
  113. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +80 -0
  114. notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
  115. notionary/page/content/parser/pre_processsing/handlers/whitespace.py +68 -0
  116. notionary/page/content/parser/pre_processsing/service.py +15 -0
  117. notionary/page/content/parser/service.py +69 -0
  118. notionary/page/content/renderer/context.py +48 -0
  119. notionary/page/content/renderer/factory.py +240 -0
  120. notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
  121. notionary/page/content/renderer/post_processing/handlers/numbered_list_placeholdere.py +62 -0
  122. notionary/page/content/renderer/post_processing/port.py +7 -0
  123. notionary/page/content/renderer/post_processing/service.py +15 -0
  124. notionary/page/content/renderer/renderers/__init__.py +57 -0
  125. notionary/page/content/renderer/renderers/audio.py +31 -0
  126. notionary/page/content/renderer/renderers/base.py +31 -0
  127. notionary/page/content/renderer/renderers/bookmark.py +25 -0
  128. notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
  129. notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
  130. notionary/page/content/renderer/renderers/callout.py +65 -0
  131. notionary/page/content/renderer/renderers/captioned_block.py +58 -0
  132. notionary/page/content/renderer/renderers/code.py +34 -0
  133. notionary/page/content/renderer/renderers/column.py +44 -0
  134. notionary/page/content/renderer/renderers/column_list.py +31 -0
  135. notionary/page/content/renderer/renderers/divider.py +22 -0
  136. notionary/page/content/renderer/renderers/embed.py +25 -0
  137. notionary/page/content/renderer/renderers/equation.py +37 -0
  138. notionary/page/content/renderer/renderers/fallback.py +24 -0
  139. notionary/page/content/renderer/renderers/file.py +40 -0
  140. notionary/page/content/renderer/renderers/heading.py +69 -0
  141. notionary/page/content/renderer/renderers/image.py +31 -0
  142. notionary/page/content/renderer/renderers/numbered_list.py +41 -0
  143. notionary/page/content/renderer/renderers/paragraph.py +40 -0
  144. notionary/page/content/renderer/renderers/pdf.py +31 -0
  145. notionary/page/content/renderer/renderers/quote.py +49 -0
  146. notionary/page/content/renderer/renderers/table.py +115 -0
  147. notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
  148. notionary/page/content/renderer/renderers/table_row.py +17 -0
  149. notionary/page/content/renderer/renderers/todo.py +56 -0
  150. notionary/page/content/renderer/renderers/toggle.py +53 -0
  151. notionary/page/content/renderer/renderers/toggleable_heading.py +78 -0
  152. notionary/page/content/renderer/renderers/video.py +31 -0
  153. notionary/page/content/renderer/service.py +50 -0
  154. notionary/page/content/service.py +65 -0
  155. notionary/page/content/syntax/models.py +68 -0
  156. notionary/page/content/syntax/service.py +453 -0
  157. notionary/page/page_context.py +7 -16
  158. notionary/page/page_http_client.py +15 -0
  159. notionary/page/page_metadata_update_client.py +19 -0
  160. notionary/page/properties/client.py +144 -0
  161. notionary/page/properties/factory.py +26 -0
  162. notionary/page/properties/models.py +307 -0
  163. notionary/page/properties/service.py +257 -0
  164. notionary/page/schemas.py +13 -0
  165. notionary/page/service.py +222 -0
  166. notionary/shared/entity/client.py +29 -0
  167. notionary/shared/entity/dto_parsers.py +53 -0
  168. notionary/shared/entity/entity_metadata_update_client.py +41 -0
  169. notionary/shared/entity/schemas.py +45 -0
  170. notionary/shared/entity/service.py +171 -0
  171. notionary/shared/models/cover.py +20 -0
  172. notionary/shared/models/file.py +21 -0
  173. notionary/shared/models/icon.py +28 -0
  174. notionary/shared/models/parent.py +41 -0
  175. notionary/shared/properties/type.py +30 -0
  176. notionary/user/__init__.py +4 -8
  177. notionary/user/base.py +89 -0
  178. notionary/user/bot.py +70 -0
  179. notionary/user/client.py +22 -111
  180. notionary/user/person.py +41 -0
  181. notionary/user/schemas.py +67 -0
  182. notionary/user/service.py +65 -0
  183. notionary/utils/async_retry.py +39 -0
  184. notionary/utils/date.py +51 -0
  185. notionary/utils/fuzzy.py +56 -0
  186. notionary/{util/logging_mixin.py → utils/mixins/logging.py} +4 -16
  187. notionary/utils/pagination.py +50 -0
  188. notionary/utils/singleton.py +13 -0
  189. notionary/utils/uuid_utils.py +20 -0
  190. notionary/workspace/__init__.py +3 -0
  191. notionary/workspace/client.py +62 -0
  192. notionary/workspace/query/builder.py +60 -0
  193. notionary/workspace/query/models.py +60 -0
  194. notionary/workspace/query/service.py +93 -0
  195. notionary/workspace/schemas.py +21 -0
  196. notionary/workspace/service.py +116 -0
  197. {notionary-0.2.27.dist-info → notionary-0.2.28.dist-info}/METADATA +54 -49
  198. notionary-0.2.28.dist-info/RECORD +200 -0
  199. {notionary-0.2.27.dist-info → notionary-0.2.28.dist-info}/WHEEL +1 -1
  200. {notionary-0.2.27.dist-info → notionary-0.2.28.dist-info/licenses}/LICENSE +9 -9
  201. notionary/base_notion_client.py +0 -219
  202. notionary/blocks/__init__.py +0 -5
  203. notionary/blocks/_bootstrap.py +0 -271
  204. notionary/blocks/audio/__init__.py +0 -11
  205. notionary/blocks/audio/audio_element.py +0 -158
  206. notionary/blocks/audio/audio_markdown_node.py +0 -24
  207. notionary/blocks/audio/audio_models.py +0 -10
  208. notionary/blocks/base_block_element.py +0 -42
  209. notionary/blocks/bookmark/__init__.py +0 -12
  210. notionary/blocks/bookmark/bookmark_element.py +0 -83
  211. notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
  212. notionary/blocks/bookmark/bookmark_models.py +0 -15
  213. notionary/blocks/breadcrumbs/__init__.py +0 -15
  214. notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
  215. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
  216. notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
  217. notionary/blocks/bulleted_list/__init__.py +0 -15
  218. notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
  219. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
  220. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
  221. notionary/blocks/callout/__init__.py +0 -12
  222. notionary/blocks/callout/callout_element.py +0 -99
  223. notionary/blocks/callout/callout_markdown_node.py +0 -19
  224. notionary/blocks/callout/callout_models.py +0 -33
  225. notionary/blocks/child_database/__init__.py +0 -14
  226. notionary/blocks/child_database/child_database_element.py +0 -59
  227. notionary/blocks/child_database/child_database_models.py +0 -12
  228. notionary/blocks/child_page/__init__.py +0 -9
  229. notionary/blocks/child_page/child_page_element.py +0 -94
  230. notionary/blocks/child_page/child_page_models.py +0 -12
  231. notionary/blocks/code/__init__.py +0 -11
  232. notionary/blocks/code/code_element.py +0 -149
  233. notionary/blocks/code/code_markdown_node.py +0 -80
  234. notionary/blocks/code/code_models.py +0 -94
  235. notionary/blocks/column/__init__.py +0 -25
  236. notionary/blocks/column/column_element.py +0 -65
  237. notionary/blocks/column/column_list_element.py +0 -52
  238. notionary/blocks/column/column_list_markdown_node.py +0 -34
  239. notionary/blocks/column/column_markdown_node.py +0 -42
  240. notionary/blocks/column/column_models.py +0 -26
  241. notionary/blocks/divider/__init__.py +0 -12
  242. notionary/blocks/divider/divider_element.py +0 -41
  243. notionary/blocks/divider/divider_markdown_node.py +0 -11
  244. notionary/blocks/divider/divider_models.py +0 -12
  245. notionary/blocks/embed/__init__.py +0 -12
  246. notionary/blocks/embed/embed_element.py +0 -98
  247. notionary/blocks/embed/embed_markdown_node.py +0 -19
  248. notionary/blocks/embed/embed_models.py +0 -14
  249. notionary/blocks/equation/__init__.py +0 -13
  250. notionary/blocks/equation/equation_element.py +0 -133
  251. notionary/blocks/equation/equation_element_markdown_node.py +0 -23
  252. notionary/blocks/equation/equation_models.py +0 -11
  253. notionary/blocks/file/__init__.py +0 -23
  254. notionary/blocks/file/file_element.py +0 -133
  255. notionary/blocks/file/file_element_markdown_node.py +0 -24
  256. notionary/blocks/file/file_element_models.py +0 -39
  257. notionary/blocks/heading/__init__.py +0 -19
  258. notionary/blocks/heading/heading_element.py +0 -112
  259. notionary/blocks/heading/heading_markdown_node.py +0 -16
  260. notionary/blocks/heading/heading_models.py +0 -29
  261. notionary/blocks/image_block/__init__.py +0 -11
  262. notionary/blocks/image_block/image_element.py +0 -130
  263. notionary/blocks/image_block/image_markdown_node.py +0 -25
  264. notionary/blocks/image_block/image_models.py +0 -10
  265. notionary/blocks/markdown/markdown_builder.py +0 -525
  266. notionary/blocks/markdown/markdown_document_model.py +0 -0
  267. notionary/blocks/markdown/markdown_node.py +0 -25
  268. notionary/blocks/mixins/captions/__init__.py +0 -4
  269. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
  270. notionary/blocks/mixins/captions/caption_mixin.py +0 -92
  271. notionary/blocks/mixins/file_upload/__init__.py +0 -3
  272. notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
  273. notionary/blocks/models.py +0 -174
  274. notionary/blocks/numbered_list/__init__.py +0 -16
  275. notionary/blocks/numbered_list/numbered_list_element.py +0 -65
  276. notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
  277. notionary/blocks/numbered_list/numbered_list_models.py +0 -17
  278. notionary/blocks/paragraph/__init__.py +0 -15
  279. notionary/blocks/paragraph/paragraph_element.py +0 -58
  280. notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
  281. notionary/blocks/paragraph/paragraph_models.py +0 -16
  282. notionary/blocks/pdf/__init__.py +0 -11
  283. notionary/blocks/pdf/pdf_element.py +0 -146
  284. notionary/blocks/pdf/pdf_markdown_node.py +0 -24
  285. notionary/blocks/pdf/pdf_models.py +0 -11
  286. notionary/blocks/quote/__init__.py +0 -14
  287. notionary/blocks/quote/quote_element.py +0 -75
  288. notionary/blocks/quote/quote_markdown_node.py +0 -16
  289. notionary/blocks/quote/quote_models.py +0 -18
  290. notionary/blocks/registry/__init__.py +0 -3
  291. notionary/blocks/registry/block_registry.py +0 -150
  292. notionary/blocks/rich_text/__init__.py +0 -33
  293. notionary/blocks/rich_text/rich_text_models.py +0 -221
  294. notionary/blocks/rich_text/text_inline_formatter.py +0 -456
  295. notionary/blocks/syntax_prompt_builder.py +0 -137
  296. notionary/blocks/table/__init__.py +0 -19
  297. notionary/blocks/table/table_element.py +0 -225
  298. notionary/blocks/table/table_markdown_node.py +0 -42
  299. notionary/blocks/table/table_models.py +0 -28
  300. notionary/blocks/table_of_contents/__init__.py +0 -17
  301. notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
  302. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
  303. notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
  304. notionary/blocks/todo/__init__.py +0 -12
  305. notionary/blocks/todo/todo_element.py +0 -81
  306. notionary/blocks/todo/todo_markdown_node.py +0 -21
  307. notionary/blocks/todo/todo_models.py +0 -18
  308. notionary/blocks/toggle/__init__.py +0 -12
  309. notionary/blocks/toggle/toggle_element.py +0 -112
  310. notionary/blocks/toggle/toggle_markdown_node.py +0 -31
  311. notionary/blocks/toggle/toggle_models.py +0 -17
  312. notionary/blocks/toggleable_heading/__init__.py +0 -11
  313. notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
  314. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
  315. notionary/blocks/types.py +0 -130
  316. notionary/blocks/video/__init__.py +0 -11
  317. notionary/blocks/video/video_element.py +0 -187
  318. notionary/blocks/video/video_element_models.py +0 -10
  319. notionary/blocks/video/video_markdown_node.py +0 -26
  320. notionary/comments/__init__.py +0 -26
  321. notionary/database/__init__.py +0 -4
  322. notionary/database/database.py +0 -480
  323. notionary/database/database_filter_builder.py +0 -173
  324. notionary/database/database_provider.py +0 -227
  325. notionary/database/exceptions.py +0 -13
  326. notionary/database/factory.py +0 -0
  327. notionary/database/models.py +0 -337
  328. notionary/database/notion_database.py +0 -487
  329. notionary/file_upload/__init__.py +0 -7
  330. notionary/page/client.py +0 -124
  331. notionary/page/markdown_whitespace_processor.py +0 -129
  332. notionary/page/models.py +0 -322
  333. notionary/page/notion_page.py +0 -712
  334. notionary/page/page_content_deleting_service.py +0 -117
  335. notionary/page/page_content_writer.py +0 -80
  336. notionary/page/property_formatter.py +0 -99
  337. notionary/page/reader/handler/__init__.py +0 -19
  338. notionary/page/reader/handler/base_block_renderer.py +0 -44
  339. notionary/page/reader/handler/block_processing_context.py +0 -35
  340. notionary/page/reader/handler/block_rendering_context.py +0 -48
  341. notionary/page/reader/handler/column_list_renderer.py +0 -51
  342. notionary/page/reader/handler/column_renderer.py +0 -60
  343. notionary/page/reader/handler/equation_renderer.py +0 -0
  344. notionary/page/reader/handler/line_renderer.py +0 -73
  345. notionary/page/reader/handler/numbered_list_renderer.py +0 -85
  346. notionary/page/reader/handler/toggle_renderer.py +0 -69
  347. notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
  348. notionary/page/reader/page_content_retriever.py +0 -81
  349. notionary/page/search_filter_builder.py +0 -132
  350. notionary/page/utils.py +0 -60
  351. notionary/page/writer/handler/__init__.py +0 -24
  352. notionary/page/writer/handler/code_handler.py +0 -72
  353. notionary/page/writer/handler/column_handler.py +0 -141
  354. notionary/page/writer/handler/column_list_handler.py +0 -139
  355. notionary/page/writer/handler/equation_handler.py +0 -74
  356. notionary/page/writer/handler/line_handler.py +0 -35
  357. notionary/page/writer/handler/line_processing_context.py +0 -54
  358. notionary/page/writer/handler/regular_line_handler.py +0 -86
  359. notionary/page/writer/handler/table_handler.py +0 -66
  360. notionary/page/writer/handler/toggle_handler.py +0 -159
  361. notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
  362. notionary/page/writer/markdown_to_notion_converter.py +0 -139
  363. notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
  364. notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
  365. notionary/page/writer/notion_text_length_processor.py +0 -150
  366. notionary/schemas/__init__.py +0 -3
  367. notionary/schemas/base.py +0 -73
  368. notionary/shared/__init__.py +0 -3
  369. notionary/shared/name_to_id_resolver.py +0 -203
  370. notionary/telemetry/__init__.py +0 -19
  371. notionary/telemetry/service.py +0 -136
  372. notionary/telemetry/views.py +0 -73
  373. notionary/user/base_notion_user.py +0 -53
  374. notionary/user/models.py +0 -84
  375. notionary/user/notion_bot_user.py +0 -226
  376. notionary/user/notion_user.py +0 -255
  377. notionary/user/notion_user_manager.py +0 -101
  378. notionary/util/__init__.py +0 -15
  379. notionary/util/concurrency_limiter.py +0 -0
  380. notionary/util/factory_decorator.py +0 -0
  381. notionary/util/factory_only.py +0 -37
  382. notionary/util/fuzzy.py +0 -75
  383. notionary/util/page_id_utils.py +0 -27
  384. notionary/util/singleton.py +0 -18
  385. notionary/util/singleton_metaclass.py +0 -22
  386. notionary/workspace.py +0 -105
  387. notionary-0.2.27.dist-info/RECORD +0 -202
notionary/schemas/base.py DELETED
@@ -1,73 +0,0 @@
1
- """
2
- NotionContentSchema Base Class
3
- =============================
4
-
5
- Base class for all Notion structured output schemas with injected MarkdownBuilder
6
- """
7
-
8
- from pydantic import BaseModel
9
- from notionary.blocks.markdown.markdown_builder import MarkdownBuilder
10
-
11
-
12
- class NotionContentSchema(BaseModel):
13
- """
14
- Base class for all Notion content schemas.
15
-
16
- Inherit from this and implement to_notion_content() to create
17
- schemas that work with LLM structured output.
18
-
19
- Example usage:
20
-
21
- class BlogPost(NotionContentSchema):
22
- title: str = Field(description="Catchy blog post title")
23
- introduction: str = Field(description="Engaging opening paragraph")
24
- main_points: List[str] = Field(description="3-5 key takeaways")
25
- conclusion: str = Field(description="Summary and call-to-action")
26
-
27
- def to_notion_content(self, builder: MarkdownBuilder) -> str:
28
- return (builder
29
- .h1(self.title)
30
- .paragraph(self.introduction)
31
- .h2("Key Points")
32
- .bulleted_list(self.main_points)
33
- .h2("Conclusion")
34
- .paragraph(self.conclusion)
35
- .build()
36
- )
37
-
38
- # Usage with LLM:
39
- llm = ChatOpenAI(model="gpt-4o")
40
- structured_llm = llm.with_structured_output(BlogPost)
41
- blog = structured_llm.invoke("Write about Python async/await")
42
-
43
- # Upload to Notion:
44
- await blog.append_to_page("My Blog")
45
- """
46
-
47
- def to_notion_content(self, builder: MarkdownBuilder) -> str:
48
- """
49
- Build Notion content using the provided MarkdownBuilder.
50
-
51
- Args:
52
- builder: Empty MarkdownBuilder instance to build content with
53
-
54
- Returns:
55
- str: The final markdown string (user should call build() on the builder)
56
- """
57
- raise NotImplementedError("Subclasses must implement to_notion_content()")
58
-
59
- async def append_to_page(self, page_name: str):
60
- """
61
- Upload this content directly to a Notion page.
62
-
63
- Args:
64
- page_name: Name of the target Notion page
65
- """
66
- from notionary import NotionPage
67
-
68
- # Create fresh builder and let subclass build content
69
- builder = MarkdownBuilder()
70
- markdown = self.to_notion_content(builder)
71
-
72
- page = await NotionPage.from_page_name(page_name)
73
- await page.append_markdown(markdown)
@@ -1,3 +0,0 @@
1
- from .name_to_id_resolver import NameIdResolver
2
-
3
- __all__ = ["NameIdResolver"]
@@ -1,203 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Optional
4
-
5
- from notionary.user.notion_user_manager import NotionUserManager
6
- from notionary.util import format_uuid
7
- from notionary.util.fuzzy import find_best_match
8
-
9
-
10
- class NameIdResolver:
11
- """
12
- Bidirectional resolver for Notion page and database names and IDs.
13
- """
14
-
15
- def __init__(
16
- self,
17
- *,
18
- token: Optional[str] = None,
19
- search_limit: int = 10,
20
- ):
21
- """
22
- Initialize the resolver with a Notion workspace.
23
- """
24
- from notionary import NotionWorkspace
25
-
26
- self.workspace = NotionWorkspace(token=token)
27
- self.notion_user_manager = NotionUserManager(token=token)
28
- self.search_limit = search_limit
29
-
30
- async def resolve_page_id(self, name: str) -> Optional[str]:
31
- """
32
- Convert a page name to its Notion page ID.
33
- Specifically searches only pages, not databases.
34
- """
35
- if not name:
36
- return None
37
-
38
- cleaned_name = name.strip()
39
-
40
- # Return if already a valid Notion ID
41
- formatted_uuid = format_uuid(cleaned_name)
42
- if formatted_uuid:
43
- return formatted_uuid
44
-
45
- # Search for page by name
46
- return await self._resolve_page_id(cleaned_name)
47
-
48
- async def resolve_database_id(self, name: str) -> Optional[str]:
49
- """
50
- Convert a database name to its Notion database ID.
51
- Specifically searches only databases, not pages.
52
- """
53
- if not name:
54
- return None
55
-
56
- cleaned_name = name.strip()
57
-
58
- formatted_uuid = format_uuid(cleaned_name)
59
- if formatted_uuid:
60
- return formatted_uuid
61
-
62
- return await self._resolve_database_id(cleaned_name)
63
-
64
- async def resolve_page_name(self, page_id: str) -> Optional[str]:
65
- """
66
- Convert a Notion page ID to its human-readable title.
67
- """
68
- if not page_id:
69
- return None
70
-
71
- formatted_id = format_uuid(page_id)
72
- if not formatted_id:
73
- return None
74
-
75
- try:
76
- from notionary import NotionPage
77
-
78
- page = await NotionPage.from_page_id(formatted_id)
79
- return page.title if page else None
80
- except Exception:
81
- return None
82
-
83
- async def resolve_database_name(self, database_id: str) -> Optional[str]:
84
- """
85
- Convert a Notion database ID to its human-readable title.
86
- """
87
- if not database_id:
88
- return None
89
-
90
- # Validate and format UUID
91
- formatted_id = format_uuid(database_id)
92
- if not formatted_id:
93
- return None
94
-
95
- try:
96
- from notionary.database import NotionDatabase
97
-
98
- database = await NotionDatabase.from_database_id(formatted_id)
99
- return database.title if database else None
100
- except Exception:
101
- return None
102
-
103
- async def resolve_user_id(self, name: str) -> Optional[str]:
104
- """
105
- Convert a user name to its Notion user ID.
106
- Specifically searches only users.
107
- """
108
- if not name:
109
- return None
110
-
111
- cleaned_name = name.strip()
112
-
113
- # Return if already a valid Notion ID
114
- formatted_uuid = format_uuid(cleaned_name)
115
- if formatted_uuid:
116
- return formatted_uuid
117
-
118
- # Search for user by name
119
- return await self._resolve_user_id(cleaned_name)
120
-
121
- async def resolve_user_name(self, user_id: str) -> Optional[str]:
122
- """
123
- Convert a Notion user ID to its human-readable name.
124
-
125
- Args:
126
- user_id: Notion user ID to resolve
127
-
128
- Returns:
129
- User name if found, None if not found or inaccessible
130
- """
131
- if not user_id:
132
- return None
133
-
134
- # Validate and format UUID
135
- formatted_id = format_uuid(user_id)
136
- if not formatted_id:
137
- return None
138
-
139
- try:
140
- user = await self.notion_user_manager.get_user_by_id(formatted_id)
141
- return user.name if user else None
142
- except Exception:
143
- return None
144
-
145
- async def _resolve_user_id(self, name: str) -> Optional[str]:
146
- """Search for users matching the name."""
147
- try:
148
- users = await self.notion_user_manager.find_users_by_name(name)
149
-
150
- if not users:
151
- return None
152
-
153
- # Use fuzzy matching to find best match
154
- best_match = find_best_match(
155
- query=name,
156
- items=users,
157
- text_extractor=lambda user: user.name or "",
158
- )
159
-
160
- return best_match.item.id if best_match else None
161
- except Exception:
162
- return None
163
-
164
- async def _resolve_page_id(self, name: str) -> Optional[str]:
165
- """Search for pages matching the name."""
166
- search_results = await self.workspace.search_pages(
167
- query=name, limit=self.search_limit
168
- )
169
-
170
- return self._find_best_fuzzy_match(query=name, candidate_objects=search_results)
171
-
172
- async def _resolve_database_id(self, name: str) -> Optional[str]:
173
- """Search for databases matching the name."""
174
- search_results = await self.workspace.search_databases(
175
- query=name, limit=self.search_limit
176
- )
177
-
178
- return self._find_best_fuzzy_match(query=name, candidate_objects=search_results)
179
-
180
- def _find_best_fuzzy_match(
181
- self, query: str, candidate_objects: list
182
- ) -> Optional[str]:
183
- """
184
- Find the best fuzzy match among candidate objects using existing fuzzy matching logic.
185
-
186
- Args:
187
- query: The search query to match against
188
- candidate_objects: Objects (pages or databases) with .id and .title attributes
189
-
190
- Returns:
191
- ID of best match, or None if no match meets threshold
192
- """
193
- if not candidate_objects:
194
- return None
195
-
196
- # Use existing fuzzy matching logic
197
- best_match = find_best_match(
198
- query=query,
199
- items=candidate_objects,
200
- text_extractor=lambda obj: obj.title,
201
- )
202
-
203
- return best_match.item.id if best_match else None
@@ -1,19 +0,0 @@
1
- from .service import ProductTelemetry
2
- from .views import (
3
- BaseTelemetryEvent,
4
- DatabaseFactoryUsedEvent,
5
- MarkdownToNotionConversionEvent,
6
- NotionMarkdownSyntaxPromptEvent,
7
- NotionToMarkdownConversionEvent,
8
- QueryOperationEvent,
9
- )
10
-
11
- __all__ = [
12
- "ProductTelemetry",
13
- "BaseTelemetryEvent",
14
- "DatabaseFactoryUsedEvent",
15
- "QueryOperationEvent",
16
- "NotionMarkdownSyntaxPromptEvent",
17
- "MarkdownToNotionConversionEvent",
18
- "NotionToMarkdownConversionEvent",
19
- ]
@@ -1,136 +0,0 @@
1
- import os
2
- import uuid
3
- from pathlib import Path
4
-
5
- from dotenv import load_dotenv
6
- from posthog import Posthog
7
-
8
- from notionary.telemetry.views import BaseTelemetryEvent
9
- from notionary.util import LoggingMixin, SingletonMetaClass
10
-
11
- load_dotenv()
12
-
13
- POSTHOG_EVENT_SETTINGS = {
14
- "process_person_profile": True,
15
- }
16
-
17
-
18
- class ProductTelemetry(LoggingMixin, metaclass=SingletonMetaClass):
19
- """
20
- Anonymous telemetry for Notionary - enabled by default.
21
- Disable via: ANONYMIZED_NOTIONARY_TELEMETRY=false
22
- """
23
-
24
- USER_ID_PATH = str(Path.home() / ".cache" / "notionary" / "telemetry_user_id")
25
- PROJECT_API_KEY = "phc_gItKOx21Tc0l07C1taD0QPpqFnbWgWjVfRjF6z24kke"
26
- HOST = "https://eu.i.posthog.com"
27
- UNKNOWN_USER_ID = "UNKNOWN"
28
-
29
- _logged_init_message = False
30
- _curr_user_id = None
31
-
32
- def __init__(self):
33
- # Default: enabled, disable via environment variable
34
- telemetry_setting = os.getenv("ANONYMIZED_NOTIONARY_TELEMETRY", "true").lower()
35
- telemetry_disabled = telemetry_setting == "false"
36
- self.debug_logging = os.getenv("NOTIONARY_DEBUG", "false").lower() == "true"
37
-
38
- if telemetry_disabled:
39
- self._posthog_client = None
40
- else:
41
- if not self._logged_init_message:
42
- self.logger.info(
43
- "Anonymous telemetry enabled to improve Notionary. "
44
- "To disable: export ANONYMIZED_NOTIONARY_TELEMETRY=false"
45
- )
46
- self._logged_init_message = True
47
-
48
- self._posthog_client = Posthog(
49
- project_api_key=self.PROJECT_API_KEY,
50
- host=self.HOST,
51
- disable_geoip=True,
52
- enable_exception_autocapture=True,
53
- )
54
-
55
- # Silence posthog's logging unless debug mode
56
- if not self.debug_logging:
57
- import logging
58
-
59
- posthog_logger = logging.getLogger("posthog")
60
- posthog_logger.disabled = True
61
-
62
- if self._posthog_client is None:
63
- self.logger.debug("Telemetry disabled")
64
-
65
- def capture(self, event: BaseTelemetryEvent) -> None:
66
- """
67
- Safe event tracking that never affects library functionality
68
-
69
- Args:
70
- event: BaseTelemetryEvent instance to capture
71
- """
72
- if self._posthog_client is None:
73
- return
74
-
75
- self._direct_capture(event)
76
-
77
- def _direct_capture(self, event: BaseTelemetryEvent) -> None:
78
- """
79
- Direct capture method - PostHog handles threading internally
80
- Should not be thread blocking because posthog magically handles it
81
- """
82
- if self._posthog_client is None:
83
- return
84
-
85
- try:
86
- self._posthog_client.capture(
87
- distinct_id=self.user_id,
88
- event=event.name,
89
- properties={
90
- "library": "notionary",
91
- **event.properties,
92
- **POSTHOG_EVENT_SETTINGS,
93
- },
94
- )
95
-
96
- except Exception as e:
97
- self.logger.error(f"Failed to send telemetry event {event.name}: {e}")
98
-
99
- def flush(self) -> None:
100
- """
101
- Flush pending events - simplified without threading complexity
102
- """
103
- if not self._posthog_client:
104
- self.logger.debug("PostHog client not available, skipping flush.")
105
- return
106
-
107
- try:
108
- self._posthog_client.flush()
109
- self.logger.debug("PostHog client telemetry queue flushed.")
110
- except Exception as e:
111
- self.logger.error(f"Failed to flush PostHog client: {e}")
112
-
113
- @property
114
- def user_id(self) -> str:
115
- """Anonymous, persistent user ID"""
116
- if self._curr_user_id:
117
- return self._curr_user_id
118
-
119
- # File access may fail due to permissions or other reasons.
120
- # We don't want to crash so we catch all exceptions.
121
- try:
122
- if not os.path.exists(self.USER_ID_PATH):
123
- os.makedirs(os.path.dirname(self.USER_ID_PATH), exist_ok=True)
124
- with open(self.USER_ID_PATH, "w") as f:
125
- new_user_id = str(uuid.uuid4())
126
- f.write(new_user_id)
127
- self._curr_user_id = new_user_id
128
- else:
129
- with open(self.USER_ID_PATH, "r") as f:
130
- self._curr_user_id = f.read().strip()
131
-
132
- return self._curr_user_id
133
- except Exception as e:
134
- self.logger.debug(f"Error getting user ID: {e}")
135
- self._curr_user_id = self.UNKNOWN_USER_ID
136
- return self._curr_user_id
@@ -1,73 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from dataclasses import asdict, dataclass
3
- from typing import Any, Optional
4
-
5
-
6
- @dataclass
7
- class BaseTelemetryEvent(ABC):
8
- @property
9
- @abstractmethod
10
- def name(self) -> str:
11
- pass
12
-
13
- @property
14
- def properties(self) -> dict[str, Any]:
15
- return {k: v for k, v in asdict(self).items() if k != "name"}
16
-
17
-
18
- @dataclass
19
- class DatabaseFactoryUsedEvent(BaseTelemetryEvent):
20
- """Event fired when a database factory method is used"""
21
-
22
- factory_method: str
23
-
24
- @property
25
- def name(self) -> str:
26
- return "database_factory_used"
27
-
28
-
29
- @dataclass
30
- class QueryOperationEvent(BaseTelemetryEvent):
31
- """Event fired when a query operation is performed"""
32
-
33
- query_type: str
34
-
35
- @property
36
- def name(self) -> str:
37
- return "query_operation"
38
-
39
-
40
- @dataclass
41
- class NotionMarkdownSyntaxPromptEvent(BaseTelemetryEvent):
42
- """Event fired when Notion Markdown syntax is used"""
43
-
44
- @property
45
- def name(self) -> str:
46
- return "notion_markdown_syntax_used"
47
-
48
-
49
- # Tracks markdown conversion
50
- @dataclass
51
- class MarkdownToNotionConversionEvent(BaseTelemetryEvent):
52
- """Event fired when markdown is converted to Notion blocks"""
53
-
54
- handler_element_name: Optional[str] = (
55
- None # e.g. "HeadingElement", "ParagraphElement"
56
- )
57
-
58
- @property
59
- def name(self) -> str:
60
- return "markdown_to_notion_conversion"
61
-
62
-
63
- @dataclass
64
- class NotionToMarkdownConversionEvent(BaseTelemetryEvent):
65
- """Event fired when Notion blocks are converted to markdown"""
66
-
67
- handler_element_name: Optional[str] = (
68
- None # e.g. "HeadingElement", "ParagraphElement"
69
- )
70
-
71
- @property
72
- def name(self) -> str:
73
- return "notion_to_markdown_conversion"
@@ -1,53 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from abc import ABC
4
- from typing import Optional
5
-
6
- from notionary.user.client import NotionUserClient
7
- from notionary.util import LoggingMixin
8
-
9
-
10
- class BaseNotionUser(LoggingMixin, ABC):
11
- """
12
- Base class for all Notion user types with common functionality.
13
- """
14
-
15
- def __init__(
16
- self,
17
- user_id: str,
18
- name: Optional[str] = None,
19
- avatar_url: Optional[str] = None,
20
- token: Optional[str] = None,
21
- ):
22
- """Initialize base user properties."""
23
- self._user_id = user_id
24
- self._name = name
25
- self._avatar_url = avatar_url
26
- self.client = NotionUserClient(token=token)
27
-
28
- @property
29
- def id(self) -> str:
30
- """Get the user ID."""
31
- return self._user_id
32
-
33
- @property
34
- def name(self) -> Optional[str]:
35
- """Get the user name."""
36
- return self._name
37
-
38
- @property
39
- def avatar_url(self) -> Optional[str]:
40
- """Get the avatar URL."""
41
- return self._avatar_url
42
-
43
- def get_display_name(self) -> str:
44
- """Get a display name for the user."""
45
- return self._name or f"User {self._user_id[:8]}"
46
-
47
- def __str__(self) -> str:
48
- """String representation of the user."""
49
- return f"{self.__class__.__name__}(name='{self.get_display_name()}', id='{self._user_id[:8]}...')"
50
-
51
- def __repr__(self) -> str:
52
- """Detailed string representation."""
53
- return self.__str__()
notionary/user/models.py DELETED
@@ -1,84 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Literal, Optional
3
-
4
- from pydantic import BaseModel
5
-
6
-
7
- class PersonUser(BaseModel):
8
- """Person user details"""
9
-
10
- email: Optional[str] = None
11
-
12
-
13
- class BotOwner(BaseModel):
14
- """Bot owner information - simplified structure"""
15
-
16
- type: Literal["workspace", "user"]
17
- workspace: Optional[bool] = None
18
-
19
-
20
- class WorkspaceLimits(BaseModel):
21
- """Workspace limits for bot users"""
22
-
23
- max_file_upload_size_in_bytes: int
24
-
25
-
26
- class BotUser(BaseModel):
27
- """Bot user details"""
28
-
29
- owner: Optional[BotOwner] = None
30
- workspace_name: Optional[str] = None
31
- workspace_limits: Optional[WorkspaceLimits] = None
32
-
33
-
34
- class NotionUserResponse(BaseModel):
35
- """
36
- Represents a Notion user object as returned by the Users API.
37
- Can represent both person and bot users.
38
- """
39
-
40
- object: Literal["user"]
41
- id: str
42
- type: Optional[Literal["person", "bot"]] = None
43
- name: Optional[str] = None
44
- avatar_url: Optional[str] = None
45
-
46
- # Person-specific fields
47
- person: Optional[PersonUser] = None
48
-
49
- # Bot-specific fields
50
- bot: Optional[BotUser] = None
51
-
52
-
53
- class NotionBotUserResponse(NotionUserResponse):
54
- """
55
- Specialized response for bot user (from /users/me endpoint)
56
- """
57
-
58
- # Bot users should have these fields, but they can still be None
59
- type: Literal["bot"]
60
- bot: Optional[BotUser] = None
61
-
62
-
63
- class NotionUsersListResponse(BaseModel):
64
- """
65
- Response model for paginated users list from /v1/users endpoint.
66
- Follows Notion's standard pagination pattern.
67
- """
68
-
69
- object: Literal["list"]
70
- results: list[NotionUserResponse]
71
- next_cursor: Optional[str] = None
72
- has_more: bool
73
- type: Literal["user"]
74
- user: dict = {}
75
-
76
-
77
- @dataclass
78
- class WorkspaceInfo:
79
- """Dataclass to hold workspace information for bot users."""
80
-
81
- name: Optional[str] = None
82
- limits: Optional[WorkspaceLimits] = None
83
- owner_type: Optional[str] = None
84
- is_workspace_owned: bool = False