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.
- notionary/__init__.py +5 -20
- notionary/blocks/__init__.py +4 -4
- notionary/blocks/client.py +90 -216
- notionary/blocks/enums.py +167 -0
- notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
- notionary/blocks/rich_text/models.py +178 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
- notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
- notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
- notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
- notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
- notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
- notionary/blocks/rich_text/rich_text_markdown_converter.py +144 -0
- notionary/blocks/rich_text/rich_text_patterns.py +42 -0
- notionary/blocks/schemas.py +778 -0
- notionary/comments/__init__.py +1 -22
- notionary/comments/client.py +52 -187
- notionary/comments/factory.py +38 -0
- notionary/comments/models.py +5 -127
- notionary/comments/schemas.py +240 -0
- notionary/comments/service.py +34 -0
- notionary/data_source/http/client.py +11 -0
- notionary/data_source/http/data_source_instance_client.py +104 -0
- notionary/data_source/properties/schemas.py +402 -0
- notionary/data_source/query/builder.py +448 -0
- notionary/data_source/query/resolver.py +114 -0
- notionary/data_source/query/schema.py +302 -0
- notionary/data_source/query/validator.py +73 -0
- notionary/data_source/schema/registry.py +104 -0
- notionary/data_source/schema/service.py +136 -0
- notionary/data_source/schemas.py +27 -0
- notionary/data_source/service.py +377 -0
- notionary/database/client.py +30 -135
- notionary/database/database_metadata_update_client.py +19 -0
- notionary/database/schemas.py +29 -0
- notionary/database/service.py +168 -0
- notionary/exceptions/__init__.py +33 -0
- notionary/exceptions/api.py +41 -0
- notionary/exceptions/base.py +2 -0
- notionary/exceptions/block_parsing.py +16 -0
- notionary/exceptions/data_source/__init__.py +6 -0
- notionary/exceptions/data_source/builder.py +182 -0
- notionary/exceptions/data_source/properties.py +34 -0
- notionary/exceptions/properties.py +58 -0
- notionary/exceptions/search.py +57 -0
- notionary/file_upload/client.py +18 -30
- notionary/file_upload/models.py +7 -8
- notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
- notionary/http/client.py +204 -0
- notionary/http/models.py +50 -0
- notionary/page/blocks/client.py +1 -0
- notionary/page/content/factory.py +73 -0
- notionary/page/content/markdown/__init__.py +5 -0
- notionary/page/content/markdown/builder.py +226 -0
- notionary/page/content/markdown/nodes/__init__.py +52 -0
- notionary/page/content/markdown/nodes/audio.py +23 -0
- notionary/page/content/markdown/nodes/base.py +12 -0
- notionary/page/content/markdown/nodes/bookmark.py +25 -0
- notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
- notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
- notionary/page/content/markdown/nodes/callout.py +34 -0
- notionary/page/content/markdown/nodes/code.py +28 -0
- notionary/page/content/markdown/nodes/columns.py +69 -0
- notionary/page/content/markdown/nodes/container.py +64 -0
- notionary/page/content/markdown/nodes/divider.py +14 -0
- notionary/page/content/markdown/nodes/embed.py +23 -0
- notionary/page/content/markdown/nodes/equation.py +19 -0
- notionary/page/content/markdown/nodes/file.py +23 -0
- notionary/page/content/markdown/nodes/heading.py +36 -0
- notionary/page/content/markdown/nodes/image.py +23 -0
- notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
- notionary/page/content/markdown/nodes/numbered_list.py +38 -0
- notionary/page/content/markdown/nodes/paragraph.py +14 -0
- notionary/page/content/markdown/nodes/pdf.py +23 -0
- notionary/page/content/markdown/nodes/quote.py +27 -0
- notionary/page/content/markdown/nodes/space.py +14 -0
- notionary/page/content/markdown/nodes/table.py +45 -0
- notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
- notionary/page/content/markdown/nodes/todo.py +38 -0
- notionary/page/content/markdown/nodes/toggle.py +27 -0
- notionary/page/content/markdown/nodes/video.py +23 -0
- notionary/page/content/parser/context.py +126 -0
- notionary/page/content/parser/factory.py +210 -0
- notionary/page/content/parser/parsers/__init__.py +58 -0
- notionary/page/content/parser/parsers/audio.py +40 -0
- notionary/page/content/parser/parsers/base.py +30 -0
- notionary/page/content/parser/parsers/bookmark.py +33 -0
- notionary/page/content/parser/parsers/breadcrumb.py +33 -0
- notionary/page/content/parser/parsers/bulleted_list.py +85 -0
- notionary/page/content/parser/parsers/callout.py +100 -0
- notionary/page/content/parser/parsers/caption.py +55 -0
- notionary/page/content/parser/parsers/code.py +81 -0
- notionary/page/content/parser/parsers/column.py +76 -0
- notionary/page/content/parser/parsers/column_list.py +81 -0
- notionary/page/content/parser/parsers/divider.py +33 -0
- notionary/page/content/parser/parsers/embed.py +33 -0
- notionary/page/content/parser/parsers/equation.py +65 -0
- notionary/page/content/parser/parsers/file.py +42 -0
- notionary/page/content/parser/parsers/heading.py +115 -0
- notionary/page/content/parser/parsers/image.py +42 -0
- notionary/page/content/parser/parsers/numbered_list.py +89 -0
- notionary/page/content/parser/parsers/paragraph.py +37 -0
- notionary/page/content/parser/parsers/pdf.py +42 -0
- notionary/page/content/parser/parsers/quote.py +125 -0
- notionary/page/content/parser/parsers/space.py +41 -0
- notionary/page/content/parser/parsers/table.py +144 -0
- notionary/page/content/parser/parsers/table_of_contents.py +32 -0
- notionary/page/content/parser/parsers/todo.py +96 -0
- notionary/page/content/parser/parsers/toggle.py +70 -0
- notionary/page/content/parser/parsers/video.py +42 -0
- notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length.py +95 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -0
- notionary/page/content/parser/post_processing/port.py +9 -0
- notionary/page/content/parser/post_processing/service.py +16 -0
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +11 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
- notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
- notionary/page/content/parser/pre_processsing/service.py +15 -0
- notionary/page/content/parser/service.py +78 -0
- notionary/page/content/renderer/context.py +51 -0
- notionary/page/content/renderer/factory.py +231 -0
- notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
- notionary/page/content/renderer/post_processing/port.py +7 -0
- notionary/page/content/renderer/post_processing/service.py +15 -0
- notionary/page/content/renderer/renderers/__init__.py +55 -0
- notionary/page/content/renderer/renderers/audio.py +31 -0
- notionary/page/content/renderer/renderers/base.py +31 -0
- notionary/page/content/renderer/renderers/bookmark.py +25 -0
- notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
- notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
- notionary/page/content/renderer/renderers/callout.py +50 -0
- notionary/page/content/renderer/renderers/captioned_block.py +58 -0
- notionary/page/content/renderer/renderers/code.py +34 -0
- notionary/page/content/renderer/renderers/column.py +53 -0
- notionary/page/content/renderer/renderers/column_list.py +44 -0
- notionary/page/content/renderer/renderers/divider.py +22 -0
- notionary/page/content/renderer/renderers/embed.py +25 -0
- notionary/page/content/renderer/renderers/equation.py +37 -0
- notionary/page/content/renderer/renderers/fallback.py +24 -0
- notionary/page/content/renderer/renderers/file.py +40 -0
- notionary/page/content/renderer/renderers/heading.py +95 -0
- notionary/page/content/renderer/renderers/image.py +31 -0
- notionary/page/content/renderer/renderers/numbered_list.py +42 -0
- notionary/page/content/renderer/renderers/paragraph.py +40 -0
- notionary/page/content/renderer/renderers/pdf.py +31 -0
- notionary/page/content/renderer/renderers/quote.py +49 -0
- notionary/page/content/renderer/renderers/table.py +115 -0
- notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
- notionary/page/content/renderer/renderers/table_row.py +17 -0
- notionary/page/content/renderer/renderers/todo.py +56 -0
- notionary/page/content/renderer/renderers/toggle.py +52 -0
- notionary/page/content/renderer/renderers/video.py +31 -0
- notionary/page/content/renderer/service.py +50 -0
- notionary/page/content/service.py +68 -0
- notionary/page/content/syntax/__init__.py +4 -0
- notionary/page/content/syntax/grammar.py +10 -0
- notionary/page/content/syntax/models.py +66 -0
- notionary/page/content/syntax/registry.py +393 -0
- notionary/page/page_context.py +7 -16
- notionary/page/page_http_client.py +15 -0
- notionary/page/page_metadata_update_client.py +19 -0
- notionary/page/properties/client.py +144 -0
- notionary/page/properties/factory.py +26 -0
- notionary/page/properties/models.py +308 -0
- notionary/page/properties/service.py +261 -0
- notionary/page/schemas.py +13 -0
- notionary/page/service.py +225 -0
- notionary/shared/entity/client.py +29 -0
- notionary/shared/entity/dto_parsers.py +53 -0
- notionary/shared/entity/entity_metadata_update_client.py +41 -0
- notionary/shared/entity/schemas.py +45 -0
- notionary/shared/entity/service.py +171 -0
- notionary/shared/models/cover.py +20 -0
- notionary/shared/models/file.py +21 -0
- notionary/shared/models/icon.py +28 -0
- notionary/shared/models/parent.py +41 -0
- notionary/shared/properties/type.py +30 -0
- notionary/shared/typings.py +3 -0
- notionary/user/__init__.py +4 -8
- notionary/user/base.py +138 -0
- notionary/user/bot.py +70 -0
- notionary/user/client.py +22 -111
- notionary/user/person.py +41 -0
- notionary/user/schemas.py +67 -0
- notionary/user/service.py +65 -0
- notionary/utils/date.py +51 -0
- notionary/utils/decorators.py +122 -0
- notionary/utils/fuzzy.py +68 -0
- notionary/utils/mixins/logging.py +58 -0
- notionary/utils/pagination.py +100 -0
- notionary/utils/uuid_utils.py +20 -0
- notionary/workspace/__init__.py +4 -0
- notionary/workspace/client.py +62 -0
- notionary/workspace/query/__init__.py +3 -0
- notionary/workspace/query/builder.py +60 -0
- notionary/workspace/query/models.py +61 -0
- notionary/workspace/query/service.py +100 -0
- notionary/workspace/schemas.py +21 -0
- notionary/workspace/service.py +116 -0
- notionary-0.3.0.dist-info/METADATA +201 -0
- notionary-0.3.0.dist-info/RECORD +209 -0
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
- notionary/base_notion_client.py +0 -219
- notionary/blocks/_bootstrap.py +0 -271
- notionary/blocks/audio/__init__.py +0 -11
- notionary/blocks/audio/audio_element.py +0 -158
- notionary/blocks/audio/audio_markdown_node.py +0 -24
- notionary/blocks/audio/audio_models.py +0 -10
- notionary/blocks/base_block_element.py +0 -42
- notionary/blocks/bookmark/__init__.py +0 -12
- notionary/blocks/bookmark/bookmark_element.py +0 -83
- notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
- notionary/blocks/bookmark/bookmark_models.py +0 -15
- notionary/blocks/breadcrumbs/__init__.py +0 -15
- notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
- notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
- notionary/blocks/bulleted_list/__init__.py +0 -15
- notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
- notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
- notionary/blocks/callout/__init__.py +0 -12
- notionary/blocks/callout/callout_element.py +0 -99
- notionary/blocks/callout/callout_markdown_node.py +0 -19
- notionary/blocks/callout/callout_models.py +0 -33
- notionary/blocks/child_database/__init__.py +0 -14
- notionary/blocks/child_database/child_database_element.py +0 -59
- notionary/blocks/child_database/child_database_models.py +0 -12
- notionary/blocks/child_page/__init__.py +0 -9
- notionary/blocks/child_page/child_page_element.py +0 -94
- notionary/blocks/child_page/child_page_models.py +0 -12
- notionary/blocks/code/__init__.py +0 -11
- notionary/blocks/code/code_element.py +0 -149
- notionary/blocks/code/code_markdown_node.py +0 -80
- notionary/blocks/code/code_models.py +0 -94
- notionary/blocks/column/__init__.py +0 -25
- notionary/blocks/column/column_element.py +0 -65
- notionary/blocks/column/column_list_element.py +0 -52
- notionary/blocks/column/column_list_markdown_node.py +0 -34
- notionary/blocks/column/column_markdown_node.py +0 -42
- notionary/blocks/column/column_models.py +0 -26
- notionary/blocks/divider/__init__.py +0 -12
- notionary/blocks/divider/divider_element.py +0 -41
- notionary/blocks/divider/divider_markdown_node.py +0 -11
- notionary/blocks/divider/divider_models.py +0 -12
- notionary/blocks/embed/__init__.py +0 -12
- notionary/blocks/embed/embed_element.py +0 -98
- notionary/blocks/embed/embed_markdown_node.py +0 -19
- notionary/blocks/embed/embed_models.py +0 -14
- notionary/blocks/equation/__init__.py +0 -13
- notionary/blocks/equation/equation_element.py +0 -133
- notionary/blocks/equation/equation_element_markdown_node.py +0 -23
- notionary/blocks/equation/equation_models.py +0 -11
- notionary/blocks/file/__init__.py +0 -23
- notionary/blocks/file/file_element.py +0 -133
- notionary/blocks/file/file_element_markdown_node.py +0 -24
- notionary/blocks/file/file_element_models.py +0 -39
- notionary/blocks/heading/__init__.py +0 -19
- notionary/blocks/heading/heading_element.py +0 -112
- notionary/blocks/heading/heading_markdown_node.py +0 -16
- notionary/blocks/heading/heading_models.py +0 -29
- notionary/blocks/image_block/__init__.py +0 -11
- notionary/blocks/image_block/image_element.py +0 -130
- notionary/blocks/image_block/image_markdown_node.py +0 -25
- notionary/blocks/image_block/image_models.py +0 -10
- notionary/blocks/markdown/markdown_builder.py +0 -525
- notionary/blocks/markdown/markdown_document_model.py +0 -0
- notionary/blocks/markdown/markdown_node.py +0 -25
- notionary/blocks/mixins/captions/__init__.py +0 -4
- notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
- notionary/blocks/mixins/captions/caption_mixin.py +0 -92
- notionary/blocks/mixins/file_upload/__init__.py +0 -3
- notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
- notionary/blocks/models.py +0 -174
- notionary/blocks/numbered_list/__init__.py +0 -16
- notionary/blocks/numbered_list/numbered_list_element.py +0 -65
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
- notionary/blocks/numbered_list/numbered_list_models.py +0 -17
- notionary/blocks/paragraph/__init__.py +0 -15
- notionary/blocks/paragraph/paragraph_element.py +0 -58
- notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
- notionary/blocks/paragraph/paragraph_models.py +0 -16
- notionary/blocks/pdf/__init__.py +0 -11
- notionary/blocks/pdf/pdf_element.py +0 -146
- notionary/blocks/pdf/pdf_markdown_node.py +0 -24
- notionary/blocks/pdf/pdf_models.py +0 -11
- notionary/blocks/quote/__init__.py +0 -14
- notionary/blocks/quote/quote_element.py +0 -75
- notionary/blocks/quote/quote_markdown_node.py +0 -16
- notionary/blocks/quote/quote_models.py +0 -18
- notionary/blocks/registry/__init__.py +0 -3
- notionary/blocks/registry/block_registry.py +0 -150
- notionary/blocks/rich_text/__init__.py +0 -33
- notionary/blocks/rich_text/rich_text_models.py +0 -221
- notionary/blocks/rich_text/text_inline_formatter.py +0 -456
- notionary/blocks/syntax_prompt_builder.py +0 -137
- notionary/blocks/table/__init__.py +0 -19
- notionary/blocks/table/table_element.py +0 -225
- notionary/blocks/table/table_markdown_node.py +0 -42
- notionary/blocks/table/table_models.py +0 -28
- notionary/blocks/table_of_contents/__init__.py +0 -17
- notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
- notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
- notionary/blocks/todo/__init__.py +0 -12
- notionary/blocks/todo/todo_element.py +0 -81
- notionary/blocks/todo/todo_markdown_node.py +0 -21
- notionary/blocks/todo/todo_models.py +0 -18
- notionary/blocks/toggle/__init__.py +0 -12
- notionary/blocks/toggle/toggle_element.py +0 -112
- notionary/blocks/toggle/toggle_markdown_node.py +0 -31
- notionary/blocks/toggle/toggle_models.py +0 -17
- notionary/blocks/toggleable_heading/__init__.py +0 -11
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
- notionary/blocks/types.py +0 -130
- notionary/blocks/video/__init__.py +0 -11
- notionary/blocks/video/video_element.py +0 -187
- notionary/blocks/video/video_element_models.py +0 -10
- notionary/blocks/video/video_markdown_node.py +0 -26
- notionary/database/__init__.py +0 -4
- notionary/database/database.py +0 -480
- notionary/database/database_filter_builder.py +0 -173
- notionary/database/database_provider.py +0 -227
- notionary/database/exceptions.py +0 -13
- notionary/database/models.py +0 -337
- notionary/database/notion_database.py +0 -487
- notionary/file_upload/__init__.py +0 -7
- notionary/page/client.py +0 -124
- notionary/page/markdown_whitespace_processor.py +0 -129
- notionary/page/models.py +0 -322
- notionary/page/notion_page.py +0 -712
- notionary/page/page_content_deleting_service.py +0 -117
- notionary/page/page_content_writer.py +0 -80
- notionary/page/property_formatter.py +0 -99
- notionary/page/reader/handler/__init__.py +0 -19
- notionary/page/reader/handler/base_block_renderer.py +0 -44
- notionary/page/reader/handler/block_processing_context.py +0 -35
- notionary/page/reader/handler/block_rendering_context.py +0 -48
- notionary/page/reader/handler/column_list_renderer.py +0 -51
- notionary/page/reader/handler/column_renderer.py +0 -60
- notionary/page/reader/handler/equation_renderer.py +0 -0
- notionary/page/reader/handler/line_renderer.py +0 -73
- notionary/page/reader/handler/numbered_list_renderer.py +0 -85
- notionary/page/reader/handler/toggle_renderer.py +0 -69
- notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
- notionary/page/reader/page_content_retriever.py +0 -81
- notionary/page/search_filter_builder.py +0 -132
- notionary/page/utils.py +0 -60
- notionary/page/writer/handler/__init__.py +0 -24
- notionary/page/writer/handler/code_handler.py +0 -72
- notionary/page/writer/handler/column_handler.py +0 -141
- notionary/page/writer/handler/column_list_handler.py +0 -139
- notionary/page/writer/handler/equation_handler.py +0 -74
- notionary/page/writer/handler/line_handler.py +0 -35
- notionary/page/writer/handler/line_processing_context.py +0 -54
- notionary/page/writer/handler/regular_line_handler.py +0 -86
- notionary/page/writer/handler/table_handler.py +0 -66
- notionary/page/writer/handler/toggle_handler.py +0 -159
- notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
- notionary/page/writer/markdown_to_notion_converter.py +0 -139
- notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
- notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
- notionary/page/writer/notion_text_length_processor.py +0 -150
- notionary/schemas/__init__.py +0 -3
- notionary/schemas/base.py +0 -73
- notionary/shared/__init__.py +0 -3
- notionary/shared/name_to_id_resolver.py +0 -203
- notionary/telemetry/__init__.py +0 -19
- notionary/telemetry/service.py +0 -136
- notionary/telemetry/views.py +0 -73
- notionary/user/base_notion_user.py +0 -53
- notionary/user/models.py +0 -84
- notionary/user/notion_bot_user.py +0 -226
- notionary/user/notion_user.py +0 -255
- notionary/user/notion_user_manager.py +0 -101
- notionary/util/__init__.py +0 -15
- notionary/util/concurrency_limiter.py +0 -0
- notionary/util/factory_decorator.py +0 -0
- notionary/util/factory_only.py +0 -37
- notionary/util/fuzzy.py +0 -75
- notionary/util/logging_mixin.py +0 -59
- notionary/util/page_id_utils.py +0 -27
- notionary/util/singleton.py +0 -18
- notionary/util/singleton_metaclass.py +0 -22
- notionary/workspace.py +0 -105
- notionary-0.2.27.dist-info/METADATA +0 -270
- notionary-0.2.27.dist-info/RECORD +0 -202
- /notionary/{database → user}/factory.py +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from collections.abc import Awaitable, Callable
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block
|
|
4
|
+
from notionary.page.content.syntax.grammar import MarkdownGrammar
|
|
5
|
+
|
|
6
|
+
ConvertChildrenCallback = Callable[[list[Block], int], Awaitable[str]]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MarkdownRenderingContext:
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
block: Block,
|
|
13
|
+
indent_level: int,
|
|
14
|
+
convert_children_callback: ConvertChildrenCallback | None = None,
|
|
15
|
+
markdown_grammar: MarkdownGrammar | None = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
self.block = block
|
|
18
|
+
self.indent_level = indent_level
|
|
19
|
+
self.convert_children_callback = convert_children_callback
|
|
20
|
+
markdown_grammar = markdown_grammar or MarkdownGrammar()
|
|
21
|
+
self._spaces_per_nesting_level = markdown_grammar.spaces_per_nesting_level
|
|
22
|
+
|
|
23
|
+
self.markdown_result: str | None = None
|
|
24
|
+
|
|
25
|
+
async def render_children(self) -> str:
|
|
26
|
+
return await self._convert_children_to_markdown(self.indent_level)
|
|
27
|
+
|
|
28
|
+
async def render_children_with_additional_indent(self, additional_indent: int) -> str:
|
|
29
|
+
return await self._convert_children_to_markdown(self.indent_level + additional_indent)
|
|
30
|
+
|
|
31
|
+
async def _convert_children_to_markdown(self, indent_level: int) -> str:
|
|
32
|
+
if not self._has_children() or not self.convert_children_callback:
|
|
33
|
+
return ""
|
|
34
|
+
|
|
35
|
+
return await self.convert_children_callback(self._get_children_blocks(), indent_level)
|
|
36
|
+
|
|
37
|
+
def _get_children_blocks(self) -> list[Block]:
|
|
38
|
+
if self._has_children():
|
|
39
|
+
return self.block.children
|
|
40
|
+
return []
|
|
41
|
+
|
|
42
|
+
def _has_children(self) -> bool:
|
|
43
|
+
return self.block.has_children and self.block.children and len(self.block.children) > 0
|
|
44
|
+
|
|
45
|
+
def indent_text(self, text: str) -> str:
|
|
46
|
+
if not text:
|
|
47
|
+
return text
|
|
48
|
+
|
|
49
|
+
spaces = " " * self._spaces_per_nesting_level * self.indent_level
|
|
50
|
+
lines = text.split("\n")
|
|
51
|
+
return "\n".join(f"{spaces}{line}" if line.strip() else line for line in lines)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import (
|
|
2
|
+
RichTextToMarkdownConverter,
|
|
3
|
+
)
|
|
4
|
+
from notionary.page.content.renderer.renderers import (
|
|
5
|
+
AudioRenderer,
|
|
6
|
+
BlockRenderer,
|
|
7
|
+
BookmarkRenderer,
|
|
8
|
+
BreadcrumbRenderer,
|
|
9
|
+
BulletedListRenderer,
|
|
10
|
+
CalloutRenderer,
|
|
11
|
+
CodeRenderer,
|
|
12
|
+
ColumnListRenderer,
|
|
13
|
+
ColumnRenderer,
|
|
14
|
+
DividerRenderer,
|
|
15
|
+
EmbedRenderer,
|
|
16
|
+
EquationRenderer,
|
|
17
|
+
FallbackRenderer,
|
|
18
|
+
FileRenderer,
|
|
19
|
+
HeadingRenderer,
|
|
20
|
+
ImageRenderer,
|
|
21
|
+
NumberedListRenderer,
|
|
22
|
+
ParagraphRenderer,
|
|
23
|
+
PdfRenderer,
|
|
24
|
+
QuoteRenderer,
|
|
25
|
+
TableOfContentsRenderer,
|
|
26
|
+
TableRenderer,
|
|
27
|
+
TableRowHandler,
|
|
28
|
+
TodoRenderer,
|
|
29
|
+
ToggleRenderer,
|
|
30
|
+
VideoRenderer,
|
|
31
|
+
)
|
|
32
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class RendererChainFactory:
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
39
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
42
|
+
self._syntax_registry = syntax_registry or SyntaxRegistry()
|
|
43
|
+
|
|
44
|
+
def create(self) -> BlockRenderer:
|
|
45
|
+
# Strukturelle Blocks
|
|
46
|
+
toggle_handler = self._create_toggle_renderer()
|
|
47
|
+
heading_handler = self._create_heading_renderer()
|
|
48
|
+
|
|
49
|
+
# Content Blocks
|
|
50
|
+
callout_handler = self._create_callout_renderer()
|
|
51
|
+
code_handler = self._create_code_renderer()
|
|
52
|
+
quote_handler = self._create_quote_renderer()
|
|
53
|
+
todo_handler = self._create_todo_renderer()
|
|
54
|
+
bulleted_list_handler = self._create_bulleted_list_renderer()
|
|
55
|
+
|
|
56
|
+
# Layout Blocks
|
|
57
|
+
divider_handler = self._create_divider_renderer()
|
|
58
|
+
column_list_handler = self._create_column_list_renderer()
|
|
59
|
+
column_handler = self._create_column_renderer()
|
|
60
|
+
numbered_list_handler = self._create_numbered_list_renderer()
|
|
61
|
+
|
|
62
|
+
# Media Blocks
|
|
63
|
+
bookmark_handler = self._create_bookmark_renderer()
|
|
64
|
+
image_handler = self._create_image_renderer()
|
|
65
|
+
video_handler = self._create_video_renderer()
|
|
66
|
+
audio_handler = self._create_audio_renderer()
|
|
67
|
+
file_handler = self._create_file_renderer()
|
|
68
|
+
pdf_handler = self._create_pdf_renderer()
|
|
69
|
+
embed_handler = self._create_embed_renderer()
|
|
70
|
+
|
|
71
|
+
# Special Blocks
|
|
72
|
+
equation_handler = self._create_equation_renderer()
|
|
73
|
+
table_of_contents_handler = self._create_table_of_contents_renderer()
|
|
74
|
+
breadcrumb_handler = self._create_breadcrumb_renderer()
|
|
75
|
+
table_handler = self._create_table_renderer()
|
|
76
|
+
table_row_handler = self._create_table_row_handler()
|
|
77
|
+
|
|
78
|
+
# Standard & Fallback
|
|
79
|
+
paragraph_handler = self._create_paragraph_renderer()
|
|
80
|
+
fallback_handler = self._create_fallback_renderer()
|
|
81
|
+
|
|
82
|
+
# Chain verketten - most specific first, fallback last
|
|
83
|
+
(
|
|
84
|
+
toggle_handler.set_next(heading_handler)
|
|
85
|
+
.set_next(callout_handler)
|
|
86
|
+
.set_next(code_handler)
|
|
87
|
+
.set_next(quote_handler)
|
|
88
|
+
.set_next(todo_handler)
|
|
89
|
+
.set_next(bulleted_list_handler)
|
|
90
|
+
.set_next(divider_handler)
|
|
91
|
+
.set_next(column_list_handler)
|
|
92
|
+
.set_next(column_handler)
|
|
93
|
+
.set_next(numbered_list_handler)
|
|
94
|
+
.set_next(bookmark_handler)
|
|
95
|
+
.set_next(image_handler)
|
|
96
|
+
.set_next(video_handler)
|
|
97
|
+
.set_next(audio_handler)
|
|
98
|
+
.set_next(file_handler)
|
|
99
|
+
.set_next(pdf_handler)
|
|
100
|
+
.set_next(embed_handler)
|
|
101
|
+
.set_next(equation_handler)
|
|
102
|
+
.set_next(table_of_contents_handler)
|
|
103
|
+
.set_next(breadcrumb_handler)
|
|
104
|
+
.set_next(table_handler)
|
|
105
|
+
.set_next(table_row_handler)
|
|
106
|
+
.set_next(paragraph_handler)
|
|
107
|
+
.set_next(fallback_handler)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return toggle_handler
|
|
111
|
+
|
|
112
|
+
# Renderer Creation Methods
|
|
113
|
+
def _create_toggle_renderer(self) -> ToggleRenderer:
|
|
114
|
+
return ToggleRenderer(
|
|
115
|
+
syntax_registry=self._syntax_registry,
|
|
116
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def _create_heading_renderer(self) -> HeadingRenderer:
|
|
120
|
+
return HeadingRenderer(
|
|
121
|
+
syntax_registry=self._syntax_registry,
|
|
122
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
def _create_callout_renderer(self) -> CalloutRenderer:
|
|
126
|
+
return CalloutRenderer(
|
|
127
|
+
syntax_registry=self._syntax_registry,
|
|
128
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def _create_code_renderer(self) -> CodeRenderer:
|
|
132
|
+
return CodeRenderer(
|
|
133
|
+
syntax_registry=self._syntax_registry,
|
|
134
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def _create_quote_renderer(self) -> QuoteRenderer:
|
|
138
|
+
return QuoteRenderer(
|
|
139
|
+
syntax_registry=self._syntax_registry,
|
|
140
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _create_todo_renderer(self) -> TodoRenderer:
|
|
144
|
+
return TodoRenderer(
|
|
145
|
+
syntax_registry=self._syntax_registry,
|
|
146
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def _create_bulleted_list_renderer(self) -> BulletedListRenderer:
|
|
150
|
+
return BulletedListRenderer(
|
|
151
|
+
syntax_registry=self._syntax_registry,
|
|
152
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def _create_divider_renderer(self) -> DividerRenderer:
|
|
156
|
+
return DividerRenderer(syntax_registry=self._syntax_registry)
|
|
157
|
+
|
|
158
|
+
def _create_column_list_renderer(self) -> ColumnListRenderer:
|
|
159
|
+
return ColumnListRenderer(syntax_registry=self._syntax_registry)
|
|
160
|
+
|
|
161
|
+
def _create_column_renderer(self) -> ColumnRenderer:
|
|
162
|
+
return ColumnRenderer(syntax_registry=self._syntax_registry)
|
|
163
|
+
|
|
164
|
+
def _create_numbered_list_renderer(self) -> NumberedListRenderer:
|
|
165
|
+
return NumberedListRenderer(
|
|
166
|
+
syntax_registry=self._syntax_registry,
|
|
167
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def _create_bookmark_renderer(self) -> BookmarkRenderer:
|
|
171
|
+
return BookmarkRenderer(
|
|
172
|
+
syntax_registry=self._syntax_registry,
|
|
173
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def _create_image_renderer(self) -> ImageRenderer:
|
|
177
|
+
return ImageRenderer(
|
|
178
|
+
syntax_registry=self._syntax_registry,
|
|
179
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def _create_video_renderer(self) -> VideoRenderer:
|
|
183
|
+
return VideoRenderer(
|
|
184
|
+
syntax_registry=self._syntax_registry,
|
|
185
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def _create_audio_renderer(self) -> AudioRenderer:
|
|
189
|
+
return AudioRenderer(
|
|
190
|
+
syntax_registry=self._syntax_registry,
|
|
191
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def _create_file_renderer(self) -> FileRenderer:
|
|
195
|
+
return FileRenderer(
|
|
196
|
+
syntax_registry=self._syntax_registry,
|
|
197
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def _create_pdf_renderer(self) -> PdfRenderer:
|
|
201
|
+
return PdfRenderer(
|
|
202
|
+
syntax_registry=self._syntax_registry,
|
|
203
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def _create_embed_renderer(self) -> EmbedRenderer:
|
|
207
|
+
return EmbedRenderer(
|
|
208
|
+
syntax_registry=self._syntax_registry,
|
|
209
|
+
rich_text_markdown_converter=self._rich_text_markdown_converter,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def _create_equation_renderer(self) -> EquationRenderer:
|
|
213
|
+
return EquationRenderer(syntax_registry=self._syntax_registry)
|
|
214
|
+
|
|
215
|
+
def _create_table_of_contents_renderer(self) -> TableOfContentsRenderer:
|
|
216
|
+
return TableOfContentsRenderer(syntax_registry=self._syntax_registry)
|
|
217
|
+
|
|
218
|
+
def _create_breadcrumb_renderer(self) -> BreadcrumbRenderer:
|
|
219
|
+
return BreadcrumbRenderer(syntax_registry=self._syntax_registry)
|
|
220
|
+
|
|
221
|
+
def _create_table_renderer(self) -> TableRenderer:
|
|
222
|
+
return TableRenderer(rich_text_markdown_converter=self._rich_text_markdown_converter)
|
|
223
|
+
|
|
224
|
+
def _create_table_row_handler(self) -> TableRowHandler:
|
|
225
|
+
return TableRowHandler()
|
|
226
|
+
|
|
227
|
+
def _create_paragraph_renderer(self) -> ParagraphRenderer:
|
|
228
|
+
return ParagraphRenderer(rich_text_markdown_converter=self._rich_text_markdown_converter)
|
|
229
|
+
|
|
230
|
+
def _create_fallback_renderer(self) -> FallbackRenderer:
|
|
231
|
+
return FallbackRenderer()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from enum import IntEnum
|
|
3
|
+
from typing import override
|
|
4
|
+
|
|
5
|
+
from notionary.page.content.renderer.post_processing.port import PostProcessor
|
|
6
|
+
from notionary.page.content.syntax.grammar import MarkdownGrammar
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class _NumberingStyle(IntEnum):
|
|
10
|
+
NUMERIC = 0
|
|
11
|
+
ALPHABETIC = 1
|
|
12
|
+
ROMAN = 2
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _ListNumberingState:
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self._counters_by_level: dict[int, int] = {}
|
|
18
|
+
self._current_level = -1
|
|
19
|
+
|
|
20
|
+
def advance_to_level(self, new_level: int) -> None:
|
|
21
|
+
self._forget_deeper_levels_than(new_level)
|
|
22
|
+
self._increment_counter_at_level(new_level)
|
|
23
|
+
self._current_level = new_level
|
|
24
|
+
|
|
25
|
+
def get_number_for_current_level(self) -> str:
|
|
26
|
+
counter = self._counters_by_level[self._current_level]
|
|
27
|
+
style = self._determine_numbering_style(self._current_level)
|
|
28
|
+
return self._format_number(counter, style)
|
|
29
|
+
|
|
30
|
+
def reset(self) -> None:
|
|
31
|
+
self._counters_by_level.clear()
|
|
32
|
+
self._current_level = -1
|
|
33
|
+
|
|
34
|
+
def _forget_deeper_levels_than(self, level: int) -> None:
|
|
35
|
+
self._counters_by_level = {
|
|
36
|
+
existing_level: count
|
|
37
|
+
for existing_level, count in self._counters_by_level.items()
|
|
38
|
+
if existing_level <= level
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def _increment_counter_at_level(self, level: int) -> None:
|
|
42
|
+
self._counters_by_level[level] = self._counters_by_level.get(level, 0) + 1
|
|
43
|
+
|
|
44
|
+
def _determine_numbering_style(self, nesting_level: int) -> _NumberingStyle:
|
|
45
|
+
return _NumberingStyle(nesting_level % 3)
|
|
46
|
+
|
|
47
|
+
def _format_number(self, counter: int, style: _NumberingStyle) -> str:
|
|
48
|
+
if style == _NumberingStyle.NUMERIC:
|
|
49
|
+
return str(counter)
|
|
50
|
+
elif style == _NumberingStyle.ALPHABETIC:
|
|
51
|
+
return self._to_alphabetic(counter)
|
|
52
|
+
else:
|
|
53
|
+
return self._to_roman(counter)
|
|
54
|
+
|
|
55
|
+
def _to_alphabetic(self, number: int) -> str:
|
|
56
|
+
result = ""
|
|
57
|
+
number -= 1
|
|
58
|
+
while number >= 0:
|
|
59
|
+
result = chr(ord("a") + (number % 26)) + result
|
|
60
|
+
number = number // 26 - 1
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
def _to_roman(self, number: int) -> str:
|
|
64
|
+
conversions = [
|
|
65
|
+
(1000, "m"),
|
|
66
|
+
(900, "cm"),
|
|
67
|
+
(500, "d"),
|
|
68
|
+
(400, "cd"),
|
|
69
|
+
(100, "c"),
|
|
70
|
+
(90, "xc"),
|
|
71
|
+
(50, "l"),
|
|
72
|
+
(40, "xl"),
|
|
73
|
+
(10, "x"),
|
|
74
|
+
(9, "ix"),
|
|
75
|
+
(5, "v"),
|
|
76
|
+
(4, "iv"),
|
|
77
|
+
(1, "i"),
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
result = ""
|
|
81
|
+
for arabic, roman in conversions:
|
|
82
|
+
while number >= arabic:
|
|
83
|
+
result += roman
|
|
84
|
+
number -= arabic
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class NumberedListPlaceholderReplacerPostProcessor(PostProcessor):
|
|
89
|
+
def __init__(self, markdown_grammar: MarkdownGrammar | None = None) -> None:
|
|
90
|
+
self._markdown_grammar = markdown_grammar or MarkdownGrammar()
|
|
91
|
+
self._spaces_per_nesting_level = self._markdown_grammar.spaces_per_nesting_level
|
|
92
|
+
self._numbered_list_placeholder = self._markdown_grammar.numbered_list_placeholder
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
def process(self, markdown_text: str) -> str:
|
|
96
|
+
lines = markdown_text.splitlines()
|
|
97
|
+
return self._convert_placeholder_lists_to_numbered_lists(lines)
|
|
98
|
+
|
|
99
|
+
def _convert_placeholder_lists_to_numbered_lists(self, lines: list[str]) -> str:
|
|
100
|
+
result = []
|
|
101
|
+
list_state = _ListNumberingState()
|
|
102
|
+
|
|
103
|
+
for line_index, line in enumerate(lines):
|
|
104
|
+
if self._is_placeholder_list_item(line):
|
|
105
|
+
numbered_line = self._convert_to_numbered_item(line, list_state)
|
|
106
|
+
result.append(numbered_line)
|
|
107
|
+
elif self._is_blank_between_list_items(lines, line_index, result):
|
|
108
|
+
continue
|
|
109
|
+
else:
|
|
110
|
+
list_state.reset()
|
|
111
|
+
result.append(line)
|
|
112
|
+
|
|
113
|
+
return "\n".join(result)
|
|
114
|
+
|
|
115
|
+
def _convert_to_numbered_item(self, line: str, state: _ListNumberingState) -> str:
|
|
116
|
+
indentation = self._extract_indentation(line)
|
|
117
|
+
content = self._extract_content(line)
|
|
118
|
+
nesting_level = self._calculate_nesting_level(indentation)
|
|
119
|
+
|
|
120
|
+
state.advance_to_level(nesting_level)
|
|
121
|
+
number = state.get_number_for_current_level()
|
|
122
|
+
|
|
123
|
+
return f"{indentation}{number}. {content}"
|
|
124
|
+
|
|
125
|
+
def _calculate_nesting_level(self, indentation: str) -> int:
|
|
126
|
+
return len(indentation) // self._spaces_per_nesting_level
|
|
127
|
+
|
|
128
|
+
def _extract_indentation(self, line: str) -> str:
|
|
129
|
+
match = re.match(rf"^(\s*){re.escape(self._numbered_list_placeholder)}\.", line)
|
|
130
|
+
return match.group(1) if match else ""
|
|
131
|
+
|
|
132
|
+
def _extract_content(self, line: str) -> str:
|
|
133
|
+
match = re.match(rf"^\s*{re.escape(self._numbered_list_placeholder)}\.\s*(.*)", line)
|
|
134
|
+
return match.group(1) if match else ""
|
|
135
|
+
|
|
136
|
+
def _is_placeholder_list_item(self, line: str) -> bool:
|
|
137
|
+
return bool(re.match(rf"^\s*{re.escape(self._numbered_list_placeholder)}\.", line))
|
|
138
|
+
|
|
139
|
+
def _is_blank_between_list_items(self, lines: list[str], current_index: int, processed_lines: list[str]) -> bool:
|
|
140
|
+
if not self._is_blank(lines[current_index]):
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
previous_line_was_list_item = processed_lines and self._looks_like_numbered_list_item(processed_lines[-1])
|
|
144
|
+
if not previous_line_was_list_item:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
next_line_is_list_item = current_index + 1 < len(lines) and self._is_placeholder_list_item(
|
|
148
|
+
lines[current_index + 1]
|
|
149
|
+
)
|
|
150
|
+
return next_line_is_list_item
|
|
151
|
+
|
|
152
|
+
def _is_blank(self, line: str) -> bool:
|
|
153
|
+
return not line.strip()
|
|
154
|
+
|
|
155
|
+
def _looks_like_numbered_list_item(self, line: str) -> bool:
|
|
156
|
+
return bool(re.match(r"^\s*(\d+|[a-z]+|[ivxlcdm]+)\.\s+", line))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from notionary.page.content.parser.post_processing.port import PostProcessor
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MarkdownRenderingPostProcessor:
|
|
5
|
+
def __init__(self) -> None:
|
|
6
|
+
self._processors: list[PostProcessor] = []
|
|
7
|
+
|
|
8
|
+
def register(self, processor: PostProcessor) -> None:
|
|
9
|
+
self._processors.append(processor)
|
|
10
|
+
|
|
11
|
+
def process(self, markdown_text: str) -> str:
|
|
12
|
+
result = markdown_text
|
|
13
|
+
for processor in self._processors:
|
|
14
|
+
result = processor.process(result)
|
|
15
|
+
return result
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from .audio import AudioRenderer
|
|
2
|
+
from .base import BlockRenderer
|
|
3
|
+
from .bookmark import BookmarkRenderer
|
|
4
|
+
from .breadcrumb import BreadcrumbRenderer
|
|
5
|
+
from .bulleted_list import BulletedListRenderer
|
|
6
|
+
from .callout import CalloutRenderer
|
|
7
|
+
from .code import CodeRenderer
|
|
8
|
+
from .column import ColumnRenderer
|
|
9
|
+
from .column_list import ColumnListRenderer
|
|
10
|
+
from .divider import DividerRenderer
|
|
11
|
+
from .embed import EmbedRenderer
|
|
12
|
+
from .equation import EquationRenderer
|
|
13
|
+
from .fallback import FallbackRenderer
|
|
14
|
+
from .file import FileRenderer
|
|
15
|
+
from .heading import HeadingRenderer
|
|
16
|
+
from .image import ImageRenderer
|
|
17
|
+
from .numbered_list import NumberedListRenderer
|
|
18
|
+
from .paragraph import ParagraphRenderer
|
|
19
|
+
from .pdf import PdfRenderer
|
|
20
|
+
from .quote import QuoteRenderer
|
|
21
|
+
from .table import TableRenderer
|
|
22
|
+
from .table_of_contents import TableOfContentsRenderer
|
|
23
|
+
from .table_row import TableRowHandler
|
|
24
|
+
from .todo import TodoRenderer
|
|
25
|
+
from .toggle import ToggleRenderer
|
|
26
|
+
from .video import VideoRenderer
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"AudioRenderer",
|
|
30
|
+
"BlockRenderer",
|
|
31
|
+
"BookmarkRenderer",
|
|
32
|
+
"BreadcrumbRenderer",
|
|
33
|
+
"BulletedListRenderer",
|
|
34
|
+
"CalloutRenderer",
|
|
35
|
+
"CodeRenderer",
|
|
36
|
+
"ColumnListRenderer",
|
|
37
|
+
"ColumnRenderer",
|
|
38
|
+
"DividerRenderer",
|
|
39
|
+
"EmbedRenderer",
|
|
40
|
+
"EquationRenderer",
|
|
41
|
+
"FallbackRenderer",
|
|
42
|
+
"FileRenderer",
|
|
43
|
+
"HeadingRenderer",
|
|
44
|
+
"ImageRenderer",
|
|
45
|
+
"NumberedListRenderer",
|
|
46
|
+
"ParagraphRenderer",
|
|
47
|
+
"PdfRenderer",
|
|
48
|
+
"QuoteRenderer",
|
|
49
|
+
"TableOfContentsRenderer",
|
|
50
|
+
"TableRenderer",
|
|
51
|
+
"TableRowHandler",
|
|
52
|
+
"TodoRenderer",
|
|
53
|
+
"ToggleRenderer",
|
|
54
|
+
"VideoRenderer",
|
|
55
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AudioRenderer(CaptionedBlockRenderer):
|
|
8
|
+
@override
|
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
|
10
|
+
return block.type == BlockType.AUDIO
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
14
|
+
url = self._extract_audio_url(block)
|
|
15
|
+
|
|
16
|
+
if not url:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
syntax = self._syntax_registry.get_audio_syntax()
|
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
|
|
22
|
+
def _extract_audio_url(self, block: Block) -> str:
|
|
23
|
+
if not block.audio:
|
|
24
|
+
return ""
|
|
25
|
+
|
|
26
|
+
if block.audio.external:
|
|
27
|
+
return block.audio.external.url or ""
|
|
28
|
+
elif block.audio.file:
|
|
29
|
+
return block.audio.file.url or ""
|
|
30
|
+
|
|
31
|
+
return ""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
from notionary.blocks.schemas import Block
|
|
6
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
7
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BlockRenderer(ABC):
|
|
11
|
+
def __init__(self, syntax_registry: SyntaxRegistry | None = None) -> None:
|
|
12
|
+
self._syntax_registry = syntax_registry or SyntaxRegistry()
|
|
13
|
+
self._next_handler: BlockRenderer | None = None
|
|
14
|
+
|
|
15
|
+
def set_next(self, handler: BlockRenderer) -> BlockRenderer:
|
|
16
|
+
self._next_handler = handler
|
|
17
|
+
return handler
|
|
18
|
+
|
|
19
|
+
async def handle(self, context: MarkdownRenderingContext) -> None:
|
|
20
|
+
if self._can_handle(context.block):
|
|
21
|
+
await self._process(context)
|
|
22
|
+
elif self._next_handler:
|
|
23
|
+
await self._next_handler.handle(context)
|
|
24
|
+
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def _can_handle(self, block: Block) -> bool:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
31
|
+
pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.renderers.captioned_block import CaptionedBlockRenderer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BookmarkRenderer(CaptionedBlockRenderer):
|
|
8
|
+
@override
|
|
9
|
+
def _can_handle(self, block: Block) -> bool:
|
|
10
|
+
return block.type == BlockType.BOOKMARK
|
|
11
|
+
|
|
12
|
+
@override
|
|
13
|
+
async def _render_main_content(self, block: Block) -> str:
|
|
14
|
+
url = self._extract_bookmark_url(block)
|
|
15
|
+
|
|
16
|
+
if not url:
|
|
17
|
+
return ""
|
|
18
|
+
|
|
19
|
+
syntax = self._syntax_registry.get_bookmark_syntax()
|
|
20
|
+
return f"{syntax.start_delimiter}{url}{syntax.end_delimiter}"
|
|
21
|
+
|
|
22
|
+
def _extract_bookmark_url(self, block: Block) -> str:
|
|
23
|
+
if not block.bookmark:
|
|
24
|
+
return ""
|
|
25
|
+
return block.bookmark.url or ""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
4
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
5
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BreadcrumbRenderer(BlockRenderer):
|
|
9
|
+
@override
|
|
10
|
+
def _can_handle(self, block: Block) -> bool:
|
|
11
|
+
return block.type == BlockType.BREADCRUMB
|
|
12
|
+
|
|
13
|
+
@override
|
|
14
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
15
|
+
syntax = self._syntax_registry.get_breadcrumb_syntax()
|
|
16
|
+
breadcrumb_markdown = syntax.start_delimiter
|
|
17
|
+
|
|
18
|
+
if context.indent_level > 0:
|
|
19
|
+
breadcrumb_markdown = context.indent_text(breadcrumb_markdown)
|
|
20
|
+
|
|
21
|
+
context.markdown_result = breadcrumb_markdown
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import override
|
|
2
|
+
|
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
|
|
4
|
+
from notionary.blocks.schemas import Block, BlockType
|
|
5
|
+
from notionary.page.content.renderer.context import MarkdownRenderingContext
|
|
6
|
+
from notionary.page.content.renderer.renderers.base import BlockRenderer
|
|
7
|
+
from notionary.page.content.syntax import SyntaxRegistry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BulletedListRenderer(BlockRenderer):
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
syntax_registry: SyntaxRegistry | None = None,
|
|
14
|
+
rich_text_markdown_converter: RichTextToMarkdownConverter | None = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(syntax_registry=syntax_registry)
|
|
17
|
+
self._rich_text_markdown_converter = rich_text_markdown_converter or RichTextToMarkdownConverter()
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def _can_handle(self, block: Block) -> bool:
|
|
21
|
+
return block.type == BlockType.BULLETED_LIST_ITEM
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
async def _process(self, context: MarkdownRenderingContext) -> None:
|
|
25
|
+
markdown = await self._convert_bulleted_list_to_markdown(context.block)
|
|
26
|
+
|
|
27
|
+
if not markdown:
|
|
28
|
+
context.markdown_result = ""
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
syntax = self._syntax_registry.get_bulleted_list_syntax()
|
|
32
|
+
list_item_markdown = f"{syntax.start_delimiter}{markdown}"
|
|
33
|
+
|
|
34
|
+
if context.indent_level > 0:
|
|
35
|
+
list_item_markdown = context.indent_text(list_item_markdown)
|
|
36
|
+
|
|
37
|
+
children_markdown = await context.render_children_with_additional_indent(1)
|
|
38
|
+
|
|
39
|
+
if children_markdown:
|
|
40
|
+
context.markdown_result = f"{list_item_markdown}\n{children_markdown}"
|
|
41
|
+
else:
|
|
42
|
+
context.markdown_result = list_item_markdown
|
|
43
|
+
|
|
44
|
+
async def _convert_bulleted_list_to_markdown(self, block: Block) -> str | None:
|
|
45
|
+
if not block.bulleted_list_item or not block.bulleted_list_item.rich_text:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
return await self._rich_text_markdown_converter.to_markdown(block.bulleted_list_item.rich_text)
|