tracktolib 0.62.1__py3-none-any.whl → 0.63.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.
- tracktolib/notion/__init__.py +0 -0
- tracktolib/notion/fetch.py +352 -0
- tracktolib/notion/models.py +281 -0
- {tracktolib-0.62.1.dist-info → tracktolib-0.63.0.dist-info}/METADATA +3 -1
- {tracktolib-0.62.1.dist-info → tracktolib-0.63.0.dist-info}/RECORD +6 -3
- {tracktolib-0.62.1.dist-info → tracktolib-0.63.0.dist-info}/WHEEL +1 -1
|
File without changes
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
import niquests
|
|
6
|
+
except ImportError:
|
|
7
|
+
raise ImportError('Please install niquests or tracktolib with "notion" to use this module')
|
|
8
|
+
|
|
9
|
+
from .models import (
|
|
10
|
+
Block,
|
|
11
|
+
BlockListResponse,
|
|
12
|
+
Database,
|
|
13
|
+
IntrospectTokenResponse,
|
|
14
|
+
Page,
|
|
15
|
+
PageListResponse,
|
|
16
|
+
RevokeTokenResponse,
|
|
17
|
+
SearchResponse,
|
|
18
|
+
TokenResponse,
|
|
19
|
+
User,
|
|
20
|
+
UserListResponse,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = (
|
|
24
|
+
# Auth helpers
|
|
25
|
+
"get_notion_headers",
|
|
26
|
+
# OAuth
|
|
27
|
+
"create_token",
|
|
28
|
+
"introspect_token",
|
|
29
|
+
"revoke_token",
|
|
30
|
+
"refresh_token",
|
|
31
|
+
# Users
|
|
32
|
+
"fetch_users",
|
|
33
|
+
"fetch_user",
|
|
34
|
+
"fetch_me",
|
|
35
|
+
# Pages
|
|
36
|
+
"fetch_page",
|
|
37
|
+
"create_page",
|
|
38
|
+
"update_page",
|
|
39
|
+
# Databases
|
|
40
|
+
"fetch_database",
|
|
41
|
+
"query_database",
|
|
42
|
+
# Blocks
|
|
43
|
+
"fetch_block",
|
|
44
|
+
"fetch_block_children",
|
|
45
|
+
"fetch_append_block_children",
|
|
46
|
+
# Search
|
|
47
|
+
"fetch_search",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
NOTION_API_URL = "https://api.notion.com"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_notion_token() -> str:
|
|
54
|
+
"""Get Notion token from config or environment."""
|
|
55
|
+
token = os.environ.get("NOTION_TOKEN")
|
|
56
|
+
if not token:
|
|
57
|
+
raise ValueError("Notion token not found. Set NOTION_TOKEN env var.")
|
|
58
|
+
return token
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_notion_headers(api_version: str = "2025-09-03", token: str | None = None):
|
|
62
|
+
"""Get headers for Notion API requests."""
|
|
63
|
+
_token = token or _get_notion_token()
|
|
64
|
+
return {
|
|
65
|
+
"Authorization": f"Bearer {_token}",
|
|
66
|
+
"Notion-Version": api_version,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# OAuth endpoints
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def create_token(
|
|
74
|
+
session: niquests.AsyncSession,
|
|
75
|
+
client_id: str,
|
|
76
|
+
client_secret: str,
|
|
77
|
+
code: str,
|
|
78
|
+
redirect_uri: str | None = None,
|
|
79
|
+
) -> TokenResponse:
|
|
80
|
+
"""Create an access token from an OAuth authorization code."""
|
|
81
|
+
payload: dict[str, str] = {
|
|
82
|
+
"grant_type": "authorization_code",
|
|
83
|
+
"code": code,
|
|
84
|
+
}
|
|
85
|
+
if redirect_uri:
|
|
86
|
+
payload["redirect_uri"] = redirect_uri
|
|
87
|
+
|
|
88
|
+
response = await session.post(f"{NOTION_API_URL}/v1/oauth/token", json=payload, auth=(client_id, client_secret))
|
|
89
|
+
response.raise_for_status()
|
|
90
|
+
return response.json() # type: ignore[return-value]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def introspect_token(
|
|
94
|
+
session: niquests.AsyncSession,
|
|
95
|
+
client_id: str,
|
|
96
|
+
client_secret: str,
|
|
97
|
+
token: str,
|
|
98
|
+
) -> IntrospectTokenResponse:
|
|
99
|
+
"""Get a token's active status, scope, and issued time."""
|
|
100
|
+
payload = {"token": token}
|
|
101
|
+
response = await session.post(
|
|
102
|
+
f"{NOTION_API_URL}/v1/oauth/introspect", json=payload, auth=(client_id, client_secret)
|
|
103
|
+
)
|
|
104
|
+
response.raise_for_status()
|
|
105
|
+
return response.json() # type: ignore[return-value]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
async def revoke_token(
|
|
109
|
+
session: niquests.AsyncSession,
|
|
110
|
+
client_id: str,
|
|
111
|
+
client_secret: str,
|
|
112
|
+
token: str,
|
|
113
|
+
) -> RevokeTokenResponse:
|
|
114
|
+
"""Revoke an access token."""
|
|
115
|
+
payload = {"token": token}
|
|
116
|
+
|
|
117
|
+
response = await session.post(f"{NOTION_API_URL}/v1/oauth/revoke", json=payload, auth=(client_id, client_secret))
|
|
118
|
+
response.raise_for_status()
|
|
119
|
+
return response.json() # type: ignore[return-value]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def refresh_token(
|
|
123
|
+
session: niquests.AsyncSession,
|
|
124
|
+
client_id: str,
|
|
125
|
+
client_secret: str,
|
|
126
|
+
refresh_token_value: str,
|
|
127
|
+
) -> TokenResponse:
|
|
128
|
+
"""Refresh an access token, generating new access and refresh tokens."""
|
|
129
|
+
payload = {
|
|
130
|
+
"grant_type": "refresh_token",
|
|
131
|
+
"refresh_token": refresh_token_value,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
response = await session.post(f"{NOTION_API_URL}/v1/oauth/token", json=payload, auth=(client_id, client_secret))
|
|
135
|
+
response.raise_for_status()
|
|
136
|
+
return response.json() # type: ignore[return-value]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# Users endpoints
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
async def fetch_users(
|
|
143
|
+
session: niquests.AsyncSession,
|
|
144
|
+
*,
|
|
145
|
+
start_cursor: str | None = None,
|
|
146
|
+
page_size: int | None = None,
|
|
147
|
+
) -> UserListResponse:
|
|
148
|
+
"""List all users in the workspace."""
|
|
149
|
+
params: dict[str, str] = {}
|
|
150
|
+
if start_cursor:
|
|
151
|
+
params["start_cursor"] = start_cursor
|
|
152
|
+
if page_size:
|
|
153
|
+
params["page_size"] = str(page_size)
|
|
154
|
+
|
|
155
|
+
response = await session.get(f"{NOTION_API_URL}/v1/users", params=params or None)
|
|
156
|
+
response.raise_for_status()
|
|
157
|
+
return response.json() # type: ignore[return-value]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
async def fetch_user(session: niquests.AsyncSession, user_id: str) -> User:
|
|
161
|
+
"""Retrieve a user by ID."""
|
|
162
|
+
response = await session.get(f"{NOTION_API_URL}/v1/users/{user_id}")
|
|
163
|
+
response.raise_for_status()
|
|
164
|
+
return response.json() # type: ignore[return-value]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
async def fetch_me(session: niquests.AsyncSession) -> User:
|
|
168
|
+
"""Retrieve the bot user associated with the token."""
|
|
169
|
+
response = await session.get(f"{NOTION_API_URL}/v1/users/me")
|
|
170
|
+
response.raise_for_status()
|
|
171
|
+
return response.json() # type: ignore[return-value]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# Pages endpoints
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
async def fetch_page(session: niquests.AsyncSession, page_id: str) -> Page:
|
|
178
|
+
"""Retrieve a page by ID."""
|
|
179
|
+
response = await session.get(f"{NOTION_API_URL}/v1/pages/{page_id}")
|
|
180
|
+
response.raise_for_status()
|
|
181
|
+
return response.json() # type: ignore[return-value]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def create_page(
|
|
185
|
+
session: niquests.AsyncSession,
|
|
186
|
+
*,
|
|
187
|
+
parent: dict[str, Any],
|
|
188
|
+
properties: dict[str, Any],
|
|
189
|
+
children: list[dict[str, Any]] | None = None,
|
|
190
|
+
icon: dict[str, Any] | None = None,
|
|
191
|
+
cover: dict[str, Any] | None = None,
|
|
192
|
+
) -> Page:
|
|
193
|
+
"""Create a new page."""
|
|
194
|
+
payload: dict[str, Any] = {
|
|
195
|
+
"parent": parent,
|
|
196
|
+
"properties": properties,
|
|
197
|
+
}
|
|
198
|
+
if children:
|
|
199
|
+
payload["children"] = children
|
|
200
|
+
if icon:
|
|
201
|
+
payload["icon"] = icon
|
|
202
|
+
if cover:
|
|
203
|
+
payload["cover"] = cover
|
|
204
|
+
|
|
205
|
+
response = await session.post(f"{NOTION_API_URL}/v1/pages", json=payload)
|
|
206
|
+
response.raise_for_status()
|
|
207
|
+
return response.json() # type: ignore[return-value]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
async def update_page(
|
|
211
|
+
session: niquests.AsyncSession,
|
|
212
|
+
page_id: str,
|
|
213
|
+
*,
|
|
214
|
+
properties: dict[str, Any] | None = None,
|
|
215
|
+
archived: bool | None = None,
|
|
216
|
+
icon: dict[str, Any] | None = None,
|
|
217
|
+
cover: dict[str, Any] | None = None,
|
|
218
|
+
) -> Page:
|
|
219
|
+
"""Update a page's properties."""
|
|
220
|
+
payload: dict[str, Any] = {}
|
|
221
|
+
if properties is not None:
|
|
222
|
+
payload["properties"] = properties
|
|
223
|
+
if archived is not None:
|
|
224
|
+
payload["archived"] = archived
|
|
225
|
+
if icon is not None:
|
|
226
|
+
payload["icon"] = icon
|
|
227
|
+
if cover is not None:
|
|
228
|
+
payload["cover"] = cover
|
|
229
|
+
|
|
230
|
+
response = await session.patch(f"{NOTION_API_URL}/v1/pages/{page_id}", json=payload)
|
|
231
|
+
response.raise_for_status()
|
|
232
|
+
return response.json() # type: ignore[return-value]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# Databases endpoints
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
async def fetch_database(session: niquests.AsyncSession, database_id: str) -> Database:
|
|
239
|
+
"""Retrieve a database by ID."""
|
|
240
|
+
response = await session.get(f"{NOTION_API_URL}/v1/databases/{database_id}")
|
|
241
|
+
response.raise_for_status()
|
|
242
|
+
return response.json() # type: ignore[return-value]
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
async def query_database(
|
|
246
|
+
session: niquests.AsyncSession,
|
|
247
|
+
database_id: str,
|
|
248
|
+
*,
|
|
249
|
+
filter: dict[str, Any] | None = None,
|
|
250
|
+
sorts: list[dict[str, Any]] | None = None,
|
|
251
|
+
start_cursor: str | None = None,
|
|
252
|
+
page_size: int | None = None,
|
|
253
|
+
) -> PageListResponse:
|
|
254
|
+
"""Query a database."""
|
|
255
|
+
payload: dict[str, Any] = {}
|
|
256
|
+
if filter:
|
|
257
|
+
payload["filter"] = filter
|
|
258
|
+
if sorts:
|
|
259
|
+
payload["sorts"] = sorts
|
|
260
|
+
if start_cursor:
|
|
261
|
+
payload["start_cursor"] = start_cursor
|
|
262
|
+
if page_size:
|
|
263
|
+
payload["page_size"] = page_size
|
|
264
|
+
|
|
265
|
+
response = await session.post(f"{NOTION_API_URL}/v1/databases/{database_id}/query", json=payload or None)
|
|
266
|
+
response.raise_for_status()
|
|
267
|
+
return response.json() # type: ignore[return-value]
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
# Blocks endpoints
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
async def fetch_block(session: niquests.AsyncSession, block_id: str) -> Block:
|
|
274
|
+
"""Retrieve a block by ID."""
|
|
275
|
+
response = await session.get(f"{NOTION_API_URL}/v1/blocks/{block_id}")
|
|
276
|
+
response.raise_for_status()
|
|
277
|
+
return response.json() # type: ignore[return-value]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
async def fetch_block_children(
|
|
281
|
+
session: niquests.AsyncSession,
|
|
282
|
+
block_id: str,
|
|
283
|
+
*,
|
|
284
|
+
start_cursor: str | None = None,
|
|
285
|
+
page_size: int | None = None,
|
|
286
|
+
) -> BlockListResponse:
|
|
287
|
+
"""Retrieve a block's children."""
|
|
288
|
+
params: dict[str, str] = {}
|
|
289
|
+
if start_cursor:
|
|
290
|
+
params["start_cursor"] = start_cursor
|
|
291
|
+
if page_size:
|
|
292
|
+
params["page_size"] = str(page_size)
|
|
293
|
+
|
|
294
|
+
response = await session.get(f"{NOTION_API_URL}/v1/blocks/{block_id}/children", params=params or None)
|
|
295
|
+
response.raise_for_status()
|
|
296
|
+
return response.json() # type: ignore[return-value]
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
async def fetch_append_block_children(
|
|
300
|
+
session: niquests.AsyncSession,
|
|
301
|
+
block_id: str,
|
|
302
|
+
children: list[dict[str, Any]],
|
|
303
|
+
) -> BlockListResponse:
|
|
304
|
+
"""Append children blocks to a parent block."""
|
|
305
|
+
payload = {"children": children}
|
|
306
|
+
|
|
307
|
+
response = await session.patch(f"{NOTION_API_URL}/v1/blocks/{block_id}/children", json=payload)
|
|
308
|
+
response.raise_for_status()
|
|
309
|
+
return response.json() # type: ignore[return-value]
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# Search endpoint
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
async def fetch_search(
|
|
316
|
+
session: niquests.AsyncSession,
|
|
317
|
+
*,
|
|
318
|
+
query: str | None = None,
|
|
319
|
+
filter: dict[str, Any] | None = None,
|
|
320
|
+
sort: dict[str, Any] | None = None,
|
|
321
|
+
start_cursor: str | None = None,
|
|
322
|
+
page_size: int | None = None,
|
|
323
|
+
) -> SearchResponse:
|
|
324
|
+
"""Search pages and databases."""
|
|
325
|
+
payload: dict[str, Any] = {}
|
|
326
|
+
if query:
|
|
327
|
+
payload["query"] = query
|
|
328
|
+
if filter:
|
|
329
|
+
payload["filter"] = filter
|
|
330
|
+
if sort:
|
|
331
|
+
payload["sort"] = sort
|
|
332
|
+
if start_cursor:
|
|
333
|
+
payload["start_cursor"] = start_cursor
|
|
334
|
+
if page_size:
|
|
335
|
+
payload["page_size"] = page_size
|
|
336
|
+
|
|
337
|
+
response = await session.post(f"{NOTION_API_URL}/v1/search", json=payload or None)
|
|
338
|
+
response.raise_for_status()
|
|
339
|
+
return response.json() # type: ignore[return-value]
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
if __name__ == "__main__":
|
|
343
|
+
import asyncio
|
|
344
|
+
|
|
345
|
+
async def main():
|
|
346
|
+
async with niquests.AsyncSession() as session:
|
|
347
|
+
session.headers.update(get_notion_headers())
|
|
348
|
+
me = await fetch_me(session)
|
|
349
|
+
print("Me:", me)
|
|
350
|
+
# print(await fetch_search(session, filter="aaa"))
|
|
351
|
+
|
|
352
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Notion API models based on official notion-sdk-js types."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal, NotRequired, TypedDict
|
|
4
|
+
|
|
5
|
+
# Base types
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PartialUser(TypedDict):
|
|
9
|
+
"""Partial user object."""
|
|
10
|
+
|
|
11
|
+
id: str
|
|
12
|
+
object: Literal["user"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class PersonDetails(TypedDict):
|
|
16
|
+
"""Person details."""
|
|
17
|
+
|
|
18
|
+
email: NotRequired[str]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PersonUserObjectResponse(TypedDict):
|
|
22
|
+
"""Person user type details."""
|
|
23
|
+
|
|
24
|
+
type: Literal["person"]
|
|
25
|
+
person: PersonDetails
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BotUserObjectResponse(TypedDict):
|
|
29
|
+
"""Bot user type details."""
|
|
30
|
+
|
|
31
|
+
type: Literal["bot"]
|
|
32
|
+
bot: dict[str, Any]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class UserObjectResponseCommon(TypedDict):
|
|
36
|
+
"""Common user object fields."""
|
|
37
|
+
|
|
38
|
+
id: str
|
|
39
|
+
object: Literal["user"]
|
|
40
|
+
name: str | None
|
|
41
|
+
avatar_url: str | None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# User object is common fields + either person or bot
|
|
45
|
+
class User(UserObjectResponseCommon):
|
|
46
|
+
"""Full user object."""
|
|
47
|
+
|
|
48
|
+
type: Literal["person", "bot"]
|
|
49
|
+
person: NotRequired[PersonDetails]
|
|
50
|
+
bot: NotRequired[dict[str, Any]]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# OAuth types
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class UserOwner(TypedDict):
|
|
57
|
+
"""User owner for OAuth."""
|
|
58
|
+
|
|
59
|
+
type: Literal["user"]
|
|
60
|
+
user: User | PartialUser
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class WorkspaceOwner(TypedDict):
|
|
64
|
+
"""Workspace owner for OAuth."""
|
|
65
|
+
|
|
66
|
+
type: Literal["workspace"]
|
|
67
|
+
workspace: Literal[True]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Owner = UserOwner | WorkspaceOwner
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TokenResponse(TypedDict):
|
|
74
|
+
"""Response from creating or refreshing an access token."""
|
|
75
|
+
|
|
76
|
+
access_token: str
|
|
77
|
+
token_type: Literal["bearer"]
|
|
78
|
+
refresh_token: str | None
|
|
79
|
+
bot_id: str
|
|
80
|
+
workspace_icon: str | None
|
|
81
|
+
workspace_name: str | None
|
|
82
|
+
workspace_id: str
|
|
83
|
+
owner: Owner
|
|
84
|
+
duplicated_template_id: str | None
|
|
85
|
+
request_id: NotRequired[str]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class IntrospectTokenResponse(TypedDict):
|
|
89
|
+
"""Response from introspecting a token."""
|
|
90
|
+
|
|
91
|
+
active: bool
|
|
92
|
+
scope: NotRequired[str]
|
|
93
|
+
iat: NotRequired[int]
|
|
94
|
+
request_id: NotRequired[str]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class RevokeTokenResponse(TypedDict):
|
|
98
|
+
"""Response from revoking a token."""
|
|
99
|
+
|
|
100
|
+
request_id: NotRequired[str]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Rich text types
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class RichTextItemResponse(TypedDict):
|
|
107
|
+
"""Rich text item."""
|
|
108
|
+
|
|
109
|
+
type: str
|
|
110
|
+
plain_text: str
|
|
111
|
+
href: str | None
|
|
112
|
+
annotations: dict[str, Any]
|
|
113
|
+
text: NotRequired[dict[str, Any]]
|
|
114
|
+
mention: NotRequired[dict[str, Any]]
|
|
115
|
+
equation: NotRequired[dict[str, Any]]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Parent types
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class PageParent(TypedDict):
|
|
122
|
+
"""Page parent."""
|
|
123
|
+
|
|
124
|
+
type: Literal["page_id"]
|
|
125
|
+
page_id: str
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class DatabaseParent(TypedDict):
|
|
129
|
+
"""Database parent."""
|
|
130
|
+
|
|
131
|
+
type: Literal["database_id"]
|
|
132
|
+
database_id: str
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class WorkspaceParent(TypedDict):
|
|
136
|
+
"""Workspace parent."""
|
|
137
|
+
|
|
138
|
+
type: Literal["workspace"]
|
|
139
|
+
workspace: Literal[True]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class BlockParent(TypedDict):
|
|
143
|
+
"""Block parent."""
|
|
144
|
+
|
|
145
|
+
type: Literal["block_id"]
|
|
146
|
+
block_id: str
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
Parent = PageParent | DatabaseParent | WorkspaceParent | BlockParent
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Page types
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class Page(TypedDict):
|
|
156
|
+
"""Page object response."""
|
|
157
|
+
|
|
158
|
+
object: Literal["page"]
|
|
159
|
+
id: str
|
|
160
|
+
created_time: str
|
|
161
|
+
last_edited_time: str
|
|
162
|
+
created_by: PartialUser
|
|
163
|
+
last_edited_by: PartialUser
|
|
164
|
+
archived: bool
|
|
165
|
+
in_trash: bool
|
|
166
|
+
is_locked: bool
|
|
167
|
+
url: str
|
|
168
|
+
public_url: str | None
|
|
169
|
+
parent: Parent
|
|
170
|
+
properties: dict[str, Any]
|
|
171
|
+
icon: dict[str, Any] | None
|
|
172
|
+
cover: dict[str, Any] | None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class PartialPage(TypedDict):
|
|
176
|
+
"""Partial page object."""
|
|
177
|
+
|
|
178
|
+
object: Literal["page"]
|
|
179
|
+
id: str
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# Database types
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class Database(TypedDict):
|
|
186
|
+
"""Database object response."""
|
|
187
|
+
|
|
188
|
+
object: Literal["database"]
|
|
189
|
+
id: str
|
|
190
|
+
title: list[RichTextItemResponse]
|
|
191
|
+
description: list[RichTextItemResponse]
|
|
192
|
+
parent: Parent
|
|
193
|
+
is_inline: bool
|
|
194
|
+
in_trash: bool
|
|
195
|
+
is_locked: bool
|
|
196
|
+
created_time: str
|
|
197
|
+
last_edited_time: str
|
|
198
|
+
icon: dict[str, Any] | None
|
|
199
|
+
cover: dict[str, Any] | None
|
|
200
|
+
properties: dict[str, Any]
|
|
201
|
+
url: str
|
|
202
|
+
public_url: str | None
|
|
203
|
+
archived: bool
|
|
204
|
+
created_by: PartialUser
|
|
205
|
+
last_edited_by: PartialUser
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class PartialDatabase(TypedDict):
|
|
209
|
+
"""Partial database object."""
|
|
210
|
+
|
|
211
|
+
object: Literal["database"]
|
|
212
|
+
id: str
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Block types
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class Block(TypedDict):
|
|
219
|
+
"""Block object response."""
|
|
220
|
+
|
|
221
|
+
object: Literal["block"]
|
|
222
|
+
id: str
|
|
223
|
+
parent: Parent
|
|
224
|
+
type: str
|
|
225
|
+
created_time: str
|
|
226
|
+
last_edited_time: str
|
|
227
|
+
created_by: PartialUser
|
|
228
|
+
last_edited_by: PartialUser
|
|
229
|
+
has_children: bool
|
|
230
|
+
archived: bool
|
|
231
|
+
in_trash: bool
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class PartialBlock(TypedDict):
|
|
235
|
+
"""Partial block object."""
|
|
236
|
+
|
|
237
|
+
object: Literal["block"]
|
|
238
|
+
id: str
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# List response types
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class UserListResponse(TypedDict):
|
|
245
|
+
"""Response from listing users."""
|
|
246
|
+
|
|
247
|
+
object: Literal["list"]
|
|
248
|
+
type: Literal["user"]
|
|
249
|
+
results: list[User]
|
|
250
|
+
next_cursor: str | None
|
|
251
|
+
has_more: bool
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class PageListResponse(TypedDict):
|
|
255
|
+
"""Response from querying a database."""
|
|
256
|
+
|
|
257
|
+
object: Literal["list"]
|
|
258
|
+
type: Literal["page_or_data_source"]
|
|
259
|
+
results: list[Page | PartialPage]
|
|
260
|
+
next_cursor: str | None
|
|
261
|
+
has_more: bool
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class BlockListResponse(TypedDict):
|
|
265
|
+
"""Response from listing block children."""
|
|
266
|
+
|
|
267
|
+
object: Literal["list"]
|
|
268
|
+
type: Literal["block"]
|
|
269
|
+
results: list[Block | PartialBlock]
|
|
270
|
+
next_cursor: str | None
|
|
271
|
+
has_more: bool
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class SearchResponse(TypedDict):
|
|
275
|
+
"""Response from search."""
|
|
276
|
+
|
|
277
|
+
object: Literal["list"]
|
|
278
|
+
type: Literal["page_or_data_source"]
|
|
279
|
+
results: list[Page | PartialPage | Database | PartialDatabase]
|
|
280
|
+
next_cursor: str | None
|
|
281
|
+
has_more: bool
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tracktolib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.63.0
|
|
4
4
|
Summary: Utility library for python
|
|
5
5
|
Keywords: utility
|
|
6
6
|
Author-email: julien.brayere@tracktor.fr
|
|
@@ -10,6 +10,7 @@ Requires-Dist: fastapi>=0.103.2 ; extra == 'api'
|
|
|
10
10
|
Requires-Dist: pydantic>=2 ; extra == 'api'
|
|
11
11
|
Requires-Dist: httpx>=0.25.0 ; extra == 'http'
|
|
12
12
|
Requires-Dist: python-json-logger>=3.2.1 ; extra == 'logs'
|
|
13
|
+
Requires-Dist: niquests>=3.15.2 ; extra == 'notion'
|
|
13
14
|
Requires-Dist: asyncpg>=0.27.0 ; extra == 'pg'
|
|
14
15
|
Requires-Dist: rich>=13.6.0 ; extra == 'pg'
|
|
15
16
|
Requires-Dist: psycopg>=3.1.12 ; extra == 'pg-sync'
|
|
@@ -21,6 +22,7 @@ Requires-Python: >=3.12, <4.0
|
|
|
21
22
|
Provides-Extra: api
|
|
22
23
|
Provides-Extra: http
|
|
23
24
|
Provides-Extra: logs
|
|
25
|
+
Provides-Extra: notion
|
|
24
26
|
Provides-Extra: pg
|
|
25
27
|
Provides-Extra: pg-sync
|
|
26
28
|
Provides-Extra: s3
|
|
@@ -2,6 +2,9 @@ tracktolib/__init__.py,sha256=Q9d6h2lNjcYzxvfJ3zlNcpiP_Ak0T3TBPWINzZNrhu0,173
|
|
|
2
2
|
tracktolib/api.py,sha256=ZLMgjH3Y8r3MpXc8m3IuZbzTj3fgrZKZORtSVgbuP-M,10221
|
|
3
3
|
tracktolib/http_utils.py,sha256=c10JGmHaBw3VSDMYhz2dvVw2lo4PUAq1xMub74I7xDc,2625
|
|
4
4
|
tracktolib/logs.py,sha256=D2hx6urXl5l4PBGP8mCpcT4GX7tJeFfNY-7oBfHczBU,2191
|
|
5
|
+
tracktolib/notion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
tracktolib/notion/fetch.py,sha256=zIhFo9T18eS75l0wZN_WaXD51OUxTsgvFuvqk3R87o4,9974
|
|
7
|
+
tracktolib/notion/models.py,sha256=D-px7Ht2xoXKKZICuEkZhRwVBvVCaKCvhpzPtVVYawI,5390
|
|
5
8
|
tracktolib/pg/__init__.py,sha256=Ul_hgwvTXZvQBt7sHKi4ZI-0DDpnXmoFtmVkGRy-1J0,366
|
|
6
9
|
tracktolib/pg/query.py,sha256=_wL9MQU_z8Sk0ZOYGVE0TjUbwBZ1OJJuEq2jlmWoeeM,16693
|
|
7
10
|
tracktolib/pg/utils.py,sha256=ygQn63EBDaEGB0p7P2ibellO2mv-StafanpXKcCUiZU,6324
|
|
@@ -12,6 +15,6 @@ tracktolib/s3/minio.py,sha256=wMEjkSes9Fp39fD17IctALpD6zB2xwDRQEmO7Vzan3g,1387
|
|
|
12
15
|
tracktolib/s3/s3.py,sha256=0HbSAPoaup5-W4LK54zRCjrQ5mr8OWR-N9WjW99Q4aw,5937
|
|
13
16
|
tracktolib/tests.py,sha256=gKE--epQjgMZGXc5ydbl4zjOdmwztJS42UMV0p4hXEA,399
|
|
14
17
|
tracktolib/utils.py,sha256=ysTBF9V35fVXQVBPk0kfE_84SGRxzrayqmg9RbtoJq4,5761
|
|
15
|
-
tracktolib-0.
|
|
16
|
-
tracktolib-0.
|
|
17
|
-
tracktolib-0.
|
|
18
|
+
tracktolib-0.63.0.dist-info/WHEEL,sha256=z-mOpxbJHqy3cq6SvUThBZdaLGFZzdZPtgWLcP2NKjQ,79
|
|
19
|
+
tracktolib-0.63.0.dist-info/METADATA,sha256=q3HcfLHVoybHIDc4Bgayt3kYd5v1spd62OCGyxjCfZw,3128
|
|
20
|
+
tracktolib-0.63.0.dist-info/RECORD,,
|