notionary 0.2.21__tar.gz → 0.2.23__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notionary-0.2.23/PKG-INFO +235 -0
- notionary-0.2.23/README.md +212 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/_bootstrap.py +9 -1
- notionary-0.2.23/notionary/blocks/audio/audio_element.py +115 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/audio/audio_markdown_node.py +10 -4
- notionary-0.2.23/notionary/blocks/base_block_element.py +42 -0
- notionary-0.2.23/notionary/blocks/bookmark/bookmark_element.py +83 -0
- notionary-0.2.23/notionary/blocks/bookmark/bookmark_markdown_node.py +44 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/breadcrumb_element.py +2 -2
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/bulleted_list_element.py +21 -4
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/callout_element.py +20 -4
- notionary-0.2.23/notionary/blocks/child_database/__init__.py +14 -0
- notionary-0.2.23/notionary/blocks/child_database/child_database_element.py +59 -0
- notionary-0.2.23/notionary/blocks/child_database/child_database_models.py +12 -0
- notionary-0.2.23/notionary/blocks/child_page/child_page_element.py +94 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/client.py +0 -1
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/code/code_element.py +51 -2
- notionary-0.2.23/notionary/blocks/code/code_markdown_node.py +94 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_element.py +9 -3
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_list_element.py +18 -3
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/divider_element.py +3 -11
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/embed_element.py +27 -6
- notionary-0.2.23/notionary/blocks/equation/equation_element.py +133 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/equation/equation_element_markdown_node.py +8 -9
- notionary-0.2.23/notionary/blocks/file/file_element.py +112 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/file/file_element_markdown_node.py +9 -7
- notionary-0.2.23/notionary/blocks/guards.py +22 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/heading_element.py +23 -4
- notionary-0.2.23/notionary/blocks/image_block/image_element.py +89 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/image_block/image_markdown_node.py +10 -5
- notionary-0.2.23/notionary/blocks/mixins/captions/__init__.py +4 -0
- notionary-0.2.23/notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +31 -0
- notionary-0.2.23/notionary/blocks/mixins/captions/caption_mixin.py +92 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/models.py +3 -1
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/numbered_list_element.py +21 -4
- notionary-0.2.23/notionary/blocks/paragraph/paragraph_element.py +58 -0
- notionary-0.2.23/notionary/blocks/pdf/pdf_element.py +97 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/pdf/pdf_markdown_node.py +9 -7
- notionary-0.2.23/notionary/blocks/quote/quote_element.py +75 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/quote/quote_markdown_node.py +2 -2
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/registry/block_registry.py +1 -46
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/registry/block_registry_builder.py +8 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/rich_text/rich_text_models.py +62 -29
- notionary-0.2.23/notionary/blocks/rich_text/text_inline_formatter.py +456 -0
- notionary-0.2.23/notionary/blocks/syntax_prompt_builder.py +137 -0
- notionary-0.2.23/notionary/blocks/table/table_element.py +225 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/table_of_contents_element.py +19 -2
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/todo_element.py +21 -4
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/toggle_element.py +19 -3
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/toggle_markdown_node.py +1 -1
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggleable_heading/toggleable_heading_element.py +19 -4
- notionary-0.2.23/notionary/blocks/types.py +130 -0
- notionary-0.2.23/notionary/blocks/video/video_element.py +111 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/video/video_markdown_node.py +10 -5
- notionary-0.2.23/notionary/comments/__init__.py +26 -0
- notionary-0.2.23/notionary/comments/client.py +211 -0
- notionary-0.2.23/notionary/comments/models.py +129 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/client.py +23 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/models.py +2 -2
- {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/markdown_builder.py +34 -27
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/client.py +21 -6
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/notion_page.py +77 -2
- notionary-0.2.21/notionary/page/page_content_writer.py → notionary-0.2.23/notionary/page/page_content_deleting_service.py +4 -88
- notionary-0.2.23/notionary/page/page_content_writer.py +177 -0
- notionary-0.2.23/notionary/page/page_context.py +64 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/__init__.py +2 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/base_block_renderer.py +4 -4
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/block_rendering_context.py +5 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/line_renderer.py +16 -3
- notionary-0.2.23/notionary/page/reader/handler/numbered_list_renderer.py +85 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/page_content_retriever.py +17 -5
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/__init__.py +2 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/code_handler.py +12 -40
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/column_handler.py +12 -12
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/column_list_handler.py +13 -13
- notionary-0.2.23/notionary/page/writer/handler/equation_handler.py +74 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/line_handler.py +4 -4
- notionary-0.2.23/notionary/page/writer/handler/regular_line_handler.py +86 -0
- notionary-0.2.23/notionary/page/writer/handler/table_handler.py +66 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/toggle_handler.py +14 -12
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/toggleable_heading_handler.py +22 -16
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/markdown_to_notion_converter.py +28 -9
- notionary-0.2.23/notionary/page/writer/markdown_to_notion_converter_context.py +30 -0
- notionary-0.2.23/notionary/page/writer/markdown_to_notion_formatting_post_processor.py +73 -0
- notionary-0.2.23/notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
- notionary-0.2.23/notionary/page/writer/notion_text_length_processor.py +150 -0
- notionary-0.2.23/notionary/shared/__init__.py +5 -0
- notionary-0.2.23/notionary/shared/name_to_id_resolver.py +203 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/telemetry/service.py +0 -1
- notionary-0.2.23/notionary/user/notion_user_manager.py +101 -0
- notionary-0.2.23/notionary/util/concurrency_limiter.py +0 -0
- notionary-0.2.23/notionary/util/factory_decorator.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/workspace.py +4 -4
- {notionary-0.2.21 → notionary-0.2.23}/pyproject.toml +2 -3
- notionary-0.2.21/PKG-INFO +0 -229
- notionary-0.2.21/README.md +0 -204
- notionary-0.2.21/notionary/blocks/audio/audio_element.py +0 -90
- notionary-0.2.21/notionary/blocks/base_block_element.py +0 -30
- notionary-0.2.21/notionary/blocks/bookmark/bookmark_element.py +0 -80
- notionary-0.2.21/notionary/blocks/bookmark/bookmark_markdown_node.py +0 -45
- notionary-0.2.21/notionary/blocks/child_database/__init__.py +0 -7
- notionary-0.2.21/notionary/blocks/child_database/child_database_models.py +0 -19
- notionary-0.2.21/notionary/blocks/code/code_markdown_node.py +0 -43
- notionary-0.2.21/notionary/blocks/equation/equation_element.py +0 -80
- notionary-0.2.21/notionary/blocks/file/file_element.py +0 -93
- notionary-0.2.21/notionary/blocks/image_block/image_element.py +0 -84
- notionary-0.2.21/notionary/blocks/paragraph/paragraph_element.py +0 -42
- notionary-0.2.21/notionary/blocks/pdf/pdf_element.py +0 -91
- notionary-0.2.21/notionary/blocks/quote/quote_element.py +0 -58
- notionary-0.2.21/notionary/blocks/rich_text/text_inline_formatter.py +0 -125
- notionary-0.2.21/notionary/blocks/table/table_element.py +0 -124
- notionary-0.2.21/notionary/blocks/types.py +0 -61
- notionary-0.2.21/notionary/blocks/video/video_element.py +0 -106
- notionary-0.2.21/notionary/page/markdown_whitespace_processor.py +0 -80
- notionary-0.2.21/notionary/page/notion_text_length_utils.py +0 -119
- notionary-0.2.21/notionary/page/writer/handler/regular_line_handler.py +0 -92
- notionary-0.2.21/notionary/page/writer/handler/table_handler.py +0 -130
- notionary-0.2.21/notionary/user/notion_user_manager.py +0 -174
- notionary-0.2.21/notionary/user/notion_user_provider.py +0 -1
- {notionary-0.2.21 → notionary-0.2.23}/LICENSE +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/base_notion_client.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/audio/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/audio/audio_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bookmark/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bookmark/bookmark_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/bulleted_list/bulleted_list_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/callout_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/callout/callout_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/child_page/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/child_page/child_page_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/code/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/code/code_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_list_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/column/column_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/divider_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/divider/divider_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/embed_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/embed/embed_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/equation/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/equation/equation_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/file/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/file/file_element_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/heading_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/heading/heading_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/image_block/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/image_block/image_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/numbered_list/numbered_list_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/paragraph/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/paragraph/paragraph_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/paragraph/paragraph_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/pdf/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/pdf/pdf_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/quote/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/quote/quote_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/registry/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/rich_text/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table/table_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table/table_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/table_of_contents/table_of_contents_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/todo_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/todo/todo_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggle/toggle_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggleable_heading/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/video/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/blocks/video/video_element_models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/database.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/database_filter_builder.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/database_provider.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/exceptions.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/factory.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/database/notion_database.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/client.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/file_upload/notion_file_upload.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/___init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/makdown_document_model.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/markdown_document_model.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/markdown/markdown_node.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/models/notion_database_response.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/property_formatter.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/block_processing_context.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/column_list_renderer.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/column_renderer.py +0 -0
- /notionary-0.2.21/notionary/page/reader/handler/context.py → /notionary-0.2.23/notionary/page/reader/handler/equation_renderer.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/toggle_renderer.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/reader/handler/toggleable_heading_renderer.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/search_filter_builder.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/utils.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/page/writer/handler/line_processing_context.py +0 -0
- /notionary-0.2.21/notionary/util/factory_decorator.py → /notionary-0.2.23/notionary/page/writer/markdown_to_notion_post_processor.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/telemetry/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/telemetry/views.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/user/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/user/base_notion_user.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/user/client.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/user/models.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/user/notion_bot_user.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/user/notion_user.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/__init__.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/factory_only.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/fuzzy.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/logging_mixin.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/page_id_utils.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/singleton.py +0 -0
- {notionary-0.2.21 → notionary-0.2.23}/notionary/util/singleton_metaclass.py +0 -0
@@ -0,0 +1,235 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: notionary
|
3
|
+
Version: 0.2.23
|
4
|
+
Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
|
5
|
+
License: MIT
|
6
|
+
Author: Mathis Arends
|
7
|
+
Author-email: mathisarends27@gmail.com
|
8
|
+
Requires-Python: >=3.9
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
16
|
+
Requires-Dist: httpx (>=0.28.0)
|
17
|
+
Requires-Dist: posthog (>=6.3.1,<7.0.0)
|
18
|
+
Requires-Dist: pydantic (>=2.11.4)
|
19
|
+
Requires-Dist: python-dotenv (>=1.1.0)
|
20
|
+
Project-URL: Homepage, https://github.com/mathisarends/notionary
|
21
|
+
Description-Content-Type: text/markdown
|
22
|
+
|
23
|
+
<picture>
|
24
|
+
<source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
|
25
|
+
<source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
|
26
|
+
<img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
|
27
|
+
</picture>
|
28
|
+
|
29
|
+
<h1 align="center">Notion API simplified for Python developers 🐍</h1>
|
30
|
+
|
31
|
+
<div align="center">
|
32
|
+
|
33
|
+
[](https://www.python.org/downloads/)
|
34
|
+
[](LICENSE)
|
35
|
+
[](https://mathisarends.github.io/notionary/)
|
36
|
+
|
37
|
+
Transform complex Notion API interactions into simple, Pythonic code. Build AI agents, automate workflows, and create dynamic content with ease.
|
38
|
+
|
39
|
+
</div>
|
40
|
+
|
41
|
+
---
|
42
|
+
|
43
|
+
## Why Notionary?
|
44
|
+
|
45
|
+
- **Smart Discovery**: Find pages and databases by name—no more hunting for URLs or IDs
|
46
|
+
- **Rich Markdown**: Convert extended Markdown (callouts, toggles, columns) directly into beautiful Notion blocks
|
47
|
+
- **Async-First**: Built for modern Python with full async/await support and high performance
|
48
|
+
- **AI-Ready**: Perfect foundation for AI agents that generate and manage Notion content
|
49
|
+
- **Round-Trip**: Read existing content, modify it, and write it back while preserving formatting
|
50
|
+
|
51
|
+
---
|
52
|
+
|
53
|
+
## Quick Start
|
54
|
+
|
55
|
+
```bash
|
56
|
+
pip install notionary
|
57
|
+
```
|
58
|
+
|
59
|
+
Set up your [Notion integration](https://www.notion.so/profile/integrations) and add your token:
|
60
|
+
|
61
|
+
```bash
|
62
|
+
NOTION_SECRET=your_integration_key
|
63
|
+
```
|
64
|
+
|
65
|
+
### Simple Flow: Find → Create → Update
|
66
|
+
|
67
|
+
```python
|
68
|
+
import asyncio
|
69
|
+
from notionary import NotionPage, NotionDatabase
|
70
|
+
|
71
|
+
async def main():
|
72
|
+
# Work with pages - find by name, no exact match needed!
|
73
|
+
page = await NotionPage.from_page_name("Meeting Notes")
|
74
|
+
|
75
|
+
# Direct Markdown - quick & intuitive
|
76
|
+
await page.append_markdown("""
|
77
|
+
## Action Items
|
78
|
+
- Review project proposal
|
79
|
+
- Schedule team meeting
|
80
|
+
- Update documentation
|
81
|
+
|
82
|
+
[callout](Important meeting decisions require follow-up "💡")
|
83
|
+
""")
|
84
|
+
|
85
|
+
# Builder Pattern - type-safe & powerful for complex layouts
|
86
|
+
await page.append_markdown(lambda builder: (
|
87
|
+
builder
|
88
|
+
.h2("Project Status")
|
89
|
+
.callout("Project milestone reached!", "🎉")
|
90
|
+
.columns(
|
91
|
+
lambda col: (col
|
92
|
+
.h3("Completed")
|
93
|
+
.bulleted_list(["API design", "Database setup", "Authentication"])
|
94
|
+
),
|
95
|
+
lambda col: (col
|
96
|
+
.h3("In Progress")
|
97
|
+
.bulleted_list(["Frontend UI", "Testing", "Documentation"])
|
98
|
+
)
|
99
|
+
)
|
100
|
+
.table(
|
101
|
+
headers=["Task", "Owner", "Due Date"],
|
102
|
+
rows=[
|
103
|
+
["Launch prep", "Alice", "2024-03-15"],
|
104
|
+
["Marketing", "Bob", "2024-03-20"]
|
105
|
+
]
|
106
|
+
)
|
107
|
+
))
|
108
|
+
|
109
|
+
asyncio.run(main())
|
110
|
+
```
|
111
|
+
|
112
|
+
### Create Rich Database Entries
|
113
|
+
|
114
|
+
```python
|
115
|
+
# Work with databases - connect and create styled entries
|
116
|
+
db = await NotionDatabase.from_database_name("Projects")
|
117
|
+
|
118
|
+
# Create new project with full styling
|
119
|
+
project = await db.create_blank_page()
|
120
|
+
await project.set_title("New Marketing Campaign")
|
121
|
+
await project.set_emoji_icon("🚀")
|
122
|
+
await project.set_random_gradient_cover()
|
123
|
+
|
124
|
+
# Set database properties
|
125
|
+
await project.set_property_value_by_name("Status", "Planning")
|
126
|
+
await project.set_property_value_by_name("Priority", "High")
|
127
|
+
await project.set_property_value_by_name("Team Lead", "sarah@company.com")
|
128
|
+
|
129
|
+
# Add rich content to the new page
|
130
|
+
await project.replace_content(lambda builder: (
|
131
|
+
builder
|
132
|
+
.h1("Campaign Overview")
|
133
|
+
.callout("New marketing initiative targeting Q2 growth", "🎯")
|
134
|
+
.h2("Goals & Objectives")
|
135
|
+
.numbered_list([
|
136
|
+
"Increase brand awareness by 25%",
|
137
|
+
"Generate 500 qualified leads",
|
138
|
+
"Launch in 3 target markets"
|
139
|
+
])
|
140
|
+
.h2("Budget Breakdown")
|
141
|
+
.table(
|
142
|
+
headers=["Category", "Allocated", "Spent", "Remaining"],
|
143
|
+
rows=[
|
144
|
+
["Digital Ads", "$15,000", "$3,200", "$11,800"],
|
145
|
+
["Content Creation", "$8,000", "$1,500", "$6,500"],
|
146
|
+
["Events", "$12,000", "$0", "$12,000"]
|
147
|
+
]
|
148
|
+
)
|
149
|
+
.divider()
|
150
|
+
.toggle("Technical Requirements", lambda toggle: (
|
151
|
+
toggle
|
152
|
+
.paragraph("Platform specifications and integration details.")
|
153
|
+
.bulleted_list([
|
154
|
+
"CRM integration with Salesforce",
|
155
|
+
"Analytics tracking setup",
|
156
|
+
"Landing page development"
|
157
|
+
])
|
158
|
+
))
|
159
|
+
))
|
160
|
+
|
161
|
+
print(f"✅ Created styled project: {project.url}")
|
162
|
+
```
|
163
|
+
|
164
|
+
### Extended Markdown Syntax
|
165
|
+
|
166
|
+
Notionary supports rich formatting with callouts, toggles, multi-column layouts, tables, media embeds, and more. Use either direct markdown syntax or the type-safe builder pattern.
|
167
|
+
|
168
|
+
See the complete [Block Types documentation](https://mathisarends.github.io/notionary/blocks/) for all available formatting options and syntax examples.
|
169
|
+
|
170
|
+
## What You Can Build
|
171
|
+
|
172
|
+
- **AI Content Generation** - Perfect for AI agents that create structured reports and documentation
|
173
|
+
- **Workflow Automation** - Update project status, sync data between databases, generate reports
|
174
|
+
- **Dynamic Documentation** - Auto-generate team docs, API references, and knowledge bases
|
175
|
+
- **Content Management** - Bulk page updates, template generation, and content migration
|
176
|
+
|
177
|
+
## Core Features
|
178
|
+
|
179
|
+
| Feature | Description |
|
180
|
+
| -------------------- | ------------------------------------------------------ |
|
181
|
+
| **Smart Discovery** | Find pages/databases by name with fuzzy matching |
|
182
|
+
| **Rich Markdown** | Extended syntax for callouts, toggles, columns, tables |
|
183
|
+
| **Async-First** | Modern Python with full async/await support |
|
184
|
+
| **Round-Trip** | Read content as markdown, edit, and write back |
|
185
|
+
| **AI-Ready** | Generate system prompts for AI content creation |
|
186
|
+
| **All Block Types** | Support for every Notion block type |
|
187
|
+
| **Type Safety** | Full type hints for better IDE support |
|
188
|
+
| **High Performance** | Efficient batch operations and caching |
|
189
|
+
|
190
|
+
## Examples & Documentation
|
191
|
+
|
192
|
+
Explore comprehensive guides and real-world examples:
|
193
|
+
|
194
|
+
- **[📖 Full Documentation](https://mathisarends.github.io/notionary/)** - Complete API reference and guides
|
195
|
+
- **[Getting Started](https://mathisarends.github.io/notionary/get-started/)** - Quick setup and first steps
|
196
|
+
- **[Page Management](https://mathisarends.github.io/notionary/page/)** - Work with page content and properties
|
197
|
+
- **[Database Operations](https://mathisarends.github.io/notionary/database/)** - Query and manage databases
|
198
|
+
- **[Block Types](https://mathisarends.github.io/notionary/blocks/)** - Complete formatting reference
|
199
|
+
|
200
|
+
Check out the `examples/` directory for hands-on tutorials:
|
201
|
+
|
202
|
+
### Core Examples
|
203
|
+
|
204
|
+
- **[Page Management](examples/page_example.py)** - Create, update, and manage pages
|
205
|
+
- **[Database Operations](examples/database.py)** - Connect to and query databases
|
206
|
+
- **[Workspace Discovery](examples/workspace_discovery.py)** - Explore your workspace
|
207
|
+
|
208
|
+
### Markdown Examples
|
209
|
+
|
210
|
+
- **[Basic Formatting](examples/markdown/basic.py)** - Text, lists, and links
|
211
|
+
- **[Callouts](examples/markdown/callout.py)** - Eye-catching information boxes
|
212
|
+
- **[Toggles](examples/markdown/toggle.py)** - Collapsible content sections
|
213
|
+
- **[Multi-Column](examples/markdown/columns.py)** - Side-by-side layouts
|
214
|
+
- **[Tables](examples/markdown/table.py)** - Structured data presentation
|
215
|
+
|
216
|
+
## Contributing
|
217
|
+
|
218
|
+
We'd love your help making Notionary even better!
|
219
|
+
|
220
|
+
Whether it's fixing bugs, adding features, improving docs, or sharing examples - all contributions are welcome.
|
221
|
+
|
222
|
+
See our [Contributing Guide](https://mathisarends.github.io/notionary/contributing/) to get started.
|
223
|
+
|
224
|
+
---
|
225
|
+
|
226
|
+
<div align="center">
|
227
|
+
|
228
|
+
**Ready to transform your Notion workflow?**
|
229
|
+
|
230
|
+
[📖 Read the Docs](https://mathisarends.github.io/notionary/) • [Getting Started](https://mathisarends.github.io/notionary/get-started/) • [Examples](examples/)
|
231
|
+
|
232
|
+
Built with ❤️ for the Python community
|
233
|
+
|
234
|
+
</div>
|
235
|
+
|
@@ -0,0 +1,212 @@
|
|
1
|
+
<picture>
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="./static/notionary-dark.png">
|
3
|
+
<source media="(prefers-color-scheme: light)" srcset="./static/notionary-light.png">
|
4
|
+
<img alt="Notionary logo: dark mode shows a white logo, light mode shows a black logo." src="./static/browser-use.png" width="full">
|
5
|
+
</picture>
|
6
|
+
|
7
|
+
<h1 align="center">Notion API simplified for Python developers 🐍</h1>
|
8
|
+
|
9
|
+
<div align="center">
|
10
|
+
|
11
|
+
[](https://www.python.org/downloads/)
|
12
|
+
[](LICENSE)
|
13
|
+
[](https://mathisarends.github.io/notionary/)
|
14
|
+
|
15
|
+
Transform complex Notion API interactions into simple, Pythonic code. Build AI agents, automate workflows, and create dynamic content with ease.
|
16
|
+
|
17
|
+
</div>
|
18
|
+
|
19
|
+
---
|
20
|
+
|
21
|
+
## Why Notionary?
|
22
|
+
|
23
|
+
- **Smart Discovery**: Find pages and databases by name—no more hunting for URLs or IDs
|
24
|
+
- **Rich Markdown**: Convert extended Markdown (callouts, toggles, columns) directly into beautiful Notion blocks
|
25
|
+
- **Async-First**: Built for modern Python with full async/await support and high performance
|
26
|
+
- **AI-Ready**: Perfect foundation for AI agents that generate and manage Notion content
|
27
|
+
- **Round-Trip**: Read existing content, modify it, and write it back while preserving formatting
|
28
|
+
|
29
|
+
---
|
30
|
+
|
31
|
+
## Quick Start
|
32
|
+
|
33
|
+
```bash
|
34
|
+
pip install notionary
|
35
|
+
```
|
36
|
+
|
37
|
+
Set up your [Notion integration](https://www.notion.so/profile/integrations) and add your token:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
NOTION_SECRET=your_integration_key
|
41
|
+
```
|
42
|
+
|
43
|
+
### Simple Flow: Find → Create → Update
|
44
|
+
|
45
|
+
```python
|
46
|
+
import asyncio
|
47
|
+
from notionary import NotionPage, NotionDatabase
|
48
|
+
|
49
|
+
async def main():
|
50
|
+
# Work with pages - find by name, no exact match needed!
|
51
|
+
page = await NotionPage.from_page_name("Meeting Notes")
|
52
|
+
|
53
|
+
# Direct Markdown - quick & intuitive
|
54
|
+
await page.append_markdown("""
|
55
|
+
## Action Items
|
56
|
+
- Review project proposal
|
57
|
+
- Schedule team meeting
|
58
|
+
- Update documentation
|
59
|
+
|
60
|
+
[callout](Important meeting decisions require follow-up "💡")
|
61
|
+
""")
|
62
|
+
|
63
|
+
# Builder Pattern - type-safe & powerful for complex layouts
|
64
|
+
await page.append_markdown(lambda builder: (
|
65
|
+
builder
|
66
|
+
.h2("Project Status")
|
67
|
+
.callout("Project milestone reached!", "🎉")
|
68
|
+
.columns(
|
69
|
+
lambda col: (col
|
70
|
+
.h3("Completed")
|
71
|
+
.bulleted_list(["API design", "Database setup", "Authentication"])
|
72
|
+
),
|
73
|
+
lambda col: (col
|
74
|
+
.h3("In Progress")
|
75
|
+
.bulleted_list(["Frontend UI", "Testing", "Documentation"])
|
76
|
+
)
|
77
|
+
)
|
78
|
+
.table(
|
79
|
+
headers=["Task", "Owner", "Due Date"],
|
80
|
+
rows=[
|
81
|
+
["Launch prep", "Alice", "2024-03-15"],
|
82
|
+
["Marketing", "Bob", "2024-03-20"]
|
83
|
+
]
|
84
|
+
)
|
85
|
+
))
|
86
|
+
|
87
|
+
asyncio.run(main())
|
88
|
+
```
|
89
|
+
|
90
|
+
### Create Rich Database Entries
|
91
|
+
|
92
|
+
```python
|
93
|
+
# Work with databases - connect and create styled entries
|
94
|
+
db = await NotionDatabase.from_database_name("Projects")
|
95
|
+
|
96
|
+
# Create new project with full styling
|
97
|
+
project = await db.create_blank_page()
|
98
|
+
await project.set_title("New Marketing Campaign")
|
99
|
+
await project.set_emoji_icon("🚀")
|
100
|
+
await project.set_random_gradient_cover()
|
101
|
+
|
102
|
+
# Set database properties
|
103
|
+
await project.set_property_value_by_name("Status", "Planning")
|
104
|
+
await project.set_property_value_by_name("Priority", "High")
|
105
|
+
await project.set_property_value_by_name("Team Lead", "sarah@company.com")
|
106
|
+
|
107
|
+
# Add rich content to the new page
|
108
|
+
await project.replace_content(lambda builder: (
|
109
|
+
builder
|
110
|
+
.h1("Campaign Overview")
|
111
|
+
.callout("New marketing initiative targeting Q2 growth", "🎯")
|
112
|
+
.h2("Goals & Objectives")
|
113
|
+
.numbered_list([
|
114
|
+
"Increase brand awareness by 25%",
|
115
|
+
"Generate 500 qualified leads",
|
116
|
+
"Launch in 3 target markets"
|
117
|
+
])
|
118
|
+
.h2("Budget Breakdown")
|
119
|
+
.table(
|
120
|
+
headers=["Category", "Allocated", "Spent", "Remaining"],
|
121
|
+
rows=[
|
122
|
+
["Digital Ads", "$15,000", "$3,200", "$11,800"],
|
123
|
+
["Content Creation", "$8,000", "$1,500", "$6,500"],
|
124
|
+
["Events", "$12,000", "$0", "$12,000"]
|
125
|
+
]
|
126
|
+
)
|
127
|
+
.divider()
|
128
|
+
.toggle("Technical Requirements", lambda toggle: (
|
129
|
+
toggle
|
130
|
+
.paragraph("Platform specifications and integration details.")
|
131
|
+
.bulleted_list([
|
132
|
+
"CRM integration with Salesforce",
|
133
|
+
"Analytics tracking setup",
|
134
|
+
"Landing page development"
|
135
|
+
])
|
136
|
+
))
|
137
|
+
))
|
138
|
+
|
139
|
+
print(f"✅ Created styled project: {project.url}")
|
140
|
+
```
|
141
|
+
|
142
|
+
### Extended Markdown Syntax
|
143
|
+
|
144
|
+
Notionary supports rich formatting with callouts, toggles, multi-column layouts, tables, media embeds, and more. Use either direct markdown syntax or the type-safe builder pattern.
|
145
|
+
|
146
|
+
See the complete [Block Types documentation](https://mathisarends.github.io/notionary/blocks/) for all available formatting options and syntax examples.
|
147
|
+
|
148
|
+
## What You Can Build
|
149
|
+
|
150
|
+
- **AI Content Generation** - Perfect for AI agents that create structured reports and documentation
|
151
|
+
- **Workflow Automation** - Update project status, sync data between databases, generate reports
|
152
|
+
- **Dynamic Documentation** - Auto-generate team docs, API references, and knowledge bases
|
153
|
+
- **Content Management** - Bulk page updates, template generation, and content migration
|
154
|
+
|
155
|
+
## Core Features
|
156
|
+
|
157
|
+
| Feature | Description |
|
158
|
+
| -------------------- | ------------------------------------------------------ |
|
159
|
+
| **Smart Discovery** | Find pages/databases by name with fuzzy matching |
|
160
|
+
| **Rich Markdown** | Extended syntax for callouts, toggles, columns, tables |
|
161
|
+
| **Async-First** | Modern Python with full async/await support |
|
162
|
+
| **Round-Trip** | Read content as markdown, edit, and write back |
|
163
|
+
| **AI-Ready** | Generate system prompts for AI content creation |
|
164
|
+
| **All Block Types** | Support for every Notion block type |
|
165
|
+
| **Type Safety** | Full type hints for better IDE support |
|
166
|
+
| **High Performance** | Efficient batch operations and caching |
|
167
|
+
|
168
|
+
## Examples & Documentation
|
169
|
+
|
170
|
+
Explore comprehensive guides and real-world examples:
|
171
|
+
|
172
|
+
- **[📖 Full Documentation](https://mathisarends.github.io/notionary/)** - Complete API reference and guides
|
173
|
+
- **[Getting Started](https://mathisarends.github.io/notionary/get-started/)** - Quick setup and first steps
|
174
|
+
- **[Page Management](https://mathisarends.github.io/notionary/page/)** - Work with page content and properties
|
175
|
+
- **[Database Operations](https://mathisarends.github.io/notionary/database/)** - Query and manage databases
|
176
|
+
- **[Block Types](https://mathisarends.github.io/notionary/blocks/)** - Complete formatting reference
|
177
|
+
|
178
|
+
Check out the `examples/` directory for hands-on tutorials:
|
179
|
+
|
180
|
+
### Core Examples
|
181
|
+
|
182
|
+
- **[Page Management](examples/page_example.py)** - Create, update, and manage pages
|
183
|
+
- **[Database Operations](examples/database.py)** - Connect to and query databases
|
184
|
+
- **[Workspace Discovery](examples/workspace_discovery.py)** - Explore your workspace
|
185
|
+
|
186
|
+
### Markdown Examples
|
187
|
+
|
188
|
+
- **[Basic Formatting](examples/markdown/basic.py)** - Text, lists, and links
|
189
|
+
- **[Callouts](examples/markdown/callout.py)** - Eye-catching information boxes
|
190
|
+
- **[Toggles](examples/markdown/toggle.py)** - Collapsible content sections
|
191
|
+
- **[Multi-Column](examples/markdown/columns.py)** - Side-by-side layouts
|
192
|
+
- **[Tables](examples/markdown/table.py)** - Structured data presentation
|
193
|
+
|
194
|
+
## Contributing
|
195
|
+
|
196
|
+
We'd love your help making Notionary even better!
|
197
|
+
|
198
|
+
Whether it's fixing bugs, adding features, improving docs, or sharing examples - all contributions are welcome.
|
199
|
+
|
200
|
+
See our [Contributing Guide](https://mathisarends.github.io/notionary/contributing/) to get started.
|
201
|
+
|
202
|
+
---
|
203
|
+
|
204
|
+
<div align="center">
|
205
|
+
|
206
|
+
**Ready to transform your Notion workflow?**
|
207
|
+
|
208
|
+
[📖 Read the Docs](https://mathisarends.github.io/notionary/) • [Getting Started](https://mathisarends.github.io/notionary/get-started/) • [Examples](examples/)
|
209
|
+
|
210
|
+
Built with ❤️ for the Python community
|
211
|
+
|
212
|
+
</div>
|
@@ -34,6 +34,7 @@ def bootstrap_blocks() -> None:
|
|
34
34
|
toggle,
|
35
35
|
toggleable_heading,
|
36
36
|
video,
|
37
|
+
child_database,
|
37
38
|
)
|
38
39
|
|
39
40
|
# Collect all exports from modules
|
@@ -61,6 +62,7 @@ def bootstrap_blocks() -> None:
|
|
61
62
|
video,
|
62
63
|
toggleable_heading,
|
63
64
|
table_of_contents,
|
65
|
+
child_database,
|
64
66
|
):
|
65
67
|
ns.update(vars(m))
|
66
68
|
|
@@ -123,6 +125,10 @@ def bootstrap_blocks() -> None:
|
|
123
125
|
from notionary.blocks.toggle.toggle_models import CreateToggleBlock, ToggleBlock
|
124
126
|
from notionary.blocks.types import BlockType
|
125
127
|
from notionary.blocks.video.video_element_models import CreateVideoBlock
|
128
|
+
from notionary.blocks.child_database.child_database_models import (
|
129
|
+
CreateChildDatabaseBlock,
|
130
|
+
ChildDatabaseBlock,
|
131
|
+
)
|
126
132
|
|
127
133
|
# Define the Union types that are needed for model rebuilding
|
128
134
|
BlockCreateRequest = Union[
|
@@ -150,9 +156,10 @@ def bootstrap_blocks() -> None:
|
|
150
156
|
CreateVideoBlock,
|
151
157
|
CreateTableOfContentsBlock,
|
152
158
|
CreatePdfBlock,
|
159
|
+
CreateChildDatabaseBlock,
|
153
160
|
]
|
154
161
|
|
155
|
-
BlockCreateResult = Optional[
|
162
|
+
BlockCreateResult = Optional[BlockCreateRequest]
|
156
163
|
|
157
164
|
# Add all block types to namespace
|
158
165
|
ns.update(
|
@@ -202,6 +209,7 @@ def bootstrap_blocks() -> None:
|
|
202
209
|
"CreateVideoBlock": CreateVideoBlock,
|
203
210
|
"TableOfContentsBlock": TableOfContentsBlock,
|
204
211
|
"CreateTableOfContentsBlock": CreateTableOfContentsBlock,
|
212
|
+
"ChildDatabaseBlock": ChildDatabaseBlock,
|
205
213
|
# Add the Union types
|
206
214
|
"BlockCreateRequest": BlockCreateRequest,
|
207
215
|
"BlockCreateResult": BlockCreateResult,
|
@@ -0,0 +1,115 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
from notionary.blocks.audio.audio_models import CreateAudioBlock
|
7
|
+
from notionary.blocks.base_block_element import BaseBlockElement
|
8
|
+
from notionary.blocks.file.file_element_models import ExternalFile, FileBlock, FileType
|
9
|
+
from notionary.blocks.mixins.captions import CaptionMixin
|
10
|
+
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
11
|
+
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
12
|
+
|
13
|
+
|
14
|
+
class AudioElement(BaseBlockElement, CaptionMixin):
|
15
|
+
"""
|
16
|
+
Handles conversion between Markdown audio embeds and Notion audio blocks.
|
17
|
+
|
18
|
+
Markdown audio syntax:
|
19
|
+
- [audio](https://example.com/audio.mp3) - Simple audio embed
|
20
|
+
- [audio](https://example.com/audio.mp3)(caption:Episode 1) - Audio with caption
|
21
|
+
- (caption:Background music)[audio](https://example.com/song.mp3) - caption before URL
|
22
|
+
|
23
|
+
Where:
|
24
|
+
- URL is the required audio file URL
|
25
|
+
- Caption supports rich text formatting and is optional
|
26
|
+
"""
|
27
|
+
|
28
|
+
# Simple pattern that matches just the audio link, CaptionMixin handles caption separately
|
29
|
+
AUDIO_PATTERN = re.compile(r"\[audio\]\((https?://[^\s\"]+)\)")
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def _extract_audio_url(cls, text: str) -> Optional[str]:
|
33
|
+
"""Extract audio URL from text, handling caption patterns."""
|
34
|
+
# First remove any captions to get clean text for URL extraction
|
35
|
+
clean_text = cls.remove_caption(text)
|
36
|
+
|
37
|
+
# Now extract the URL from clean text
|
38
|
+
match = cls.AUDIO_PATTERN.search(clean_text)
|
39
|
+
if match:
|
40
|
+
return match.group(1)
|
41
|
+
|
42
|
+
return None
|
43
|
+
|
44
|
+
SUPPORTED_EXTENSIONS = {".mp3", ".wav", ".ogg", ".oga", ".m4a"}
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def match_notion(cls, block: Block) -> bool:
|
48
|
+
"""Check if this element can handle the given Notion block."""
|
49
|
+
return block.type == BlockType.AUDIO
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
53
|
+
"""Convert markdown audio embed to Notion audio block."""
|
54
|
+
# Use our helper method to extract the URL
|
55
|
+
url = cls._extract_audio_url(text.strip())
|
56
|
+
if not url:
|
57
|
+
return None
|
58
|
+
|
59
|
+
if not cls._is_likely_audio_url(url):
|
60
|
+
return None
|
61
|
+
|
62
|
+
# Use mixin to extract caption (if present anywhere in text)
|
63
|
+
caption_text = cls.extract_caption(text.strip())
|
64
|
+
caption_rich_text = cls.build_caption_rich_text(caption_text or "")
|
65
|
+
|
66
|
+
audio_content = FileBlock(
|
67
|
+
type=FileType.EXTERNAL,
|
68
|
+
external=ExternalFile(url=url),
|
69
|
+
caption=caption_rich_text,
|
70
|
+
)
|
71
|
+
|
72
|
+
return CreateAudioBlock(audio=audio_content)
|
73
|
+
|
74
|
+
@classmethod
|
75
|
+
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
76
|
+
"""Convert Notion audio block to markdown audio embed."""
|
77
|
+
if block.type != BlockType.AUDIO or block.audio is None:
|
78
|
+
return None
|
79
|
+
|
80
|
+
audio = block.audio
|
81
|
+
|
82
|
+
# Only handle external audio
|
83
|
+
if audio.type != FileType.EXTERNAL or audio.external is None:
|
84
|
+
return None
|
85
|
+
url = audio.external.url
|
86
|
+
if not url:
|
87
|
+
return None
|
88
|
+
|
89
|
+
result = f"[audio]({url})"
|
90
|
+
|
91
|
+
# Add caption if present
|
92
|
+
caption_markdown = await cls.format_caption_for_markdown(audio.caption or [])
|
93
|
+
if caption_markdown:
|
94
|
+
result += caption_markdown
|
95
|
+
|
96
|
+
return result
|
97
|
+
|
98
|
+
@classmethod
|
99
|
+
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
100
|
+
"""Get system prompt information for audio blocks."""
|
101
|
+
return BlockElementMarkdownInformation(
|
102
|
+
block_type=cls.__name__,
|
103
|
+
description="Audio blocks embed audio files from external URLs with optional captions",
|
104
|
+
syntax_examples=[
|
105
|
+
"[audio](https://example.com/song.mp3)",
|
106
|
+
"[audio](https://example.com/podcast.wav)(caption:Episode 1)",
|
107
|
+
"(caption:Background music)[audio](https://soundcloud.com/track/123)",
|
108
|
+
"[audio](https://example.com/interview.mp3)(caption:**Live** interview)",
|
109
|
+
],
|
110
|
+
usage_guidelines="Use for embedding audio files like music, podcasts, or sound effects. Supports common audio formats (mp3, wav, ogg, m4a). Caption supports rich text formatting and is optional.",
|
111
|
+
)
|
112
|
+
|
113
|
+
@classmethod
|
114
|
+
def _is_likely_audio_url(cls, url: str) -> bool:
|
115
|
+
return any(url.lower().endswith(ext) for ext in cls.SUPPORTED_EXTENSIONS)
|
@@ -5,6 +5,7 @@ from typing import Optional
|
|
5
5
|
from pydantic import BaseModel
|
6
6
|
|
7
7
|
from notionary.markdown.markdown_node import MarkdownNode
|
8
|
+
from notionary.blocks.mixins.captions import CaptionMarkdownNodeMixin
|
8
9
|
|
9
10
|
|
10
11
|
class AudioMarkdownBlockParams(BaseModel):
|
@@ -12,7 +13,7 @@ class AudioMarkdownBlockParams(BaseModel):
|
|
12
13
|
caption: Optional[str] = None
|
13
14
|
|
14
15
|
|
15
|
-
class AudioMarkdownNode(MarkdownNode):
|
16
|
+
class AudioMarkdownNode(MarkdownNode, CaptionMarkdownNodeMixin):
|
16
17
|
"""
|
17
18
|
Programmatic interface for creating Notion-style audio blocks.
|
18
19
|
"""
|
@@ -26,6 +27,11 @@ class AudioMarkdownNode(MarkdownNode):
|
|
26
27
|
return cls(url=params.url, caption=params.caption)
|
27
28
|
|
28
29
|
def to_markdown(self) -> str:
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
"""Return the Markdown representation.
|
31
|
+
|
32
|
+
Examples:
|
33
|
+
- [audio](https://example.com/song.mp3)
|
34
|
+
- [audio](https://example.com/song.mp3)(caption:Background music)
|
35
|
+
"""
|
36
|
+
base_markdown = f"[audio]({self.url})"
|
37
|
+
return self.append_caption_to_markdown(base_markdown, self.caption)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from abc import ABC
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
7
|
+
from notionary.blocks.models import Block, BlockCreateResult
|
8
|
+
|
9
|
+
|
10
|
+
class BaseBlockElement(ABC):
|
11
|
+
"""Base class for elements that can be converted between Markdown and Notion."""
|
12
|
+
|
13
|
+
@classmethod
|
14
|
+
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
15
|
+
"""
|
16
|
+
Convert markdown to Notion block content.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
- BlockContent: Single block content (e.g., ToDoBlock, ParagraphBlock)
|
20
|
+
- list[BlockContent]: Multiple block contents
|
21
|
+
- None: Cannot convert this markdown
|
22
|
+
"""
|
23
|
+
|
24
|
+
@classmethod
|
25
|
+
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
26
|
+
"""Convert Notion block to markdown."""
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def match_notion(cls, block: Block) -> bool:
|
30
|
+
"""Check if this element can handle the given Notion block."""
|
31
|
+
# Default implementation - subclasses should override this method
|
32
|
+
# Cannot call async notion_to_markdown here
|
33
|
+
return False
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
37
|
+
"""Get system prompt information for this block element.
|
38
|
+
|
39
|
+
Subclasses should override this method to provide their specific information.
|
40
|
+
Return None if the element should not be included in documentation.
|
41
|
+
"""
|
42
|
+
return None
|