notionary 0.3.0__py3-none-any.whl → 0.4.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 +14 -2
- notionary/blocks/enums.py +27 -6
- notionary/blocks/schemas.py +32 -78
- notionary/comments/client.py +6 -9
- notionary/comments/schemas.py +2 -29
- notionary/data_source/http/data_source_instance_client.py +4 -4
- notionary/data_source/properties/schemas.py +128 -107
- notionary/data_source/query/__init__.py +9 -0
- notionary/data_source/query/builder.py +12 -3
- notionary/data_source/query/schema.py +5 -0
- notionary/data_source/schemas.py +2 -2
- notionary/data_source/service.py +43 -132
- notionary/database/schemas.py +2 -2
- notionary/database/service.py +19 -63
- notionary/exceptions/__init__.py +10 -2
- notionary/exceptions/api.py +2 -2
- notionary/exceptions/base.py +1 -1
- notionary/exceptions/block_parsing.py +24 -3
- notionary/exceptions/data_source/builder.py +2 -2
- notionary/exceptions/data_source/properties.py +3 -3
- notionary/exceptions/file_upload.py +67 -0
- notionary/exceptions/properties.py +4 -4
- notionary/exceptions/search.py +4 -4
- notionary/file_upload/__init__.py +4 -0
- notionary/file_upload/client.py +124 -210
- notionary/file_upload/config/__init__.py +17 -0
- notionary/file_upload/config/config.py +32 -0
- notionary/file_upload/config/constants.py +16 -0
- notionary/file_upload/file/reader.py +28 -0
- notionary/file_upload/query/__init__.py +7 -0
- notionary/file_upload/query/builder.py +54 -0
- notionary/file_upload/query/models.py +37 -0
- notionary/file_upload/schemas.py +78 -0
- notionary/file_upload/service.py +152 -289
- notionary/file_upload/validation/factory.py +64 -0
- notionary/file_upload/validation/impl/file_name_length.py +23 -0
- notionary/file_upload/validation/models.py +124 -0
- notionary/file_upload/validation/port.py +7 -0
- notionary/file_upload/validation/service.py +17 -0
- notionary/file_upload/validation/validators/__init__.py +11 -0
- notionary/file_upload/validation/validators/file_exists.py +15 -0
- notionary/file_upload/validation/validators/file_extension.py +122 -0
- notionary/file_upload/validation/validators/file_name_length.py +21 -0
- notionary/file_upload/validation/validators/upload_limit.py +31 -0
- notionary/http/client.py +7 -23
- notionary/page/content/factory.py +2 -0
- notionary/page/content/parser/factory.py +8 -5
- notionary/page/content/parser/parsers/audio.py +8 -33
- notionary/page/content/parser/parsers/embed.py +0 -2
- notionary/page/content/parser/parsers/file.py +8 -35
- notionary/page/content/parser/parsers/file_like_block.py +89 -0
- notionary/page/content/parser/parsers/image.py +8 -35
- notionary/page/content/parser/parsers/pdf.py +8 -35
- notionary/page/content/parser/parsers/video.py +8 -35
- notionary/page/content/parser/pre_processsing/handlers/__init__.py +2 -0
- notionary/page/content/parser/pre_processsing/handlers/column_syntax.py +12 -8
- notionary/page/content/parser/pre_processsing/handlers/indentation.py +2 -0
- notionary/page/content/parser/pre_processsing/handlers/video_syntax.py +66 -0
- notionary/page/content/parser/pre_processsing/handlers/whitespace.py +2 -0
- notionary/page/content/renderer/renderers/audio.py +9 -21
- notionary/page/content/renderer/renderers/file.py +9 -21
- notionary/page/content/renderer/renderers/file_like_block.py +43 -0
- notionary/page/content/renderer/renderers/image.py +9 -21
- notionary/page/content/renderer/renderers/pdf.py +9 -21
- notionary/page/content/renderer/renderers/video.py +9 -21
- notionary/page/content/syntax/__init__.py +2 -1
- notionary/page/content/syntax/registry.py +38 -60
- notionary/page/properties/client.py +3 -3
- notionary/page/properties/{models.py → schemas.py} +93 -107
- notionary/page/properties/service.py +15 -4
- notionary/page/schemas.py +3 -3
- notionary/page/service.py +18 -79
- notionary/shared/entity/dto_parsers.py +1 -36
- notionary/shared/entity/entity_metadata_update_client.py +18 -4
- notionary/shared/entity/schemas.py +6 -6
- notionary/shared/entity/service.py +121 -40
- notionary/shared/models/file.py +34 -6
- notionary/shared/models/icon.py +5 -12
- notionary/user/bot.py +12 -12
- notionary/utils/decorators.py +8 -8
- notionary/utils/pagination.py +36 -32
- notionary/workspace/__init__.py +2 -2
- notionary/workspace/client.py +2 -0
- notionary/workspace/query/__init__.py +3 -2
- notionary/workspace/query/builder.py +25 -1
- notionary/workspace/query/models.py +9 -1
- notionary/workspace/query/service.py +15 -11
- notionary/workspace/service.py +46 -36
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/METADATA +9 -5
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/RECORD +92 -71
- notionary/file_upload/models.py +0 -69
- notionary/page/page_context.py +0 -50
- notionary/shared/models/cover.py +0 -20
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/WHEEL +0 -0
- {notionary-0.3.0.dist-info → notionary-0.4.0.dist-info}/licenses/LICENSE +0 -0
notionary/utils/pagination.py
CHANGED
|
@@ -12,89 +12,93 @@ class PaginatedResponse(BaseModel):
|
|
|
12
12
|
|
|
13
13
|
async def _fetch_data(
|
|
14
14
|
api_call: Callable[..., Coroutine[Any, Any, PaginatedResponse]],
|
|
15
|
-
|
|
15
|
+
total_results_limit: int | None = None,
|
|
16
16
|
**kwargs,
|
|
17
17
|
) -> AsyncGenerator[PaginatedResponse]:
|
|
18
|
-
next_cursor = None
|
|
19
|
-
has_more = True
|
|
20
|
-
total_fetched = 0
|
|
18
|
+
next_cursor: str | None = None
|
|
19
|
+
has_more: bool = True
|
|
20
|
+
total_fetched: int = 0
|
|
21
|
+
api_page_size: int = kwargs.get("page_size", 100)
|
|
21
22
|
|
|
22
|
-
while has_more and _should_continue_fetching(
|
|
23
|
-
request_params = _build_request_params(kwargs, next_cursor
|
|
23
|
+
while has_more and _should_continue_fetching(total_results_limit, total_fetched):
|
|
24
|
+
request_params = _build_request_params(kwargs, next_cursor)
|
|
24
25
|
response = await api_call(**request_params)
|
|
25
26
|
|
|
26
|
-
limited_results = _apply_result_limit(response.results,
|
|
27
|
+
limited_results = _apply_result_limit(response.results, total_results_limit, total_fetched)
|
|
27
28
|
total_fetched += len(limited_results)
|
|
28
29
|
|
|
29
|
-
yield _create_limited_response(response, limited_results)
|
|
30
|
+
yield _create_limited_response(response, limited_results, api_page_size)
|
|
30
31
|
|
|
31
|
-
if _has_reached_limit(
|
|
32
|
+
if _has_reached_limit(total_results_limit, total_fetched):
|
|
32
33
|
break
|
|
33
34
|
|
|
34
35
|
has_more = response.has_more
|
|
35
36
|
next_cursor = response.next_cursor
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def _should_continue_fetching(
|
|
39
|
-
if
|
|
39
|
+
def _should_continue_fetching(total_limit: int | None, total_fetched: int) -> bool:
|
|
40
|
+
if total_limit is None:
|
|
40
41
|
return True
|
|
41
|
-
return total_fetched <
|
|
42
|
+
return total_fetched < total_limit
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
def _build_request_params(
|
|
45
46
|
base_kwargs: dict[str, Any],
|
|
46
47
|
cursor: str | None,
|
|
47
|
-
page_size: int | None,
|
|
48
48
|
) -> dict[str, Any]:
|
|
49
49
|
params = base_kwargs.copy()
|
|
50
|
-
|
|
51
50
|
if cursor:
|
|
52
51
|
params["start_cursor"] = cursor
|
|
53
|
-
|
|
54
|
-
if page_size:
|
|
55
|
-
params["page_size"] = page_size
|
|
56
|
-
|
|
57
52
|
return params
|
|
58
53
|
|
|
59
54
|
|
|
60
|
-
def _apply_result_limit(results: list[Any],
|
|
61
|
-
if
|
|
55
|
+
def _apply_result_limit(results: list[Any], total_limit: int | None, total_fetched: int) -> list[Any]:
|
|
56
|
+
if total_limit is None:
|
|
62
57
|
return results
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
return results[:
|
|
59
|
+
remaining_space = total_limit - total_fetched
|
|
60
|
+
return results[:remaining_space]
|
|
66
61
|
|
|
67
62
|
|
|
68
|
-
def _has_reached_limit(
|
|
69
|
-
if
|
|
63
|
+
def _has_reached_limit(total_limit: int | None, total_fetched: int) -> bool:
|
|
64
|
+
if total_limit is None:
|
|
70
65
|
return False
|
|
71
|
-
return total_fetched >=
|
|
66
|
+
return total_fetched >= total_limit
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _create_limited_response(
|
|
70
|
+
original: PaginatedResponse,
|
|
71
|
+
limited_results: list[Any],
|
|
72
|
+
api_page_size: int,
|
|
73
|
+
) -> PaginatedResponse:
|
|
74
|
+
results_were_limited_by_client = len(limited_results) < len(original.results)
|
|
75
|
+
api_returned_full_page = len(original.results) == api_page_size
|
|
72
76
|
|
|
77
|
+
has_more_after_limit = original.has_more and not results_were_limited_by_client and api_returned_full_page
|
|
73
78
|
|
|
74
|
-
def _create_limited_response(original: PaginatedResponse, limited_results: list[Any]) -> PaginatedResponse:
|
|
75
79
|
return PaginatedResponse(
|
|
76
80
|
results=limited_results,
|
|
77
|
-
has_more=
|
|
78
|
-
next_cursor=original.next_cursor,
|
|
81
|
+
has_more=has_more_after_limit,
|
|
82
|
+
next_cursor=original.next_cursor if has_more_after_limit else None,
|
|
79
83
|
)
|
|
80
84
|
|
|
81
85
|
|
|
82
86
|
async def paginate_notion_api(
|
|
83
87
|
api_call: Callable[..., Coroutine[Any, Any, PaginatedResponse]],
|
|
84
|
-
|
|
88
|
+
total_results_limit: int | None = None,
|
|
85
89
|
**kwargs,
|
|
86
90
|
) -> list[Any]:
|
|
87
91
|
all_results = []
|
|
88
|
-
async for page in _fetch_data(api_call,
|
|
92
|
+
async for page in _fetch_data(api_call, total_results_limit=total_results_limit, **kwargs):
|
|
89
93
|
all_results.extend(page.results)
|
|
90
94
|
return all_results
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
async def paginate_notion_api_generator(
|
|
94
98
|
api_call: Callable[..., Coroutine[Any, Any, PaginatedResponse]],
|
|
95
|
-
|
|
99
|
+
total_results_limit: int | None = None,
|
|
96
100
|
**kwargs,
|
|
97
101
|
) -> AsyncGenerator[Any]:
|
|
98
|
-
async for page in _fetch_data(api_call,
|
|
102
|
+
async for page in _fetch_data(api_call, total_results_limit, **kwargs):
|
|
99
103
|
for item in page.results:
|
|
100
104
|
yield item
|
notionary/workspace/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .query import
|
|
1
|
+
from .query import NotionWorkspaceQueryConfigBuilder, WorkspaceQueryConfig
|
|
2
2
|
from .service import NotionWorkspace
|
|
3
3
|
|
|
4
|
-
__all__ = ["NotionWorkspace", "
|
|
4
|
+
__all__ = ["NotionWorkspace", "NotionWorkspaceQueryConfigBuilder", "WorkspaceQueryConfig"]
|
notionary/workspace/client.py
CHANGED
|
@@ -22,6 +22,7 @@ class WorkspaceClient:
|
|
|
22
22
|
async for page in paginate_notion_api_generator(
|
|
23
23
|
self._query_pages,
|
|
24
24
|
search_config=search_config,
|
|
25
|
+
total_results_limit=search_config.total_results_limit,
|
|
25
26
|
):
|
|
26
27
|
yield page
|
|
27
28
|
|
|
@@ -32,6 +33,7 @@ class WorkspaceClient:
|
|
|
32
33
|
async for data_source in paginate_notion_api_generator(
|
|
33
34
|
self._query_data_sources,
|
|
34
35
|
search_config=search_config,
|
|
36
|
+
total_results_limit=search_config.total_results_limit,
|
|
35
37
|
):
|
|
36
38
|
yield data_source
|
|
37
39
|
|
|
@@ -8,7 +8,7 @@ from notionary.workspace.query.models import (
|
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class
|
|
11
|
+
class NotionWorkspaceQueryConfigBuilder:
|
|
12
12
|
def __init__(self, config: WorkspaceQueryConfig = None) -> None:
|
|
13
13
|
self.config = config or WorkspaceQueryConfig()
|
|
14
14
|
|
|
@@ -16,6 +16,10 @@ class WorkspaceQueryConfigBuilder:
|
|
|
16
16
|
self.config.query = query
|
|
17
17
|
return self
|
|
18
18
|
|
|
19
|
+
def with_total_results_limit(self, limit: int) -> Self:
|
|
20
|
+
self.config.total_results_limit = limit
|
|
21
|
+
return self
|
|
22
|
+
|
|
19
23
|
def with_pages_only(self) -> Self:
|
|
20
24
|
self.config.object_type = WorkspaceQueryObjectType.PAGE
|
|
21
25
|
return self
|
|
@@ -44,6 +48,26 @@ class WorkspaceQueryConfigBuilder:
|
|
|
44
48
|
def with_sort_by_last_edited(self) -> Self:
|
|
45
49
|
return self.with_sort_timestamp(SortTimestamp.LAST_EDITED_TIME)
|
|
46
50
|
|
|
51
|
+
def with_sort_by_created_time_ascending(self) -> Self:
|
|
52
|
+
self.config.sort_timestamp = SortTimestamp.CREATED_TIME
|
|
53
|
+
self.config.sort_direction = SortDirection.ASCENDING
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def with_sort_by_created_time_descending(self) -> Self:
|
|
57
|
+
self.config.sort_timestamp = SortTimestamp.CREATED_TIME
|
|
58
|
+
self.config.sort_direction = SortDirection.DESCENDING
|
|
59
|
+
return self
|
|
60
|
+
|
|
61
|
+
def with_sort_by_last_edited_ascending(self) -> Self:
|
|
62
|
+
self.config.sort_timestamp = SortTimestamp.LAST_EDITED_TIME
|
|
63
|
+
self.config.sort_direction = SortDirection.ASCENDING
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def with_sort_by_last_edited_descending(self) -> Self:
|
|
67
|
+
self.config.sort_timestamp = SortTimestamp.LAST_EDITED_TIME
|
|
68
|
+
self.config.sort_direction = SortDirection.DESCENDING
|
|
69
|
+
return self
|
|
70
|
+
|
|
47
71
|
def with_page_size(self, size: int) -> Self:
|
|
48
72
|
self.config.page_size = min(size, 100)
|
|
49
73
|
return self
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
from enum import StrEnum
|
|
2
|
+
from typing import Protocol
|
|
2
3
|
|
|
3
4
|
from pydantic import BaseModel, Field, field_validator, model_serializer
|
|
4
5
|
|
|
5
6
|
from notionary.shared.typings import JsonDict
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
class SearchableEntity(Protocol):
|
|
10
|
+
title: str
|
|
11
|
+
|
|
12
|
+
|
|
8
13
|
class SortDirection(StrEnum):
|
|
9
14
|
ASCENDING = "ascending"
|
|
10
15
|
DESCENDING = "descending"
|
|
@@ -25,9 +30,12 @@ class WorkspaceQueryConfig(BaseModel):
|
|
|
25
30
|
object_type: WorkspaceQueryObjectType | None = None
|
|
26
31
|
sort_direction: SortDirection = SortDirection.DESCENDING
|
|
27
32
|
sort_timestamp: SortTimestamp = SortTimestamp.LAST_EDITED_TIME
|
|
33
|
+
|
|
28
34
|
page_size: int = Field(default=100, ge=1, le=100)
|
|
29
35
|
start_cursor: str | None = None
|
|
30
36
|
|
|
37
|
+
total_results_limit: int | None = None
|
|
38
|
+
|
|
31
39
|
@field_validator("query")
|
|
32
40
|
@classmethod
|
|
33
41
|
def replace_empty_query_with_none(cls, value: str | None) -> str | None:
|
|
@@ -36,7 +44,7 @@ class WorkspaceQueryConfig(BaseModel):
|
|
|
36
44
|
return value
|
|
37
45
|
|
|
38
46
|
@model_serializer
|
|
39
|
-
def
|
|
47
|
+
def to_api_params(self) -> JsonDict:
|
|
40
48
|
search_dict: JsonDict = {}
|
|
41
49
|
|
|
42
50
|
if self.query:
|
|
@@ -2,22 +2,18 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
from collections.abc import AsyncIterator
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from notionary.exceptions.search import DatabaseNotFound, DataSourceNotFound, PageNotFound
|
|
8
8
|
from notionary.utils.fuzzy import find_all_matches
|
|
9
9
|
from notionary.workspace.client import WorkspaceClient
|
|
10
|
-
from notionary.workspace.query.builder import
|
|
11
|
-
from notionary.workspace.query.models import WorkspaceQueryConfig
|
|
10
|
+
from notionary.workspace.query.builder import NotionWorkspaceQueryConfigBuilder
|
|
11
|
+
from notionary.workspace.query.models import SearchableEntity, WorkspaceQueryConfig
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from notionary import NotionDatabase, NotionDataSource, NotionPage
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class SearchableEntity(Protocol):
|
|
18
|
-
title: str
|
|
19
|
-
|
|
20
|
-
|
|
21
17
|
class WorkspaceQueryService:
|
|
22
18
|
def __init__(self, client: WorkspaceClient | None = None) -> None:
|
|
23
19
|
self._client = client or WorkspaceClient()
|
|
@@ -49,20 +45,28 @@ class WorkspaceQueryService:
|
|
|
49
45
|
return await asyncio.gather(*data_source_tasks)
|
|
50
46
|
|
|
51
47
|
async def find_data_source(self, query: str) -> NotionDataSource:
|
|
52
|
-
config =
|
|
48
|
+
config = (
|
|
49
|
+
NotionWorkspaceQueryConfigBuilder().with_query(query).with_data_sources_only().with_page_size(100).build()
|
|
50
|
+
)
|
|
53
51
|
data_sources = await self.get_data_sources(config)
|
|
54
52
|
return self._find_exact_match(data_sources, query, DataSourceNotFound)
|
|
55
53
|
|
|
56
54
|
async def find_page(self, query: str) -> NotionPage:
|
|
57
|
-
config =
|
|
55
|
+
config = NotionWorkspaceQueryConfigBuilder().with_query(query).with_pages_only().with_page_size(100).build()
|
|
58
56
|
pages = await self.get_pages(config)
|
|
59
57
|
return self._find_exact_match(pages, query, PageNotFound)
|
|
60
58
|
|
|
61
59
|
async def find_database(self, query: str) -> NotionDatabase:
|
|
62
|
-
config =
|
|
60
|
+
config = (
|
|
61
|
+
NotionWorkspaceQueryConfigBuilder().with_query(query).with_data_sources_only().with_page_size(100).build()
|
|
62
|
+
)
|
|
63
63
|
data_sources = await self.get_data_sources(config)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
parent_database_ids = [data_sources.get_parent_database_id_if_present() for data_sources in data_sources]
|
|
66
|
+
# filter none values which should not happen but for safety
|
|
67
|
+
parent_database_ids = [id for id in parent_database_ids if id is not None]
|
|
68
|
+
|
|
69
|
+
parent_database_tasks = [NotionDatabase.from_id(db_id) for db_id in parent_database_ids]
|
|
66
70
|
parent_databases = await asyncio.gather(*parent_database_tasks)
|
|
67
71
|
potential_databases = [database for database in parent_databases if database is not None]
|
|
68
72
|
|
notionary/workspace/service.py
CHANGED
|
@@ -4,17 +4,14 @@ from collections.abc import AsyncIterator, Callable
|
|
|
4
4
|
from typing import TYPE_CHECKING, Self
|
|
5
5
|
|
|
6
6
|
from notionary.user.service import UserService
|
|
7
|
-
from notionary.workspace.query.builder import
|
|
7
|
+
from notionary.workspace.query.builder import NotionWorkspaceQueryConfigBuilder
|
|
8
8
|
from notionary.workspace.query.models import WorkspaceQueryConfig, WorkspaceQueryObjectType
|
|
9
9
|
from notionary.workspace.query.service import WorkspaceQueryService
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
|
-
from notionary
|
|
13
|
-
from notionary.page.service import NotionPage
|
|
12
|
+
from notionary import NotionDataSource, NotionPage
|
|
14
13
|
from notionary.user import BotUser, PersonUser
|
|
15
14
|
|
|
16
|
-
type _QueryConfigInput = WorkspaceQueryConfig | Callable[[WorkspaceQueryConfigBuilder], WorkspaceQueryConfigBuilder]
|
|
17
|
-
|
|
18
15
|
|
|
19
16
|
class NotionWorkspace:
|
|
20
17
|
def __init__(
|
|
@@ -41,58 +38,71 @@ class NotionWorkspace:
|
|
|
41
38
|
|
|
42
39
|
async def get_pages(
|
|
43
40
|
self,
|
|
44
|
-
|
|
41
|
+
*,
|
|
42
|
+
filter_fn: Callable[[NotionWorkspaceQueryConfigBuilder], NotionWorkspaceQueryConfigBuilder] | None = None,
|
|
43
|
+
query_config: WorkspaceQueryConfig | None = None,
|
|
45
44
|
) -> list[NotionPage]:
|
|
46
|
-
query_config
|
|
47
|
-
|
|
45
|
+
if filter_fn is not None and query_config is not None:
|
|
46
|
+
raise ValueError("Use either filter_fn OR query_config, not both")
|
|
47
|
+
|
|
48
|
+
resolved_config = self._resolve_query_config(filter_fn, query_config, WorkspaceQueryObjectType.PAGE)
|
|
49
|
+
return await self._query_service.get_pages(resolved_config)
|
|
48
50
|
|
|
49
51
|
async def get_pages_stream(
|
|
50
52
|
self,
|
|
51
|
-
|
|
53
|
+
*,
|
|
54
|
+
filter_fn: Callable[[NotionWorkspaceQueryConfigBuilder], NotionWorkspaceQueryConfigBuilder] | None = None,
|
|
55
|
+
query_config: WorkspaceQueryConfig | None = None,
|
|
52
56
|
) -> AsyncIterator[NotionPage]:
|
|
53
|
-
query_config
|
|
54
|
-
|
|
57
|
+
if filter_fn is not None and query_config is not None:
|
|
58
|
+
raise ValueError("Use either filter_fn OR query_config, not both")
|
|
59
|
+
|
|
60
|
+
resolved_config = self._resolve_query_config(filter_fn, query_config, WorkspaceQueryObjectType.PAGE)
|
|
61
|
+
async for page in self._query_service.get_pages_stream(resolved_config):
|
|
55
62
|
yield page
|
|
56
63
|
|
|
57
64
|
async def get_data_sources(
|
|
58
65
|
self,
|
|
59
|
-
|
|
66
|
+
*,
|
|
67
|
+
filter_fn: Callable[[NotionWorkspaceQueryConfigBuilder], NotionWorkspaceQueryConfigBuilder] | None = None,
|
|
68
|
+
query_config: WorkspaceQueryConfig | None = None,
|
|
60
69
|
) -> list[NotionDataSource]:
|
|
61
|
-
query_config
|
|
62
|
-
|
|
70
|
+
if filter_fn is not None and query_config is not None:
|
|
71
|
+
raise ValueError("Use either filter_fn OR query_config, not both")
|
|
72
|
+
|
|
73
|
+
resolved_config = self._resolve_query_config(filter_fn, query_config, WorkspaceQueryObjectType.DATA_SOURCE)
|
|
74
|
+
return await self._query_service.get_data_sources(resolved_config)
|
|
63
75
|
|
|
64
76
|
async def get_data_sources_stream(
|
|
65
77
|
self,
|
|
66
|
-
|
|
78
|
+
*,
|
|
79
|
+
filter_fn: Callable[[NotionWorkspaceQueryConfigBuilder], NotionWorkspaceQueryConfigBuilder] | None = None,
|
|
80
|
+
query_config: WorkspaceQueryConfig | None = None,
|
|
67
81
|
) -> AsyncIterator[NotionDataSource]:
|
|
68
|
-
query_config
|
|
69
|
-
|
|
82
|
+
if filter_fn is not None and query_config is not None:
|
|
83
|
+
raise ValueError("Use either filter_fn OR query_config, not both")
|
|
84
|
+
|
|
85
|
+
resolved_config = self._resolve_query_config(filter_fn, query_config, WorkspaceQueryObjectType.DATA_SOURCE)
|
|
86
|
+
async for data_source in self._query_service.get_data_sources_stream(resolved_config):
|
|
70
87
|
yield data_source
|
|
71
88
|
|
|
72
|
-
def
|
|
89
|
+
def _resolve_query_config(
|
|
73
90
|
self,
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
filter_fn: Callable[[NotionWorkspaceQueryConfigBuilder], NotionWorkspaceQueryConfigBuilder] | None,
|
|
92
|
+
query_config: WorkspaceQueryConfig | None,
|
|
93
|
+
expected_object_type: WorkspaceQueryObjectType,
|
|
76
94
|
) -> WorkspaceQueryConfig:
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if callable(config):
|
|
83
|
-
config(builder)
|
|
84
|
-
|
|
85
|
-
return builder.build()
|
|
86
|
-
|
|
87
|
-
def _create_builder_with_defaults(self, object_type: WorkspaceQueryObjectType) -> WorkspaceQueryConfigBuilder:
|
|
88
|
-
builder = WorkspaceQueryConfigBuilder()
|
|
95
|
+
if filter_fn is not None:
|
|
96
|
+
builder = NotionWorkspaceQueryConfigBuilder()
|
|
97
|
+
configured_builder = filter_fn(builder)
|
|
98
|
+
query_config = configured_builder.build()
|
|
89
99
|
|
|
90
|
-
if
|
|
91
|
-
|
|
100
|
+
if query_config is None:
|
|
101
|
+
query_config = WorkspaceQueryConfig(object_type=expected_object_type)
|
|
92
102
|
else:
|
|
93
|
-
|
|
103
|
+
query_config.object_type = expected_object_type
|
|
94
104
|
|
|
95
|
-
return
|
|
105
|
+
return query_config
|
|
96
106
|
|
|
97
107
|
async def get_users(self) -> list[PersonUser]:
|
|
98
108
|
return [user async for user in self._user_service.list_users_stream()]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: notionary
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Python library for programmatic Notion workspace management - databases, pages, and content with advanced Markdown support
|
|
5
5
|
Project-URL: Homepage, https://github.com/mathisarends/notionary
|
|
6
6
|
Author-email: Mathis Arends <mathisarends27@gmail.com>
|
|
7
7
|
License: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
10
|
Requires-Dist: aiofiles<25.0.0,>=24.1.0
|
|
11
11
|
Requires-Dist: httpx>=0.28.0
|
|
12
12
|
Requires-Dist: pydantic>=2.11.4
|
|
@@ -23,9 +23,13 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
|
|
24
24
|
<div align="center">
|
|
25
25
|
|
|
26
|
-
[](https://badge.fury.io/py/notionary)
|
|
27
|
+
[](https://www.python.org/downloads/)
|
|
28
|
+
[](https://opensource.org/licenses/MIT)
|
|
29
|
+
[](https://github.com/mathisarends/notionary)
|
|
30
|
+
[](https://pypi.org/project/notionary/)
|
|
31
|
+
[](https://mathisarends.github.io/notionary/)
|
|
32
|
+
[](https://developers.notion.com/)
|
|
29
33
|
|
|
30
34
|
**Transform complex Notion API interactions into simple, Pythonic code.**
|
|
31
35
|
Perfect for developers building AI agents, automation workflows, and dynamic content systems.
|