notionary 0.2.27__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notionary/__init__.py +5 -20
- notionary/blocks/__init__.py +4 -4
- notionary/blocks/client.py +90 -216
- notionary/blocks/enums.py +167 -0
- notionary/blocks/rich_text/markdown_rich_text_converter.py +280 -0
- notionary/blocks/rich_text/models.py +178 -0
- notionary/blocks/rich_text/name_id_resolver/__init__.py +13 -0
- notionary/blocks/rich_text/name_id_resolver/data_source.py +32 -0
- notionary/blocks/rich_text/name_id_resolver/database.py +31 -0
- notionary/blocks/rich_text/name_id_resolver/page.py +34 -0
- notionary/blocks/rich_text/name_id_resolver/person.py +37 -0
- notionary/blocks/rich_text/name_id_resolver/port.py +11 -0
- notionary/blocks/rich_text/rich_text_markdown_converter.py +144 -0
- notionary/blocks/rich_text/rich_text_patterns.py +42 -0
- notionary/blocks/schemas.py +778 -0
- notionary/comments/__init__.py +1 -22
- notionary/comments/client.py +52 -187
- notionary/comments/factory.py +38 -0
- notionary/comments/models.py +5 -127
- notionary/comments/schemas.py +240 -0
- notionary/comments/service.py +34 -0
- notionary/data_source/http/client.py +11 -0
- notionary/data_source/http/data_source_instance_client.py +104 -0
- notionary/data_source/properties/schemas.py +402 -0
- notionary/data_source/query/builder.py +448 -0
- notionary/data_source/query/resolver.py +114 -0
- notionary/data_source/query/schema.py +302 -0
- notionary/data_source/query/validator.py +73 -0
- notionary/data_source/schema/registry.py +104 -0
- notionary/data_source/schema/service.py +136 -0
- notionary/data_source/schemas.py +27 -0
- notionary/data_source/service.py +377 -0
- notionary/database/client.py +30 -135
- notionary/database/database_metadata_update_client.py +19 -0
- notionary/database/schemas.py +29 -0
- notionary/database/service.py +168 -0
- notionary/exceptions/__init__.py +33 -0
- notionary/exceptions/api.py +41 -0
- notionary/exceptions/base.py +2 -0
- notionary/exceptions/block_parsing.py +16 -0
- notionary/exceptions/data_source/__init__.py +6 -0
- notionary/exceptions/data_source/builder.py +182 -0
- notionary/exceptions/data_source/properties.py +34 -0
- notionary/exceptions/properties.py +58 -0
- notionary/exceptions/search.py +57 -0
- notionary/file_upload/client.py +18 -30
- notionary/file_upload/models.py +7 -8
- notionary/file_upload/{notion_file_upload.py → service.py} +29 -64
- notionary/http/client.py +204 -0
- notionary/http/models.py +50 -0
- notionary/page/blocks/client.py +1 -0
- notionary/page/content/factory.py +73 -0
- notionary/page/content/markdown/__init__.py +5 -0
- notionary/page/content/markdown/builder.py +226 -0
- notionary/page/content/markdown/nodes/__init__.py +52 -0
- notionary/page/content/markdown/nodes/audio.py +23 -0
- notionary/page/content/markdown/nodes/base.py +12 -0
- notionary/page/content/markdown/nodes/bookmark.py +25 -0
- notionary/page/content/markdown/nodes/breadcrumb.py +14 -0
- notionary/page/content/markdown/nodes/bulleted_list.py +41 -0
- notionary/page/content/markdown/nodes/callout.py +34 -0
- notionary/page/content/markdown/nodes/code.py +28 -0
- notionary/page/content/markdown/nodes/columns.py +69 -0
- notionary/page/content/markdown/nodes/container.py +64 -0
- notionary/page/content/markdown/nodes/divider.py +14 -0
- notionary/page/content/markdown/nodes/embed.py +23 -0
- notionary/page/content/markdown/nodes/equation.py +19 -0
- notionary/page/content/markdown/nodes/file.py +23 -0
- notionary/page/content/markdown/nodes/heading.py +36 -0
- notionary/page/content/markdown/nodes/image.py +23 -0
- notionary/page/content/markdown/nodes/mixins/__init__.py +5 -0
- notionary/page/content/markdown/nodes/mixins/caption.py +12 -0
- notionary/page/content/markdown/nodes/numbered_list.py +38 -0
- notionary/page/content/markdown/nodes/paragraph.py +14 -0
- notionary/page/content/markdown/nodes/pdf.py +23 -0
- notionary/page/content/markdown/nodes/quote.py +27 -0
- notionary/page/content/markdown/nodes/space.py +14 -0
- notionary/page/content/markdown/nodes/table.py +45 -0
- notionary/page/content/markdown/nodes/table_of_contents.py +14 -0
- notionary/page/content/markdown/nodes/todo.py +38 -0
- notionary/page/content/markdown/nodes/toggle.py +27 -0
- notionary/page/content/markdown/nodes/video.py +23 -0
- notionary/page/content/parser/context.py +126 -0
- notionary/page/content/parser/factory.py +210 -0
- notionary/page/content/parser/parsers/__init__.py +58 -0
- notionary/page/content/parser/parsers/audio.py +40 -0
- notionary/page/content/parser/parsers/base.py +30 -0
- notionary/page/content/parser/parsers/bookmark.py +33 -0
- notionary/page/content/parser/parsers/breadcrumb.py +33 -0
- notionary/page/content/parser/parsers/bulleted_list.py +85 -0
- notionary/page/content/parser/parsers/callout.py +100 -0
- notionary/page/content/parser/parsers/caption.py +55 -0
- notionary/page/content/parser/parsers/code.py +81 -0
- notionary/page/content/parser/parsers/column.py +76 -0
- notionary/page/content/parser/parsers/column_list.py +81 -0
- notionary/page/content/parser/parsers/divider.py +33 -0
- notionary/page/content/parser/parsers/embed.py +33 -0
- notionary/page/content/parser/parsers/equation.py +65 -0
- notionary/page/content/parser/parsers/file.py +42 -0
- notionary/page/content/parser/parsers/heading.py +115 -0
- notionary/page/content/parser/parsers/image.py +42 -0
- notionary/page/content/parser/parsers/numbered_list.py +89 -0
- notionary/page/content/parser/parsers/paragraph.py +37 -0
- notionary/page/content/parser/parsers/pdf.py +42 -0
- notionary/page/content/parser/parsers/quote.py +125 -0
- notionary/page/content/parser/parsers/space.py +41 -0
- notionary/page/content/parser/parsers/table.py +144 -0
- notionary/page/content/parser/parsers/table_of_contents.py +32 -0
- notionary/page/content/parser/parsers/todo.py +96 -0
- notionary/page/content/parser/parsers/toggle.py +70 -0
- notionary/page/content/parser/parsers/video.py +42 -0
- notionary/page/content/parser/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length.py +95 -0
- notionary/page/content/parser/post_processing/handlers/rich_text_length_truncation.py +114 -0
- notionary/page/content/parser/post_processing/port.py +9 -0
- notionary/page/content/parser/post_processing/service.py +16 -0
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +11 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +130 -0
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +84 -0
- notionary/page/content/parser/pre_processsing/handlers/port.py +7 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +73 -0
- notionary/page/content/parser/pre_processsing/service.py +15 -0
- notionary/page/content/parser/service.py +78 -0
- notionary/page/content/renderer/context.py +51 -0
- notionary/page/content/renderer/factory.py +231 -0
- notionary/page/content/renderer/post_processing/handlers/__init__.py +5 -0
- notionary/page/content/renderer/post_processing/handlers/numbered_list.py +156 -0
- notionary/page/content/renderer/post_processing/port.py +7 -0
- notionary/page/content/renderer/post_processing/service.py +15 -0
- notionary/page/content/renderer/renderers/__init__.py +55 -0
- notionary/page/content/renderer/renderers/audio.py +31 -0
- notionary/page/content/renderer/renderers/base.py +31 -0
- notionary/page/content/renderer/renderers/bookmark.py +25 -0
- notionary/page/content/renderer/renderers/breadcrumb.py +21 -0
- notionary/page/content/renderer/renderers/bulleted_list.py +48 -0
- notionary/page/content/renderer/renderers/callout.py +50 -0
- notionary/page/content/renderer/renderers/captioned_block.py +58 -0
- notionary/page/content/renderer/renderers/code.py +34 -0
- notionary/page/content/renderer/renderers/column.py +53 -0
- notionary/page/content/renderer/renderers/column_list.py +44 -0
- notionary/page/content/renderer/renderers/divider.py +22 -0
- notionary/page/content/renderer/renderers/embed.py +25 -0
- notionary/page/content/renderer/renderers/equation.py +37 -0
- notionary/page/content/renderer/renderers/fallback.py +24 -0
- notionary/page/content/renderer/renderers/file.py +40 -0
- notionary/page/content/renderer/renderers/heading.py +95 -0
- notionary/page/content/renderer/renderers/image.py +31 -0
- notionary/page/content/renderer/renderers/numbered_list.py +42 -0
- notionary/page/content/renderer/renderers/paragraph.py +40 -0
- notionary/page/content/renderer/renderers/pdf.py +31 -0
- notionary/page/content/renderer/renderers/quote.py +49 -0
- notionary/page/content/renderer/renderers/table.py +115 -0
- notionary/page/content/renderer/renderers/table_of_contents.py +26 -0
- notionary/page/content/renderer/renderers/table_row.py +17 -0
- notionary/page/content/renderer/renderers/todo.py +56 -0
- notionary/page/content/renderer/renderers/toggle.py +52 -0
- notionary/page/content/renderer/renderers/video.py +31 -0
- notionary/page/content/renderer/service.py +50 -0
- notionary/page/content/service.py +68 -0
- notionary/page/content/syntax/__init__.py +4 -0
- notionary/page/content/syntax/grammar.py +10 -0
- notionary/page/content/syntax/models.py +66 -0
- notionary/page/content/syntax/registry.py +393 -0
- notionary/page/page_context.py +7 -16
- notionary/page/page_http_client.py +15 -0
- notionary/page/page_metadata_update_client.py +19 -0
- notionary/page/properties/client.py +144 -0
- notionary/page/properties/factory.py +26 -0
- notionary/page/properties/models.py +308 -0
- notionary/page/properties/service.py +261 -0
- notionary/page/schemas.py +13 -0
- notionary/page/service.py +225 -0
- notionary/shared/entity/client.py +29 -0
- notionary/shared/entity/dto_parsers.py +53 -0
- notionary/shared/entity/entity_metadata_update_client.py +41 -0
- notionary/shared/entity/schemas.py +45 -0
- notionary/shared/entity/service.py +171 -0
- notionary/shared/models/cover.py +20 -0
- notionary/shared/models/file.py +21 -0
- notionary/shared/models/icon.py +28 -0
- notionary/shared/models/parent.py +41 -0
- notionary/shared/properties/type.py +30 -0
- notionary/shared/typings.py +3 -0
- notionary/user/__init__.py +4 -8
- notionary/user/base.py +138 -0
- notionary/user/bot.py +70 -0
- notionary/user/client.py +22 -111
- notionary/user/person.py +41 -0
- notionary/user/schemas.py +67 -0
- notionary/user/service.py +65 -0
- notionary/utils/date.py +51 -0
- notionary/utils/decorators.py +122 -0
- notionary/utils/fuzzy.py +68 -0
- notionary/utils/mixins/logging.py +58 -0
- notionary/utils/pagination.py +100 -0
- notionary/utils/uuid_utils.py +20 -0
- notionary/workspace/__init__.py +4 -0
- notionary/workspace/client.py +62 -0
- notionary/workspace/query/__init__.py +3 -0
- notionary/workspace/query/builder.py +60 -0
- notionary/workspace/query/models.py +61 -0
- notionary/workspace/query/service.py +100 -0
- notionary/workspace/schemas.py +21 -0
- notionary/workspace/service.py +116 -0
- notionary-0.3.0.dist-info/METADATA +201 -0
- notionary-0.3.0.dist-info/RECORD +209 -0
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info}/WHEEL +1 -1
- {notionary-0.2.27.dist-info → notionary-0.3.0.dist-info/licenses}/LICENSE +9 -9
- notionary/base_notion_client.py +0 -219
- notionary/blocks/_bootstrap.py +0 -271
- notionary/blocks/audio/__init__.py +0 -11
- notionary/blocks/audio/audio_element.py +0 -158
- notionary/blocks/audio/audio_markdown_node.py +0 -24
- notionary/blocks/audio/audio_models.py +0 -10
- notionary/blocks/base_block_element.py +0 -42
- notionary/blocks/bookmark/__init__.py +0 -12
- notionary/blocks/bookmark/bookmark_element.py +0 -83
- notionary/blocks/bookmark/bookmark_markdown_node.py +0 -28
- notionary/blocks/bookmark/bookmark_models.py +0 -15
- notionary/blocks/breadcrumbs/__init__.py +0 -15
- notionary/blocks/breadcrumbs/breadcrumb_element.py +0 -39
- notionary/blocks/breadcrumbs/breadcrumb_markdown_node.py +0 -13
- notionary/blocks/breadcrumbs/breadcrumb_models.py +0 -12
- notionary/blocks/bulleted_list/__init__.py +0 -15
- notionary/blocks/bulleted_list/bulleted_list_element.py +0 -74
- notionary/blocks/bulleted_list/bulleted_list_markdown_node.py +0 -20
- notionary/blocks/bulleted_list/bulleted_list_models.py +0 -17
- notionary/blocks/callout/__init__.py +0 -12
- notionary/blocks/callout/callout_element.py +0 -99
- notionary/blocks/callout/callout_markdown_node.py +0 -19
- notionary/blocks/callout/callout_models.py +0 -33
- notionary/blocks/child_database/__init__.py +0 -14
- notionary/blocks/child_database/child_database_element.py +0 -59
- notionary/blocks/child_database/child_database_models.py +0 -12
- notionary/blocks/child_page/__init__.py +0 -9
- notionary/blocks/child_page/child_page_element.py +0 -94
- notionary/blocks/child_page/child_page_models.py +0 -12
- notionary/blocks/code/__init__.py +0 -11
- notionary/blocks/code/code_element.py +0 -149
- notionary/blocks/code/code_markdown_node.py +0 -80
- notionary/blocks/code/code_models.py +0 -94
- notionary/blocks/column/__init__.py +0 -25
- notionary/blocks/column/column_element.py +0 -65
- notionary/blocks/column/column_list_element.py +0 -52
- notionary/blocks/column/column_list_markdown_node.py +0 -34
- notionary/blocks/column/column_markdown_node.py +0 -42
- notionary/blocks/column/column_models.py +0 -26
- notionary/blocks/divider/__init__.py +0 -12
- notionary/blocks/divider/divider_element.py +0 -41
- notionary/blocks/divider/divider_markdown_node.py +0 -11
- notionary/blocks/divider/divider_models.py +0 -12
- notionary/blocks/embed/__init__.py +0 -12
- notionary/blocks/embed/embed_element.py +0 -98
- notionary/blocks/embed/embed_markdown_node.py +0 -19
- notionary/blocks/embed/embed_models.py +0 -14
- notionary/blocks/equation/__init__.py +0 -13
- notionary/blocks/equation/equation_element.py +0 -133
- notionary/blocks/equation/equation_element_markdown_node.py +0 -23
- notionary/blocks/equation/equation_models.py +0 -11
- notionary/blocks/file/__init__.py +0 -23
- notionary/blocks/file/file_element.py +0 -133
- notionary/blocks/file/file_element_markdown_node.py +0 -24
- notionary/blocks/file/file_element_models.py +0 -39
- notionary/blocks/heading/__init__.py +0 -19
- notionary/blocks/heading/heading_element.py +0 -112
- notionary/blocks/heading/heading_markdown_node.py +0 -16
- notionary/blocks/heading/heading_models.py +0 -29
- notionary/blocks/image_block/__init__.py +0 -11
- notionary/blocks/image_block/image_element.py +0 -130
- notionary/blocks/image_block/image_markdown_node.py +0 -25
- notionary/blocks/image_block/image_models.py +0 -10
- notionary/blocks/markdown/markdown_builder.py +0 -525
- notionary/blocks/markdown/markdown_document_model.py +0 -0
- notionary/blocks/markdown/markdown_node.py +0 -25
- notionary/blocks/mixins/captions/__init__.py +0 -4
- notionary/blocks/mixins/captions/caption_markdown_node_mixin.py +0 -31
- notionary/blocks/mixins/captions/caption_mixin.py +0 -92
- notionary/blocks/mixins/file_upload/__init__.py +0 -3
- notionary/blocks/mixins/file_upload/file_upload_mixin.py +0 -320
- notionary/blocks/models.py +0 -174
- notionary/blocks/numbered_list/__init__.py +0 -16
- notionary/blocks/numbered_list/numbered_list_element.py +0 -65
- notionary/blocks/numbered_list/numbered_list_markdown_node.py +0 -17
- notionary/blocks/numbered_list/numbered_list_models.py +0 -17
- notionary/blocks/paragraph/__init__.py +0 -15
- notionary/blocks/paragraph/paragraph_element.py +0 -58
- notionary/blocks/paragraph/paragraph_markdown_node.py +0 -16
- notionary/blocks/paragraph/paragraph_models.py +0 -16
- notionary/blocks/pdf/__init__.py +0 -11
- notionary/blocks/pdf/pdf_element.py +0 -146
- notionary/blocks/pdf/pdf_markdown_node.py +0 -24
- notionary/blocks/pdf/pdf_models.py +0 -11
- notionary/blocks/quote/__init__.py +0 -14
- notionary/blocks/quote/quote_element.py +0 -75
- notionary/blocks/quote/quote_markdown_node.py +0 -16
- notionary/blocks/quote/quote_models.py +0 -18
- notionary/blocks/registry/__init__.py +0 -3
- notionary/blocks/registry/block_registry.py +0 -150
- notionary/blocks/rich_text/__init__.py +0 -33
- notionary/blocks/rich_text/rich_text_models.py +0 -221
- notionary/blocks/rich_text/text_inline_formatter.py +0 -456
- notionary/blocks/syntax_prompt_builder.py +0 -137
- notionary/blocks/table/__init__.py +0 -19
- notionary/blocks/table/table_element.py +0 -225
- notionary/blocks/table/table_markdown_node.py +0 -42
- notionary/blocks/table/table_models.py +0 -28
- notionary/blocks/table_of_contents/__init__.py +0 -17
- notionary/blocks/table_of_contents/table_of_contents_element.py +0 -80
- notionary/blocks/table_of_contents/table_of_contents_markdown_node.py +0 -21
- notionary/blocks/table_of_contents/table_of_contents_models.py +0 -18
- notionary/blocks/todo/__init__.py +0 -12
- notionary/blocks/todo/todo_element.py +0 -81
- notionary/blocks/todo/todo_markdown_node.py +0 -21
- notionary/blocks/todo/todo_models.py +0 -18
- notionary/blocks/toggle/__init__.py +0 -12
- notionary/blocks/toggle/toggle_element.py +0 -112
- notionary/blocks/toggle/toggle_markdown_node.py +0 -31
- notionary/blocks/toggle/toggle_models.py +0 -17
- notionary/blocks/toggleable_heading/__init__.py +0 -11
- notionary/blocks/toggleable_heading/toggleable_heading_element.py +0 -115
- notionary/blocks/toggleable_heading/toggleable_heading_markdown_node.py +0 -34
- notionary/blocks/types.py +0 -130
- notionary/blocks/video/__init__.py +0 -11
- notionary/blocks/video/video_element.py +0 -187
- notionary/blocks/video/video_element_models.py +0 -10
- notionary/blocks/video/video_markdown_node.py +0 -26
- notionary/database/__init__.py +0 -4
- notionary/database/database.py +0 -480
- notionary/database/database_filter_builder.py +0 -173
- notionary/database/database_provider.py +0 -227
- notionary/database/exceptions.py +0 -13
- notionary/database/models.py +0 -337
- notionary/database/notion_database.py +0 -487
- notionary/file_upload/__init__.py +0 -7
- notionary/page/client.py +0 -124
- notionary/page/markdown_whitespace_processor.py +0 -129
- notionary/page/models.py +0 -322
- notionary/page/notion_page.py +0 -712
- notionary/page/page_content_deleting_service.py +0 -117
- notionary/page/page_content_writer.py +0 -80
- notionary/page/property_formatter.py +0 -99
- notionary/page/reader/handler/__init__.py +0 -19
- notionary/page/reader/handler/base_block_renderer.py +0 -44
- notionary/page/reader/handler/block_processing_context.py +0 -35
- notionary/page/reader/handler/block_rendering_context.py +0 -48
- notionary/page/reader/handler/column_list_renderer.py +0 -51
- notionary/page/reader/handler/column_renderer.py +0 -60
- notionary/page/reader/handler/equation_renderer.py +0 -0
- notionary/page/reader/handler/line_renderer.py +0 -73
- notionary/page/reader/handler/numbered_list_renderer.py +0 -85
- notionary/page/reader/handler/toggle_renderer.py +0 -69
- notionary/page/reader/handler/toggleable_heading_renderer.py +0 -89
- notionary/page/reader/page_content_retriever.py +0 -81
- notionary/page/search_filter_builder.py +0 -132
- notionary/page/utils.py +0 -60
- notionary/page/writer/handler/__init__.py +0 -24
- notionary/page/writer/handler/code_handler.py +0 -72
- notionary/page/writer/handler/column_handler.py +0 -141
- notionary/page/writer/handler/column_list_handler.py +0 -139
- notionary/page/writer/handler/equation_handler.py +0 -74
- notionary/page/writer/handler/line_handler.py +0 -35
- notionary/page/writer/handler/line_processing_context.py +0 -54
- notionary/page/writer/handler/regular_line_handler.py +0 -86
- notionary/page/writer/handler/table_handler.py +0 -66
- notionary/page/writer/handler/toggle_handler.py +0 -159
- notionary/page/writer/handler/toggleable_heading_handler.py +0 -174
- notionary/page/writer/markdown_to_notion_converter.py +0 -139
- notionary/page/writer/markdown_to_notion_converter_context.py +0 -30
- notionary/page/writer/markdown_to_notion_text_length_post_processor.py +0 -0
- notionary/page/writer/notion_text_length_processor.py +0 -150
- notionary/schemas/__init__.py +0 -3
- notionary/schemas/base.py +0 -73
- notionary/shared/__init__.py +0 -3
- notionary/shared/name_to_id_resolver.py +0 -203
- notionary/telemetry/__init__.py +0 -19
- notionary/telemetry/service.py +0 -136
- notionary/telemetry/views.py +0 -73
- notionary/user/base_notion_user.py +0 -53
- notionary/user/models.py +0 -84
- notionary/user/notion_bot_user.py +0 -226
- notionary/user/notion_user.py +0 -255
- notionary/user/notion_user_manager.py +0 -101
- notionary/util/__init__.py +0 -15
- notionary/util/concurrency_limiter.py +0 -0
- notionary/util/factory_decorator.py +0 -0
- notionary/util/factory_only.py +0 -37
- notionary/util/fuzzy.py +0 -75
- notionary/util/logging_mixin.py +0 -59
- notionary/util/page_id_utils.py +0 -27
- notionary/util/singleton.py +0 -18
- notionary/util/singleton_metaclass.py +0 -22
- notionary/workspace.py +0 -105
- notionary-0.2.27.dist-info/METADATA +0 -270
- notionary-0.2.27.dist-info/RECORD +0 -202
- /notionary/{database → user}/factory.py +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
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
|
+
search_service: WorkspaceQueryService | None = None,
|
|
86
|
+
) -> Self:
|
|
87
|
+
service = search_service or WorkspaceQueryService()
|
|
88
|
+
return await service.find_page(page_title)
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
async def _fetch_page_dto(cls, page_id: str) -> NotionPageDto:
|
|
92
|
+
async with NotionPageHttpClient(page_id=page_id) as client:
|
|
93
|
+
return await client.get_page()
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
async def _create_from_dto(
|
|
97
|
+
cls,
|
|
98
|
+
response: NotionPageDto,
|
|
99
|
+
page_property_handler_factory: PagePropertyHandlerFactory,
|
|
100
|
+
) -> Self:
|
|
101
|
+
title_task = cls._extract_title_from_dto(response)
|
|
102
|
+
page_property_handler = page_property_handler_factory.create_from_page_response(response)
|
|
103
|
+
|
|
104
|
+
title = await title_task
|
|
105
|
+
|
|
106
|
+
return cls._create_with_dependencies(
|
|
107
|
+
id=response.id,
|
|
108
|
+
title=title,
|
|
109
|
+
created_time=response.created_time,
|
|
110
|
+
created_by=response.created_by,
|
|
111
|
+
last_edited_time=response.last_edited_time,
|
|
112
|
+
last_edited_by=response.last_edited_by,
|
|
113
|
+
archived=response.archived,
|
|
114
|
+
in_trash=response.in_trash,
|
|
115
|
+
url=response.url,
|
|
116
|
+
page_property_handler=page_property_handler,
|
|
117
|
+
public_url=response.public_url,
|
|
118
|
+
emoji_icon=extract_emoji_icon_from_dto(response),
|
|
119
|
+
external_icon_url=extract_external_icon_url_from_dto(response),
|
|
120
|
+
cover_image_url=extract_cover_image_url_from_dto(response),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def _create_with_dependencies(
|
|
125
|
+
cls,
|
|
126
|
+
id: str,
|
|
127
|
+
title: str,
|
|
128
|
+
created_time: str,
|
|
129
|
+
created_by: PartialUserDto,
|
|
130
|
+
last_edited_time: str,
|
|
131
|
+
last_edited_by: PartialUserDto,
|
|
132
|
+
url: str,
|
|
133
|
+
archived: bool,
|
|
134
|
+
in_trash: bool,
|
|
135
|
+
page_property_handler: PagePropertyHandler,
|
|
136
|
+
public_url: str | None = None,
|
|
137
|
+
emoji_icon: str | None = None,
|
|
138
|
+
external_icon_url: str | None = None,
|
|
139
|
+
cover_image_url: str | None = None,
|
|
140
|
+
) -> Self:
|
|
141
|
+
block_client = NotionBlockHttpClient()
|
|
142
|
+
comment_service = CommentService()
|
|
143
|
+
|
|
144
|
+
page_content_service_factory = PageContentServiceFactory()
|
|
145
|
+
page_content_service = page_content_service_factory.create(page_id=id, block_client=block_client)
|
|
146
|
+
|
|
147
|
+
metadata_update_client = PageMetadataUpdateClient(page_id=id)
|
|
148
|
+
|
|
149
|
+
return cls(
|
|
150
|
+
id=id,
|
|
151
|
+
title=title,
|
|
152
|
+
created_time=created_time,
|
|
153
|
+
created_by=created_by,
|
|
154
|
+
last_edited_time=last_edited_time,
|
|
155
|
+
last_edited_by=last_edited_by,
|
|
156
|
+
url=url,
|
|
157
|
+
archived=archived,
|
|
158
|
+
in_trash=in_trash,
|
|
159
|
+
page_property_handler=page_property_handler,
|
|
160
|
+
block_client=block_client,
|
|
161
|
+
comment_service=comment_service,
|
|
162
|
+
page_content_service=page_content_service,
|
|
163
|
+
metadata_update_client=metadata_update_client,
|
|
164
|
+
public_url=public_url,
|
|
165
|
+
emoji_icon=emoji_icon,
|
|
166
|
+
external_icon_url=external_icon_url,
|
|
167
|
+
cover_image_url=cover_image_url,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
async def _extract_title_from_dto(response: NotionPageDto) -> str:
|
|
172
|
+
title_property = next(
|
|
173
|
+
(prop for prop in response.properties.values() if isinstance(prop, PageTitleProperty)),
|
|
174
|
+
None,
|
|
175
|
+
)
|
|
176
|
+
rich_text_title = title_property.title if title_property else []
|
|
177
|
+
return await convert_rich_text_to_markdown(rich_text_title)
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def _entity_metadata_update_client(self) -> PageMetadataUpdateClient:
|
|
181
|
+
return self._metadata_update_client
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def title(self) -> str:
|
|
185
|
+
return self._title
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def url(self) -> str:
|
|
189
|
+
return self._url
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def markdown_builder() -> MarkdownBuilder:
|
|
193
|
+
return MarkdownBuilder()
|
|
194
|
+
|
|
195
|
+
async def get_comments(self) -> list[Comment]:
|
|
196
|
+
return await self._comment_service.list_all_comments_for_page(page_id=self._id)
|
|
197
|
+
|
|
198
|
+
async def post_top_level_comment(self, comment: str) -> None:
|
|
199
|
+
await self._comment_service.create_comment_on_page(page_id=self._id, text=comment)
|
|
200
|
+
|
|
201
|
+
async def post_reply_to_discussion(self, discussion_id: str, comment: str) -> None:
|
|
202
|
+
await self._comment_service.reply_to_discussion_by_id(discussion_id=discussion_id, text=comment)
|
|
203
|
+
|
|
204
|
+
async def set_title(self, title: str) -> None:
|
|
205
|
+
await self.properties.set_title_property(title)
|
|
206
|
+
self._title = title
|
|
207
|
+
|
|
208
|
+
async def append_markdown(
|
|
209
|
+
self,
|
|
210
|
+
content: (str | Callable[[MarkdownBuilder], MarkdownBuilder]),
|
|
211
|
+
) -> None:
|
|
212
|
+
await self._page_content_service.append_markdown(content=content)
|
|
213
|
+
|
|
214
|
+
async def replace_content(
|
|
215
|
+
self,
|
|
216
|
+
content: (str | Callable[[MarkdownBuilder], MarkdownBuilder]),
|
|
217
|
+
) -> None:
|
|
218
|
+
await self._page_content_service.clear()
|
|
219
|
+
await self._page_content_service.append_markdown(content=content)
|
|
220
|
+
|
|
221
|
+
async def clear_page_content(self) -> None:
|
|
222
|
+
await self._page_content_service.clear()
|
|
223
|
+
|
|
224
|
+
async def get_markdown_content(self) -> str:
|
|
225
|
+
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
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
from typing import TYPE_CHECKING, Self
|
|
7
|
+
|
|
8
|
+
from notionary.shared.entity.entity_metadata_update_client import EntityMetadataUpdateClient
|
|
9
|
+
from notionary.user.schemas import PartialUserDto
|
|
10
|
+
from notionary.user.service import UserService
|
|
11
|
+
from notionary.utils.mixins.logging import LoggingMixin
|
|
12
|
+
from notionary.utils.uuid_utils import extract_uuid
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from notionary.user.base import BaseUser
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Entity(LoggingMixin, ABC):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
id: str,
|
|
22
|
+
created_time: str,
|
|
23
|
+
created_by: PartialUserDto,
|
|
24
|
+
last_edited_time: str,
|
|
25
|
+
last_edited_by: PartialUserDto,
|
|
26
|
+
in_trash: bool,
|
|
27
|
+
emoji_icon: str | None = None,
|
|
28
|
+
external_icon_url: str | None = None,
|
|
29
|
+
cover_image_url: str | None = None,
|
|
30
|
+
user_service: UserService | None = None,
|
|
31
|
+
) -> None:
|
|
32
|
+
self._id = id
|
|
33
|
+
self._created_time = created_time
|
|
34
|
+
self._created_by = created_by
|
|
35
|
+
self._last_edited_time = last_edited_time
|
|
36
|
+
self._last_edited_by = last_edited_by
|
|
37
|
+
self._emoji_icon = emoji_icon
|
|
38
|
+
self._external_icon_url = external_icon_url
|
|
39
|
+
self._cover_image_url = cover_image_url
|
|
40
|
+
self._in_trash = in_trash
|
|
41
|
+
self._user_service = user_service or UserService()
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
@abstractmethod
|
|
45
|
+
async def from_id(cls, id: str) -> Self:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
@abstractmethod
|
|
50
|
+
async def from_title(cls, title: str) -> Self:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
async def from_url(cls, url: str) -> Self:
|
|
55
|
+
entity_id = extract_uuid(url)
|
|
56
|
+
if not entity_id:
|
|
57
|
+
raise ValueError(f"Could not extract entity ID from URL: {url}")
|
|
58
|
+
return await cls.from_id(entity_id)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def _entity_metadata_update_client(self) -> EntityMetadataUpdateClient:
|
|
63
|
+
# functionality for updating properties like title, icon, cover, archive status depends on interface for template like implementation
|
|
64
|
+
# has to be implementated by inheritants to correctly use the methods below
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def id(self) -> str:
|
|
69
|
+
return self._id
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def created_time(self) -> str:
|
|
73
|
+
return self._created_time
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def last_edited_time(self) -> str:
|
|
77
|
+
return self._last_edited_time
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def in_trash(self) -> bool:
|
|
81
|
+
return self._in_trash
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def emoji_icon(self) -> str | None:
|
|
85
|
+
return self._emoji_icon
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def external_icon_url(self) -> str | None:
|
|
89
|
+
return self._external_icon_url
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def cover_image_url(self) -> str | None:
|
|
93
|
+
return self._cover_image_url
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def created_by(self) -> PartialUserDto:
|
|
97
|
+
return self._created_by
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def last_edited_by(self) -> PartialUserDto:
|
|
101
|
+
return self._last_edited_by
|
|
102
|
+
|
|
103
|
+
async def get_created_by_user(self) -> BaseUser | None:
|
|
104
|
+
return await self._user_service.get_user_by_id(self._created_by.id)
|
|
105
|
+
|
|
106
|
+
async def get_last_edited_by_user(self) -> BaseUser | None:
|
|
107
|
+
return await self._user_service.get_user_by_id(self._last_edited_by.id)
|
|
108
|
+
|
|
109
|
+
async def set_emoji_icon(self, emoji: str) -> None:
|
|
110
|
+
entity_response = await self._entity_metadata_update_client.patch_emoji_icon(emoji)
|
|
111
|
+
self._emoji_icon = entity_response.icon.emoji if entity_response.icon else None
|
|
112
|
+
self._external_icon_url = None
|
|
113
|
+
|
|
114
|
+
async def set_external_icon(self, icon_url: str) -> None:
|
|
115
|
+
entity_response = await self._entity_metadata_update_client.patch_external_icon(icon_url)
|
|
116
|
+
self._emoji_icon = None
|
|
117
|
+
self._external_icon_url = (
|
|
118
|
+
entity_response.icon.external.url if entity_response.icon and entity_response.icon.external else None
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
async def remove_icon(self) -> None:
|
|
122
|
+
await self._entity_metadata_update_client.remove_icon()
|
|
123
|
+
self._emoji_icon = None
|
|
124
|
+
self._external_icon_url = None
|
|
125
|
+
|
|
126
|
+
async def set_cover_image_by_url(self, image_url: str) -> None:
|
|
127
|
+
entity_response = await self._entity_metadata_update_client.patch_external_cover(image_url)
|
|
128
|
+
self._cover_image_url = (
|
|
129
|
+
entity_response.cover.external.url if entity_response.cover and entity_response.cover.external else None
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
async def set_random_gradient_cover(self) -> None:
|
|
133
|
+
random_cover_url = self._get_random_gradient_cover()
|
|
134
|
+
await self.set_cover_image_by_url(random_cover_url)
|
|
135
|
+
|
|
136
|
+
async def remove_cover_image(self) -> None:
|
|
137
|
+
await self._entity_metadata_update_client.remove_cover()
|
|
138
|
+
self._cover_image_url = None
|
|
139
|
+
|
|
140
|
+
async def move_to_trash(self) -> None:
|
|
141
|
+
if self._in_trash:
|
|
142
|
+
self.logger.warning("Entity is already in trash.")
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
entity_response = await self._entity_metadata_update_client.move_to_trash()
|
|
146
|
+
self._in_trash = entity_response.in_trash
|
|
147
|
+
|
|
148
|
+
async def restore_from_trash(self) -> None:
|
|
149
|
+
if not self._in_trash:
|
|
150
|
+
self.logger.warning("Entity is not in trash.")
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
entity_response = await self._entity_metadata_update_client.restore_from_trash()
|
|
154
|
+
self._in_trash = entity_response.in_trash
|
|
155
|
+
|
|
156
|
+
def __repr__(self) -> str:
|
|
157
|
+
attrs = []
|
|
158
|
+
for key, value in self.__dict__.items():
|
|
159
|
+
if key.startswith("_") and not key.startswith("__"):
|
|
160
|
+
attr_name = key[1:]
|
|
161
|
+
attrs.append(f"{attr_name}={value!r}")
|
|
162
|
+
|
|
163
|
+
attrs_str = ", ".join(attrs)
|
|
164
|
+
return f"{self.__class__.__name__}({attrs_str})"
|
|
165
|
+
|
|
166
|
+
def _get_random_gradient_cover(self) -> str:
|
|
167
|
+
DEFAULT_NOTION_COVERS: Sequence[str] = [
|
|
168
|
+
f"https://www.notion.so/images/page-cover/gradients_{i}.png" for i in range(1, 10)
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
return random.choice(DEFAULT_NOTION_COVERS)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Literal, Self
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from notionary.shared.models.file import ExternalFile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CoverType(StrEnum):
|
|
10
|
+
EXTERNAL = "external"
|
|
11
|
+
FILE = "file"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NotionCover(BaseModel):
|
|
15
|
+
type: Literal[CoverType.EXTERNAL, CoverType.FILE] = CoverType.EXTERNAL
|
|
16
|
+
external: ExternalFile | None = None
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_url(cls, url: str) -> Self:
|
|
20
|
+
return cls(external=ExternalFile(url=url))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Literal, Self
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FileType(StrEnum):
|
|
8
|
+
EXTERNAL = "external"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ExternalFile(BaseModel):
|
|
12
|
+
url: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExternalRessource(BaseModel):
|
|
16
|
+
type: Literal[FileType.EXTERNAL] = FileType.EXTERNAL
|
|
17
|
+
external: ExternalFile
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def from_url(cls, url: str) -> Self:
|
|
21
|
+
return cls(external=ExternalFile(url=url))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Literal, Self
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from notionary.shared.models.file import ExternalFile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IconType(StrEnum):
|
|
10
|
+
EMOJI = "emoji"
|
|
11
|
+
EXTERNAL = "external"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EmojiIcon(BaseModel):
|
|
15
|
+
type: Literal[IconType.EMOJI] = IconType.EMOJI
|
|
16
|
+
emoji: str
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExternalIcon(BaseModel):
|
|
20
|
+
type: Literal[IconType.EXTERNAL] = IconType.EXTERNAL
|
|
21
|
+
external: ExternalFile
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_url(cls, url: str) -> Self:
|
|
25
|
+
return cls(external=ExternalFile(url=url))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Icon = EmojiIcon | ExternalIcon
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ParentType(StrEnum):
|
|
8
|
+
DATABASE_ID = "database_id"
|
|
9
|
+
DATA_SOURCE_ID = "data_source_id"
|
|
10
|
+
PAGE_ID = "page_id"
|
|
11
|
+
BLOCK_ID = "block_id"
|
|
12
|
+
WORKSPACE = "workspace"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DataSourceParent(BaseModel):
|
|
16
|
+
type: Literal[ParentType.DATA_SOURCE_ID]
|
|
17
|
+
data_source_id: str
|
|
18
|
+
database_id: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PageParent(BaseModel):
|
|
22
|
+
type: Literal[ParentType.PAGE_ID]
|
|
23
|
+
page_id: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BlockParent(BaseModel):
|
|
27
|
+
type: Literal[ParentType.BLOCK_ID]
|
|
28
|
+
block_id: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class WorkspaceParent(BaseModel):
|
|
32
|
+
type: Literal[ParentType.WORKSPACE]
|
|
33
|
+
workspace: bool
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class DatabaseParent(BaseModel):
|
|
37
|
+
type: Literal[ParentType.DATABASE_ID]
|
|
38
|
+
database_id: str
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Parent = DataSourceParent | PageParent | BlockParent | WorkspaceParent | DatabaseParent
|