notionary 0.2.26__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.26.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.26.dist-info → notionary-0.2.28.dist-info}/WHEEL +1 -1
  200. {notionary-0.2.26.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 -674
  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.26.dist-info/RECORD +0 -202
@@ -1,226 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import List, Optional
4
-
5
- from notionary.user.base_notion_user import BaseNotionUser
6
- from notionary.user.client import NotionUserClient
7
- from notionary.user.models import NotionBotUserResponse, WorkspaceLimits
8
- from notionary.util import factory_only
9
- from notionary.util.fuzzy import find_best_match
10
-
11
-
12
- class NotionBotUser(BaseNotionUser):
13
- """
14
- Manager for Notion bot users.
15
- Handles bot-specific operations and workspace information.
16
- """
17
-
18
- NO_USERS_FOUND_MSG = "No users found in workspace"
19
- NO_BOT_USERS_FOUND_MSG = "No bot users found in workspace"
20
-
21
- @factory_only("from_current_integration", "from_bot_response", "from_name")
22
- def __init__(
23
- self,
24
- user_id: str,
25
- name: Optional[str] = None,
26
- avatar_url: Optional[str] = None,
27
- workspace_name: Optional[str] = None,
28
- workspace_limits: Optional[WorkspaceLimits] = None,
29
- owner_type: Optional[str] = None,
30
- token: Optional[str] = None,
31
- ):
32
- """Initialize bot user with bot-specific properties."""
33
- super().__init__(user_id, name, avatar_url, token)
34
- self._workspace_name = workspace_name
35
- self._workspace_limits = workspace_limits
36
- self._owner_type = owner_type
37
-
38
- @classmethod
39
- async def from_current_integration(
40
- cls, token: Optional[str] = None
41
- ) -> Optional[NotionBotUser]:
42
- """
43
- Get the current bot user (from the API token).
44
-
45
- Args:
46
- token: Optional Notion API token
47
-
48
- Returns:
49
- Optional[NotionBotUser]: Bot user instance or None if failed
50
- """
51
- client = NotionUserClient(token=token)
52
- bot_response = await client.get_bot_user()
53
-
54
- if bot_response is None:
55
- cls.logger.error("Failed to load bot user data")
56
- return None
57
-
58
- return cls._create_from_response(bot_response, token)
59
-
60
- @classmethod
61
- async def from_name(
62
- cls, name: str, token: Optional[str] = None, min_similarity: float = 0.6
63
- ) -> Optional[NotionBotUser]:
64
- """
65
- Create a NotionBotUser by finding a bot user with fuzzy matching on the name.
66
- Uses Notion's list users API and fuzzy matching to find the best result.
67
- """
68
- client = NotionUserClient(token=token)
69
-
70
- try:
71
- # Get all users from workspace
72
- all_users_response = await client.get_all_users()
73
-
74
- if not all_users_response:
75
- cls.logger.warning(cls.NO_USERS_FOUND_MSG)
76
- raise ValueError(cls.NO_USERS_FOUND_MSG)
77
-
78
- # Filter to only bot users
79
- bot_users = [
80
- user for user in all_users_response if user.type == "bot" and user.name
81
- ]
82
-
83
- if not bot_users:
84
- cls.logger.warning(cls.NO_BOT_USERS_FOUND_MSG)
85
- raise ValueError(cls.NO_BOT_USERS_FOUND_MSG)
86
-
87
- cls.logger.debug(
88
- "Found %d bot users for fuzzy matching: %s",
89
- len(bot_users),
90
- [user.name for user in bot_users[:5]], # Log first 5 names
91
- )
92
-
93
- # Use fuzzy matching to find best match
94
- best_match = find_best_match(
95
- query=name,
96
- items=bot_users,
97
- text_extractor=lambda user: user.name or "",
98
- min_similarity=min_similarity,
99
- )
100
-
101
- if not best_match:
102
- available_names = [user.name for user in bot_users[:5]]
103
- cls.logger.warning(
104
- "No sufficiently similar bot user found for '%s' (min: %.3f). Available: %s",
105
- name,
106
- min_similarity,
107
- available_names,
108
- )
109
- raise ValueError(f"No sufficiently similar bot user found for '{name}'")
110
-
111
- cls.logger.info(
112
- "Found best match: '%s' with similarity %.3f for query '%s'",
113
- best_match.matched_text,
114
- best_match.similarity,
115
- name,
116
- )
117
-
118
- # Create NotionBotUser from the matched user response
119
- return cls._create_from_response(best_match.item, token)
120
-
121
- except Exception as e:
122
- cls.logger.error("Error finding bot user by name '%s': %s", name, str(e))
123
- raise
124
-
125
- @classmethod
126
- def from_bot_response(
127
- cls, bot_response: NotionBotUserResponse, token: Optional[str] = None
128
- ) -> NotionBotUser:
129
- """
130
- Create a NotionBotUser from an existing bot API response.
131
-
132
- Args:
133
- bot_response: Bot user response from Notion API
134
- token: Optional Notion API token
135
-
136
- Returns:
137
- NotionBotUser: Bot user instance
138
- """
139
- return cls._create_from_response(bot_response, token)
140
-
141
- @property
142
- def workspace_name(self) -> Optional[str]:
143
- """Get the workspace name."""
144
- return self._workspace_name
145
-
146
- @property
147
- def workspace_limits(self) -> Optional[WorkspaceLimits]:
148
- """Get the workspace limits."""
149
- return self._workspace_limits
150
-
151
- @property
152
- def owner_type(self) -> Optional[str]:
153
- """Get the owner type ('workspace' or 'user')."""
154
- return self._owner_type
155
-
156
- @property
157
- def user_type(self) -> str:
158
- """Get the user type."""
159
- return "bot"
160
-
161
- @property
162
- def is_person(self) -> bool:
163
- """Check if this is a person user."""
164
- return False
165
-
166
- @property
167
- def is_bot(self) -> bool:
168
- """Check if this is a bot user."""
169
- return True
170
-
171
- @property
172
- def is_workspace_integration(self) -> bool:
173
- """Check if this is a workspace-owned integration."""
174
- return self._owner_type == "workspace"
175
-
176
- @property
177
- def is_user_integration(self) -> bool:
178
- """Check if this is a user-owned integration."""
179
- return self._owner_type == "user"
180
-
181
- @property
182
- def max_file_upload_size(self) -> Optional[int]:
183
- """The maximum file upload size in bytes."""
184
- return (
185
- self._workspace_limits.max_file_upload_size_in_bytes
186
- if self._workspace_limits
187
- else None
188
- )
189
-
190
- def __str__(self) -> str:
191
- """String representation of the bot user."""
192
- workspace = self._workspace_name or "Unknown Workspace"
193
- return f"NotionBotUser(name='{self.get_display_name()}', workspace='{workspace}', id='{self._user_id[:8]}...')"
194
-
195
- @classmethod
196
- def _create_from_response(
197
- cls, bot_response: NotionBotUserResponse, token: Optional[str]
198
- ) -> NotionBotUser:
199
- """Create NotionBotUser instance from API response."""
200
- workspace_name = None
201
- workspace_limits = None
202
- owner_type = None
203
-
204
- if bot_response.bot:
205
- workspace_name = bot_response.bot.workspace_name
206
- workspace_limits = bot_response.bot.workspace_limits
207
- owner_type = bot_response.bot.owner.type if bot_response.bot.owner else None
208
-
209
- instance = cls(
210
- user_id=bot_response.id,
211
- name=bot_response.name,
212
- avatar_url=bot_response.avatar_url,
213
- workspace_name=workspace_name,
214
- workspace_limits=workspace_limits,
215
- owner_type=owner_type,
216
- token=token,
217
- )
218
-
219
- cls.logger.info(
220
- "Created bot user: '%s' (ID: %s, Workspace: %s)",
221
- bot_response.name or "Unknown Bot",
222
- bot_response.id,
223
- workspace_name or "Unknown",
224
- )
225
-
226
- return instance
@@ -1,255 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import List, Optional
4
-
5
- from notionary.user.base_notion_user import BaseNotionUser
6
- from notionary.user.client import NotionUserClient
7
- from notionary.user.models import NotionUserResponse
8
- from notionary.util import factory_only
9
- from notionary.util.fuzzy import find_best_matches
10
-
11
-
12
- class NotionUser(BaseNotionUser):
13
- """
14
- Manager for Notion person users.
15
- Handles person-specific operations and information.
16
- """
17
-
18
- NO_USERS_FOUND_MSG = "No users found in workspace"
19
- NO_PERSON_USERS_FOUND_MSG = "No person users found in workspace"
20
-
21
- @factory_only("from_user_id", "from_user_response", "from_name")
22
- def __init__(
23
- self,
24
- user_id: str,
25
- name: Optional[str] = None,
26
- avatar_url: Optional[str] = None,
27
- email: Optional[str] = None,
28
- token: Optional[str] = None,
29
- ):
30
- """Initialize person user with person-specific properties."""
31
- super().__init__(user_id, name, avatar_url, token)
32
- self._email = email
33
-
34
- @classmethod
35
- async def from_user_id(
36
- cls, user_id: str, token: Optional[str] = None
37
- ) -> Optional[NotionUser]:
38
- """
39
- Create a NotionUser from a user ID.
40
- """
41
- client = NotionUserClient(token=token)
42
- user_response = await client.get_user(user_id)
43
-
44
- if user_response is None:
45
- cls.logger.error("Failed to load user data for ID: %s", user_id)
46
- return None
47
-
48
- # Ensure this is actually a person user
49
- if user_response.type != "person":
50
- cls.logger.error(
51
- "User %s is not a person user (type: %s)", user_id, user_response.type
52
- )
53
- return None
54
-
55
- return cls._create_from_response(user_response, token)
56
-
57
- @classmethod
58
- async def from_name(
59
- cls, name: str, token: Optional[str] = None, min_similarity: float = 0.6
60
- ) -> Optional[NotionUser]:
61
- """
62
- Create a NotionUser by finding a person user with fuzzy matching on the name.
63
- """
64
- from notionary.util import find_best_match
65
-
66
- client = NotionUserClient(token=token)
67
-
68
- try:
69
- # Get all users from workspace
70
- all_users_response = await client.get_all_users()
71
-
72
- if not all_users_response:
73
- cls.logger.warning(cls.NO_USERS_FOUND_MSG)
74
- raise ValueError(cls.NO_USERS_FOUND_MSG)
75
-
76
- person_users = [
77
- user
78
- for user in all_users_response
79
- if user.type == "person" and user.name
80
- ]
81
-
82
- if not person_users:
83
- cls.logger.warning(cls.NO_PERSON_USERS_FOUND_MSG)
84
- raise ValueError(cls.NO_PERSON_USERS_FOUND_MSG)
85
-
86
- cls.logger.debug(
87
- "Found %d person users for fuzzy matching: %s",
88
- len(person_users),
89
- [user.name for user in person_users[:5]],
90
- )
91
-
92
- # Use fuzzy matching to find best match
93
- best_match = find_best_match(
94
- query=name,
95
- items=person_users,
96
- text_extractor=lambda user: user.name or "",
97
- min_similarity=min_similarity,
98
- )
99
-
100
- if not best_match:
101
- available_names = [user.name for user in person_users[:5]]
102
- cls.logger.warning(
103
- "No sufficiently similar person user found for '%s' (min: %.3f). Available: %s",
104
- name,
105
- min_similarity,
106
- available_names,
107
- )
108
- raise ValueError(
109
- f"No sufficiently similar person user found for '{name}'"
110
- )
111
-
112
- cls.logger.info(
113
- "Found best match: '%s' with similarity %.3f for query '%s'",
114
- best_match.matched_text,
115
- best_match.similarity,
116
- name,
117
- )
118
-
119
- # Create NotionUser from the matched user response
120
- return cls._create_from_response(best_match.item, token)
121
-
122
- except Exception as e:
123
- cls.logger.error("Error finding user by name '%s': %s", name, str(e))
124
- raise
125
-
126
- @classmethod
127
- def from_user_response(
128
- cls, user_response: NotionUserResponse, token: Optional[str] = None
129
- ) -> NotionUser:
130
- """
131
- Create a NotionUser from an existing API response.
132
- """
133
- if user_response.type != "person":
134
- raise ValueError(f"Cannot create NotionUser from {user_response.type} user")
135
-
136
- return cls._create_from_response(user_response, token)
137
-
138
- @classmethod
139
- async def search_users_by_name(
140
- cls,
141
- name: str,
142
- token: Optional[str] = None,
143
- min_similarity: float = 0.3,
144
- limit: Optional[int] = 5,
145
- ) -> List[NotionUser]:
146
- """
147
- Search for multiple person users by name using fuzzy matching.
148
-
149
- Args:
150
- name: The name to search for
151
- token: Optional Notion API token
152
- min_similarity: Minimum similarity threshold (0.0 to 1.0), default 0.3
153
- limit: Maximum number of results to return, default 5
154
-
155
- Returns:
156
- List[NotionUser]: List of matching users sorted by similarity (best first)
157
- """
158
- client = NotionUserClient(token=token)
159
-
160
- try:
161
- # Get all users from workspace
162
- all_users_response = await client.get_all_users()
163
-
164
- if not all_users_response:
165
- cls.logger.warning(cls.NO_USERS_FOUND_MSG)
166
- return []
167
-
168
- # Filter to only person users (not bots)
169
- person_users = [
170
- user
171
- for user in all_users_response
172
- if user.type == "person" and user.name
173
- ]
174
-
175
- if not person_users:
176
- cls.logger.warning(cls.NO_PERSON_USERS_FOUND_MSG)
177
- return []
178
-
179
- # Use fuzzy matching to find all matches
180
- matches = find_best_matches(
181
- query=name,
182
- items=person_users,
183
- text_extractor=lambda user: user.name or "",
184
- min_similarity=min_similarity,
185
- limit=limit,
186
- )
187
-
188
- cls.logger.info(
189
- "Found %d matching users for query '%s'", len(matches), name
190
- )
191
-
192
- # Convert to NotionUser instances
193
- result_users = []
194
- for match in matches:
195
- try:
196
- user = cls._create_from_response(match.item, token)
197
- result_users.append(user)
198
- except Exception as e:
199
- cls.logger.warning(
200
- "Failed to create user from match '%s': %s",
201
- match.matched_text,
202
- str(e),
203
- )
204
- continue
205
-
206
- return result_users
207
-
208
- except Exception as e:
209
- cls.logger.error("Error searching users by name '%s': %s", name, str(e))
210
- return []
211
-
212
- @property
213
- def email(self) -> Optional[str]:
214
- """
215
- Get the user email (requires proper integration capabilities).
216
- """
217
- return self._email
218
-
219
- @property
220
- def user_type(self) -> str:
221
- """Get the user type."""
222
- return "person"
223
-
224
- @property
225
- def is_person(self) -> bool:
226
- """Check if this is a person user."""
227
- return True
228
-
229
- @property
230
- def is_bot(self) -> bool:
231
- """Check if this is a bot user."""
232
- return False
233
-
234
- @classmethod
235
- def _create_from_response(
236
- cls, user_response: NotionUserResponse, token: Optional[str]
237
- ) -> NotionUser:
238
- """Create NotionUser instance from API response."""
239
- email = user_response.person.email if user_response.person else None
240
-
241
- instance = cls(
242
- user_id=user_response.id,
243
- name=user_response.name,
244
- avatar_url=user_response.avatar_url,
245
- email=email,
246
- token=token,
247
- )
248
-
249
- cls.logger.info(
250
- "Created person user: '%s' (ID: %s)",
251
- user_response.name or "Unknown",
252
- user_response.id,
253
- )
254
-
255
- return instance
@@ -1,101 +0,0 @@
1
- from typing import Optional
2
-
3
- from notionary.user.client import NotionUserClient
4
- from notionary.user.notion_user import NotionUser
5
- from notionary.util import LoggingMixin
6
-
7
-
8
- class NotionUserManager(LoggingMixin):
9
- """
10
- Manager for user operations within API limitations.
11
-
12
- Note: The Notion API provides endpoints to list workspace users (excluding guests).
13
- This manager provides utility functions for working with individual users and user lists.
14
- """
15
-
16
- def __init__(self, token: Optional[str] = None):
17
- """Initialize the user manager."""
18
- self.client = NotionUserClient(token=token)
19
-
20
- async def get_user_by_id(self, user_id: str) -> Optional[NotionUser]:
21
- """
22
- Get a specific user by their ID.
23
- """
24
- return await NotionUser.from_user_id(user_id, token=self.client.token)
25
-
26
- async def get_all_users(self) -> list[NotionUser]:
27
- """
28
- Get all users in the workspace as NotionUser objects.
29
- Automatically handles pagination and converts responses to NotionUser instances.
30
- Only returns person users, excludes bots and integrations.
31
- """
32
- try:
33
- # Get raw user responses
34
- user_responses = await self.client.get_all_users()
35
-
36
- # Filter for person users only and convert to NotionUser objects
37
- notion_users = []
38
- for user_response in user_responses:
39
- # Skip bot users and integrations
40
- if user_response.type != "person":
41
- self.logger.debug(
42
- "Skipping non-person user %s (type: %s)",
43
- user_response.id,
44
- user_response.type,
45
- )
46
- continue
47
-
48
- try:
49
- # Use the internal creation method to convert response to NotionUser
50
- notion_user = NotionUser.from_user_response(
51
- user_response, self.client.token
52
- )
53
- notion_users.append(notion_user)
54
- except Exception as e:
55
- self.logger.warning(
56
- "Failed to convert person user %s to NotionUser: %s",
57
- user_response.id,
58
- str(e),
59
- )
60
- continue
61
-
62
- self.logger.info(
63
- "Successfully converted %d person users to NotionUser objects (skipped %d non-person users)",
64
- len(notion_users),
65
- len(user_responses) - len(notion_users),
66
- )
67
- return notion_users
68
-
69
- except Exception as e:
70
- self.logger.error("Error getting all users: %s", str(e))
71
- return []
72
-
73
- async def find_users_by_name(self, name_pattern: str) -> list[NotionUser]:
74
- """
75
- Find person users by name pattern (case-insensitive partial match).
76
- Only returns person users, excludes bots and integrations.
77
-
78
- Note: The API doesn't support server-side filtering, so this fetches all users
79
- and filters client-side.
80
- """
81
- try:
82
- # get_all_users() already filters for person users only
83
- all_users = await self.get_all_users()
84
- pattern_lower = name_pattern.lower()
85
-
86
- matching_users = [
87
- user
88
- for user in all_users
89
- if user.name and pattern_lower in user.name.lower()
90
- ]
91
-
92
- self.logger.info(
93
- "Found %d person users matching pattern '%s'",
94
- len(matching_users),
95
- name_pattern,
96
- )
97
- return matching_users
98
-
99
- except Exception as e:
100
- self.logger.error("Error finding users by name: %s", str(e))
101
- return []
@@ -1,15 +0,0 @@
1
- from .factory_only import factory_only
2
- from .logging_mixin import LoggingMixin
3
- from .page_id_utils import extract_uuid, format_uuid
4
- from .singleton import singleton
5
- from .singleton_metaclass import SingletonMetaClass
6
-
7
- __all__ = [
8
- "LoggingMixin",
9
- "singleton",
10
- "format_uuid",
11
- "extract_uuid",
12
- "factory_only",
13
- "singleton",
14
- "SingletonMetaClass",
15
- ]
File without changes
File without changes
@@ -1,37 +0,0 @@
1
- import functools
2
- import inspect
3
- import warnings
4
-
5
-
6
- def factory_only(*allowed_factories):
7
- """
8
- Decorator that warns when __init__ is not called from allowed factory methods.
9
-
10
- Args:
11
- *allowed_factories: Names of allowed factory methods (e.g. 'from_database_id')
12
- """
13
-
14
- def decorator(init_method):
15
- @functools.wraps(init_method)
16
- def wrapper(self, *args, **kwargs):
17
- frame = inspect.currentframe()
18
- try:
19
- caller_frame = frame.f_back.f_back
20
- if not caller_frame:
21
- return init_method(self, *args, **kwargs)
22
- caller_name = caller_frame.f_code.co_name
23
- if caller_name in allowed_factories or caller_name.startswith("_"):
24
- return init_method(self, *args, **kwargs)
25
-
26
- warnings.warn(
27
- f"Direct instantiation not recommended! Consider using one of: {', '.join(allowed_factories)}",
28
- UserWarning,
29
- stacklevel=3,
30
- )
31
- return init_method(self, *args, **kwargs)
32
- finally:
33
- del frame
34
-
35
- return wrapper
36
-
37
- return decorator