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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. notionary/__init__.py +5 -20
  2. notionary/blocks/__init__.py +4 -4
  3. notionary/blocks/client.py +90 -216
  4. notionary/blocks/enums.py +167 -0
  5. notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
  6. notionary/blocks/rich_text/models.py +178 -0
  7. notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
  8. notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
  9. notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
  10. notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
  11. notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
  12. notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
  13. notionary/blocks/rich_text/rich_text_markdown_converter.py +144 -0
  14. notionary/blocks/rich_text/rich_text_patterns.py +42 -0
  15. notionary/blocks/schemas.py +778 -0
  16. notionary/comments/__init__.py +1 -22
  17. notionary/comments/client.py +52 -187
  18. notionary/comments/factory.py +38 -0
  19. notionary/comments/models.py +5 -127
  20. notionary/comments/schemas.py +240 -0
  21. notionary/comments/service.py +34 -0
  22. notionary/data_source/http/client.py +11 -0
  23. notionary/data_source/http/data_source_instance_client.py +104 -0
  24. notionary/data_source/properties/schemas.py +402 -0
  25. notionary/data_source/query/builder.py +448 -0
  26. notionary/data_source/query/resolver.py +114 -0
  27. notionary/data_source/query/schema.py +302 -0
  28. notionary/data_source/query/validator.py +73 -0
  29. notionary/data_source/schema/registry.py +104 -0
  30. notionary/data_source/schema/service.py +136 -0
  31. notionary/data_source/schemas.py +27 -0
  32. notionary/data_source/service.py +377 -0
  33. notionary/database/client.py +30 -135
  34. notionary/database/database_metadata_update_client.py +19 -0
  35. notionary/database/schemas.py +29 -0
  36. notionary/database/service.py +168 -0
  37. notionary/exceptions/__init__.py +33 -0
  38. notionary/exceptions/api.py +41 -0
  39. notionary/exceptions/base.py +2 -0
  40. notionary/exceptions/block_parsing.py +16 -0
  41. notionary/exceptions/data_source/__init__.py +6 -0
  42. notionary/exceptions/data_source/builder.py +182 -0
  43. notionary/exceptions/data_source/properties.py +34 -0
  44. notionary/exceptions/properties.py +58 -0
  45. notionary/exceptions/search.py +57 -0
  46. notionary/file_upload/client.py +18 -30
  47. notionary/file_upload/models.py +7 -8
  48. notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
  49. notionary/http/client.py +204 -0
  50. notionary/http/models.py +50 -0
  51. notionary/page/blocks/client.py +1 -0
  52. notionary/page/content/factory.py +73 -0
  53. notionary/page/content/markdown/__init__.py +5 -0
  54. notionary/page/content/markdown/builder.py +226 -0
  55. notionary/page/content/markdown/nodes/__init__.py +52 -0
  56. notionary/page/content/markdown/nodes/audio.py +23 -0
  57. notionary/page/content/markdown/nodes/base.py +12 -0
  58. notionary/page/content/markdown/nodes/bookmark.py +25 -0
  59. notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
  60. notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
  61. notionary/page/content/markdown/nodes/callout.py +34 -0
  62. notionary/page/content/markdown/nodes/code.py +28 -0
  63. notionary/page/content/markdown/nodes/columns.py +69 -0
  64. notionary/page/content/markdown/nodes/container.py +64 -0
  65. notionary/page/content/markdown/nodes/divider.py +14 -0
  66. notionary/page/content/markdown/nodes/embed.py +23 -0
  67. notionary/page/content/markdown/nodes/equation.py +19 -0
  68. notionary/page/content/markdown/nodes/file.py +23 -0
  69. notionary/page/content/markdown/nodes/heading.py +36 -0
  70. notionary/page/content/markdown/nodes/image.py +23 -0
  71. notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
  72. notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
  73. notionary/page/content/markdown/nodes/numbered_list.py +38 -0
  74. notionary/page/content/markdown/nodes/paragraph.py +14 -0
  75. notionary/page/content/markdown/nodes/pdf.py +23 -0
  76. notionary/page/content/markdown/nodes/quote.py +27 -0
  77. notionary/page/content/markdown/nodes/space.py +14 -0
  78. notionary/page/content/markdown/nodes/table.py +45 -0
  79. notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
  80. notionary/page/content/markdown/nodes/todo.py +38 -0
  81. notionary/page/content/markdown/nodes/toggle.py +27 -0
  82. notionary/page/content/markdown/nodes/video.py +23 -0
  83. notionary/page/content/parser/context.py +126 -0
  84. notionary/page/content/parser/factory.py +210 -0
  85. notionary/page/content/parser/parsers/__init__.py +58 -0
  86. notionary/page/content/parser/parsers/audio.py +40 -0
  87. notionary/page/content/parser/parsers/base.py +30 -0
  88. notionary/page/content/parser/parsers/bookmark.py +33 -0
  89. notionary/page/content/parser/parsers/breadcrumb.py +33 -0
  90. notionary/page/content/parser/parsers/bulleted_list.py +85 -0
  91. notionary/page/content/parser/parsers/callout.py +100 -0
  92. notionary/page/content/parser/parsers/caption.py +55 -0
  93. notionary/page/content/parser/parsers/code.py +81 -0
  94. notionary/page/content/parser/parsers/column.py +76 -0
  95. notionary/page/content/parser/parsers/column_list.py +81 -0
  96. notionary/page/content/parser/parsers/divider.py +33 -0
  97. notionary/page/content/parser/parsers/embed.py +33 -0
  98. notionary/page/content/parser/parsers/equation.py +65 -0
  99. notionary/page/content/parser/parsers/file.py +42 -0
  100. notionary/page/content/parser/parsers/heading.py +115 -0
  101. notionary/page/content/parser/parsers/image.py +42 -0
  102. notionary/page/content/parser/parsers/numbered_list.py +89 -0
  103. notionary/page/content/parser/parsers/paragraph.py +37 -0
  104. notionary/page/content/parser/parsers/pdf.py +42 -0
  105. notionary/page/content/parser/parsers/quote.py +125 -0
  106. notionary/page/content/parser/parsers/space.py +41 -0
  107. notionary/page/content/parser/parsers/table.py +144 -0
  108. notionary/page/content/parser/parsers/table_of_contents.py +32 -0
  109. notionary/page/content/parser/parsers/todo.py +96 -0
  110. notionary/page/content/parser/parsers/toggle.py +70 -0
  111. notionary/page/content/parser/parsers/video.py +42 -0
  112. notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
  113. notionary/page/content/parser/post_processing/handlers/rich_text_length.py +95 -0
  114. notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -0
  115. notionary/page/content/parser/post_processing/port.py +9 -0
  116. notionary/page/content/parser/post_processing/service.py +16 -0
  117. notionary/page/content/parser/pre_processsing/handlers/__init__.py +11 -0
  118. notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
  119. notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
  120. notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
  121. notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
  122. notionary/page/content/parser/pre_processsing/service.py +15 -0
  123. notionary/page/content/parser/service.py +78 -0
  124. notionary/page/content/renderer/context.py +51 -0
  125. notionary/page/content/renderer/factory.py +231 -0
  126. notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
  127. notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
  128. notionary/page/content/renderer/post_processing/port.py +7 -0
  129. notionary/page/content/renderer/post_processing/service.py +15 -0
  130. notionary/page/content/renderer/renderers/__init__.py +55 -0
  131. notionary/page/content/renderer/renderers/audio.py +31 -0
  132. notionary/page/content/renderer/renderers/base.py +31 -0
  133. notionary/page/content/renderer/renderers/bookmark.py +25 -0
  134. notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
  135. notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
  136. notionary/page/content/renderer/renderers/callout.py +50 -0
  137. notionary/page/content/renderer/renderers/captioned_block.py +58 -0
  138. notionary/page/content/renderer/renderers/code.py +34 -0
  139. notionary/page/content/renderer/renderers/column.py +53 -0
  140. notionary/page/content/renderer/renderers/column_list.py +44 -0
  141. notionary/page/content/renderer/renderers/divider.py +22 -0
  142. notionary/page/content/renderer/renderers/embed.py +25 -0
  143. notionary/page/content/renderer/renderers/equation.py +37 -0
  144. notionary/page/content/renderer/renderers/fallback.py +24 -0
  145. notionary/page/content/renderer/renderers/file.py +40 -0
  146. notionary/page/content/renderer/renderers/heading.py +95 -0
  147. notionary/page/content/renderer/renderers/image.py +31 -0
  148. notionary/page/content/renderer/renderers/numbered_list.py +42 -0
  149. notionary/page/content/renderer/renderers/paragraph.py +40 -0
  150. notionary/page/content/renderer/renderers/pdf.py +31 -0
  151. notionary/page/content/renderer/renderers/quote.py +49 -0
  152. notionary/page/content/renderer/renderers/table.py +115 -0
  153. notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
  154. notionary/page/content/renderer/renderers/table_row.py +17 -0
  155. notionary/page/content/renderer/renderers/todo.py +56 -0
  156. notionary/page/content/renderer/renderers/toggle.py +52 -0
  157. notionary/page/content/renderer/renderers/video.py +31 -0
  158. notionary/page/content/renderer/service.py +50 -0
  159. notionary/page/content/service.py +68 -0
  160. notionary/page/content/syntax/__init__.py +4 -0
  161. notionary/page/content/syntax/grammar.py +10 -0
  162. notionary/page/content/syntax/models.py +66 -0
  163. notionary/page/content/syntax/registry.py +393 -0
  164. notionary/page/page_context.py +7 -16
  165. notionary/page/page_http_client.py +15 -0
  166. notionary/page/page_metadata_update_client.py +19 -0
  167. notionary/page/properties/client.py +144 -0
  168. notionary/page/properties/factory.py +26 -0
  169. notionary/page/properties/models.py +308 -0
  170. notionary/page/properties/service.py +261 -0
  171. notionary/page/schemas.py +13 -0
  172. notionary/page/service.py +225 -0
  173. notionary/shared/entity/client.py +29 -0
  174. notionary/shared/entity/dto_parsers.py +53 -0
  175. notionary/shared/entity/entity_metadata_update_client.py +41 -0
  176. notionary/shared/entity/schemas.py +45 -0
  177. notionary/shared/entity/service.py +171 -0
  178. notionary/shared/models/cover.py +20 -0
  179. notionary/shared/models/file.py +21 -0
  180. notionary/shared/models/icon.py +28 -0
  181. notionary/shared/models/parent.py +41 -0
  182. notionary/shared/properties/type.py +30 -0
  183. notionary/shared/typings.py +3 -0
  184. notionary/user/__init__.py +4 -8
  185. notionary/user/base.py +138 -0
  186. notionary/user/bot.py +70 -0
  187. notionary/user/client.py +22 -111
  188. notionary/user/person.py +41 -0
  189. notionary/user/schemas.py +67 -0
  190. notionary/user/service.py +65 -0
  191. notionary/utils/date.py +51 -0
  192. notionary/utils/decorators.py +122 -0
  193. notionary/utils/fuzzy.py +68 -0
  194. notionary/utils/mixins/logging.py +58 -0
  195. notionary/utils/pagination.py +100 -0
  196. notionary/utils/uuid_utils.py +20 -0
  197. notionary/workspace/__init__.py +4 -0
  198. notionary/workspace/client.py +62 -0
  199. notionary/workspace/query/__init__.py +3 -0
  200. notionary/workspace/query/builder.py +60 -0
  201. notionary/workspace/query/models.py +61 -0
  202. notionary/workspace/query/service.py +100 -0
  203. notionary/workspace/schemas.py +21 -0
  204. notionary/workspace/service.py +116 -0
  205. notionary-0.3.0.dist-info/METADATA +201 -0
  206. notionary-0.3.0.dist-info/RECORD +209 -0
  207. {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
  208. {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
  209. notionary/base_notion_client.py +0 -219
  210. notionary/blocks/_bootstrap.py +0 -271
  211. notionary/blocks/audio/__init__.py +0 -11
  212. notionary/blocks/audio/audio_element.py +0 -158
  213. notionary/blocks/audio/audio_markdown_node.py +0 -24
  214. notionary/blocks/audio/audio_models.py +0 -10
  215. notionary/blocks/base_block_element.py +0 -42
  216. notionary/blocks/bookmark/__init__.py +0 -12
  217. notionary/blocks/bookmark/bookmark_element.py +0 -83
  218. notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
  219. notionary/blocks/bookmark/bookmark_models.py +0 -15
  220. notionary/blocks/breadcrumbs/__init__.py +0 -15
  221. notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
  222. notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
  223. notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
  224. notionary/blocks/bulleted_list/__init__.py +0 -15
  225. notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
  226. notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
  227. notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
  228. notionary/blocks/callout/__init__.py +0 -12
  229. notionary/blocks/callout/callout_element.py +0 -99
  230. notionary/blocks/callout/callout_markdown_node.py +0 -19
  231. notionary/blocks/callout/callout_models.py +0 -33
  232. notionary/blocks/child_database/__init__.py +0 -14
  233. notionary/blocks/child_database/child_database_element.py +0 -59
  234. notionary/blocks/child_database/child_database_models.py +0 -12
  235. notionary/blocks/child_page/__init__.py +0 -9
  236. notionary/blocks/child_page/child_page_element.py +0 -94
  237. notionary/blocks/child_page/child_page_models.py +0 -12
  238. notionary/blocks/code/__init__.py +0 -11
  239. notionary/blocks/code/code_element.py +0 -149
  240. notionary/blocks/code/code_markdown_node.py +0 -80
  241. notionary/blocks/code/code_models.py +0 -94
  242. notionary/blocks/column/__init__.py +0 -25
  243. notionary/blocks/column/column_element.py +0 -65
  244. notionary/blocks/column/column_list_element.py +0 -52
  245. notionary/blocks/column/column_list_markdown_node.py +0 -34
  246. notionary/blocks/column/column_markdown_node.py +0 -42
  247. notionary/blocks/column/column_models.py +0 -26
  248. notionary/blocks/divider/__init__.py +0 -12
  249. notionary/blocks/divider/divider_element.py +0 -41
  250. notionary/blocks/divider/divider_markdown_node.py +0 -11
  251. notionary/blocks/divider/divider_models.py +0 -12
  252. notionary/blocks/embed/__init__.py +0 -12
  253. notionary/blocks/embed/embed_element.py +0 -98
  254. notionary/blocks/embed/embed_markdown_node.py +0 -19
  255. notionary/blocks/embed/embed_models.py +0 -14
  256. notionary/blocks/equation/__init__.py +0 -13
  257. notionary/blocks/equation/equation_element.py +0 -133
  258. notionary/blocks/equation/equation_element_markdown_node.py +0 -23
  259. notionary/blocks/equation/equation_models.py +0 -11
  260. notionary/blocks/file/__init__.py +0 -23
  261. notionary/blocks/file/file_element.py +0 -133
  262. notionary/blocks/file/file_element_markdown_node.py +0 -24
  263. notionary/blocks/file/file_element_models.py +0 -39
  264. notionary/blocks/heading/__init__.py +0 -19
  265. notionary/blocks/heading/heading_element.py +0 -112
  266. notionary/blocks/heading/heading_markdown_node.py +0 -16
  267. notionary/blocks/heading/heading_models.py +0 -29
  268. notionary/blocks/image_block/__init__.py +0 -11
  269. notionary/blocks/image_block/image_element.py +0 -130
  270. notionary/blocks/image_block/image_markdown_node.py +0 -25
  271. notionary/blocks/image_block/image_models.py +0 -10
  272. notionary/blocks/markdown/markdown_builder.py +0 -525
  273. notionary/blocks/markdown/markdown_document_model.py +0 -0
  274. notionary/blocks/markdown/markdown_node.py +0 -25
  275. notionary/blocks/mixins/captions/__init__.py +0 -4
  276. notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
  277. notionary/blocks/mixins/captions/caption_mixin.py +0 -92
  278. notionary/blocks/mixins/file_upload/__init__.py +0 -3
  279. notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
  280. notionary/blocks/models.py +0 -174
  281. notionary/blocks/numbered_list/__init__.py +0 -16
  282. notionary/blocks/numbered_list/numbered_list_element.py +0 -65
  283. notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
  284. notionary/blocks/numbered_list/numbered_list_models.py +0 -17
  285. notionary/blocks/paragraph/__init__.py +0 -15
  286. notionary/blocks/paragraph/paragraph_element.py +0 -58
  287. notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
  288. notionary/blocks/paragraph/paragraph_models.py +0 -16
  289. notionary/blocks/pdf/__init__.py +0 -11
  290. notionary/blocks/pdf/pdf_element.py +0 -146
  291. notionary/blocks/pdf/pdf_markdown_node.py +0 -24
  292. notionary/blocks/pdf/pdf_models.py +0 -11
  293. notionary/blocks/quote/__init__.py +0 -14
  294. notionary/blocks/quote/quote_element.py +0 -75
  295. notionary/blocks/quote/quote_markdown_node.py +0 -16
  296. notionary/blocks/quote/quote_models.py +0 -18
  297. notionary/blocks/registry/__init__.py +0 -3
  298. notionary/blocks/registry/block_registry.py +0 -150
  299. notionary/blocks/rich_text/__init__.py +0 -33
  300. notionary/blocks/rich_text/rich_text_models.py +0 -221
  301. notionary/blocks/rich_text/text_inline_formatter.py +0 -456
  302. notionary/blocks/syntax_prompt_builder.py +0 -137
  303. notionary/blocks/table/__init__.py +0 -19
  304. notionary/blocks/table/table_element.py +0 -225
  305. notionary/blocks/table/table_markdown_node.py +0 -42
  306. notionary/blocks/table/table_models.py +0 -28
  307. notionary/blocks/table_of_contents/__init__.py +0 -17
  308. notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
  309. notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
  310. notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
  311. notionary/blocks/todo/__init__.py +0 -12
  312. notionary/blocks/todo/todo_element.py +0 -81
  313. notionary/blocks/todo/todo_markdown_node.py +0 -21
  314. notionary/blocks/todo/todo_models.py +0 -18
  315. notionary/blocks/toggle/__init__.py +0 -12
  316. notionary/blocks/toggle/toggle_element.py +0 -112
  317. notionary/blocks/toggle/toggle_markdown_node.py +0 -31
  318. notionary/blocks/toggle/toggle_models.py +0 -17
  319. notionary/blocks/toggleable_heading/__init__.py +0 -11
  320. notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
  321. notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
  322. notionary/blocks/types.py +0 -130
  323. notionary/blocks/video/__init__.py +0 -11
  324. notionary/blocks/video/video_element.py +0 -187
  325. notionary/blocks/video/video_element_models.py +0 -10
  326. notionary/blocks/video/video_markdown_node.py +0 -26
  327. notionary/database/__init__.py +0 -4
  328. notionary/database/database.py +0 -480
  329. notionary/database/database_filter_builder.py +0 -173
  330. notionary/database/database_provider.py +0 -227
  331. notionary/database/exceptions.py +0 -13
  332. notionary/database/models.py +0 -337
  333. notionary/database/notion_database.py +0 -487
  334. notionary/file_upload/__init__.py +0 -7
  335. notionary/page/client.py +0 -124
  336. notionary/page/markdown_whitespace_processor.py +0 -129
  337. notionary/page/models.py +0 -322
  338. notionary/page/notion_page.py +0 -712
  339. notionary/page/page_content_deleting_service.py +0 -117
  340. notionary/page/page_content_writer.py +0 -80
  341. notionary/page/property_formatter.py +0 -99
  342. notionary/page/reader/handler/__init__.py +0 -19
  343. notionary/page/reader/handler/base_block_renderer.py +0 -44
  344. notionary/page/reader/handler/block_processing_context.py +0 -35
  345. notionary/page/reader/handler/block_rendering_context.py +0 -48
  346. notionary/page/reader/handler/column_list_renderer.py +0 -51
  347. notionary/page/reader/handler/column_renderer.py +0 -60
  348. notionary/page/reader/handler/equation_renderer.py +0 -0
  349. notionary/page/reader/handler/line_renderer.py +0 -73
  350. notionary/page/reader/handler/numbered_list_renderer.py +0 -85
  351. notionary/page/reader/handler/toggle_renderer.py +0 -69
  352. notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
  353. notionary/page/reader/page_content_retriever.py +0 -81
  354. notionary/page/search_filter_builder.py +0 -132
  355. notionary/page/utils.py +0 -60
  356. notionary/page/writer/handler/__init__.py +0 -24
  357. notionary/page/writer/handler/code_handler.py +0 -72
  358. notionary/page/writer/handler/column_handler.py +0 -141
  359. notionary/page/writer/handler/column_list_handler.py +0 -139
  360. notionary/page/writer/handler/equation_handler.py +0 -74
  361. notionary/page/writer/handler/line_handler.py +0 -35
  362. notionary/page/writer/handler/line_processing_context.py +0 -54
  363. notionary/page/writer/handler/regular_line_handler.py +0 -86
  364. notionary/page/writer/handler/table_handler.py +0 -66
  365. notionary/page/writer/handler/toggle_handler.py +0 -159
  366. notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
  367. notionary/page/writer/markdown_to_notion_converter.py +0 -139
  368. notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
  369. notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
  370. notionary/page/writer/notion_text_length_processor.py +0 -150
  371. notionary/schemas/__init__.py +0 -3
  372. notionary/schemas/base.py +0 -73
  373. notionary/shared/__init__.py +0 -3
  374. notionary/shared/name_to_id_resolver.py +0 -203
  375. notionary/telemetry/__init__.py +0 -19
  376. notionary/telemetry/service.py +0 -136
  377. notionary/telemetry/views.py +0 -73
  378. notionary/user/base_notion_user.py +0 -53
  379. notionary/user/models.py +0 -84
  380. notionary/user/notion_bot_user.py +0 -226
  381. notionary/user/notion_user.py +0 -255
  382. notionary/user/notion_user_manager.py +0 -101
  383. notionary/util/__init__.py +0 -15
  384. notionary/util/concurrency_limiter.py +0 -0
  385. notionary/util/factory_decorator.py +0 -0
  386. notionary/util/factory_only.py +0 -37
  387. notionary/util/fuzzy.py +0 -75
  388. notionary/util/logging_mixin.py +0 -59
  389. notionary/util/page_id_utils.py +0 -27
  390. notionary/util/singleton.py +0 -18
  391. notionary/util/singleton_metaclass.py +0 -22
  392. notionary/workspace.py +0 -105
  393. notionary-0.2.27.dist-info/METADATA +0 -270
  394. notionary-0.2.27.dist-info/RECORD +0 -202
  395. /notionary/{database → user}/factory.py +0 -0
@@ -0,0 +1,30 @@
1
+ from enum import StrEnum
2
+
3
+
4
+ class PropertyType(StrEnum):
5
+ TITLE = "title"
6
+ RICH_TEXT = "rich_text"
7
+ SELECT = "select"
8
+ MULTI_SELECT = "multi_select"
9
+ STATUS = "status"
10
+ NUMBER = "number"
11
+ DATE = "date"
12
+ CHECKBOX = "checkbox"
13
+ URL = "url"
14
+ EMAIL = "email"
15
+ PHONE_NUMBER = "phone_number"
16
+ PEOPLE = "people"
17
+ CREATED_BY = "created_by"
18
+ LAST_EDITED_BY = "last_edited_by"
19
+ CREATED_TIME = "created_time"
20
+ LAST_EDITED_TIME = "last_edited_time"
21
+ LAST_VISITED_TIME = "last_visited_time"
22
+ FORMULA = "formula"
23
+ ROLLUP = "rollup"
24
+ FILES = "files"
25
+ RELATION = "relation"
26
+ BUTTON = "button"
27
+ LOCATION = "location"
28
+ PLACE = "place"
29
+ VERIFICATION = "verification"
30
+ UNIQUE_ID = "unique_id"
@@ -0,0 +1,3 @@
1
+ from typing import Any
2
+
3
+ type JsonDict = dict[str, Any]
@@ -1,11 +1,7 @@
1
- from .client import NotionUserClient
2
- from .notion_bot_user import NotionBotUser
3
- from .notion_user import NotionUser
4
- from .notion_user_manager import NotionUserManager
1
+ from .bot import BotUser
2
+ from .person import PersonUser
5
3
 
6
4
  __all__ = [
7
- "NotionUser",
8
- "NotionUserManager",
9
- "NotionUserClient",
10
- "NotionBotUser",
5
+ "BotUser",
6
+ "PersonUser",
11
7
  ]
notionary/user/base.py ADDED
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+ from typing import TYPE_CHECKING, Self
5
+
6
+ from notionary.exceptions.search import NoUsersInWorkspace, UserNotFound
7
+ from notionary.user.client import UserHttpClient
8
+ from notionary.user.schemas import UserResponseDto, UserType
9
+ from notionary.utils.fuzzy import find_all_matches
10
+
11
+ if TYPE_CHECKING:
12
+ from notionary.user.bot import BotUser
13
+ from notionary.user.person import PersonUser
14
+
15
+
16
+ class BaseUser:
17
+ def __init__(
18
+ self,
19
+ id: str,
20
+ name: str | None = None,
21
+ avatar_url: str | None = None,
22
+ ) -> None:
23
+ self._id = id
24
+ self._name = name
25
+ self._avatar_url = avatar_url
26
+
27
+ @classmethod
28
+ async def from_id_auto(
29
+ cls,
30
+ user_id: str,
31
+ http_client: UserHttpClient | None = None,
32
+ ) -> BotUser | PersonUser:
33
+ from notionary.user.bot import BotUser
34
+ from notionary.user.person import PersonUser
35
+
36
+ client = http_client or UserHttpClient()
37
+ user_dto = await client.get_user_by_id(user_id)
38
+
39
+ if user_dto.type == UserType.BOT:
40
+ return BotUser.from_dto(user_dto)
41
+ elif user_dto.type == UserType.PERSON:
42
+ return PersonUser.from_dto(user_dto)
43
+ else:
44
+ raise ValueError(f"Unknown user type: {user_dto.type}")
45
+
46
+ @classmethod
47
+ async def from_id(
48
+ cls,
49
+ user_id: str,
50
+ http_client: UserHttpClient | None = None,
51
+ ) -> Self:
52
+ client = http_client or UserHttpClient()
53
+ user_dto = await client.get_user_by_id(user_id)
54
+
55
+ expected_type = cls._get_expected_user_type()
56
+ if user_dto.type != expected_type:
57
+ raise ValueError(f"User {user_id} is not a '{expected_type.value}', but '{user_dto.type.value}'")
58
+
59
+ return cls.from_dto(user_dto)
60
+
61
+ @classmethod
62
+ async def from_name(
63
+ cls,
64
+ name: str,
65
+ http_client: UserHttpClient | None = None,
66
+ ) -> Self:
67
+ client = http_client or UserHttpClient()
68
+ all_users = await cls._get_all_users_of_type(client)
69
+
70
+ user_type = cls._get_expected_user_type().value
71
+
72
+ if not all_users:
73
+ raise NoUsersInWorkspace(user_type)
74
+
75
+ exact_match = cls._find_exact_match(all_users, name)
76
+ if exact_match:
77
+ return exact_match
78
+
79
+ suggestions = cls._get_fuzzy_suggestions(all_users, name)
80
+ raise UserNotFound(user_type, name, suggestions)
81
+
82
+ @classmethod
83
+ def _find_exact_match(cls, users: list[Self], query: str) -> Self | None:
84
+ query_lower = query.lower()
85
+ for user in users:
86
+ if user.name and user.name.lower() == query_lower:
87
+ return user
88
+ return None
89
+
90
+ @classmethod
91
+ def _get_fuzzy_suggestions(cls, users: list[Self], query: str) -> list[str]:
92
+ sorted_by_similarity = find_all_matches(
93
+ query=query,
94
+ items=users,
95
+ text_extractor=cls._get_name_extractor(),
96
+ min_similarity=0.6,
97
+ )
98
+
99
+ if sorted_by_similarity:
100
+ return [user.name for user in sorted_by_similarity[:5] if user.name]
101
+
102
+ return [user.name for user in users[:5] if user.name]
103
+
104
+ @classmethod
105
+ async def _get_all_users_of_type(cls, http_client: UserHttpClient) -> list[Self]:
106
+ all_workspace_user_dtos = await http_client.get_all_workspace_users()
107
+ expected_type = cls._get_expected_user_type()
108
+ filtered_dtos = [dto for dto in all_workspace_user_dtos if dto.type == expected_type]
109
+ return [cls.from_dto(dto) for dto in filtered_dtos]
110
+
111
+ @classmethod
112
+ @abstractmethod
113
+ def _get_expected_user_type(cls) -> UserType:
114
+ pass
115
+
116
+ @classmethod
117
+ @abstractmethod
118
+ def from_dto(cls, user_dto: UserResponseDto) -> Self:
119
+ pass
120
+
121
+ @classmethod
122
+ def _get_name_extractor(cls):
123
+ return lambda user: user.name or ""
124
+
125
+ @property
126
+ def id(self) -> str:
127
+ return self._id
128
+
129
+ @property
130
+ def name(self) -> str | None:
131
+ return self._name
132
+
133
+ @property
134
+ def avatar_url(self) -> str | None:
135
+ return self._avatar_url
136
+
137
+ def __repr__(self) -> str:
138
+ return f"{self.__class__.__name__}(id={self._id!r}, name={self._name!r})"
notionary/user/bot.py ADDED
@@ -0,0 +1,70 @@
1
+ from typing import Self, cast
2
+
3
+ from notionary.user.base import BaseUser
4
+ from notionary.user.client import UserHttpClient
5
+ from notionary.user.schemas import BotUserDto, BotUserResponseDto, UserResponseDto, UserType, WorkspaceOwnerType
6
+
7
+
8
+ class BotUser(BaseUser):
9
+ def __init__(
10
+ self,
11
+ id: str,
12
+ workspace_file_upload_limit_in_bytes: int,
13
+ owner_type: WorkspaceOwnerType | None,
14
+ name: str | None = None,
15
+ avatar_url: str | None = None,
16
+ workspace_name: str | None = None,
17
+ ) -> None:
18
+ super().__init__(id=id, name=name, avatar_url=avatar_url)
19
+ self._workspace_name = workspace_name
20
+ self._workspace_file_upload_limit_in_bytes = workspace_file_upload_limit_in_bytes
21
+ self._owner_type = owner_type
22
+
23
+ @classmethod
24
+ def _get_expected_user_type(cls) -> UserType:
25
+ return UserType.BOT
26
+
27
+ @classmethod
28
+ async def from_current_integration(
29
+ cls,
30
+ http_client: UserHttpClient | None = None,
31
+ ) -> Self:
32
+ client = http_client or UserHttpClient()
33
+ user_dto = await client.get_current_integration_bot()
34
+ return cls.from_dto(user_dto)
35
+
36
+ @classmethod
37
+ def from_dto(cls, user_dto: UserResponseDto) -> Self:
38
+ bot_dto = cast(BotUserResponseDto, user_dto)
39
+ bot_data: BotUserDto = bot_dto.bot
40
+
41
+ owner_type = bot_data.owner.type if bot_data and bot_data.owner else None
42
+ workspace_name = bot_data.workspace_name if bot_data else None
43
+
44
+ limit = 0
45
+ if bot_data and bot_data.workspace_limits:
46
+ limit = bot_data.workspace_limits.max_file_upload_size_in_bytes
47
+
48
+ return cls(
49
+ id=bot_dto.id,
50
+ name=bot_dto.name,
51
+ avatar_url=bot_dto.avatar_url,
52
+ workspace_name=workspace_name,
53
+ workspace_file_upload_limit_in_bytes=limit,
54
+ owner_type=owner_type,
55
+ )
56
+
57
+ @property
58
+ def workspace_name(self) -> str | None:
59
+ return self._workspace_name
60
+
61
+ @property
62
+ def workspace_file_upload_limit_in_bytes(self) -> int:
63
+ return self._workspace_file_upload_limit_in_bytes
64
+
65
+ @property
66
+ def owner_type(self) -> WorkspaceOwnerType | None:
67
+ return self._owner_type
68
+
69
+ def __repr__(self) -> str:
70
+ return f"BotUser(id={self._id!r}, name={self._name!r}, avatar_url={self._avatar_url!r}, workspace_name={self._workspace_name!r}, workspace_file_upload_limit_in_bytes={self._workspace_file_upload_limit_in_bytes!r}, owner_type={self._owner_type!r})"
notionary/user/client.py CHANGED
@@ -1,128 +1,39 @@
1
- from typing import List, Optional
1
+ from pydantic import TypeAdapter
2
2
 
3
- from notionary.base_notion_client import BaseNotionClient
4
- from notionary.user.models import (
5
- NotionBotUserResponse,
6
- NotionUserResponse,
3
+ from notionary.http.client import NotionHttpClient
4
+ from notionary.user.schemas import (
5
+ BotUserResponseDto,
7
6
  NotionUsersListResponse,
7
+ UserResponseDto,
8
8
  )
9
+ from notionary.utils.pagination import paginate_notion_api
9
10
 
10
11
 
11
- class NotionUserClient(BaseNotionClient):
12
- """
13
- Client for Notion user-specific operations.
14
- Inherits base HTTP functionality from BaseNotionClient.
15
-
16
- Note: The Notion API only supports individual user queries and bot user info.
17
- List users endpoint is available but only returns workspace members (no guests).
18
- """
19
-
20
- async def get_user(self, user_id: str) -> Optional[NotionUserResponse]:
21
- """
22
- Retrieve a user by their ID.
23
- """
12
+ class UserHttpClient(NotionHttpClient):
13
+ async def get_user_by_id(self, user_id: str) -> UserResponseDto:
24
14
  response = await self.get(f"users/{user_id}")
25
- if response is None:
26
- self.logger.error("Failed to fetch user %s - API returned None", user_id)
27
- return None
28
15
 
29
- try:
30
- return NotionUserResponse.model_validate(response)
31
- except Exception as e:
32
- self.logger.error("Failed to validate user response for %s: %s", user_id, e)
33
- return None
34
-
35
- async def get_bot_user(self) -> Optional[NotionBotUserResponse]:
36
- """
37
- Retrieve your token's bot user information.
38
- """
39
- response = await self.get("users/me")
40
- if response is None:
41
- self.logger.error("Failed to fetch bot user - API returned None")
42
- return None
16
+ adapter = TypeAdapter(UserResponseDto)
17
+ return adapter.validate_python(response)
43
18
 
44
- try:
45
- return NotionBotUserResponse.model_validate(response)
46
- except Exception as e:
47
- self.logger.error("Failed to validate bot user response: %s", e)
48
- return None
19
+ async def get_all_workspace_users(self) -> list[UserResponseDto]:
20
+ all_entities = await paginate_notion_api(self._get_workspace_entities, page_size=100)
49
21
 
50
- async def list_users(
51
- self, page_size: int = 100, start_cursor: Optional[str] = None
52
- ) -> Optional[NotionUsersListResponse]:
53
- """
54
- List all users in the workspace (paginated).
22
+ self.logger.info("Fetched %d total workspace users", len(all_entities))
23
+ return all_entities
55
24
 
56
- Note: Guests are not included in the response.
57
- """
58
- params = {"page_size": min(page_size, 100)} # API max is 100
25
+ async def _get_workspace_entities(
26
+ self, page_size: int = 100, start_cursor: str | None = None
27
+ ) -> NotionUsersListResponse | None:
28
+ params = {"page_size": min(page_size, 100)}
59
29
  if start_cursor:
60
30
  params["start_cursor"] = start_cursor
61
31
 
62
32
  response = await self.get("users", params=params)
63
- if response is None:
64
- self.logger.error("Failed to fetch users list - API returned None")
65
- return None
66
-
67
- try:
68
- return NotionUsersListResponse.model_validate(response)
69
- except Exception as e:
70
- self.logger.error("Failed to validate users list response: %s", e)
71
- return None
72
-
73
- async def get_all_users(self) -> List[NotionUserResponse]:
74
- """
75
- Get all users in the workspace by handling pagination automatically.
76
- """
77
- all_users = []
78
- start_cursor = None
79
-
80
- while True:
81
- try:
82
- response = await self.list_users(
83
- page_size=100, start_cursor=start_cursor
84
- )
85
33
 
86
- if not response or not response.results:
87
- break
34
+ return NotionUsersListResponse.model_validate(response)
88
35
 
89
- all_users.extend(response.results)
90
-
91
- # Check if there are more pages
92
- if not response.has_more or not response.next_cursor:
93
- break
94
-
95
- start_cursor = response.next_cursor
96
-
97
- except Exception as e:
98
- self.logger.error("Error fetching all users: %s", str(e))
99
- break
100
-
101
- self.logger.info("Retrieved %d total users from workspace", len(all_users))
102
- return all_users
103
-
104
- async def get_workspace_name(self) -> Optional[str]:
105
- """
106
- Get the workspace name from the bot user.
107
- """
108
- try:
109
- bot_user = await self.get_bot_user()
110
- if bot_user and bot_user.bot and bot_user.bot.workspace_name:
111
- return bot_user.bot.workspace_name
112
- return None
113
- except Exception as e:
114
- self.logger.error("Error fetching workspace name: %s", str(e))
115
- return None
36
+ async def get_current_integration_bot(self) -> BotUserResponseDto:
37
+ response = await self.get("users/me")
116
38
 
117
- async def get_workspace_limits(self) -> Optional[dict]:
118
- """
119
- Get workspace limits from the bot user.
120
- """
121
- try:
122
- bot_user = await self.get_bot_user()
123
- if bot_user and bot_user.bot and bot_user.bot.workspace_limits:
124
- return bot_user.bot.workspace_limits.model_dump()
125
- return None
126
- except Exception as e:
127
- self.logger.error("Error fetching workspace limits: %s", str(e))
128
- return None
39
+ return BotUserResponseDto.model_validate(response)
@@ -0,0 +1,41 @@
1
+ from typing import Self, cast
2
+
3
+ from notionary.user.base import BaseUser
4
+ from notionary.user.schemas import PersonUserResponseDto, UserResponseDto, UserType
5
+
6
+
7
+ class PersonUser(BaseUser):
8
+ def __init__(
9
+ self,
10
+ id: str,
11
+ name: str,
12
+ avatar_url: str,
13
+ email: str,
14
+ ) -> None:
15
+ super().__init__(id=id, name=name, avatar_url=avatar_url)
16
+ self._email = email
17
+
18
+ @classmethod
19
+ def _get_expected_user_type(cls) -> UserType:
20
+ return UserType.PERSON
21
+
22
+ @classmethod
23
+ def from_dto(cls, user_dto: UserResponseDto) -> Self:
24
+ person_dto = cast(PersonUserResponseDto, user_dto)
25
+ return cls(
26
+ id=person_dto.id,
27
+ name=person_dto.name or "",
28
+ avatar_url=person_dto.avatar_url,
29
+ email=person_dto.person.email or "",
30
+ )
31
+
32
+ @property
33
+ def name(self) -> str:
34
+ return self._name or ""
35
+
36
+ @property
37
+ def email(self) -> str:
38
+ return self._email
39
+
40
+ def __repr__(self) -> str:
41
+ return f"PersonUser(id={self._id!r}, name={self._name!r}, email={self._email!r})"
@@ -0,0 +1,67 @@
1
+ from enum import StrEnum
2
+ from typing import Annotated, Literal
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class UserType(StrEnum):
8
+ PERSON = "person"
9
+ BOT = "bot"
10
+
11
+
12
+ class WorkspaceOwnerType(StrEnum):
13
+ USER = "user"
14
+ WORKSPACE = "workspace"
15
+
16
+
17
+ class PersonUserDto(BaseModel):
18
+ email: str | None = None
19
+
20
+
21
+ class BotOwnerDto(BaseModel):
22
+ type: WorkspaceOwnerType
23
+ workspace: bool | None = None
24
+
25
+
26
+ class WorkspaceLimits(BaseModel):
27
+ max_file_upload_size_in_bytes: int
28
+
29
+
30
+ class BotUserDto(BaseModel):
31
+ owner: BotOwnerDto | None = None
32
+ workspace_name: str | None = None
33
+ workspace_limits: WorkspaceLimits | None = None
34
+
35
+
36
+ class NotionUserBase(BaseModel):
37
+ object: Literal["user"] = "user"
38
+ id: str
39
+
40
+ type: UserType
41
+
42
+ name: str | None = None
43
+ avatar_url: str | None = None
44
+
45
+
46
+ class PersonUserResponseDto(NotionUserBase):
47
+ type: Literal[UserType.PERSON] = UserType.PERSON
48
+ person: PersonUserDto
49
+
50
+
51
+ class BotUserResponseDto(NotionUserBase):
52
+ type: Literal[UserType.BOT] = UserType.BOT
53
+ bot: BotUserDto
54
+
55
+
56
+ UserResponseDto = Annotated[PersonUserResponseDto | BotUserResponseDto, Field(discriminator="type")]
57
+
58
+
59
+ class NotionUsersListResponse(BaseModel):
60
+ results: list[UserResponseDto]
61
+ next_cursor: str | None = None
62
+ has_more: bool
63
+
64
+
65
+ class PartialUserDto(BaseModel):
66
+ object: Literal["user"] = "user"
67
+ id: str
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import AsyncIterator
4
+
5
+ from notionary.user import BotUser, PersonUser
6
+ from notionary.user.client import UserHttpClient
7
+ from notionary.user.schemas import UserType
8
+
9
+
10
+ class UserService:
11
+ def __init__(self, client: UserHttpClient | None = None) -> None:
12
+ self._client = client or UserHttpClient()
13
+
14
+ async def list_users(self) -> list[PersonUser]:
15
+ all_users = await self._client.get_all_workspace_users()
16
+ person_users = [user for user in all_users if user.type == UserType.PERSON]
17
+
18
+ return [PersonUser.from_dto(user) for user in person_users]
19
+
20
+ async def list_users_stream(self) -> AsyncIterator[PersonUser]:
21
+ all_users = await self._client.get_all_workspace_users()
22
+ for user in all_users:
23
+ if user.type == UserType.PERSON:
24
+ yield PersonUser.from_dto(user)
25
+
26
+ async def list_bot_users(self) -> list[BotUser]:
27
+ all_users = await self._client.get_all_workspace_users()
28
+ bot_users = [user for user in all_users if user.type == UserType.BOT]
29
+
30
+ return [BotUser.from_dto(user) for user in bot_users]
31
+
32
+ async def list_bot_users_stream(self) -> AsyncIterator[BotUser]:
33
+ all_users = await self._client.get_all_workspace_users()
34
+ for user in all_users:
35
+ if user.type == UserType.BOT:
36
+ yield BotUser.from_dto(user)
37
+
38
+ async def search_users(self, query: str) -> list[PersonUser]:
39
+ all_person_users = await self.list_users()
40
+ query_lower = query.lower()
41
+
42
+ return [
43
+ user
44
+ for user in all_person_users
45
+ if query_lower in (user.name or "").lower() or query_lower in (user.email or "").lower()
46
+ ]
47
+
48
+ async def search_users_stream(self, query: str) -> AsyncIterator[PersonUser]:
49
+ query_lower = query.lower()
50
+
51
+ async for user in self.list_users_stream():
52
+ if query_lower in (user.name or "").lower() or query_lower in (user.email or "").lower():
53
+ yield user
54
+
55
+ async def get_current_bot(self) -> BotUser:
56
+ bot_dto = await self._client.get_current_integration_bot()
57
+ return BotUser.from_dto(bot_dto)
58
+
59
+ async def get_user_by_id(self, user_id: str) -> PersonUser | BotUser | None:
60
+ user_dto = await self._client.get_user_by_id(user_id)
61
+
62
+ if user_dto.type == UserType.PERSON:
63
+ return PersonUser.from_dto(user_dto)
64
+ else:
65
+ return BotUser.from_dto(user_dto)
@@ -0,0 +1,51 @@
1
+ from datetime import datetime
2
+
3
+
4
+ def parse_date(date_str: str) -> str:
5
+ supported_formats = _get_supported_date_formats()
6
+
7
+ for date_format in supported_formats:
8
+ parsed_date = _try_parse_date_with_format(date_str, date_format)
9
+ if _date_was_successfully_parsed(parsed_date):
10
+ return _convert_to_iso_format(parsed_date)
11
+
12
+ _raise_invalid_date_format_error(date_str)
13
+
14
+
15
+ def _get_supported_date_formats() -> list[str]:
16
+ return [
17
+ "%Y-%m-%d", # ISO: 2024-12-31
18
+ "%d.%m.%Y", # German: 31.12.2024
19
+ "%m/%d/%Y", # US with slash: 12/31/2024
20
+ "%m-%d-%Y", # US with dash: 12-31-2024
21
+ "%d/%m/%Y", # Day first with slash: 31/12/2024
22
+ "%d-%m-%Y", # Day first with dash: 31-12-2024
23
+ "%d-%b-%Y", # Short month: 31-Dec-2024
24
+ "%d %b %Y", # Short month with space: 31 Dec 2024
25
+ "%d-%B-%Y", # Full month: 31-December-2024
26
+ "%d %B %Y", # Full month with space: 31 December 2024
27
+ ]
28
+
29
+
30
+ def _try_parse_date_with_format(date_str: str, date_format: str) -> datetime | None:
31
+ try:
32
+ return datetime.strptime(date_str, date_format)
33
+ except ValueError:
34
+ return None
35
+
36
+
37
+ def _date_was_successfully_parsed(parsed_date: datetime | None) -> bool:
38
+ return parsed_date is not None
39
+
40
+
41
+ def _convert_to_iso_format(parsed_date: datetime) -> str:
42
+ return parsed_date.strftime("%Y-%m-%d")
43
+
44
+
45
+ def _raise_invalid_date_format_error(date_str: str) -> None:
46
+ error_message = (
47
+ f"Invalid date format: '{date_str}'. "
48
+ f"Supported formats: YYYY-MM-DD, DD.MM.YYYY, MM/DD/YYYY, DD/MM/YYYY, "
49
+ f"DD-Mon-YYYY, DD Month YYYY"
50
+ )
51
+ raise ValueError(error_message)