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
@@ -1,480 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import asyncio
4
- import random
5
- from typing import Any, AsyncGenerator, Optional
6
-
7
- from notionary.database.client import NotionDatabaseClient
8
- from notionary.database.database_filter_builder import DatabaseFilterBuilder
9
- from notionary.database.database_provider import NotionDatabaseProvider
10
- from notionary.database.models import (
11
- NotionDatabaseResponse,
12
- NotionPageResponse,
13
- NotionQueryDatabaseResponse,
14
- )
15
- from notionary.page.notion_page import NotionPage
16
- from notionary.telemetry import (
17
- DatabaseFactoryUsedEvent,
18
- ProductTelemetry,
19
- QueryOperationEvent,
20
- )
21
- from notionary.util import LoggingMixin, factory_only
22
-
23
-
24
- class NotionDatabase(LoggingMixin):
25
- """
26
- Minimal manager for Notion databases.
27
- Focused exclusively on creating basic pages and retrieving page managers
28
- for further page operations.
29
- """
30
-
31
- telemetry = ProductTelemetry()
32
-
33
- @factory_only("from_database_id", "from_database_name")
34
- def __init__(
35
- self,
36
- id: str,
37
- title: str,
38
- url: str,
39
- emoji_icon: Optional[str] = None,
40
- properties: Optional[dict[str, Any]] = None,
41
- token: Optional[str] = None,
42
- ):
43
- """
44
- Initialize the minimal database manager.
45
- """
46
- self._id = id
47
- self._title = title
48
- self._url = url
49
- self._emoji_icon = emoji_icon
50
- self._properties = properties
51
-
52
- self.client = NotionDatabaseClient(token=token)
53
-
54
- @classmethod
55
- async def from_database_id(
56
- cls, id: str, token: Optional[str] = None
57
- ) -> NotionDatabase:
58
- """
59
- Create a NotionDatabase from a database ID using NotionDatabaseProvider.
60
- """
61
- provider = cls.get_database_provider()
62
- cls.telemetry.capture(
63
- DatabaseFactoryUsedEvent(factory_method="from_database_id")
64
- )
65
-
66
- return await provider.get_database_by_id(id, token)
67
-
68
- @classmethod
69
- async def from_database_name(
70
- cls,
71
- database_name: str,
72
- token: Optional[str] = None,
73
- min_similarity: float = 0.6,
74
- ) -> NotionDatabase:
75
- """
76
- Create a NotionDatabase by finding a database with fuzzy matching on the title using NotionDatabaseProvider.
77
- """
78
- provider = cls.get_database_provider()
79
- cls.telemetry.capture(
80
- DatabaseFactoryUsedEvent(factory_method="from_database_name")
81
- )
82
- return await provider.get_database_by_name(database_name, token, min_similarity)
83
-
84
- @property
85
- def id(self) -> str:
86
- """Get the database ID (readonly)."""
87
- return self._id
88
-
89
- @property
90
- def title(self) -> str:
91
- """Get the database title (readonly)."""
92
- return self._title
93
-
94
- @property
95
- def url(self) -> str:
96
- """Get the database URL (readonly)."""
97
- return self._url
98
-
99
- @property
100
- def emoji(self) -> Optional[str]:
101
- """Get the database emoji (readonly)."""
102
- return self._emoji_icon
103
-
104
- @property
105
- def properties(self) -> Optional[dict[str, Any]]:
106
- """Get the database properties (readonly)."""
107
- return self._properties
108
-
109
- # Database Provider is a singleton so we can instantiate it here with no worries
110
- @property
111
- def database_provider(self):
112
- """Return a NotionDatabaseProvider instance for this database."""
113
- return NotionDatabaseProvider.get_instance()
114
-
115
- @classmethod
116
- def get_database_provider(cls):
117
- """Return a NotionDatabaseProvider instance for class-level usage."""
118
- return NotionDatabaseProvider.get_instance()
119
-
120
- async def create_blank_page(self) -> Optional[NotionPage]:
121
- """
122
- Create a new blank page in the database with minimal properties.
123
- """
124
- try:
125
- create_page_response: NotionPageResponse = await self.client.create_page(
126
- parent_database_id=self.id
127
- )
128
-
129
- return await NotionPage.from_page_id(page_id=create_page_response.id)
130
-
131
- except Exception as e:
132
- self.logger.error("Error creating blank page: %s", str(e))
133
- return None
134
-
135
- async def set_title(self, new_title: str) -> bool:
136
- """
137
- Update the database title.
138
- """
139
- try:
140
- result = await self.client.update_database_title(
141
- database_id=self.id, title=new_title
142
- )
143
-
144
- self._title = result.title[0].plain_text
145
- self.logger.info(f"Successfully updated database title to: {new_title}")
146
- self.database_provider.invalidate_database_cache(database_id=self.id)
147
- return True
148
-
149
- except Exception as e:
150
- self.logger.error(f"Error updating database title: {str(e)}")
151
- return False
152
-
153
- async def set_emoji(self, new_emoji: str) -> bool:
154
- """
155
- Update the database emoji.
156
- """
157
- try:
158
- result = await self.client.update_database_emoji(
159
- database_id=self.id, emoji=new_emoji
160
- )
161
-
162
- self._emoji_icon = result.icon.emoji if result.icon else None
163
- self.logger.info(f"Successfully updated database emoji to: {new_emoji}")
164
- self.database_provider.invalidate_database_cache(database_id=self.id)
165
- return True
166
-
167
- except Exception as e:
168
- self.logger.error(f"Error updating database emoji: {str(e)}")
169
- return False
170
-
171
- async def set_cover_image(self, image_url: str) -> Optional[str]:
172
- """
173
- Update the database cover image.
174
- """
175
- try:
176
- result = await self.client.update_database_cover_image(
177
- database_id=self.id, image_url=image_url
178
- )
179
-
180
- if result.cover and result.cover.external:
181
- self.database_provider.invalidate_database_cache(database_id=self.id)
182
- return result.cover.external.url
183
- return None
184
-
185
- except Exception as e:
186
- self.logger.error(f"Error updating database cover image: {str(e)}")
187
- return None
188
-
189
- async def set_random_gradient_cover(self) -> Optional[str]:
190
- """Sets a random gradient cover from Notion's default gradient covers (always jpg)."""
191
- default_notion_covers = [
192
- f"https://www.notion.so/images/page-cover/gradients_{i}.png"
193
- for i in range(1, 10)
194
- ]
195
- random_cover_url = random.choice(default_notion_covers)
196
- return await self.set_cover_image(random_cover_url)
197
-
198
- async def set_external_icon(self, external_icon_url: str) -> Optional[str]:
199
- """
200
- Update the database icon with an external image URL.
201
- """
202
- try:
203
- result = await self.client.update_database_external_icon(
204
- database_id=self.id, icon_url=external_icon_url
205
- )
206
-
207
- if result.icon and result.icon.external:
208
- self.database_provider.invalidate_database_cache(database_id=self.id)
209
- return result.icon.external.url
210
- return None
211
-
212
- except Exception as e:
213
- self.logger.error(f"Error updating database external icon: {str(e)}")
214
- return None
215
-
216
- async def get_options_by_property_name(self, property_name: str) -> list[str]:
217
- """
218
- Retrieve all option names for a select, multi_select, status, or relation property.
219
-
220
- Args:
221
- property_name: The name of the property in the database schema.
222
-
223
- Returns:
224
- A list of option names for the given property. For select, multi_select, or status,
225
- returns the option names directly. For relation properties, returns the titles of related pages.
226
- """
227
- property_schema = self.properties.get(property_name)
228
-
229
- property_type = property_schema.get("type")
230
-
231
- if property_type in ["select", "multi_select", "status"]:
232
- options = property_schema.get(property_type, {}).get("options", [])
233
- return [option.get("name", "") for option in options]
234
-
235
- if property_type == "relation":
236
- return await self._get_relation_options(property_name)
237
-
238
- return []
239
-
240
- def get_property_type(self, property_name: str) -> Optional[str]:
241
- """
242
- Get the type of a property by its name.
243
- """
244
- property_schema = self.properties.get(property_name)
245
- return property_schema.get("type") if property_schema else None
246
-
247
- async def query_database_by_title(self, page_title: str) -> list[NotionPage]:
248
- """
249
- Query the database for pages with a specific title.
250
- """
251
- search_results: NotionQueryDatabaseResponse = (
252
- await self.client.query_database_by_title(
253
- database_id=self.id, page_title=page_title
254
- )
255
- )
256
-
257
- page_results: list[NotionPage] = []
258
-
259
- if search_results.results:
260
- page_tasks = [
261
- NotionPage.from_page_id(
262
- page_id=page_response.id, token=self.client.token
263
- )
264
- for page_response in search_results.results
265
- ]
266
- page_results = await asyncio.gather(*page_tasks)
267
-
268
- self.telemetry.capture(
269
- QueryOperationEvent(query_type="query_database_by_title")
270
- )
271
-
272
- return page_results
273
-
274
- async def iter_pages_updated_within(
275
- self, hours: int = 24, page_size: int = 100
276
- ) -> AsyncGenerator[NotionPage, None]:
277
- """
278
- Iterate through pages edited in the last N hours using DatabaseFilterBuilder.
279
- """
280
- filter_builder = DatabaseFilterBuilder()
281
- filter_builder.with_updated_last_n_hours(hours)
282
- filter_conditions = filter_builder.build()
283
-
284
- async for page in self._iter_pages(page_size, filter_conditions):
285
- yield page
286
-
287
- async def get_all_pages(self) -> list[NotionPage]:
288
- """
289
- Get all pages in the database (use with caution for large databases).
290
- Uses asyncio.gather to parallelize NotionPage creation per API batch.
291
- """
292
- pages: list[NotionPage] = []
293
-
294
- async for batch in self._paginate_database(page_size=100):
295
- # Parallelize NotionPage creation for this batch
296
- page_tasks = [
297
- NotionPage.from_page_id(
298
- page_id=page_response.id, token=self.client.token
299
- )
300
- for page_response in batch
301
- ]
302
- batch_pages = await asyncio.gather(*page_tasks)
303
- pages.extend(batch_pages)
304
-
305
- return pages
306
-
307
- async def get_last_edited_time(self) -> Optional[str]:
308
- """
309
- Retrieve the last edited time of the database.
310
-
311
- Returns:
312
- ISO 8601 timestamp string of the last database edit, or None if request fails.
313
- """
314
- try:
315
- db = await self.client.get_database(self.id)
316
-
317
- return db.last_edited_time
318
-
319
- except Exception as e:
320
- self.logger.error(
321
- "Error fetching last_edited_time for database %s: %s",
322
- self.id,
323
- str(e),
324
- )
325
- return None
326
-
327
- async def _iter_pages(
328
- self,
329
- page_size: int = 100,
330
- filter_conditions: Optional[dict[str, Any]] = None,
331
- ) -> AsyncGenerator[NotionPage, None]:
332
- """
333
- Asynchronous generator that yields NotionPage objects from the database.
334
- Directly queries the Notion API without using the schema.
335
-
336
- Args:
337
- page_size: Number of pages to fetch per request
338
- filter_conditions: Optional filter conditions
339
-
340
- Yields:
341
- NotionPage objects
342
- """
343
- self.logger.debug(
344
- "Iterating pages with page_size: %d, filter: %s",
345
- page_size,
346
- filter_conditions,
347
- )
348
-
349
- async for batch in self._paginate_database(page_size, filter_conditions):
350
- for page_response in batch:
351
- yield await NotionPage.from_page_id(
352
- page_id=page_response.id, token=self.client.token
353
- )
354
-
355
- @classmethod
356
- def _create_from_response(
357
- cls, db_response: NotionDatabaseResponse, token: Optional[str]
358
- ) -> NotionDatabase:
359
- """
360
- Create NotionDatabase instance from API response.
361
- """
362
- title = cls._extract_title(db_response)
363
- emoji_icon = cls._extract_emoji_icon(db_response)
364
-
365
- instance = cls(
366
- id=db_response.id,
367
- title=title,
368
- url=db_response.url,
369
- emoji_icon=emoji_icon,
370
- properties=db_response.properties,
371
- token=token,
372
- )
373
-
374
- cls.logger.info(
375
- "Created database manager: '%s' (ID: %s)", title, db_response.id
376
- )
377
-
378
- return instance
379
-
380
- @staticmethod
381
- def _extract_title(db_response: NotionDatabaseResponse) -> str:
382
- """Extract title from database response."""
383
- if db_response.title and len(db_response.title) > 0:
384
- return db_response.title[0].plain_text
385
- return "Untitled Database"
386
-
387
- @staticmethod
388
- def _extract_emoji_icon(db_response: NotionDatabaseResponse) -> Optional[str]:
389
- """Extract emoji from database response."""
390
- if not db_response.icon:
391
- return None
392
-
393
- if db_response.icon.type == "emoji":
394
- return db_response.icon.emoji
395
-
396
- return None
397
-
398
- def _extract_title_from_page(self, page: NotionPageResponse) -> Optional[str]:
399
- """
400
- Extracts the title from a NotionPageResponse object.
401
- """
402
- if not page.properties:
403
- return None
404
-
405
- title_property = next(
406
- (
407
- prop
408
- for prop in page.properties.values()
409
- if isinstance(prop, dict) and prop.get("type") == "title"
410
- ),
411
- None,
412
- )
413
-
414
- if not title_property or "title" not in title_property:
415
- return None
416
-
417
- try:
418
- title_parts = title_property["title"]
419
- return "".join(part.get("plain_text", "") for part in title_parts)
420
-
421
- except (KeyError, TypeError, AttributeError):
422
- return None
423
-
424
- async def _get_relation_options(self, property_name: str) -> list[dict[str, Any]]:
425
- """
426
- Retrieve the titles of all pages related to a relation property.
427
-
428
- Args:
429
- property_name: The name of the relation property in the database schema.
430
-
431
- Returns:
432
- A list of titles for all related pages. Returns an empty list if no related pages are found.
433
- """
434
- property_schema = self.properties.get(property_name)
435
-
436
- relation_database_id = property_schema.get("relation", {}).get("database_id")
437
-
438
- search_results = await self.client.query_database(
439
- database_id=relation_database_id
440
- )
441
-
442
- return [self._extract_title_from_page(page) for page in search_results.results]
443
-
444
- async def _paginate_database(
445
- self,
446
- page_size: int = 100,
447
- filter_conditions: Optional[dict[str, Any]] = None,
448
- ) -> AsyncGenerator[list[NotionPageResponse], None]:
449
- """
450
- Central pagination logic for Notion Database queries.
451
-
452
- Args:
453
- page_size: Number of pages per request (max 100)
454
- filter_conditions: Optional filter conditions for the query
455
-
456
- Yields:
457
- Batches of NotionPageResponse objects
458
- """
459
- start_cursor: Optional[str] = None
460
- has_more = True
461
-
462
- while has_more:
463
- query_data: dict[str, Any] = {"page_size": page_size}
464
-
465
- if start_cursor:
466
- query_data["start_cursor"] = start_cursor
467
- if filter_conditions:
468
- query_data["filter"] = filter_conditions
469
-
470
- result: NotionQueryDatabaseResponse = await self.client.query_database(
471
- database_id=self.id, query_data=query_data
472
- )
473
-
474
- if not result or not result.results:
475
- return
476
-
477
- yield result.results
478
-
479
- has_more = result.has_more
480
- start_cursor = result.next_cursor if has_more else None
@@ -1,173 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import dataclass, field
4
- from datetime import datetime, timedelta
5
- from typing import Any, Dict, List
6
-
7
-
8
- @dataclass
9
- class FilterConfig:
10
- """Simple configuration for Notion Database filters."""
11
-
12
- conditions: List[Dict[str, Any]] = field(default_factory=list)
13
- page_size: int = 100
14
-
15
- def to_filter_dict(self) -> Dict[str, Any]:
16
- """Convert to a Notion filter dictionary."""
17
- if len(self.conditions) == 0:
18
- return {}
19
- if len(self.conditions) == 1:
20
- return self.conditions[0]
21
-
22
- return {"and": self.conditions}
23
-
24
-
25
- class DatabaseFilterBuilder:
26
- """
27
- Builder class for creating complex Notion filters with comprehensive property type support.
28
- """
29
-
30
- def __init__(self, config: FilterConfig = None):
31
- self.config = config or FilterConfig()
32
-
33
- def with_page_object_filter(self):
34
- """Filter: Only page objects (Notion API search)."""
35
- self.config.conditions.append({"value": "page", "property": "object"})
36
- return self
37
-
38
- def with_database_object_filter(self):
39
- """Filter: Only database objects (Notion API search)."""
40
- self.config.conditions.append({"value": "database", "property": "object"})
41
- return self
42
-
43
- # TIMESTAMP FILTERS (Created/Updated)
44
- def with_created_after(self, date: datetime):
45
- """Add condition: created after specific date."""
46
- self.config.conditions.append(
47
- {"timestamp": "created_time", "created_time": {"after": date.isoformat()}}
48
- )
49
- return self
50
-
51
- def with_created_before(self, date: datetime):
52
- """Add condition: created before specific date."""
53
- self.config.conditions.append(
54
- {"timestamp": "created_time", "created_time": {"before": date.isoformat()}}
55
- )
56
- return self
57
-
58
- def with_updated_after(self, date: datetime):
59
- """Add condition: updated after specific date."""
60
- self.config.conditions.append(
61
- {
62
- "timestamp": "last_edited_time",
63
- "last_edited_time": {"after": date.isoformat()},
64
- }
65
- )
66
- return self
67
-
68
- def with_created_last_n_days(self, days: int):
69
- """Created in the last N days."""
70
- cutoff = datetime.now() - timedelta(days=days)
71
- return self.with_created_after(cutoff)
72
-
73
- def with_updated_last_n_hours(self, hours: int):
74
- """Updated in the last N hours."""
75
- cutoff = datetime.now() - timedelta(hours=hours)
76
- return self.with_updated_after(cutoff)
77
-
78
- # RICH TEXT FILTERS
79
- def with_text_contains(self, property_name: str, value: str):
80
- """Rich text contains value."""
81
- self.config.conditions.append(
82
- {"property": property_name, "rich_text": {"contains": value}}
83
- )
84
- return self
85
-
86
- def with_text_equals(self, property_name: str, value: str):
87
- """Rich text equals value."""
88
- self.config.conditions.append(
89
- {"property": property_name, "rich_text": {"equals": value}}
90
- )
91
- return self
92
-
93
- # TITLE FILTERS
94
- def with_title_contains(self, value: str):
95
- """Title contains value."""
96
- self.config.conditions.append(
97
- {"property": "title", "title": {"contains": value}}
98
- )
99
- return self
100
-
101
- def with_title_equals(self, value: str):
102
- """Title equals value."""
103
- self.config.conditions.append({"property": "title", "title": {"equals": value}})
104
- return self
105
-
106
- # SELECT FILTERS (Single Select)
107
- def with_select_equals(self, property_name: str, value: str):
108
- """Select equals value."""
109
- self.config.conditions.append(
110
- {"property": property_name, "select": {"equals": value}}
111
- )
112
- return self
113
-
114
- def with_select_is_empty(self, property_name: str):
115
- """Select is empty."""
116
- self.config.conditions.append(
117
- {"property": property_name, "select": {"is_empty": True}}
118
- )
119
- return self
120
-
121
- def with_multi_select_contains(self, property_name: str, value: str):
122
- """Multi-select contains value."""
123
- self.config.conditions.append(
124
- {"property": property_name, "multi_select": {"contains": value}}
125
- )
126
- return self
127
-
128
- def with_status_equals(self, property_name: str, value: str):
129
- """Status equals value."""
130
- self.config.conditions.append(
131
- {"property": property_name, "status": {"equals": value}}
132
- )
133
- return self
134
-
135
- def with_page_size(self, size: int):
136
- """Set page size for pagination."""
137
- self.config.page_size = size
138
- return self
139
-
140
- def with_or_condition(self, *builders):
141
- """Add OR condition with multiple sub-conditions."""
142
- or_conditions = []
143
- for builder in builders:
144
- filter_dict = builder.build()
145
- if filter_dict:
146
- or_conditions.append(filter_dict)
147
-
148
- if len(or_conditions) > 1:
149
- self.config.conditions.append({"or": or_conditions})
150
- elif len(or_conditions) == 1:
151
- self.config.conditions.append(or_conditions[0])
152
-
153
- return self
154
-
155
- def build(self) -> Dict[str, Any]:
156
- """Build the final filter dictionary."""
157
- return self.config.to_filter_dict()
158
-
159
- def get_config(self) -> FilterConfig:
160
- """Get the underlying FilterConfig."""
161
- return self.config
162
-
163
- def copy(self):
164
- """Create a copy of the builder."""
165
- new_config = FilterConfig(
166
- conditions=self.config.conditions.copy(), page_size=self.config.page_size
167
- )
168
- return DatabaseFilterBuilder(new_config)
169
-
170
- def reset(self):
171
- """Reset all conditions."""
172
- self.config = FilterConfig()
173
- return self