notionary 0.2.27__py3-none-any.whl → 0.2.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.27.dist-info → notionary-0.2.28.dist-info}/METADATA +54 -49
- notionary-0.2.28.dist-info/RECORD +200 -0
- {notionary-0.2.27.dist-info → notionary-0.2.28.dist-info}/WHEEL +1 -1
- {notionary-0.2.27.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 -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/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/RECORD +0 -202
@@ -0,0 +1,257 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
from typing import TYPE_CHECKING, Never
|
5
|
+
|
6
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import convert_rich_text_to_markdown
|
7
|
+
from notionary.exceptions.properties import (
|
8
|
+
AccessPagePropertyWithoutDataSourceError,
|
9
|
+
PagePropertyNotFoundError,
|
10
|
+
PagePropertyTypeError,
|
11
|
+
)
|
12
|
+
from notionary.page.properties.client import PagePropertyHttpClient
|
13
|
+
from notionary.page.properties.models import (
|
14
|
+
PageCheckboxProperty,
|
15
|
+
PageCreatedTimeProperty,
|
16
|
+
PageDateProperty,
|
17
|
+
PageEmailProperty,
|
18
|
+
PageMultiSelectProperty,
|
19
|
+
PageNumberProperty,
|
20
|
+
PagePeopleProperty,
|
21
|
+
PagePhoneNumberProperty,
|
22
|
+
PageProperty,
|
23
|
+
PagePropertyT,
|
24
|
+
PageRelationProperty,
|
25
|
+
PageRichTextProperty,
|
26
|
+
PageSelectProperty,
|
27
|
+
PageStatusProperty,
|
28
|
+
PageTitleProperty,
|
29
|
+
PageURLProperty,
|
30
|
+
)
|
31
|
+
from notionary.shared.models.parent import ParentType
|
32
|
+
|
33
|
+
if TYPE_CHECKING:
|
34
|
+
from notionary import NotionDataSource
|
35
|
+
|
36
|
+
|
37
|
+
class PagePropertyHandler:
|
38
|
+
def __init__(
|
39
|
+
self,
|
40
|
+
properties: dict[str, PageProperty],
|
41
|
+
parent_type: ParentType,
|
42
|
+
page_url: str,
|
43
|
+
page_property_http_client: PagePropertyHttpClient,
|
44
|
+
parent_data_source: str,
|
45
|
+
) -> None:
|
46
|
+
self._properties = properties
|
47
|
+
self._parent_type = parent_type
|
48
|
+
self._page_url = page_url
|
49
|
+
self._property_http_client = page_property_http_client
|
50
|
+
self._parent_data_source_id = parent_data_source
|
51
|
+
self._parent_data_source: NotionDataSource | None = None
|
52
|
+
self._data_source_loaded = False
|
53
|
+
|
54
|
+
# =========================================================================
|
55
|
+
# Reader Methods
|
56
|
+
# =========================================================================
|
57
|
+
|
58
|
+
def get_value_of_status_property(self, name: str) -> str | None:
|
59
|
+
status_property = self._get_typed_property_or_raise(name, PageStatusProperty)
|
60
|
+
return status_property.status.name if status_property.status else None
|
61
|
+
|
62
|
+
def get_value_of_select_property(self, name: str) -> str | None:
|
63
|
+
select_property = self._get_typed_property_or_raise(name, PageSelectProperty)
|
64
|
+
return select_property.select.name if select_property.select else None
|
65
|
+
|
66
|
+
async def get_value_of_title_property(self, name: str) -> str:
|
67
|
+
title_property = self._get_typed_property_or_raise(name, PageTitleProperty)
|
68
|
+
return await convert_rich_text_to_markdown(title_property.title)
|
69
|
+
|
70
|
+
def get_values_of_people_property(self, property_name: str) -> list[str]:
|
71
|
+
people_prop = self._get_typed_property_or_raise(property_name, PagePeopleProperty)
|
72
|
+
return [person.name for person in people_prop.people if person.name]
|
73
|
+
|
74
|
+
def get_value_of_created_time_property(self, name: str) -> str | None:
|
75
|
+
created_time_property = self._get_typed_property_or_raise(name, PageCreatedTimeProperty)
|
76
|
+
return created_time_property.created_time
|
77
|
+
|
78
|
+
async def get_values_of_relation_property(self, name: str) -> list[str]:
|
79
|
+
from notionary import NotionPage
|
80
|
+
|
81
|
+
relation_property = self._get_typed_property_or_raise(name, PageRelationProperty)
|
82
|
+
relation_page_ids = [rel.id for rel in relation_property.relation]
|
83
|
+
notion_pages = [await NotionPage.from_id(page_id) for page_id in relation_page_ids]
|
84
|
+
return [page.title for page in notion_pages if page]
|
85
|
+
|
86
|
+
def get_values_of_multiselect_property(self, name: str) -> list[str]:
|
87
|
+
multiselect_property = self._get_typed_property_or_raise(name, PageMultiSelectProperty)
|
88
|
+
return [option.name for option in multiselect_property.multi_select]
|
89
|
+
|
90
|
+
def get_value_of_url_property(self, name: str) -> str | None:
|
91
|
+
url_property = self._get_typed_property_or_raise(name, PageURLProperty)
|
92
|
+
return url_property.url
|
93
|
+
|
94
|
+
def get_value_of_number_property(self, name: str) -> float | None:
|
95
|
+
number_property = self._get_typed_property_or_raise(name, PageNumberProperty)
|
96
|
+
return number_property.number
|
97
|
+
|
98
|
+
def get_value_of_checkbox_property(self, name: str) -> bool:
|
99
|
+
checkbox_property = self._get_typed_property_or_raise(name, PageCheckboxProperty)
|
100
|
+
return checkbox_property.checkbox
|
101
|
+
|
102
|
+
def get_value_of_date_property(self, name: str) -> str | None:
|
103
|
+
date_property = self._get_typed_property_or_raise(name, PageDateProperty)
|
104
|
+
return date_property.date.start if date_property.date else None
|
105
|
+
|
106
|
+
async def get_value_of_rich_text_property(self, name: str) -> str:
|
107
|
+
rich_text_property = self._get_typed_property_or_raise(name, PageRichTextProperty)
|
108
|
+
return await convert_rich_text_to_markdown(rich_text_property.rich_text)
|
109
|
+
|
110
|
+
def get_value_of_email_property(self, name: str) -> str | None:
|
111
|
+
email_property = self._get_typed_property_or_raise(name, PageEmailProperty)
|
112
|
+
return email_property.email
|
113
|
+
|
114
|
+
def get_value_of_phone_number_property(self, name: str) -> str | None:
|
115
|
+
phone_property = self._get_typed_property_or_raise(name, PagePhoneNumberProperty)
|
116
|
+
return phone_property.phone_number
|
117
|
+
|
118
|
+
# =========================================================================
|
119
|
+
# Options Getters
|
120
|
+
# =========================================================================
|
121
|
+
|
122
|
+
async def get_select_options_by_property_name(self, property_name: str) -> list[str]:
|
123
|
+
data_source = await self._get_parent_data_source_or_raise()
|
124
|
+
return data_source.get_select_options_by_property_name(property_name)
|
125
|
+
|
126
|
+
async def get_multi_select_options_by_property_name(self, property_name: str) -> list[str]:
|
127
|
+
data_source = await self._get_parent_data_source_or_raise()
|
128
|
+
return data_source.get_multi_select_options_by_property_name(property_name)
|
129
|
+
|
130
|
+
async def get_status_options_by_property_name(self, property_name: str) -> list[str]:
|
131
|
+
data_source = await self._get_parent_data_source_or_raise()
|
132
|
+
return data_source.get_status_options_by_property_name(property_name)
|
133
|
+
|
134
|
+
async def get_relation_options_by_property_name(self, property_name: str) -> list[str]:
|
135
|
+
data_source = await self._get_parent_data_source_or_raise()
|
136
|
+
return await data_source.get_relation_options_by_property_name(property_name)
|
137
|
+
|
138
|
+
async def get_options_for_property_by_name(self, property_name: str) -> list[str]:
|
139
|
+
data_source = await self._get_parent_data_source_or_raise()
|
140
|
+
return await data_source.get_options_for_property_by_name(property_name)
|
141
|
+
|
142
|
+
# =========================================================================
|
143
|
+
# Writer Methods
|
144
|
+
# =========================================================================
|
145
|
+
|
146
|
+
async def set_title_property(self, property_name: str, title: str) -> None:
|
147
|
+
self._get_typed_property_or_raise(property_name, PageTitleProperty)
|
148
|
+
updated_page = await self._property_http_client.patch_title(property_name, title)
|
149
|
+
self._properties = updated_page.properties
|
150
|
+
|
151
|
+
async def set_rich_text_property(self, property_name: str, text: str) -> None:
|
152
|
+
self._get_typed_property_or_raise(property_name, PageRichTextProperty)
|
153
|
+
updated_page = await self._property_http_client.patch_rich_text_property(property_name, text)
|
154
|
+
self._properties = updated_page.properties
|
155
|
+
|
156
|
+
async def set_url_property(self, property_name: str, url: str) -> None:
|
157
|
+
self._get_typed_property_or_raise(property_name, PageURLProperty)
|
158
|
+
updated_page = await self._property_http_client.patch_url_property(property_name, url)
|
159
|
+
self._properties = updated_page.properties
|
160
|
+
|
161
|
+
async def set_email_property(self, property_name: str, email: str) -> None:
|
162
|
+
self._get_typed_property_or_raise(property_name, PageEmailProperty)
|
163
|
+
updated_page = await self._property_http_client.patch_email_property(property_name, email)
|
164
|
+
self._properties = updated_page.properties
|
165
|
+
|
166
|
+
async def set_phone_number_property(self, property_name: str, phone_number: str) -> None:
|
167
|
+
self._get_typed_property_or_raise(property_name, PagePhoneNumberProperty)
|
168
|
+
updated_page = await self._property_http_client.patch_phone_property(property_name, phone_number)
|
169
|
+
self._properties = updated_page.properties
|
170
|
+
|
171
|
+
async def set_number_property(self, property_name: str, number: int | float) -> None:
|
172
|
+
self._get_typed_property_or_raise(property_name, PageNumberProperty)
|
173
|
+
updated_page = await self._property_http_client.patch_number_property(property_name, number)
|
174
|
+
self._properties = updated_page.properties
|
175
|
+
|
176
|
+
async def set_checkbox_property(self, property_name: str, checked: bool) -> None:
|
177
|
+
self._get_typed_property_or_raise(property_name, PageCheckboxProperty)
|
178
|
+
updated_page = await self._property_http_client.patch_checkbox_property(property_name, checked)
|
179
|
+
self._properties = updated_page.properties
|
180
|
+
|
181
|
+
async def set_date_property(self, property_name: str, date_value: str | dict) -> None:
|
182
|
+
self._get_typed_property_or_raise(property_name, PageDateProperty)
|
183
|
+
updated_page = await self._property_http_client.patch_date_property(property_name, date_value)
|
184
|
+
self._properties = updated_page.properties
|
185
|
+
|
186
|
+
async def set_select_property_by_option_name(self, property_name: str, option_name: str) -> None:
|
187
|
+
self._get_typed_property_or_raise(property_name, PageSelectProperty)
|
188
|
+
updated_page = await self._property_http_client.patch_select_property(property_name, option_name)
|
189
|
+
self._properties = updated_page.properties
|
190
|
+
|
191
|
+
async def set_multi_select_property_by_option_names(self, property_name: str, option_names: list[str]) -> None:
|
192
|
+
self._get_typed_property_or_raise(property_name, PageMultiSelectProperty)
|
193
|
+
updated_page = await self._property_http_client.patch_multi_select_property(property_name, option_names)
|
194
|
+
self._properties = updated_page.properties
|
195
|
+
|
196
|
+
async def set_status_property_by_option_name(self, property_name: str, status: str) -> None:
|
197
|
+
self._get_typed_property_or_raise(property_name, PageStatusProperty)
|
198
|
+
updated_page = await self._property_http_client.patch_status_property(property_name, status)
|
199
|
+
self._properties = updated_page.properties
|
200
|
+
|
201
|
+
async def set_relation_property_by_page_titles(self, property_name: str, page_titles: list[str]) -> None:
|
202
|
+
self._get_typed_property_or_raise(property_name, PageRelationProperty)
|
203
|
+
relation_ids = await self._convert_page_titles_to_ids(page_titles)
|
204
|
+
updated_page = await self._property_http_client.patch_relation_property(property_name, relation_ids)
|
205
|
+
self._properties = updated_page.properties
|
206
|
+
|
207
|
+
async def _ensure_data_source_loaded(self) -> None:
|
208
|
+
from notionary import NotionDataSource
|
209
|
+
|
210
|
+
if self._data_source_loaded:
|
211
|
+
return
|
212
|
+
|
213
|
+
self._parent_data_source = (
|
214
|
+
await NotionDataSource.from_id(self._parent_data_source_id) if self._parent_data_source_id else None
|
215
|
+
)
|
216
|
+
self._data_source_loaded = True
|
217
|
+
|
218
|
+
async def _get_parent_data_source_or_raise(self) -> NotionDataSource:
|
219
|
+
await self._ensure_data_source_loaded()
|
220
|
+
|
221
|
+
if not self._parent_data_source:
|
222
|
+
raise AccessPagePropertyWithoutDataSourceError(self._parent_type)
|
223
|
+
return self._parent_data_source
|
224
|
+
|
225
|
+
def _get_typed_property_or_raise(self, name: str, property_type: type[PagePropertyT]) -> PagePropertyT:
|
226
|
+
prop = self._properties.get(name)
|
227
|
+
|
228
|
+
if prop is None:
|
229
|
+
self._handle_prop_not_found(name)
|
230
|
+
|
231
|
+
if not isinstance(prop, property_type):
|
232
|
+
self._handle_incorrect_type(name, type(prop))
|
233
|
+
|
234
|
+
return prop
|
235
|
+
|
236
|
+
def _handle_prop_not_found(self, name: str) -> Never:
|
237
|
+
raise PagePropertyNotFoundError(
|
238
|
+
property_name=name,
|
239
|
+
page_url=self._page_url,
|
240
|
+
available_properties=list(self._properties.keys()),
|
241
|
+
)
|
242
|
+
|
243
|
+
def _handle_incorrect_type(self, property_name: str, actual_type: type) -> Never:
|
244
|
+
raise PagePropertyTypeError(
|
245
|
+
property_name=property_name,
|
246
|
+
actual_type=actual_type.__name__,
|
247
|
+
)
|
248
|
+
|
249
|
+
async def _convert_page_titles_to_ids(self, page_titles: list[str]) -> list[str]:
|
250
|
+
from notionary import NotionPage
|
251
|
+
|
252
|
+
if not page_titles:
|
253
|
+
return []
|
254
|
+
|
255
|
+
pages = await asyncio.gather(*[NotionPage.from_title(title=title) for title in page_titles])
|
256
|
+
|
257
|
+
return [page.id for page in pages if page]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from pydantic import BaseModel
|
2
|
+
|
3
|
+
from notionary.page.properties.models import DiscriminatedPageProperty
|
4
|
+
from notionary.shared.entity.schemas import EntityResponseDto
|
5
|
+
|
6
|
+
|
7
|
+
class NotionPageDto(EntityResponseDto):
|
8
|
+
archived: bool
|
9
|
+
properties: dict[str, DiscriminatedPageProperty]
|
10
|
+
|
11
|
+
|
12
|
+
class PgePropertiesUpdateDto(BaseModel):
|
13
|
+
properties: dict[str, DiscriminatedPageProperty]
|
@@ -0,0 +1,222 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
from typing import Self
|
3
|
+
|
4
|
+
from notionary.blocks.client import NotionBlockHttpClient
|
5
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import convert_rich_text_to_markdown
|
6
|
+
from notionary.comments.models import Comment
|
7
|
+
from notionary.comments.service import CommentService
|
8
|
+
from notionary.page.content.factory import PageContentServiceFactory
|
9
|
+
from notionary.page.content.markdown.builder import MarkdownBuilder
|
10
|
+
from notionary.page.content.service import PageContentService
|
11
|
+
from notionary.page.page_http_client import NotionPageHttpClient
|
12
|
+
from notionary.page.page_metadata_update_client import PageMetadataUpdateClient
|
13
|
+
from notionary.page.properties.factory import PagePropertyHandlerFactory
|
14
|
+
from notionary.page.properties.models import PageTitleProperty
|
15
|
+
from notionary.page.properties.service import PagePropertyHandler
|
16
|
+
from notionary.page.schemas import NotionPageDto
|
17
|
+
from notionary.shared.entity.dto_parsers import (
|
18
|
+
extract_cover_image_url_from_dto,
|
19
|
+
extract_emoji_icon_from_dto,
|
20
|
+
extract_external_icon_url_from_dto,
|
21
|
+
)
|
22
|
+
from notionary.shared.entity.service import Entity
|
23
|
+
from notionary.user.schemas import PartialUserDto
|
24
|
+
from notionary.workspace.query.service import WorkspaceQueryService
|
25
|
+
|
26
|
+
|
27
|
+
class NotionPage(Entity):
|
28
|
+
def __init__(
|
29
|
+
self,
|
30
|
+
id: str,
|
31
|
+
title: str,
|
32
|
+
created_time: str,
|
33
|
+
created_by: PartialUserDto,
|
34
|
+
last_edited_time: str,
|
35
|
+
last_edited_by: PartialUserDto,
|
36
|
+
url: str,
|
37
|
+
archived: bool,
|
38
|
+
in_trash: bool,
|
39
|
+
page_property_handler: PagePropertyHandler,
|
40
|
+
block_client: NotionBlockHttpClient,
|
41
|
+
comment_service: CommentService,
|
42
|
+
page_content_service: PageContentService,
|
43
|
+
metadata_update_client: PageMetadataUpdateClient,
|
44
|
+
public_url: str | None = None,
|
45
|
+
emoji_icon: str | None = None,
|
46
|
+
external_icon_url: str | None = None,
|
47
|
+
cover_image_url: str | None = None,
|
48
|
+
) -> None:
|
49
|
+
super().__init__(
|
50
|
+
id=id,
|
51
|
+
created_time=created_time,
|
52
|
+
created_by=created_by,
|
53
|
+
last_edited_time=last_edited_time,
|
54
|
+
last_edited_by=last_edited_by,
|
55
|
+
in_trash=in_trash,
|
56
|
+
emoji_icon=emoji_icon,
|
57
|
+
external_icon_url=external_icon_url,
|
58
|
+
cover_image_url=cover_image_url,
|
59
|
+
)
|
60
|
+
self._title = title
|
61
|
+
self._archived = archived
|
62
|
+
self._url = url
|
63
|
+
self._public_url = public_url
|
64
|
+
|
65
|
+
self._block_client = block_client
|
66
|
+
self._comment_service = comment_service
|
67
|
+
self._page_content_service = page_content_service
|
68
|
+
self.properties = page_property_handler
|
69
|
+
self._metadata_update_client = metadata_update_client
|
70
|
+
|
71
|
+
@classmethod
|
72
|
+
async def from_id(
|
73
|
+
cls,
|
74
|
+
page_id: str,
|
75
|
+
page_property_handler_factory: PagePropertyHandlerFactory | None = None,
|
76
|
+
) -> Self:
|
77
|
+
factory = page_property_handler_factory or PagePropertyHandlerFactory()
|
78
|
+
response = await cls._fetch_page_dto(page_id)
|
79
|
+
return await cls._create_from_dto(response, factory)
|
80
|
+
|
81
|
+
@classmethod
|
82
|
+
async def from_title(
|
83
|
+
cls,
|
84
|
+
page_title: str,
|
85
|
+
min_similarity: float = 0.6,
|
86
|
+
search_service: WorkspaceQueryService | None = None,
|
87
|
+
) -> Self:
|
88
|
+
service = search_service or WorkspaceQueryService()
|
89
|
+
return await service.find_page(page_title, min_similarity=min_similarity)
|
90
|
+
|
91
|
+
@classmethod
|
92
|
+
async def _fetch_page_dto(cls, page_id: str) -> NotionPageDto:
|
93
|
+
async with NotionPageHttpClient(page_id=page_id) as client:
|
94
|
+
return await client.get_page()
|
95
|
+
|
96
|
+
@classmethod
|
97
|
+
async def _create_from_dto(
|
98
|
+
cls,
|
99
|
+
response: NotionPageDto,
|
100
|
+
page_property_handler_factory: PagePropertyHandlerFactory,
|
101
|
+
) -> Self:
|
102
|
+
title_task = cls._extract_title_from_dto(response)
|
103
|
+
page_property_handler = page_property_handler_factory.create_from_page_response(response)
|
104
|
+
|
105
|
+
title = await title_task
|
106
|
+
|
107
|
+
return cls._create_with_dependencies(
|
108
|
+
id=response.id,
|
109
|
+
title=title,
|
110
|
+
created_time=response.created_time,
|
111
|
+
created_by=response.created_by,
|
112
|
+
last_edited_time=response.last_edited_time,
|
113
|
+
last_edited_by=response.last_edited_by,
|
114
|
+
archived=response.archived,
|
115
|
+
in_trash=response.in_trash,
|
116
|
+
url=response.url,
|
117
|
+
page_property_handler=page_property_handler,
|
118
|
+
public_url=response.public_url,
|
119
|
+
emoji_icon=extract_emoji_icon_from_dto(response),
|
120
|
+
external_icon_url=extract_external_icon_url_from_dto(response),
|
121
|
+
cover_image_url=extract_cover_image_url_from_dto(response),
|
122
|
+
)
|
123
|
+
|
124
|
+
@classmethod
|
125
|
+
def _create_with_dependencies(
|
126
|
+
cls,
|
127
|
+
id: str,
|
128
|
+
title: str,
|
129
|
+
created_time: str,
|
130
|
+
created_by: PartialUserDto,
|
131
|
+
last_edited_time: str,
|
132
|
+
last_edited_by: PartialUserDto,
|
133
|
+
url: str,
|
134
|
+
archived: bool,
|
135
|
+
in_trash: bool,
|
136
|
+
page_property_handler: PagePropertyHandler,
|
137
|
+
public_url: str | None = None,
|
138
|
+
emoji_icon: str | None = None,
|
139
|
+
external_icon_url: str | None = None,
|
140
|
+
cover_image_url: str | None = None,
|
141
|
+
) -> Self:
|
142
|
+
block_client = NotionBlockHttpClient()
|
143
|
+
comment_service = CommentService()
|
144
|
+
|
145
|
+
page_content_service_factory = PageContentServiceFactory()
|
146
|
+
page_content_service = page_content_service_factory.create(page_id=id, block_client=block_client)
|
147
|
+
|
148
|
+
metadata_update_client = PageMetadataUpdateClient(page_id=id)
|
149
|
+
|
150
|
+
return cls(
|
151
|
+
id=id,
|
152
|
+
title=title,
|
153
|
+
created_time=created_time,
|
154
|
+
created_by=created_by,
|
155
|
+
last_edited_time=last_edited_time,
|
156
|
+
last_edited_by=last_edited_by,
|
157
|
+
url=url,
|
158
|
+
archived=archived,
|
159
|
+
in_trash=in_trash,
|
160
|
+
page_property_handler=page_property_handler,
|
161
|
+
block_client=block_client,
|
162
|
+
comment_service=comment_service,
|
163
|
+
page_content_service=page_content_service,
|
164
|
+
metadata_update_client=metadata_update_client,
|
165
|
+
public_url=public_url,
|
166
|
+
emoji_icon=emoji_icon,
|
167
|
+
external_icon_url=external_icon_url,
|
168
|
+
cover_image_url=cover_image_url,
|
169
|
+
)
|
170
|
+
|
171
|
+
@staticmethod
|
172
|
+
async def _extract_title_from_dto(response: NotionPageDto) -> str:
|
173
|
+
title_property = next(
|
174
|
+
(prop for prop in response.properties.values() if isinstance(prop, PageTitleProperty)),
|
175
|
+
None,
|
176
|
+
)
|
177
|
+
rich_text_title = title_property.title if title_property else []
|
178
|
+
return await convert_rich_text_to_markdown(rich_text_title)
|
179
|
+
|
180
|
+
@property
|
181
|
+
def _entity_metadata_update_client(self) -> PageMetadataUpdateClient:
|
182
|
+
return self._metadata_update_client
|
183
|
+
|
184
|
+
@property
|
185
|
+
def title(self) -> str:
|
186
|
+
return self._title
|
187
|
+
|
188
|
+
@property
|
189
|
+
def url(self) -> str:
|
190
|
+
return self._url
|
191
|
+
|
192
|
+
async def get_comments(self) -> list[Comment]:
|
193
|
+
return await self._comment_service.list_all_comments_for_page(page_id=self._id)
|
194
|
+
|
195
|
+
async def post_top_level_comment(self, comment: str) -> None:
|
196
|
+
await self._comment_service.create_comment_on_page(page_id=self._id, text=comment)
|
197
|
+
|
198
|
+
async def post_reply_to_discussion(self, discussion_id: str, comment: str) -> None:
|
199
|
+
await self._comment_service.reply_to_discussion_by_id(discussion_id=discussion_id, text=comment)
|
200
|
+
|
201
|
+
async def set_title(self, title: str) -> None:
|
202
|
+
await self.properties.set_title_property(title)
|
203
|
+
self._title = title
|
204
|
+
|
205
|
+
async def append_markdown(
|
206
|
+
self,
|
207
|
+
content: (str | Callable[[MarkdownBuilder], MarkdownBuilder]),
|
208
|
+
) -> None:
|
209
|
+
await self._page_content_service.append_markdown(content=content)
|
210
|
+
|
211
|
+
async def replace_content(
|
212
|
+
self,
|
213
|
+
content: (str | Callable[[MarkdownBuilder], MarkdownBuilder]),
|
214
|
+
) -> None:
|
215
|
+
await self._page_content_service.clear()
|
216
|
+
await self._page_content_service.append_markdown(content=content)
|
217
|
+
|
218
|
+
async def clear_page_content(self) -> None:
|
219
|
+
await self._page_content_service.clear()
|
220
|
+
|
221
|
+
async def get_markdown_content(self) -> str:
|
222
|
+
return await self._page_content_service.get_as_markdown()
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from typing import TypeVar, override
|
2
|
+
|
3
|
+
from notionary.http.client import NotionHttpClient
|
4
|
+
from notionary.shared.entity.entity_metadata_update_client import EntityMetadataUpdateClient
|
5
|
+
from notionary.shared.entity.schemas import EntityResponseDto, NotionEntityUpdateDto
|
6
|
+
|
7
|
+
ResponseType = TypeVar("ResponseType", bound=EntityResponseDto)
|
8
|
+
|
9
|
+
|
10
|
+
class GenericEntityMetadataUpdateClient(NotionHttpClient, EntityMetadataUpdateClient):
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
entity_id: str,
|
14
|
+
path_segment: str,
|
15
|
+
response_dto_class: type[ResponseType],
|
16
|
+
timeout: int = 30,
|
17
|
+
) -> None:
|
18
|
+
super().__init__(timeout)
|
19
|
+
self._entity_id = entity_id
|
20
|
+
self._path_segment = path_segment
|
21
|
+
self._response_dto_class = response_dto_class
|
22
|
+
|
23
|
+
@override
|
24
|
+
async def patch_metadata(self, updated_data: NotionEntityUpdateDto) -> ResponseType:
|
25
|
+
updated_data_dict = updated_data.model_dump(exclude_unset=True, exclude_none=True)
|
26
|
+
url = f"{self._path_segment}/{self._entity_id}"
|
27
|
+
|
28
|
+
response_dict = await self.patch(url, data=updated_data_dict)
|
29
|
+
return self._response_dto_class.model_validate(response_dict)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from typing import cast
|
2
|
+
|
3
|
+
from notionary.blocks.rich_text.rich_text_markdown_converter import RichTextToMarkdownConverter
|
4
|
+
from notionary.shared.entity.schemas import Describable, EntityResponseDto, Titled
|
5
|
+
from notionary.shared.models.cover import CoverType
|
6
|
+
from notionary.shared.models.icon import IconType
|
7
|
+
from notionary.shared.models.parent import DatabaseParent, DataSourceParent, ParentType
|
8
|
+
|
9
|
+
|
10
|
+
def extract_emoji_icon_from_dto(entity_dto: EntityResponseDto) -> str | None:
|
11
|
+
if not entity_dto.icon or entity_dto.icon.type != IconType.EMOJI:
|
12
|
+
return None
|
13
|
+
return entity_dto.icon.emoji
|
14
|
+
|
15
|
+
|
16
|
+
def extract_external_icon_url_from_dto(entity_dto: EntityResponseDto) -> str | None:
|
17
|
+
if not entity_dto.icon or entity_dto.icon.type != IconType.EXTERNAL:
|
18
|
+
return None
|
19
|
+
return entity_dto.icon.external.url if entity_dto.icon.external else None
|
20
|
+
|
21
|
+
|
22
|
+
def extract_cover_image_url_from_dto(entity_dto: EntityResponseDto) -> str | None:
|
23
|
+
if not entity_dto.cover or entity_dto.cover.type != CoverType.EXTERNAL:
|
24
|
+
return None
|
25
|
+
return entity_dto.cover.external.url if entity_dto.cover.external else None
|
26
|
+
|
27
|
+
|
28
|
+
def extract_database_id(entity_dto: EntityResponseDto) -> str | None:
|
29
|
+
if entity_dto.parent.type == ParentType.DATA_SOURCE_ID:
|
30
|
+
data_source_parent = cast(DataSourceParent, entity_dto.parent)
|
31
|
+
return data_source_parent.database_id if data_source_parent else None
|
32
|
+
|
33
|
+
if entity_dto.parent.type == ParentType.DATABASE_ID:
|
34
|
+
database_parent = cast(DatabaseParent, entity_dto.parent)
|
35
|
+
return database_parent.database_id if database_parent else None
|
36
|
+
|
37
|
+
return None
|
38
|
+
|
39
|
+
|
40
|
+
async def extract_title(
|
41
|
+
entity: Titled,
|
42
|
+
rich_text_converter: RichTextToMarkdownConverter,
|
43
|
+
) -> str:
|
44
|
+
return await rich_text_converter.to_markdown(entity.title)
|
45
|
+
|
46
|
+
|
47
|
+
async def extract_description(
|
48
|
+
entity: Describable,
|
49
|
+
rich_text_converter: RichTextToMarkdownConverter,
|
50
|
+
) -> str | None:
|
51
|
+
if not entity.description:
|
52
|
+
return None
|
53
|
+
return await rich_text_converter.to_markdown(entity.description)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from notionary.shared.entity.schemas import EntityResponseDto, NotionEntityUpdateDto
|
4
|
+
from notionary.shared.models.cover import NotionCover
|
5
|
+
from notionary.shared.models.icon import EmojiIcon, ExternalIcon
|
6
|
+
|
7
|
+
|
8
|
+
class EntityMetadataUpdateClient(ABC):
|
9
|
+
@abstractmethod
|
10
|
+
async def patch_metadata(self, updated_data: NotionEntityUpdateDto) -> EntityResponseDto: ...
|
11
|
+
|
12
|
+
async def patch_emoji_icon(self, emoji: str) -> EntityResponseDto:
|
13
|
+
icon = EmojiIcon(emoji=emoji)
|
14
|
+
update_dto = NotionEntityUpdateDto(icon=icon)
|
15
|
+
return await self.patch_metadata(update_dto)
|
16
|
+
|
17
|
+
async def patch_external_icon(self, icon_url: str) -> EntityResponseDto:
|
18
|
+
icon = ExternalIcon.from_url(icon_url)
|
19
|
+
update_dto = NotionEntityUpdateDto(icon=icon)
|
20
|
+
return await self.patch_metadata(update_dto)
|
21
|
+
|
22
|
+
async def remove_icon(self) -> None:
|
23
|
+
update_dto = NotionEntityUpdateDto(icon=None)
|
24
|
+
return await self.patch_metadata(update_dto)
|
25
|
+
|
26
|
+
async def patch_external_cover(self, cover_url: str) -> EntityResponseDto:
|
27
|
+
cover = NotionCover.from_url(cover_url)
|
28
|
+
update_dto = NotionEntityUpdateDto(cover=cover)
|
29
|
+
return await self.patch_metadata(update_dto)
|
30
|
+
|
31
|
+
async def remove_cover(self) -> None:
|
32
|
+
update_dto = NotionEntityUpdateDto(cover=None)
|
33
|
+
return await self.patch_metadata(update_dto)
|
34
|
+
|
35
|
+
async def move_to_trash(self) -> EntityResponseDto:
|
36
|
+
update_dto = NotionEntityUpdateDto(in_trash=True)
|
37
|
+
return await self.patch_metadata(update_dto)
|
38
|
+
|
39
|
+
async def restore_from_trash(self) -> EntityResponseDto:
|
40
|
+
update_dto = NotionEntityUpdateDto(in_trash=False)
|
41
|
+
return await self.patch_metadata(update_dto)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from enum import StrEnum
|
2
|
+
from typing import Protocol
|
3
|
+
|
4
|
+
from pydantic import BaseModel
|
5
|
+
|
6
|
+
from notionary.blocks.rich_text.models import RichText
|
7
|
+
from notionary.shared.models.cover import NotionCover
|
8
|
+
from notionary.shared.models.icon import Icon
|
9
|
+
from notionary.shared.models.parent import Parent
|
10
|
+
from notionary.user.schemas import PartialUserDto
|
11
|
+
|
12
|
+
|
13
|
+
class EntityWorkspaceQueryObjectType(StrEnum):
|
14
|
+
PAGE = "page"
|
15
|
+
DATA_SOURCE = "data_source"
|
16
|
+
DATABASE = "database"
|
17
|
+
|
18
|
+
|
19
|
+
class EntityResponseDto(BaseModel):
|
20
|
+
object: EntityWorkspaceQueryObjectType
|
21
|
+
id: str
|
22
|
+
created_time: str
|
23
|
+
created_by: PartialUserDto
|
24
|
+
last_edited_time: str
|
25
|
+
last_edited_by: PartialUserDto
|
26
|
+
cover: NotionCover | None = None
|
27
|
+
icon: Icon | None = None
|
28
|
+
parent: Parent
|
29
|
+
in_trash: bool
|
30
|
+
url: str
|
31
|
+
public_url: str | None = None
|
32
|
+
|
33
|
+
|
34
|
+
class NotionEntityUpdateDto(BaseModel):
|
35
|
+
icon: Icon | None = None
|
36
|
+
cover: NotionCover | None = None
|
37
|
+
in_trash: bool | None = None
|
38
|
+
|
39
|
+
|
40
|
+
class Titled(Protocol):
|
41
|
+
title: list[RichText]
|
42
|
+
|
43
|
+
|
44
|
+
class Describable(Protocol):
|
45
|
+
description: list[RichText] | None
|