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
notionary/page/notion_page.py
DELETED
@@ -1,674 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from ast import Dict
|
4
|
-
import asyncio
|
5
|
-
import random
|
6
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
7
|
-
|
8
|
-
from yaml import Token
|
9
|
-
|
10
|
-
from notionary.blocks.client import NotionBlockClient
|
11
|
-
from notionary.comments import CommentClient, Comment
|
12
|
-
from notionary.blocks.syntax_prompt_builder import SyntaxPromptBuilder
|
13
|
-
from notionary.blocks.models import DatabaseParent, ParentObject
|
14
|
-
from notionary.blocks.registry.block_registry import BlockRegistry
|
15
|
-
from notionary.database.client import NotionDatabaseClient
|
16
|
-
from notionary.file_upload.client import NotionFileUploadClient
|
17
|
-
from notionary.blocks.markdown.markdown_builder import MarkdownBuilder
|
18
|
-
from notionary.schemas import NotionContentSchema
|
19
|
-
from notionary.page import page_context
|
20
|
-
from notionary.page.client import NotionPageClient
|
21
|
-
from notionary.page.models import NotionPageResponse
|
22
|
-
from notionary.page.page_content_deleting_service import PageContentDeletingService
|
23
|
-
from notionary.page.page_content_writer import PageContentWriter
|
24
|
-
from notionary.page.page_context import PageContextProvider, page_context
|
25
|
-
from notionary.page.property_formatter import NotionPropertyFormatter
|
26
|
-
from notionary.page.reader.page_content_retriever import PageContentRetriever
|
27
|
-
from notionary.page.utils import extract_property_value
|
28
|
-
from notionary.util import LoggingMixin, extract_uuid, factory_only, format_uuid
|
29
|
-
from notionary.util.fuzzy import find_best_match
|
30
|
-
|
31
|
-
if TYPE_CHECKING:
|
32
|
-
from notionary import NotionDatabase
|
33
|
-
|
34
|
-
|
35
|
-
class NotionPage(LoggingMixin):
|
36
|
-
"""
|
37
|
-
Managing content and metadata of a Notion page.
|
38
|
-
"""
|
39
|
-
|
40
|
-
@factory_only("from_page_id", "from_page_name", "from_url")
|
41
|
-
def __init__(
|
42
|
-
self,
|
43
|
-
page_id: str,
|
44
|
-
title: str,
|
45
|
-
url: str,
|
46
|
-
archived: bool,
|
47
|
-
in_trash: bool,
|
48
|
-
emoji_icon: Optional[str] = None,
|
49
|
-
properties: Optional[dict[str, Any]] = None,
|
50
|
-
parent_database: Optional[NotionDatabase] = None,
|
51
|
-
token: Optional[str] = None,
|
52
|
-
):
|
53
|
-
"""
|
54
|
-
Initialize the page manager with all metadata.
|
55
|
-
"""
|
56
|
-
self._page_id = page_id
|
57
|
-
self._title = title
|
58
|
-
self._url = url
|
59
|
-
self._is_archived = archived
|
60
|
-
self._is_in_trash = in_trash
|
61
|
-
self._emoji_icon = emoji_icon
|
62
|
-
self._properties = properties
|
63
|
-
self._parent_database = parent_database
|
64
|
-
|
65
|
-
self._client = NotionPageClient(token=token)
|
66
|
-
self._block_client = NotionBlockClient(token=token)
|
67
|
-
self._database_client = NotionDatabaseClient(token=token)
|
68
|
-
self._comment_client = CommentClient(token=token)
|
69
|
-
self._page_data = None
|
70
|
-
|
71
|
-
self.block_element_registry = BlockRegistry.create_registry()
|
72
|
-
|
73
|
-
self._page_content_writer = PageContentWriter(
|
74
|
-
page_id=self._page_id,
|
75
|
-
block_registry=self.block_element_registry,
|
76
|
-
)
|
77
|
-
|
78
|
-
self._page_content_deleting_service = PageContentDeletingService(
|
79
|
-
page_id=self._page_id,
|
80
|
-
block_registry=self.block_element_registry,
|
81
|
-
)
|
82
|
-
|
83
|
-
self._page_content_retriever = PageContentRetriever(
|
84
|
-
block_registry=self.block_element_registry,
|
85
|
-
)
|
86
|
-
|
87
|
-
self.page_context_provider = self._setup_page_context_provider()
|
88
|
-
|
89
|
-
@classmethod
|
90
|
-
async def from_page_id(
|
91
|
-
cls, page_id: str, token: Optional[str] = None
|
92
|
-
) -> NotionPage:
|
93
|
-
"""
|
94
|
-
Create a NotionPage from a page ID.
|
95
|
-
"""
|
96
|
-
formatted_id = format_uuid(page_id) or page_id
|
97
|
-
|
98
|
-
async with NotionPageClient(token=token) as client:
|
99
|
-
page_response = await client.get_page(formatted_id)
|
100
|
-
return await cls._create_from_response(page_response, token)
|
101
|
-
|
102
|
-
@classmethod
|
103
|
-
async def from_page_name(
|
104
|
-
cls, page_name: str, token: Optional[str] = None, min_similarity: float = 0.6
|
105
|
-
) -> NotionPage:
|
106
|
-
"""
|
107
|
-
Create a NotionPage by finding a page with fuzzy matching on the title.
|
108
|
-
Uses Notion's search API and fuzzy matching to find the best result.
|
109
|
-
"""
|
110
|
-
from notionary.workspace import NotionWorkspace
|
111
|
-
|
112
|
-
workspace = NotionWorkspace()
|
113
|
-
|
114
|
-
try:
|
115
|
-
search_results: list[NotionPage] = await workspace.search_pages(
|
116
|
-
page_name, limit=5
|
117
|
-
)
|
118
|
-
|
119
|
-
if not search_results:
|
120
|
-
cls.logger.warning("No pages found for name: %s", page_name)
|
121
|
-
raise ValueError(f"No pages found for name: {page_name}")
|
122
|
-
|
123
|
-
best_match = find_best_match(
|
124
|
-
query=page_name,
|
125
|
-
items=search_results,
|
126
|
-
text_extractor=lambda page: page.title,
|
127
|
-
min_similarity=min_similarity,
|
128
|
-
)
|
129
|
-
|
130
|
-
if not best_match:
|
131
|
-
available_titles = [result.title for result in search_results[:5]]
|
132
|
-
cls.logger.warning(
|
133
|
-
"No sufficiently similar page found for '%s' (min: %.3f). Available: %s",
|
134
|
-
page_name,
|
135
|
-
min_similarity,
|
136
|
-
available_titles,
|
137
|
-
)
|
138
|
-
raise ValueError(
|
139
|
-
f"No sufficiently similar page found for '{page_name}'"
|
140
|
-
)
|
141
|
-
|
142
|
-
async with NotionPageClient(token=token) as client:
|
143
|
-
page_response = await client.get_page(page_id=best_match.item.id)
|
144
|
-
instance = await cls._create_from_response(
|
145
|
-
page_response=page_response, token=token
|
146
|
-
)
|
147
|
-
return instance
|
148
|
-
|
149
|
-
except Exception as e:
|
150
|
-
cls.logger.error("Error finding page by name: %s", str(e))
|
151
|
-
raise
|
152
|
-
|
153
|
-
@classmethod
|
154
|
-
async def from_url(cls, url: str, token: Optional[str] = None) -> NotionPage:
|
155
|
-
"""
|
156
|
-
Create a NotionPage from a Notion page URL.
|
157
|
-
"""
|
158
|
-
try:
|
159
|
-
page_id = extract_uuid(url)
|
160
|
-
if not page_id:
|
161
|
-
raise ValueError(f"Could not extract page ID from URL: {url}")
|
162
|
-
|
163
|
-
formatted_id = format_uuid(page_id) or page_id
|
164
|
-
|
165
|
-
async with NotionPageClient(token=token) as client:
|
166
|
-
page_response = await client.get_page(formatted_id)
|
167
|
-
return await cls._create_from_response(page_response, token)
|
168
|
-
|
169
|
-
except Exception as e:
|
170
|
-
cls.logger.error("Error creating page from URL '%s': %s", url, str(e))
|
171
|
-
raise
|
172
|
-
|
173
|
-
@property
|
174
|
-
def id(self) -> str:
|
175
|
-
"""
|
176
|
-
Get the ID of the page.
|
177
|
-
"""
|
178
|
-
return self._page_id
|
179
|
-
|
180
|
-
@property
|
181
|
-
def title(self) -> str:
|
182
|
-
"""
|
183
|
-
Get the title of the page.
|
184
|
-
"""
|
185
|
-
return self._title
|
186
|
-
|
187
|
-
@property
|
188
|
-
def url(self) -> str:
|
189
|
-
"""
|
190
|
-
Get the URL of the page.
|
191
|
-
If not set, generate it from the title and ID.
|
192
|
-
"""
|
193
|
-
return self._url
|
194
|
-
|
195
|
-
@property
|
196
|
-
def emoji_icon(self) -> Optional[str]:
|
197
|
-
"""
|
198
|
-
Get the emoji icon of the page.
|
199
|
-
"""
|
200
|
-
return self._emoji_icon
|
201
|
-
|
202
|
-
@property
|
203
|
-
def properties(self) -> Optional[dict[str, Any]]:
|
204
|
-
"""
|
205
|
-
Get the properties of the page.
|
206
|
-
"""
|
207
|
-
return self._properties
|
208
|
-
|
209
|
-
@property
|
210
|
-
def is_archived(self) -> bool:
|
211
|
-
return self._is_archived
|
212
|
-
|
213
|
-
@property
|
214
|
-
def is_in_trash(self) -> bool:
|
215
|
-
return self._is_in_trash
|
216
|
-
|
217
|
-
def get_prompt_information(self) -> str:
|
218
|
-
markdown_syntax_builder = SyntaxPromptBuilder()
|
219
|
-
return markdown_syntax_builder.build_concise_reference()
|
220
|
-
|
221
|
-
async def get_comments(self) -> list[Comment]:
|
222
|
-
return await self._comment_client.list_all_comments_for_page(
|
223
|
-
page_id=self._page_id
|
224
|
-
)
|
225
|
-
|
226
|
-
async def post_comment(
|
227
|
-
self,
|
228
|
-
content: str,
|
229
|
-
*,
|
230
|
-
discussion_id: Optional[str] = None,
|
231
|
-
rich_text: Optional[list[dict[str, Any]]] = None,
|
232
|
-
) -> Optional[Comment]:
|
233
|
-
"""
|
234
|
-
Post a comment on this page.
|
235
|
-
|
236
|
-
Args:
|
237
|
-
content: The plain text content of the comment
|
238
|
-
discussion_id: Optional discussion ID to reply to an existing discussion
|
239
|
-
rich_text: Optional rich text formatting for the comment content
|
240
|
-
|
241
|
-
Returns:
|
242
|
-
Comment: The created comment object, or None if creation failed
|
243
|
-
"""
|
244
|
-
try:
|
245
|
-
# Use the comment client to create the comment
|
246
|
-
comment = await self._comment_client.create_comment(
|
247
|
-
page_id=self._page_id,
|
248
|
-
content=content,
|
249
|
-
discussion_id=discussion_id,
|
250
|
-
rich_text=rich_text,
|
251
|
-
)
|
252
|
-
self.logger.info(f"Successfully posted comment on page '{self._title}'")
|
253
|
-
return comment
|
254
|
-
except Exception as e:
|
255
|
-
self.logger.error(
|
256
|
-
f"Failed to post comment on page '{self._title}': {str(e)}"
|
257
|
-
)
|
258
|
-
return None
|
259
|
-
|
260
|
-
async def set_title(self, title: str) -> str:
|
261
|
-
"""
|
262
|
-
Set the title of the page.
|
263
|
-
"""
|
264
|
-
try:
|
265
|
-
data = {
|
266
|
-
"properties": {
|
267
|
-
"title": {"title": [{"type": "text", "text": {"content": title}}]}
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
await self._client.patch_page(self._page_id, data)
|
272
|
-
|
273
|
-
self._title = title
|
274
|
-
return title
|
275
|
-
|
276
|
-
except Exception as e:
|
277
|
-
self.logger.error("Error setting page title: %s", str(e))
|
278
|
-
|
279
|
-
async def append_markdown(
|
280
|
-
self,
|
281
|
-
content: Union[
|
282
|
-
str, Callable[[MarkdownBuilder], MarkdownBuilder], NotionContentSchema
|
283
|
-
],
|
284
|
-
) -> bool:
|
285
|
-
"""
|
286
|
-
Append markdown content to the page using text, builder callback, MarkdownDocumentModel, or NotionContentSchema.
|
287
|
-
"""
|
288
|
-
async with page_context(self.page_context_provider):
|
289
|
-
result = await self._page_content_writer.append_markdown(
|
290
|
-
content=content,
|
291
|
-
)
|
292
|
-
return result is not None
|
293
|
-
|
294
|
-
async def replace_content(
|
295
|
-
self,
|
296
|
-
content: Union[
|
297
|
-
str, Callable[[MarkdownBuilder], MarkdownBuilder], NotionContentSchema
|
298
|
-
],
|
299
|
-
) -> bool:
|
300
|
-
"""
|
301
|
-
Replace the entire page content with new markdown content.
|
302
|
-
|
303
|
-
Args:
|
304
|
-
content: Either raw markdown text, a callback function that receives a MarkdownBuilder,
|
305
|
-
a MarkdownDocumentModel, or a NotionContentSchema
|
306
|
-
|
307
|
-
Returns:
|
308
|
-
bool: True if successful, False otherwise
|
309
|
-
"""
|
310
|
-
clear_result = await self._page_content_deleting_service.clear_page_content()
|
311
|
-
if not clear_result:
|
312
|
-
self.logger.error("Failed to clear page content before replacement")
|
313
|
-
|
314
|
-
result = await self._page_content_writer.append_markdown(
|
315
|
-
content=content,
|
316
|
-
)
|
317
|
-
return result is not None
|
318
|
-
|
319
|
-
async def clear_page_content(self) -> str:
|
320
|
-
"""
|
321
|
-
Clear all content from the page.
|
322
|
-
"""
|
323
|
-
return await self._page_content_deleting_service.clear_page_content()
|
324
|
-
|
325
|
-
async def get_text_content(self) -> str:
|
326
|
-
"""
|
327
|
-
Get the text content of the page.
|
328
|
-
|
329
|
-
Returns:
|
330
|
-
str: The text content of the page.
|
331
|
-
"""
|
332
|
-
blocks = await self._block_client.get_blocks_by_page_id_recursively(
|
333
|
-
page_id=self._page_id
|
334
|
-
)
|
335
|
-
return await self._page_content_retriever.convert_to_markdown(blocks=blocks)
|
336
|
-
|
337
|
-
async def set_emoji_icon(self, emoji: str) -> Optional[str]:
|
338
|
-
"""
|
339
|
-
Sets the page icon to an emoji.
|
340
|
-
"""
|
341
|
-
try:
|
342
|
-
icon = {"type": "emoji", "emoji": emoji}
|
343
|
-
page_response = await self._client.patch_page(
|
344
|
-
page_id=self._page_id, data={"icon": icon}
|
345
|
-
)
|
346
|
-
|
347
|
-
self._emoji = page_response.icon.emoji
|
348
|
-
return page_response.icon.emoji
|
349
|
-
except Exception as e:
|
350
|
-
|
351
|
-
self.logger.error(f"Error updating page emoji: {str(e)}")
|
352
|
-
return None
|
353
|
-
|
354
|
-
async def create_child_database(self, title: str) -> NotionDatabase:
|
355
|
-
from notionary import NotionDatabase
|
356
|
-
|
357
|
-
database_client = NotionDatabaseClient(token=self._client.token)
|
358
|
-
|
359
|
-
create_database_response = await database_client.create_database(
|
360
|
-
title=title,
|
361
|
-
parent_page_id=self._page_id,
|
362
|
-
)
|
363
|
-
|
364
|
-
return await NotionDatabase.from_database_id(
|
365
|
-
id=create_database_response.id, token=self._client.token
|
366
|
-
)
|
367
|
-
|
368
|
-
async def create_child_page(self, title: str) -> NotionPage:
|
369
|
-
from notionary import NotionPage
|
370
|
-
|
371
|
-
child_page_response = await self._client.create_page(
|
372
|
-
parent_page_id=self._page_id,
|
373
|
-
title=title,
|
374
|
-
)
|
375
|
-
|
376
|
-
return await NotionPage.from_page_id(
|
377
|
-
page_id=child_page_response.id, token=self._client.token
|
378
|
-
)
|
379
|
-
|
380
|
-
async def set_external_icon(self, url: str) -> Optional[str]:
|
381
|
-
"""
|
382
|
-
Sets the page icon to an external image.
|
383
|
-
"""
|
384
|
-
try:
|
385
|
-
icon = {"type": "external", "external": {"url": url}}
|
386
|
-
page_response = await self._client.patch_page(
|
387
|
-
page_id=self._page_id, data={"icon": icon}
|
388
|
-
)
|
389
|
-
|
390
|
-
# For external icons, we clear the emoji since we now have external icon
|
391
|
-
self._emoji = None
|
392
|
-
self.logger.info(f"Successfully updated page external icon to: {url}")
|
393
|
-
return page_response.icon.external.url
|
394
|
-
|
395
|
-
except Exception as e:
|
396
|
-
self.logger.error(f"Error updating page external icon: {str(e)}")
|
397
|
-
return None
|
398
|
-
|
399
|
-
async def get_cover_url(self) -> Optional[str]:
|
400
|
-
"""
|
401
|
-
Get the URL of the page cover image.
|
402
|
-
"""
|
403
|
-
try:
|
404
|
-
page_data = await self._client.get_page(self.id)
|
405
|
-
if not page_data or not page_data.cover:
|
406
|
-
return None
|
407
|
-
if page_data.cover.type == "external":
|
408
|
-
return page_data.cover.external.url
|
409
|
-
except Exception as e:
|
410
|
-
self.logger.error(f"Error fetching cover URL: {str(e)}")
|
411
|
-
return None
|
412
|
-
|
413
|
-
async def set_cover(self, external_url: str) -> Optional[str]:
|
414
|
-
"""
|
415
|
-
Set the cover image for the page using an external URL.
|
416
|
-
"""
|
417
|
-
data = {"cover": {"type": "external", "external": {"url": external_url}}}
|
418
|
-
try:
|
419
|
-
updated_page = await self._client.patch_page(self.id, data=data)
|
420
|
-
return updated_page.cover.external.url
|
421
|
-
except Exception as e:
|
422
|
-
self.logger.error("Failed to set cover image: %s", str(e))
|
423
|
-
return None
|
424
|
-
|
425
|
-
async def set_random_gradient_cover(self) -> Optional[str]:
|
426
|
-
"""
|
427
|
-
Set a random gradient as the page cover.
|
428
|
-
"""
|
429
|
-
default_notion_covers = [
|
430
|
-
f"https://www.notion.so/images/page-cover/gradients_{i}.png"
|
431
|
-
for i in range(1, 10)
|
432
|
-
]
|
433
|
-
random_cover_url = random.choice(default_notion_covers)
|
434
|
-
return await self.set_cover(random_cover_url)
|
435
|
-
|
436
|
-
async def get_property_value_by_name(self, property_name: str) -> Any:
|
437
|
-
"""
|
438
|
-
Get the value of a specific property.
|
439
|
-
"""
|
440
|
-
if property_name not in self._properties:
|
441
|
-
self.logger.warning(
|
442
|
-
"Property '%s' not found in page properties", property_name
|
443
|
-
)
|
444
|
-
return None
|
445
|
-
|
446
|
-
property_schema: dict = self._properties.get(property_name)
|
447
|
-
|
448
|
-
property_type = property_schema.get("type")
|
449
|
-
|
450
|
-
if property_type == "relation":
|
451
|
-
return await self._get_relation_property_values_by_name(property_name)
|
452
|
-
|
453
|
-
return extract_property_value(property_schema)
|
454
|
-
|
455
|
-
async def _get_relation_property_values_by_name(
|
456
|
-
self, property_name: str
|
457
|
-
) -> list[str]:
|
458
|
-
"""
|
459
|
-
Retrieve the titles of all related pages for a relation property.
|
460
|
-
"""
|
461
|
-
page_property_schema = self._properties.get(property_name)
|
462
|
-
relation_page_ids = [
|
463
|
-
rel.get("id") for rel in page_property_schema.get("relation", [])
|
464
|
-
]
|
465
|
-
notion_pages = [
|
466
|
-
await NotionPage.from_page_id(page_id) for page_id in relation_page_ids
|
467
|
-
]
|
468
|
-
return [page.title for page in notion_pages if page]
|
469
|
-
|
470
|
-
async def get_options_for_property_by_name(self, property_name: str) -> list[str]:
|
471
|
-
"""
|
472
|
-
Get the available options for a property (select, multi_select, status, relation).
|
473
|
-
"""
|
474
|
-
if property_name not in self.properties:
|
475
|
-
self.logger.warning(
|
476
|
-
"Property '%s' not found in page properties", property_name
|
477
|
-
)
|
478
|
-
return []
|
479
|
-
|
480
|
-
property_schema: dict = self.properties.get(property_name)
|
481
|
-
property_type = property_schema.get("type")
|
482
|
-
|
483
|
-
if property_type in ["select", "multi_select", "status"]:
|
484
|
-
options = property_schema.get(property_type, {}).get("options", [])
|
485
|
-
return [option.get("name", "") for option in options]
|
486
|
-
|
487
|
-
if property_type == "relation" and self._parent_database:
|
488
|
-
return await self._parent_database._get_relation_options(property_name)
|
489
|
-
|
490
|
-
return []
|
491
|
-
|
492
|
-
# Fix this for pages that do not ah
|
493
|
-
async def set_property_value_by_name(self, property_name: str, value: Any) -> Any:
|
494
|
-
"""
|
495
|
-
Set the value of a specific property by its name.
|
496
|
-
"""
|
497
|
-
if not self._parent_database:
|
498
|
-
return None
|
499
|
-
|
500
|
-
property_type = self._parent_database.properties.get(property_name).get("type")
|
501
|
-
|
502
|
-
if not property_type:
|
503
|
-
return None
|
504
|
-
|
505
|
-
if property_type == "relation":
|
506
|
-
return await self.set_relation_property_values_by_name(
|
507
|
-
property_name=property_name, page_titles=value
|
508
|
-
)
|
509
|
-
|
510
|
-
property_formatter = NotionPropertyFormatter()
|
511
|
-
update_data = property_formatter.format_value(
|
512
|
-
property_name=property_name, property_type=property_type, value=value
|
513
|
-
)
|
514
|
-
|
515
|
-
try:
|
516
|
-
updated_page_response = await self._client.patch_page(
|
517
|
-
page_id=self._page_id, data=update_data
|
518
|
-
)
|
519
|
-
self._properties = updated_page_response.properties
|
520
|
-
return extract_property_value(self._properties.get(property_name))
|
521
|
-
except Exception as e:
|
522
|
-
self.logger.error(
|
523
|
-
"Error setting property '%s' to value '%s': %s",
|
524
|
-
property_name,
|
525
|
-
value,
|
526
|
-
str(e),
|
527
|
-
)
|
528
|
-
return None
|
529
|
-
|
530
|
-
async def set_relation_property_values_by_name(
|
531
|
-
self, property_name: str, page_titles: list[str]
|
532
|
-
) -> list[str]:
|
533
|
-
"""
|
534
|
-
Add one or more relations to a relation property.
|
535
|
-
"""
|
536
|
-
if not self._parent_database:
|
537
|
-
return []
|
538
|
-
|
539
|
-
property_type = self._parent_database.properties.get(property_name).get("type")
|
540
|
-
|
541
|
-
# for direct calls
|
542
|
-
if property_type != "relation":
|
543
|
-
return []
|
544
|
-
|
545
|
-
relation_pages = await asyncio.gather(
|
546
|
-
*(
|
547
|
-
NotionPage.from_page_name(page_name=page_title)
|
548
|
-
for page_title in page_titles
|
549
|
-
)
|
550
|
-
)
|
551
|
-
|
552
|
-
relation_page_ids = [page.id for page in relation_pages]
|
553
|
-
|
554
|
-
property_formatter = NotionPropertyFormatter()
|
555
|
-
|
556
|
-
update_data = property_formatter.format_value(
|
557
|
-
property_name=property_name,
|
558
|
-
property_type="relation",
|
559
|
-
value=relation_page_ids,
|
560
|
-
)
|
561
|
-
|
562
|
-
try:
|
563
|
-
updated_page_response = await self._client.patch_page(
|
564
|
-
page_id=self._page_id, data=update_data
|
565
|
-
)
|
566
|
-
self._properties = updated_page_response.properties
|
567
|
-
return page_titles
|
568
|
-
except Exception as e:
|
569
|
-
self.logger.error(
|
570
|
-
"Error setting property '%s' to value '%s': %s",
|
571
|
-
property_name,
|
572
|
-
page_titles,
|
573
|
-
str(e),
|
574
|
-
)
|
575
|
-
return []
|
576
|
-
|
577
|
-
async def archive(self) -> bool:
|
578
|
-
"""
|
579
|
-
Archive the page by moving it to the trash.
|
580
|
-
"""
|
581
|
-
try:
|
582
|
-
result = await self._client.patch_page(
|
583
|
-
page_id=self._page_id, data={"archived": True}
|
584
|
-
)
|
585
|
-
return result is not None
|
586
|
-
except Exception as e:
|
587
|
-
self.logger.error("Error archiving page %s: %s", self._page_id, str(e))
|
588
|
-
return False
|
589
|
-
|
590
|
-
@classmethod
|
591
|
-
async def _create_from_response(
|
592
|
-
cls,
|
593
|
-
page_response: NotionPageResponse,
|
594
|
-
token: Optional[str],
|
595
|
-
) -> NotionPage:
|
596
|
-
"""
|
597
|
-
Create NotionPage instance from API response.
|
598
|
-
"""
|
599
|
-
from notionary.database.database import NotionDatabase
|
600
|
-
|
601
|
-
title = cls._extract_title(page_response)
|
602
|
-
emoji = cls._extract_emoji(page_response)
|
603
|
-
parent_database_id = cls._extract_parent_database_id(page_response)
|
604
|
-
|
605
|
-
parent_database = (
|
606
|
-
await NotionDatabase.from_database_id(id=parent_database_id, token=token)
|
607
|
-
if parent_database_id
|
608
|
-
else None
|
609
|
-
)
|
610
|
-
|
611
|
-
instance = cls(
|
612
|
-
page_id=page_response.id,
|
613
|
-
title=title,
|
614
|
-
url=page_response.url,
|
615
|
-
emoji_icon=emoji,
|
616
|
-
archived=page_response.archived,
|
617
|
-
in_trash=page_response.in_trash,
|
618
|
-
properties=page_response.properties,
|
619
|
-
parent_database=parent_database,
|
620
|
-
token=token,
|
621
|
-
)
|
622
|
-
|
623
|
-
cls.logger.info("Created page manager: '%s' (ID: %s)", title, page_response.id)
|
624
|
-
return instance
|
625
|
-
|
626
|
-
@staticmethod
|
627
|
-
def _extract_title(page_response: NotionPageResponse) -> str:
|
628
|
-
"""Extract title from page response. Returns empty string if not found."""
|
629
|
-
|
630
|
-
if not page_response.properties:
|
631
|
-
return ""
|
632
|
-
|
633
|
-
title_property = next(
|
634
|
-
(
|
635
|
-
prop
|
636
|
-
for prop in page_response.properties.values()
|
637
|
-
if isinstance(prop, dict) and prop.get("type") == "title"
|
638
|
-
),
|
639
|
-
None,
|
640
|
-
)
|
641
|
-
|
642
|
-
if not title_property or "title" not in title_property:
|
643
|
-
return ""
|
644
|
-
|
645
|
-
try:
|
646
|
-
title_parts = title_property["title"]
|
647
|
-
return "".join(part.get("plain_text", "") for part in title_parts)
|
648
|
-
except (KeyError, TypeError, AttributeError):
|
649
|
-
return ""
|
650
|
-
|
651
|
-
@staticmethod
|
652
|
-
def _extract_emoji(page_response: NotionPageResponse) -> Optional[str]:
|
653
|
-
"""Extract emoji from database response."""
|
654
|
-
if not page_response.icon:
|
655
|
-
return None
|
656
|
-
|
657
|
-
if page_response.icon.type == "emoji":
|
658
|
-
return page_response.icon.emoji
|
659
|
-
|
660
|
-
return None
|
661
|
-
|
662
|
-
@staticmethod
|
663
|
-
def _extract_parent_database_id(page_response: NotionPageResponse) -> Optional[str]:
|
664
|
-
"""Extract parent database ID from page response."""
|
665
|
-
parent = page_response.parent
|
666
|
-
if isinstance(parent, DatabaseParent):
|
667
|
-
return parent.database_id
|
668
|
-
|
669
|
-
def _setup_page_context_provider(self) -> PageContextProvider:
|
670
|
-
return PageContextProvider(
|
671
|
-
page_id=self._page_id,
|
672
|
-
database_client=NotionDatabaseClient(token=self._client.token),
|
673
|
-
file_upload_client=NotionFileUploadClient(),
|
674
|
-
)
|