notionary 0.2.26__py3-none-any.whl → 0.2.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notionary/__init__.py +5 -20
- notionary/blocks/client.py +87 -215
- notionary/blocks/enums.py +167 -0
- notionary/blocks/rich_text/markdown_rich_text_converter.py +266 -0
- notionary/blocks/rich_text/models.py +164 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +11 -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 +132 -0
- notionary/blocks/rich_text/rich_text_patterns.py +39 -0
- notionary/blocks/schemas.py +746 -0
- notionary/comments/client.py +52 -187
- notionary/comments/factory.py +40 -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 +94 -0
- notionary/data_source/properties/models.py +406 -0
- notionary/data_source/query/builder.py +429 -0
- notionary/data_source/query/resolver.py +114 -0
- notionary/data_source/query/schema.py +304 -0
- notionary/data_source/query/validator.py +73 -0
- notionary/data_source/schemas.py +27 -0
- notionary/data_source/service.py +353 -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 +169 -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 +33 -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 +205 -0
- notionary/http/models.py +49 -0
- notionary/page/blocks/client.py +1 -0
- notionary/page/content/factory.py +68 -0
- notionary/page/content/markdown/__init__.py +5 -0
- notionary/page/content/markdown/builder.py +304 -0
- notionary/page/content/markdown/nodes/__init__.py +54 -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 +18 -0
- notionary/page/content/markdown/nodes/callout.py +32 -0
- notionary/page/content/markdown/nodes/code.py +30 -0
- notionary/page/content/markdown/nodes/columns.py +51 -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 +16 -0
- notionary/page/content/markdown/nodes/image.py +23 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
- notionary/page/content/markdown/nodes/numbered_list.py +15 -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 +15 -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 +22 -0
- notionary/page/content/markdown/nodes/toggle.py +28 -0
- notionary/page/content/markdown/nodes/toggleable_heading.py +35 -0
- notionary/page/content/markdown/nodes/video.py +23 -0
- notionary/page/content/parser/context.py +49 -0
- notionary/page/content/parser/factory.py +219 -0
- notionary/page/content/parser/parsers/__init__.py +60 -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 +41 -0
- notionary/page/content/parser/parsers/callout.py +129 -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 +117 -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 +58 -0
- notionary/page/content/parser/parsers/image.py +42 -0
- notionary/page/content/parser/parsers/numbered_list.py +45 -0
- notionary/page/content/parser/parsers/paragraph.py +36 -0
- notionary/page/content/parser/parsers/pdf.py +42 -0
- notionary/page/content/parser/parsers/quote.py +65 -0
- notionary/page/content/parser/parsers/space.py +35 -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 +58 -0
- notionary/page/content/parser/parsers/toggle.py +127 -0
- notionary/page/content/parser/parsers/toggleable_heading.py +150 -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 +93 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +93 -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 +9 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +80 -0
- notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +68 -0
- notionary/page/content/parser/pre_processsing/service.py +15 -0
- notionary/page/content/parser/service.py +69 -0
- notionary/page/content/renderer/context.py +48 -0
- notionary/page/content/renderer/factory.py +240 -0
- notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/renderer/post_processing/handlers/numbered_list_placeholdere.py +62 -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 +57 -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 +65 -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 +44 -0
- notionary/page/content/renderer/renderers/column_list.py +31 -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 +69 -0
- notionary/page/content/renderer/renderers/image.py +31 -0
- notionary/page/content/renderer/renderers/numbered_list.py +41 -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 +53 -0
- notionary/page/content/renderer/renderers/toggleable_heading.py +78 -0
- notionary/page/content/renderer/renderers/video.py +31 -0
- notionary/page/content/renderer/service.py +50 -0
- notionary/page/content/service.py +65 -0
- notionary/page/content/syntax/models.py +68 -0
- notionary/page/content/syntax/service.py +453 -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 +307 -0
- notionary/page/properties/service.py +257 -0
- notionary/page/schemas.py +13 -0
- notionary/page/service.py +222 -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/user/__init__.py +4 -8
- notionary/user/base.py +89 -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/async_retry.py +39 -0
- notionary/utils/date.py +51 -0
- notionary/utils/fuzzy.py +56 -0
- notionary/{util/logging_mixin.py → utils/mixins/logging.py} +4 -16
- notionary/utils/pagination.py +50 -0
- notionary/utils/singleton.py +13 -0
- notionary/utils/uuid_utils.py +20 -0
- notionary/workspace/__init__.py +3 -0
- notionary/workspace/client.py +62 -0
- notionary/workspace/query/builder.py +60 -0
- notionary/workspace/query/models.py +60 -0
- notionary/workspace/query/service.py +93 -0
- notionary/workspace/schemas.py +21 -0
- notionary/workspace/service.py +116 -0
- {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info}/METADATA +54 -49
- notionary-0.2.28.dist-info/RECORD +200 -0
- {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info}/WHEEL +1 -1
- {notionary-0.2.26.dist-info → notionary-0.2.28.dist-info/licenses}/LICENSE +9 -9
- notionary/base_notion_client.py +0 -219
- notionary/blocks/__init__.py +0 -5
- 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/comments/__init__.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/factory.py +0 -0
- 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 -674
- 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/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.26.dist-info/RECORD +0 -202
@@ -1,320 +0,0 @@
|
|
1
|
-
from urllib.parse import urlparse
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import Optional
|
4
|
-
from notionary.file_upload import NotionFileUploadClient
|
5
|
-
from notionary.file_upload.models import UploadMode
|
6
|
-
from notionary.page.page_context import get_page_context
|
7
|
-
from notionary.util.logging_mixin import LoggingMixin
|
8
|
-
|
9
|
-
|
10
|
-
# TOOD: Hier überlegen wirklich nur was common ist hier in den mixin ansonstne dediziert handeln.
|
11
|
-
class FileUploadMixin(LoggingMixin):
|
12
|
-
"""
|
13
|
-
Mixin to add file upload functionality to all media block elements.
|
14
|
-
|
15
|
-
Supports uploading local files for:
|
16
|
-
- file blocks
|
17
|
-
- image blocks
|
18
|
-
- pdf blocks
|
19
|
-
- audio blocks
|
20
|
-
- video blocks
|
21
|
-
"""
|
22
|
-
|
23
|
-
@classmethod
|
24
|
-
def _get_file_upload_client(cls) -> NotionFileUploadClient:
|
25
|
-
"""Get the file upload client from the current page context."""
|
26
|
-
context = get_page_context()
|
27
|
-
return context.file_upload_client
|
28
|
-
|
29
|
-
@classmethod
|
30
|
-
def _is_local_file_path(cls, path: str) -> bool:
|
31
|
-
"""Determine if the path is a local file rather than a URL."""
|
32
|
-
if path.startswith(("http://", "https://", "ftp://")):
|
33
|
-
return False
|
34
|
-
|
35
|
-
return (
|
36
|
-
"/" in path
|
37
|
-
or "\\" in path
|
38
|
-
or path.startswith("./")
|
39
|
-
or path.startswith("../")
|
40
|
-
or ":" in path[:3]
|
41
|
-
) # Windows drive letters like C:
|
42
|
-
|
43
|
-
@classmethod
|
44
|
-
def _should_upload_file(cls, path: str, expected_category: str = "file") -> bool:
|
45
|
-
"""
|
46
|
-
Determine if a path should be uploaded vs used as external URL.
|
47
|
-
|
48
|
-
Args:
|
49
|
-
path: File path or URL
|
50
|
-
expected_category: Expected file category
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
True if file should be uploaded
|
54
|
-
"""
|
55
|
-
if not cls._is_local_file_path(path):
|
56
|
-
return False
|
57
|
-
|
58
|
-
file_path = Path(path)
|
59
|
-
if not file_path.exists():
|
60
|
-
return False
|
61
|
-
|
62
|
-
return True
|
63
|
-
|
64
|
-
@classmethod
|
65
|
-
def _get_content_type(cls, file_path: Path) -> str:
|
66
|
-
"""Get MIME type based on file extension."""
|
67
|
-
extension_map = {
|
68
|
-
# Documents
|
69
|
-
".pdf": "application/pdf",
|
70
|
-
".doc": "application/msword",
|
71
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
72
|
-
".xls": "application/vnd.ms-excel",
|
73
|
-
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
74
|
-
".ppt": "application/vnd.ms-powerpoint",
|
75
|
-
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
76
|
-
".txt": "text/plain",
|
77
|
-
".csv": "text/csv",
|
78
|
-
".json": "application/json",
|
79
|
-
".xml": "application/xml",
|
80
|
-
".zip": "application/zip",
|
81
|
-
".rar": "application/vnd.rar",
|
82
|
-
".7z": "application/x-7z-compressed",
|
83
|
-
# Images
|
84
|
-
".png": "image/png",
|
85
|
-
".jpg": "image/jpeg",
|
86
|
-
".jpeg": "image/jpeg",
|
87
|
-
".gif": "image/gif",
|
88
|
-
".webp": "image/webp",
|
89
|
-
".bmp": "image/bmp",
|
90
|
-
".tiff": "image/tiff",
|
91
|
-
".tif": "image/tiff",
|
92
|
-
".svg": "image/svg+xml",
|
93
|
-
".ico": "image/x-icon",
|
94
|
-
".heic": "image/heic",
|
95
|
-
".heif": "image/heif",
|
96
|
-
# Audio
|
97
|
-
".mp3": "audio/mpeg",
|
98
|
-
".wav": "audio/wav",
|
99
|
-
".ogg": "audio/ogg",
|
100
|
-
".m4a": "audio/mp4",
|
101
|
-
".aac": "audio/aac",
|
102
|
-
".flac": "audio/flac",
|
103
|
-
".wma": "audio/x-ms-wma",
|
104
|
-
".opus": "audio/opus",
|
105
|
-
# Video
|
106
|
-
".mp4": "video/mp4",
|
107
|
-
".avi": "video/x-msvideo",
|
108
|
-
".mov": "video/quicktime",
|
109
|
-
".wmv": "video/x-ms-wmv",
|
110
|
-
".flv": "video/x-flv",
|
111
|
-
".webm": "video/webm",
|
112
|
-
".mkv": "video/x-matroska",
|
113
|
-
".m4v": "video/mp4",
|
114
|
-
".3gp": "video/3gpp",
|
115
|
-
}
|
116
|
-
|
117
|
-
suffix = file_path.suffix.lower()
|
118
|
-
return extension_map.get(suffix, "application/octet-stream")
|
119
|
-
|
120
|
-
@classmethod
|
121
|
-
def _get_file_category(cls, file_path: Path) -> str:
|
122
|
-
"""
|
123
|
-
Determine the category of file based on extension.
|
124
|
-
|
125
|
-
Returns:
|
126
|
-
One of: 'image', 'audio', 'video', 'pdf', 'document', 'archive', 'other'
|
127
|
-
"""
|
128
|
-
suffix = file_path.suffix.lower()
|
129
|
-
|
130
|
-
# Define extension sets for each category
|
131
|
-
extension_categories = {
|
132
|
-
"image": {
|
133
|
-
".png",
|
134
|
-
".jpg",
|
135
|
-
".jpeg",
|
136
|
-
".gif",
|
137
|
-
".webp",
|
138
|
-
".bmp",
|
139
|
-
".tiff",
|
140
|
-
".tif",
|
141
|
-
".svg",
|
142
|
-
".ico",
|
143
|
-
".heic",
|
144
|
-
".heif",
|
145
|
-
},
|
146
|
-
"audio": {".mp3", ".wav", ".ogg", ".m4a", ".aac", ".flac", ".wma", ".opus"},
|
147
|
-
"video": {
|
148
|
-
".mp4",
|
149
|
-
".avi",
|
150
|
-
".mov",
|
151
|
-
".wmv",
|
152
|
-
".flv",
|
153
|
-
".webm",
|
154
|
-
".mkv",
|
155
|
-
".m4v",
|
156
|
-
".3gp",
|
157
|
-
},
|
158
|
-
"pdf": {".pdf"},
|
159
|
-
"document": {
|
160
|
-
".doc",
|
161
|
-
".docx",
|
162
|
-
".xls",
|
163
|
-
".xlsx",
|
164
|
-
".ppt",
|
165
|
-
".pptx",
|
166
|
-
".txt",
|
167
|
-
".csv",
|
168
|
-
".json",
|
169
|
-
".xml",
|
170
|
-
},
|
171
|
-
"archive": {".zip", ".rar", ".7z"},
|
172
|
-
}
|
173
|
-
|
174
|
-
# Find matching category
|
175
|
-
for category, extensions in extension_categories.items():
|
176
|
-
if suffix in extensions:
|
177
|
-
return category
|
178
|
-
|
179
|
-
return "other"
|
180
|
-
|
181
|
-
@classmethod
|
182
|
-
def _is_supported_file_type(cls, file_path: Path, expected_category: str) -> bool:
|
183
|
-
"""
|
184
|
-
Check if the file type matches the expected category.
|
185
|
-
|
186
|
-
Args:
|
187
|
-
file_path: Path to the file
|
188
|
-
expected_category: Expected category ('image', 'audio', 'video', 'pdf', 'file')
|
189
|
-
|
190
|
-
Returns:
|
191
|
-
True if file type matches expected category
|
192
|
-
"""
|
193
|
-
# 'file' category accepts any file type
|
194
|
-
if expected_category == "file":
|
195
|
-
return True
|
196
|
-
|
197
|
-
actual_category = cls._get_file_category(file_path)
|
198
|
-
return actual_category == expected_category
|
199
|
-
|
200
|
-
@classmethod
|
201
|
-
async def _upload_local_file(
|
202
|
-
cls, file_path_str: str, expected_category: str = "file"
|
203
|
-
) -> Optional[str]:
|
204
|
-
"""
|
205
|
-
Upload a local file and return the file upload ID.
|
206
|
-
|
207
|
-
Args:
|
208
|
-
file_path_str: String path to the local file
|
209
|
-
expected_category: Expected file category for validation
|
210
|
-
|
211
|
-
Returns:
|
212
|
-
File upload ID if successful, None otherwise
|
213
|
-
"""
|
214
|
-
try:
|
215
|
-
file_upload_client = cls._get_file_upload_client()
|
216
|
-
file_path = Path(file_path_str)
|
217
|
-
|
218
|
-
# Pre-upload validation
|
219
|
-
if not cls._validate_file_for_upload(file_path, expected_category):
|
220
|
-
return None
|
221
|
-
|
222
|
-
# Get file metadata
|
223
|
-
file_size = file_path.stat().st_size
|
224
|
-
content_type = cls._get_content_type(file_path)
|
225
|
-
|
226
|
-
cls.logger.info(
|
227
|
-
f"Uploading {expected_category} file: {file_path.name} "
|
228
|
-
f"({file_size} bytes, {content_type})"
|
229
|
-
)
|
230
|
-
|
231
|
-
# Create and execute upload
|
232
|
-
upload_id = await cls._execute_upload(
|
233
|
-
file_upload_client, file_path, content_type, file_size
|
234
|
-
)
|
235
|
-
|
236
|
-
if upload_id:
|
237
|
-
cls.logger.info(
|
238
|
-
f"File upload completed: {upload_id} ({file_path.name})"
|
239
|
-
)
|
240
|
-
|
241
|
-
return upload_id
|
242
|
-
|
243
|
-
except Exception as e:
|
244
|
-
cls.logger.error(
|
245
|
-
f"Error uploading {expected_category} file {file_path_str}: {e}"
|
246
|
-
)
|
247
|
-
cls.logger.debug("Upload error traceback:", exc_info=True)
|
248
|
-
return None
|
249
|
-
|
250
|
-
@classmethod
|
251
|
-
def _validate_file_for_upload(cls, file_path: Path, expected_category: str) -> bool:
|
252
|
-
"""Validate file exists and type matches expected category."""
|
253
|
-
# Check if file exists
|
254
|
-
if not file_path.exists():
|
255
|
-
cls.logger.error(f"File not found: {file_path}")
|
256
|
-
return False
|
257
|
-
|
258
|
-
# Validate file type if needed
|
259
|
-
if not cls._is_supported_file_type(file_path, expected_category):
|
260
|
-
actual_category = cls._get_file_category(file_path)
|
261
|
-
cls.logger.warning(
|
262
|
-
f"File type mismatch: expected {expected_category}, "
|
263
|
-
f"got {actual_category} for {file_path} - proceeding anyway"
|
264
|
-
)
|
265
|
-
|
266
|
-
return True
|
267
|
-
|
268
|
-
@classmethod
|
269
|
-
async def _execute_upload(
|
270
|
-
cls,
|
271
|
-
file_upload_client: NotionFileUploadClient,
|
272
|
-
file_path: Path,
|
273
|
-
content_type: str,
|
274
|
-
file_size: int,
|
275
|
-
) -> Optional[str]:
|
276
|
-
"""Execute the actual file upload process."""
|
277
|
-
# Step 1: Create file upload
|
278
|
-
upload_response = await file_upload_client.create_file_upload(
|
279
|
-
filename=file_path.name,
|
280
|
-
content_type=content_type,
|
281
|
-
content_length=file_size,
|
282
|
-
mode=UploadMode.SINGLE_PART,
|
283
|
-
)
|
284
|
-
|
285
|
-
if not upload_response:
|
286
|
-
cls.logger.error(f"Failed to create file upload for {file_path.name}")
|
287
|
-
return None
|
288
|
-
|
289
|
-
cls.logger.debug(f"Created file upload with ID: {upload_response.id}")
|
290
|
-
|
291
|
-
# Step 2: Send file content
|
292
|
-
success = await file_upload_client.send_file_from_path(
|
293
|
-
file_upload_id=upload_response.id, file_path=file_path
|
294
|
-
)
|
295
|
-
|
296
|
-
if not success:
|
297
|
-
cls.logger.error(f"Failed to send file content for {file_path.name}")
|
298
|
-
return None
|
299
|
-
|
300
|
-
cls.logger.debug(f"File content sent successfully for {file_path.name}")
|
301
|
-
return upload_response.id
|
302
|
-
|
303
|
-
@classmethod
|
304
|
-
def _get_upload_error_message(
|
305
|
-
cls, file_path_str: str, expected_category: str
|
306
|
-
) -> str:
|
307
|
-
"""Get a user-friendly error message for upload failures."""
|
308
|
-
file_path = Path(file_path_str)
|
309
|
-
|
310
|
-
if not file_path.exists():
|
311
|
-
return f"File not found: {file_path_str}"
|
312
|
-
|
313
|
-
actual_category = cls._get_file_category(file_path)
|
314
|
-
if actual_category != expected_category and expected_category != "file":
|
315
|
-
return (
|
316
|
-
f"Invalid file type for {expected_category} block: "
|
317
|
-
f"{file_path.suffix} (detected as {actual_category})"
|
318
|
-
)
|
319
|
-
|
320
|
-
return f"Failed to upload {expected_category} file: {file_path_str}"
|
notionary/blocks/models.py
DELETED
@@ -1,174 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
4
|
-
|
5
|
-
from pydantic import BaseModel
|
6
|
-
|
7
|
-
from notionary.blocks.types import BlockType
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from notionary.blocks.bookmark import BookmarkBlock, CreateBookmarkBlock
|
11
|
-
from notionary.blocks.breadcrumbs import BreadcrumbBlock, CreateBreadcrumbBlock
|
12
|
-
from notionary.blocks.bulleted_list import (
|
13
|
-
BulletedListItemBlock,
|
14
|
-
CreateBulletedListItemBlock,
|
15
|
-
)
|
16
|
-
from notionary.blocks.callout import CalloutBlock, CreateCalloutBlock
|
17
|
-
from notionary.blocks.child_page import ChildPageBlock, CreateChildPageBlock
|
18
|
-
from notionary.blocks.code import CodeBlock, CreateCodeBlock
|
19
|
-
from notionary.blocks.column import (
|
20
|
-
ColumnBlock,
|
21
|
-
ColumnListBlock,
|
22
|
-
CreateColumnBlock,
|
23
|
-
CreateColumnListBlock,
|
24
|
-
)
|
25
|
-
from notionary.blocks.divider import CreateDividerBlock, DividerBlock
|
26
|
-
from notionary.blocks.embed import CreateEmbedBlock, EmbedBlock
|
27
|
-
from notionary.blocks.equation import CreateEquationBlock, EquationBlock
|
28
|
-
from notionary.blocks.file import CreateFileBlock, FileBlock
|
29
|
-
from notionary.blocks.heading import (
|
30
|
-
CreateHeading1Block,
|
31
|
-
CreateHeading2Block,
|
32
|
-
CreateHeading3Block,
|
33
|
-
HeadingBlock,
|
34
|
-
)
|
35
|
-
from notionary.blocks.image_block import CreateImageBlock
|
36
|
-
from notionary.blocks.numbered_list import (
|
37
|
-
CreateNumberedListItemBlock,
|
38
|
-
NumberedListItemBlock,
|
39
|
-
)
|
40
|
-
from notionary.blocks.paragraph import CreateParagraphBlock, ParagraphBlock
|
41
|
-
from notionary.blocks.pdf import CreatePdfBlock
|
42
|
-
from notionary.blocks.quote import CreateQuoteBlock, QuoteBlock
|
43
|
-
from notionary.blocks.table import CreateTableBlock, TableBlock, TableRowBlock
|
44
|
-
from notionary.blocks.table_of_contents import (
|
45
|
-
CreateTableOfContentsBlock,
|
46
|
-
TableOfContentsBlock,
|
47
|
-
)
|
48
|
-
from notionary.blocks.todo import CreateToDoBlock, ToDoBlock
|
49
|
-
from notionary.blocks.toggle import CreateToggleBlock, ToggleBlock
|
50
|
-
from notionary.blocks.video import CreateVideoBlock
|
51
|
-
from notionary.blocks.child_database import ChildDatabaseBlock
|
52
|
-
|
53
|
-
|
54
|
-
class BlockChildrenResponse(BaseModel):
|
55
|
-
object: Literal["list"]
|
56
|
-
results: list["Block"]
|
57
|
-
next_cursor: Optional[str] = None
|
58
|
-
has_more: bool
|
59
|
-
type: Literal["block"]
|
60
|
-
block: dict = {}
|
61
|
-
request_id: str
|
62
|
-
|
63
|
-
|
64
|
-
class PageParent(BaseModel):
|
65
|
-
type: Literal["page_id"]
|
66
|
-
page_id: str
|
67
|
-
|
68
|
-
|
69
|
-
class DatabaseParent(BaseModel):
|
70
|
-
type: Literal["database_id"]
|
71
|
-
database_id: str
|
72
|
-
|
73
|
-
|
74
|
-
class BlockParent(BaseModel):
|
75
|
-
type: Literal["block_id"]
|
76
|
-
block_id: str
|
77
|
-
|
78
|
-
|
79
|
-
class WorkspaceParent(BaseModel):
|
80
|
-
type: Literal["workspace"]
|
81
|
-
workspace: bool = True
|
82
|
-
|
83
|
-
|
84
|
-
ParentObject = Union[PageParent, DatabaseParent, BlockParent, WorkspaceParent]
|
85
|
-
|
86
|
-
|
87
|
-
class PartialUser(BaseModel):
|
88
|
-
object: Literal["user"]
|
89
|
-
id: str
|
90
|
-
|
91
|
-
|
92
|
-
class Block(BaseModel):
|
93
|
-
object: Literal["block"]
|
94
|
-
id: str
|
95
|
-
parent: Optional[ParentObject] = None
|
96
|
-
type: BlockType
|
97
|
-
created_time: str
|
98
|
-
last_edited_time: str
|
99
|
-
created_by: PartialUser
|
100
|
-
last_edited_by: PartialUser
|
101
|
-
archived: bool = False
|
102
|
-
in_trash: bool = False
|
103
|
-
has_children: bool = False
|
104
|
-
|
105
|
-
children: Optional[list[Block]] = None
|
106
|
-
|
107
|
-
# Block type-specific content (only one will be populated based on type)
|
108
|
-
audio: Optional[FileBlock] = None
|
109
|
-
bookmark: Optional[BookmarkBlock] = None
|
110
|
-
breadcrumb: Optional[BreadcrumbBlock] = None
|
111
|
-
bulleted_list_item: Optional[BulletedListItemBlock] = None
|
112
|
-
callout: Optional[CalloutBlock] = None
|
113
|
-
child_page: Optional[ChildPageBlock] = None
|
114
|
-
code: Optional[CodeBlock] = None
|
115
|
-
column_list: Optional[ColumnListBlock] = None
|
116
|
-
column: Optional[ColumnBlock] = None
|
117
|
-
divider: Optional[DividerBlock] = None
|
118
|
-
embed: Optional[EmbedBlock] = None
|
119
|
-
equation: Optional[EquationBlock] = None
|
120
|
-
file: Optional[FileBlock] = None
|
121
|
-
heading_1: Optional[HeadingBlock] = None
|
122
|
-
heading_2: Optional[HeadingBlock] = None
|
123
|
-
heading_3: Optional[HeadingBlock] = None
|
124
|
-
image: Optional[FileBlock] = None
|
125
|
-
numbered_list_item: Optional[NumberedListItemBlock] = None
|
126
|
-
paragraph: Optional[ParagraphBlock] = None
|
127
|
-
quote: Optional[QuoteBlock] = None
|
128
|
-
table: Optional[TableBlock] = None
|
129
|
-
table_row: Optional[TableRowBlock] = None
|
130
|
-
to_do: Optional[ToDoBlock] = None
|
131
|
-
toggle: Optional[ToggleBlock] = None
|
132
|
-
video: Optional[FileBlock] = None
|
133
|
-
pdf: Optional[FileBlock] = None
|
134
|
-
table_of_contents: Optional[TableOfContentsBlock] = None
|
135
|
-
child_database: Optional[ChildDatabaseBlock] = None
|
136
|
-
|
137
|
-
def get_block_content(self) -> Optional[Any]:
|
138
|
-
"""Get the content object for this block based on its type."""
|
139
|
-
return getattr(self, self.type, None)
|
140
|
-
|
141
|
-
|
142
|
-
if TYPE_CHECKING:
|
143
|
-
BlockCreateRequest = Union[
|
144
|
-
CreateBookmarkBlock,
|
145
|
-
CreateBreadcrumbBlock,
|
146
|
-
CreateBulletedListItemBlock,
|
147
|
-
CreateCalloutBlock,
|
148
|
-
CreateChildPageBlock,
|
149
|
-
CreateCodeBlock,
|
150
|
-
CreateColumnListBlock,
|
151
|
-
CreateColumnBlock,
|
152
|
-
CreateDividerBlock,
|
153
|
-
CreateEmbedBlock,
|
154
|
-
CreateEquationBlock,
|
155
|
-
CreateFileBlock,
|
156
|
-
CreateHeading1Block,
|
157
|
-
CreateHeading2Block,
|
158
|
-
CreateHeading3Block,
|
159
|
-
CreateImageBlock,
|
160
|
-
CreateNumberedListItemBlock,
|
161
|
-
CreateParagraphBlock,
|
162
|
-
CreateQuoteBlock,
|
163
|
-
CreateToDoBlock,
|
164
|
-
CreateToggleBlock,
|
165
|
-
CreateVideoBlock,
|
166
|
-
CreateTableOfContentsBlock,
|
167
|
-
CreatePdfBlock,
|
168
|
-
CreateTableBlock,
|
169
|
-
]
|
170
|
-
BlockCreateResult = Union[BlockCreateRequest]
|
171
|
-
else:
|
172
|
-
# at runtime there are no typings anyway
|
173
|
-
BlockCreateRequest = Any
|
174
|
-
BlockCreateResult = Any
|
@@ -1,16 +0,0 @@
|
|
1
|
-
from notionary.blocks.numbered_list.numbered_list_element import NumberedListElement
|
2
|
-
from notionary.blocks.numbered_list.numbered_list_markdown_node import (
|
3
|
-
NumberedListMarkdownNode,
|
4
|
-
)
|
5
|
-
from notionary.blocks.numbered_list.numbered_list_models import (
|
6
|
-
CreateNumberedListItemBlock,
|
7
|
-
NumberedListItemBlock,
|
8
|
-
)
|
9
|
-
|
10
|
-
__all__ = [
|
11
|
-
"NumberedListElement",
|
12
|
-
"NumberedListItemBlock",
|
13
|
-
"CreateNumberedListItemBlock",
|
14
|
-
"NumberedListMarkdownNode",
|
15
|
-
"NumberedListMarkdownBlockParams",
|
16
|
-
]
|
@@ -1,65 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
from typing import Optional
|
5
|
-
|
6
|
-
from notionary.blocks.base_block_element import BaseBlockElement
|
7
|
-
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
8
|
-
from notionary.blocks.models import Block, BlockCreateResult, BlockType
|
9
|
-
from notionary.blocks.numbered_list.numbered_list_models import (
|
10
|
-
CreateNumberedListItemBlock,
|
11
|
-
NumberedListItemBlock,
|
12
|
-
)
|
13
|
-
from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
|
14
|
-
from notionary.blocks.types import BlockColor
|
15
|
-
|
16
|
-
|
17
|
-
class NumberedListElement(BaseBlockElement):
|
18
|
-
"""Converts between Markdown numbered lists and Notion numbered list items."""
|
19
|
-
|
20
|
-
PATTERN = re.compile(r"^\s*(\d+)\.\s+(.+)$")
|
21
|
-
|
22
|
-
@classmethod
|
23
|
-
def match_notion(cls, block: Block) -> bool:
|
24
|
-
return block.type == BlockType.NUMBERED_LIST_ITEM and block.numbered_list_item
|
25
|
-
|
26
|
-
@classmethod
|
27
|
-
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
28
|
-
"""Convert markdown numbered list item to Notion NumberedListItemBlock."""
|
29
|
-
match = cls.PATTERN.match(text.strip())
|
30
|
-
if not match:
|
31
|
-
return None
|
32
|
-
|
33
|
-
content = match.group(2)
|
34
|
-
rich_text = await TextInlineFormatter.parse_inline_formatting(content)
|
35
|
-
|
36
|
-
numbered_list_content = NumberedListItemBlock(
|
37
|
-
rich_text=rich_text, color=BlockColor.DEFAULT
|
38
|
-
)
|
39
|
-
return CreateNumberedListItemBlock(numbered_list_item=numbered_list_content)
|
40
|
-
|
41
|
-
# FIX: Roundtrip conversions will never work this way here
|
42
|
-
@classmethod
|
43
|
-
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
44
|
-
if block.type != BlockType.NUMBERED_LIST_ITEM or not block.numbered_list_item:
|
45
|
-
return None
|
46
|
-
|
47
|
-
rich = block.numbered_list_item.rich_text
|
48
|
-
content = await TextInlineFormatter.extract_text_with_formatting(rich)
|
49
|
-
return f"1. {content}"
|
50
|
-
|
51
|
-
@classmethod
|
52
|
-
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
53
|
-
"""Get system prompt information for numbered list blocks."""
|
54
|
-
return BlockElementMarkdownInformation(
|
55
|
-
block_type=cls.__name__,
|
56
|
-
description="Numbered list items create ordered lists with sequential numbering",
|
57
|
-
syntax_examples=[
|
58
|
-
"1. First item",
|
59
|
-
"2. Second item",
|
60
|
-
"3. Third item",
|
61
|
-
"1. Item with **bold text**",
|
62
|
-
"1. Item with *italic text*",
|
63
|
-
],
|
64
|
-
usage_guidelines="Use numbers followed by periods to create ordered lists. Supports inline formatting like bold, italic, and links. Numbering is automatically handled by Notion.",
|
65
|
-
)
|
@@ -1,17 +0,0 @@
|
|
1
|
-
from notionary.blocks.markdown.markdown_node import MarkdownNode
|
2
|
-
|
3
|
-
|
4
|
-
class NumberedListMarkdownNode(MarkdownNode):
|
5
|
-
"""
|
6
|
-
Enhanced NumberedList node with Pydantic integration.
|
7
|
-
Programmatic interface for creating Markdown numbered list items.
|
8
|
-
Example:
|
9
|
-
1. First step
|
10
|
-
2. Second step
|
11
|
-
3. Third step
|
12
|
-
"""
|
13
|
-
|
14
|
-
texts: list[str]
|
15
|
-
|
16
|
-
def to_markdown(self) -> str:
|
17
|
-
return "\n".join(f"{i + 1}. {text}" for i, text in enumerate(self.texts))
|
@@ -1,17 +0,0 @@
|
|
1
|
-
from pydantic import BaseModel
|
2
|
-
from typing import Literal, Optional
|
3
|
-
|
4
|
-
from notionary.blocks.models import Block
|
5
|
-
from notionary.blocks.rich_text.rich_text_models import RichTextObject
|
6
|
-
from notionary.blocks.types import BlockColor
|
7
|
-
|
8
|
-
|
9
|
-
class NumberedListItemBlock(BaseModel):
|
10
|
-
rich_text: list[RichTextObject]
|
11
|
-
color: BlockColor = BlockColor.DEFAULT
|
12
|
-
children: Optional[list[Block]] = None
|
13
|
-
|
14
|
-
|
15
|
-
class CreateNumberedListItemBlock(BaseModel):
|
16
|
-
type: Literal["numbered_list_item"] = "numbered_list_item"
|
17
|
-
numbered_list_item: NumberedListItemBlock
|
@@ -1,15 +0,0 @@
|
|
1
|
-
from notionary.blocks.paragraph.paragraph_element import ParagraphElement
|
2
|
-
from notionary.blocks.paragraph.paragraph_markdown_node import (
|
3
|
-
ParagraphMarkdownNode,
|
4
|
-
)
|
5
|
-
from notionary.blocks.paragraph.paragraph_models import (
|
6
|
-
CreateParagraphBlock,
|
7
|
-
ParagraphBlock,
|
8
|
-
)
|
9
|
-
|
10
|
-
__all__ = [
|
11
|
-
"ParagraphElement",
|
12
|
-
"ParagraphBlock",
|
13
|
-
"CreateParagraphBlock",
|
14
|
-
"ParagraphMarkdownNode",
|
15
|
-
]
|
@@ -1,58 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import Optional
|
4
|
-
|
5
|
-
from notionary.blocks.base_block_element import BaseBlockElement
|
6
|
-
from notionary.blocks.syntax_prompt_builder import BlockElementMarkdownInformation
|
7
|
-
from notionary.blocks.models import Block, BlockCreateResult
|
8
|
-
from notionary.blocks.paragraph.paragraph_models import (
|
9
|
-
CreateParagraphBlock,
|
10
|
-
ParagraphBlock,
|
11
|
-
)
|
12
|
-
from notionary.blocks.rich_text.text_inline_formatter import TextInlineFormatter
|
13
|
-
from notionary.blocks.types import BlockColor, BlockType
|
14
|
-
|
15
|
-
|
16
|
-
class ParagraphElement(BaseBlockElement):
|
17
|
-
"""
|
18
|
-
Handles conversion between Markdown paragraphs and Notion paragraph blocks.
|
19
|
-
"""
|
20
|
-
|
21
|
-
@classmethod
|
22
|
-
def match_notion(cls, block: Block) -> bool:
|
23
|
-
return block.type == "paragraph" and block.paragraph
|
24
|
-
|
25
|
-
@classmethod
|
26
|
-
async def markdown_to_notion(cls, text: str) -> BlockCreateResult:
|
27
|
-
"""Convert markdown text to a Notion ParagraphBlock."""
|
28
|
-
if not text.strip():
|
29
|
-
return None
|
30
|
-
|
31
|
-
rich = await TextInlineFormatter.parse_inline_formatting(text)
|
32
|
-
|
33
|
-
paragraph_content = ParagraphBlock(rich_text=rich, color=BlockColor.DEFAULT)
|
34
|
-
return CreateParagraphBlock(paragraph=paragraph_content)
|
35
|
-
|
36
|
-
@classmethod
|
37
|
-
async def notion_to_markdown(cls, block: Block) -> Optional[str]:
|
38
|
-
if block.type != BlockType.PARAGRAPH or not block.paragraph:
|
39
|
-
return None
|
40
|
-
|
41
|
-
rich_list = block.paragraph.rich_text
|
42
|
-
markdown = await TextInlineFormatter.extract_text_with_formatting(rich_list)
|
43
|
-
return markdown or None
|
44
|
-
|
45
|
-
@classmethod
|
46
|
-
def get_system_prompt_information(cls) -> Optional[BlockElementMarkdownInformation]:
|
47
|
-
"""Get system prompt information for paragraph blocks."""
|
48
|
-
return BlockElementMarkdownInformation(
|
49
|
-
block_type=cls.__name__,
|
50
|
-
description="Paragraph blocks contain regular text content with optional inline formatting",
|
51
|
-
syntax_examples=[
|
52
|
-
"This is a simple paragraph.",
|
53
|
-
"Paragraph with **bold text** and *italic text*.",
|
54
|
-
"Paragraph with [link](https://example.com) and `code`.",
|
55
|
-
"Multiple sentences in one paragraph. Each sentence flows naturally.",
|
56
|
-
],
|
57
|
-
usage_guidelines="Use for regular text content. Supports inline formatting: **bold**, *italic*, `code`, [links](url). Default block type for plain text.",
|
58
|
-
)
|