better-notion 1.4.0__py3-none-any.whl → 1.5.1__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.
- better_notion/_api/collections/databases.py +32 -0
- better_notion/_cli/commands/databases.py +147 -24
- better_notion/_cli/commands/pages.py +270 -293
- better_notion/_sdk/models/database.py +12 -7
- better_notion/plugins/official/agents.py +22 -2
- better_notion/plugins/official/agents_cli.py +29 -4
- better_notion/plugins/official/agents_sdk/managers.py +29 -1
- better_notion/utils/agents/schemas.py +91 -68
- better_notion/utils/agents/workspace.py +43 -29
- better_notion/utils/validators.py +132 -0
- {better_notion-1.4.0.dist-info → better_notion-1.5.1.dist-info}/METADATA +1 -1
- {better_notion-1.4.0.dist-info → better_notion-1.5.1.dist-info}/RECORD +15 -14
- {better_notion-1.4.0.dist-info → better_notion-1.5.1.dist-info}/WHEEL +0 -0
- {better_notion-1.4.0.dist-info → better_notion-1.5.1.dist-info}/entry_points.txt +0 -0
- {better_notion-1.4.0.dist-info → better_notion-1.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,6 @@ This module provides commands for managing Notion pages.
|
|
|
5
5
|
"""
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
import asyncio
|
|
9
8
|
import json
|
|
10
9
|
from typing import Any
|
|
11
10
|
|
|
@@ -28,40 +27,39 @@ def get_client() -> NotionClient:
|
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
@app.command()
|
|
31
|
-
def get(page_id: str) -> None:
|
|
30
|
+
async def get(page_id: str) -> None:
|
|
32
31
|
"""
|
|
33
32
|
Get a page by ID.
|
|
34
33
|
|
|
35
34
|
Retrieves detailed information about a specific Notion page.
|
|
36
35
|
"""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
page = await client.pages.get(page_id)
|
|
36
|
+
try:
|
|
37
|
+
client = get_client()
|
|
38
|
+
page = await client.pages.get(page_id)
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
# Get parent (async method - note the parentheses)
|
|
41
|
+
parent_obj = await page.parent()
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
43
|
+
result = format_success({
|
|
44
|
+
"id": page.id,
|
|
45
|
+
"title": page.title,
|
|
46
|
+
"url": page.url,
|
|
47
|
+
"parent_id": parent_obj.id if parent_obj else None,
|
|
48
|
+
"parent_type": parent_obj.object if parent_obj else None,
|
|
49
|
+
"created_time": page._data.get("created_time"),
|
|
50
|
+
"last_edited_time": page._data.get("last_edited_time"),
|
|
51
|
+
"archived": page.archived,
|
|
52
|
+
"properties": {name: str(value) for name, value in page._data.get("properties", {}).items()},
|
|
53
|
+
})
|
|
54
|
+
typer.echo(result)
|
|
55
|
+
except Exception as e:
|
|
56
|
+
result = format_error("UNKNOWN_ERROR", str(e), retry=False)
|
|
57
|
+
typer.echo(result)
|
|
58
|
+
raise typer.Exit(code=1)
|
|
61
59
|
|
|
62
60
|
|
|
63
61
|
@app.command()
|
|
64
|
-
def create(
|
|
62
|
+
async def create(
|
|
65
63
|
root: bool = typer.Option(False, "--root", "-r", help="Create page at workspace root"),
|
|
66
64
|
parent: str = typer.Option(None, "--parent", "-p", help="Parent database or page ID"),
|
|
67
65
|
title: str = typer.Option(..., "--title", "-t", help="Page title"),
|
|
@@ -74,65 +72,66 @@ def create(
|
|
|
74
72
|
|
|
75
73
|
Note: Workspace parent (--root) may require specific integration permissions.
|
|
76
74
|
"""
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
75
|
+
client = get_client()
|
|
76
|
+
props = json.loads(properties) if properties else {}
|
|
77
|
+
|
|
78
|
+
# Validate mutual exclusivity
|
|
79
|
+
if root and parent:
|
|
80
|
+
result = format_error(
|
|
81
|
+
"INVALID_ARGUMENT",
|
|
82
|
+
"Cannot specify both --root and --parent",
|
|
83
|
+
retry=False
|
|
84
|
+
)
|
|
85
|
+
typer.echo(result)
|
|
86
|
+
raise typer.Exit(code=1)
|
|
87
|
+
|
|
88
|
+
# Handle workspace parent
|
|
89
|
+
if root:
|
|
90
|
+
from better_notion._sdk.parents import WorkspaceParent
|
|
91
|
+
parent_obj = WorkspaceParent()
|
|
92
|
+
else:
|
|
93
|
+
# Existing parent resolution logic
|
|
94
|
+
# Try database first
|
|
95
|
+
try:
|
|
96
|
+
parent_obj = await client.databases.get(parent)
|
|
97
|
+
except Exception as db_err:
|
|
98
|
+
# If database fails, try as page
|
|
96
99
|
try:
|
|
97
|
-
parent_obj = await client.
|
|
98
|
-
except Exception as
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
parent_type =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"parent_type": parent_type,
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
result = asyncio.run(_create())
|
|
100
|
+
parent_obj = await client.pages.get(parent)
|
|
101
|
+
except Exception as page_err:
|
|
102
|
+
# Both failed - return detailed error
|
|
103
|
+
result = format_error(
|
|
104
|
+
"PARENT_NOT_FOUND",
|
|
105
|
+
f"Could not find parent '{parent}' as database or page. "
|
|
106
|
+
f"Database error: {str(db_err)}. Page error: {str(page_err)}",
|
|
107
|
+
retry=False
|
|
108
|
+
)
|
|
109
|
+
typer.echo(result)
|
|
110
|
+
raise typer.Exit(code=1)
|
|
111
|
+
|
|
112
|
+
page = await client.pages.create(parent=parent_obj, title=title, **props)
|
|
113
|
+
|
|
114
|
+
# Get parent info safely
|
|
115
|
+
parent_id = None
|
|
116
|
+
parent_type = None
|
|
117
|
+
if root:
|
|
118
|
+
parent_type = "workspace"
|
|
119
|
+
elif page.parent:
|
|
120
|
+
parent_id = getattr(page.parent, 'id', None)
|
|
121
|
+
parent_type = getattr(page.parent, 'object', None)
|
|
122
|
+
|
|
123
|
+
result = format_success({
|
|
124
|
+
"id": page.id,
|
|
125
|
+
"title": page.title,
|
|
126
|
+
"url": page.url,
|
|
127
|
+
"parent_id": parent_id,
|
|
128
|
+
"parent_type": parent_type,
|
|
129
|
+
})
|
|
131
130
|
typer.echo(result)
|
|
132
131
|
|
|
133
132
|
|
|
134
133
|
@app.command()
|
|
135
|
-
def update(
|
|
134
|
+
async def update(
|
|
136
135
|
page_id: str = typer.Argument(..., help="Page ID to update"),
|
|
137
136
|
properties: str = typer.Option(..., "--properties", "-p", help="JSON string of properties to update"),
|
|
138
137
|
) -> None:
|
|
@@ -141,46 +140,40 @@ def update(
|
|
|
141
140
|
|
|
142
141
|
Updates the specified properties of a page.
|
|
143
142
|
"""
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
props = json.loads(properties)
|
|
143
|
+
client = get_client()
|
|
144
|
+
page = await client.pages.get(page_id)
|
|
145
|
+
props = json.loads(properties)
|
|
148
146
|
|
|
149
|
-
|
|
147
|
+
updated_page = await page.update(**props)
|
|
150
148
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
result = asyncio.run(_update())
|
|
149
|
+
result = format_success({
|
|
150
|
+
"id": updated_page.id,
|
|
151
|
+
"title": updated_page.title,
|
|
152
|
+
"last_edited_time": updated_page.last_edited_time,
|
|
153
|
+
})
|
|
158
154
|
typer.echo(result)
|
|
159
155
|
|
|
160
156
|
|
|
161
157
|
@app.command()
|
|
162
|
-
def delete(page_id: str) -> None:
|
|
158
|
+
async def delete(page_id: str) -> None:
|
|
163
159
|
"""
|
|
164
160
|
Delete a page.
|
|
165
161
|
|
|
166
162
|
Permanently deletes a page and all its children.
|
|
167
163
|
"""
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
result = asyncio.run(_delete())
|
|
164
|
+
client = get_client()
|
|
165
|
+
page = await client.pages.get(page_id)
|
|
166
|
+
await page.delete()
|
|
167
|
+
|
|
168
|
+
result = format_success({
|
|
169
|
+
"id": page_id,
|
|
170
|
+
"status": "deleted",
|
|
171
|
+
})
|
|
179
172
|
typer.echo(result)
|
|
180
173
|
|
|
181
174
|
|
|
182
175
|
@app.command()
|
|
183
|
-
def list(
|
|
176
|
+
async def list(
|
|
184
177
|
database: str = typer.Option(..., "--database", "-d", help="Database ID to list pages from"),
|
|
185
178
|
filter: str = typer.Option(None, "--filter", "-f", help="JSON filter for query"),
|
|
186
179
|
) -> None:
|
|
@@ -189,32 +182,29 @@ def list(
|
|
|
189
182
|
|
|
190
183
|
Lists all pages in a database, optionally filtered.
|
|
191
184
|
"""
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
db = await client.databases.get(database)
|
|
195
|
-
|
|
196
|
-
filters = json.loads(filter) if filter else {}
|
|
197
|
-
pages = await db.query(client=client, **filters)
|
|
198
|
-
|
|
199
|
-
return format_success({
|
|
200
|
-
"database_id": database,
|
|
201
|
-
"count": len(pages),
|
|
202
|
-
"pages": [
|
|
203
|
-
{
|
|
204
|
-
"id": page.id,
|
|
205
|
-
"title": page.title,
|
|
206
|
-
"url": page.url,
|
|
207
|
-
}
|
|
208
|
-
for page in pages
|
|
209
|
-
],
|
|
210
|
-
})
|
|
185
|
+
client = get_client()
|
|
186
|
+
db = await client.databases.get(database)
|
|
211
187
|
|
|
212
|
-
|
|
188
|
+
filters = json.loads(filter) if filter else {}
|
|
189
|
+
pages = await db.query(client=client, **filters)
|
|
190
|
+
|
|
191
|
+
result = format_success({
|
|
192
|
+
"database_id": database,
|
|
193
|
+
"count": len(pages),
|
|
194
|
+
"pages": [
|
|
195
|
+
{
|
|
196
|
+
"id": page.id,
|
|
197
|
+
"title": page.title,
|
|
198
|
+
"url": page.url,
|
|
199
|
+
}
|
|
200
|
+
for page in pages
|
|
201
|
+
],
|
|
202
|
+
})
|
|
213
203
|
typer.echo(result)
|
|
214
204
|
|
|
215
205
|
|
|
216
206
|
@app.command()
|
|
217
|
-
def search(
|
|
207
|
+
async def search(
|
|
218
208
|
query: str = typer.Argument(..., help="Search query"),
|
|
219
209
|
filter: str = typer.Option(None, "--filter", "-f", help="JSON filter for object type"),
|
|
220
210
|
) -> None:
|
|
@@ -223,60 +213,54 @@ def search(
|
|
|
223
213
|
|
|
224
214
|
Searches for pages matching the query.
|
|
225
215
|
"""
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
filters = json.loads(filter) if filter else {}
|
|
229
|
-
|
|
230
|
-
results = await client.search.search(query=query, filter=filters)
|
|
231
|
-
|
|
232
|
-
pages = [r for r in results if hasattr(r, 'title')]
|
|
233
|
-
return format_success({
|
|
234
|
-
"query": query,
|
|
235
|
-
"count": len(pages),
|
|
236
|
-
"pages": [
|
|
237
|
-
{
|
|
238
|
-
"id": page.id,
|
|
239
|
-
"title": page.title,
|
|
240
|
-
"url": page.url,
|
|
241
|
-
}
|
|
242
|
-
for page in pages
|
|
243
|
-
],
|
|
244
|
-
})
|
|
216
|
+
client = get_client()
|
|
217
|
+
filters = json.loads(filter) if filter else {}
|
|
245
218
|
|
|
246
|
-
|
|
219
|
+
results = await client.search.search(query=query, filter=filters)
|
|
220
|
+
|
|
221
|
+
pages = [r for r in results if hasattr(r, 'title')]
|
|
222
|
+
result = format_success({
|
|
223
|
+
"query": query,
|
|
224
|
+
"count": len(pages),
|
|
225
|
+
"pages": [
|
|
226
|
+
{
|
|
227
|
+
"id": page.id,
|
|
228
|
+
"title": page.title,
|
|
229
|
+
"url": page.url,
|
|
230
|
+
}
|
|
231
|
+
for page in pages
|
|
232
|
+
],
|
|
233
|
+
})
|
|
247
234
|
typer.echo(result)
|
|
248
235
|
|
|
249
236
|
|
|
250
237
|
@app.command()
|
|
251
|
-
def blocks(page_id: str) -> None:
|
|
238
|
+
async def blocks(page_id: str) -> None:
|
|
252
239
|
"""
|
|
253
240
|
Get blocks in a page.
|
|
254
241
|
|
|
255
242
|
Retrieves all blocks contained in a page.
|
|
256
243
|
"""
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
"type": block.type,
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
return format_success({
|
|
269
|
-
"page_id": page_id,
|
|
270
|
-
"count": len(block_list),
|
|
271
|
-
"blocks": block_list,
|
|
244
|
+
client = get_client()
|
|
245
|
+
page = await client.pages.get(page_id)
|
|
246
|
+
|
|
247
|
+
block_list = []
|
|
248
|
+
async for block in page.children():
|
|
249
|
+
block_list.append({
|
|
250
|
+
"id": block.id,
|
|
251
|
+
"type": block.type,
|
|
272
252
|
})
|
|
273
253
|
|
|
274
|
-
result =
|
|
254
|
+
result = format_success({
|
|
255
|
+
"page_id": page_id,
|
|
256
|
+
"count": len(block_list),
|
|
257
|
+
"blocks": block_list,
|
|
258
|
+
})
|
|
275
259
|
typer.echo(result)
|
|
276
260
|
|
|
277
261
|
|
|
278
262
|
@app.command()
|
|
279
|
-
def copy(
|
|
263
|
+
async def copy(
|
|
280
264
|
page_id: str = typer.Argument(..., help="Page ID to copy"),
|
|
281
265
|
destination: str = typer.Option(..., "--dest", "-d", help="Destination parent ID"),
|
|
282
266
|
) -> None:
|
|
@@ -285,34 +269,31 @@ def copy(
|
|
|
285
269
|
|
|
286
270
|
Creates a copy of a page under a new parent.
|
|
287
271
|
"""
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
result = asyncio.run(_copy())
|
|
272
|
+
client = get_client()
|
|
273
|
+
page = await client.pages.get(page_id)
|
|
274
|
+
|
|
275
|
+
# Get destination parent
|
|
276
|
+
try:
|
|
277
|
+
dest_parent = await client.databases.get(destination)
|
|
278
|
+
except Exception:
|
|
279
|
+
dest_parent = await client.pages.get(destination)
|
|
280
|
+
|
|
281
|
+
# Create new page with same title
|
|
282
|
+
new_page = await client.pages.create(
|
|
283
|
+
parent=dest_parent,
|
|
284
|
+
title=page.title,
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
result = format_success({
|
|
288
|
+
"original_id": page_id,
|
|
289
|
+
"new_id": new_page.id,
|
|
290
|
+
"new_url": new_page.url,
|
|
291
|
+
})
|
|
311
292
|
typer.echo(result)
|
|
312
293
|
|
|
313
294
|
|
|
314
295
|
@app.command()
|
|
315
|
-
def move(
|
|
296
|
+
async def move(
|
|
316
297
|
page_id: str = typer.Argument(..., help="Page ID to move"),
|
|
317
298
|
destination: str = typer.Argument(..., help="Destination parent ID"),
|
|
318
299
|
) -> None:
|
|
@@ -321,74 +302,65 @@ def move(
|
|
|
321
302
|
|
|
322
303
|
Moves a page to a new parent (database or page).
|
|
323
304
|
"""
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
result = asyncio.run(_move())
|
|
305
|
+
client = get_client()
|
|
306
|
+
page = await client.pages.get(page_id)
|
|
307
|
+
|
|
308
|
+
# Get destination parent
|
|
309
|
+
try:
|
|
310
|
+
dest_parent = await client.databases.get(destination)
|
|
311
|
+
except Exception:
|
|
312
|
+
dest_parent = await client.pages.get(destination)
|
|
313
|
+
|
|
314
|
+
# Update parent
|
|
315
|
+
await page.update(parent=dest_parent._data)
|
|
316
|
+
|
|
317
|
+
result = format_success({
|
|
318
|
+
"id": page_id,
|
|
319
|
+
"new_parent_id": destination,
|
|
320
|
+
})
|
|
343
321
|
typer.echo(result)
|
|
344
322
|
|
|
345
323
|
|
|
346
324
|
@app.command()
|
|
347
|
-
def archive(page_id: str) -> None:
|
|
325
|
+
async def archive(page_id: str) -> None:
|
|
348
326
|
"""
|
|
349
327
|
Archive a page.
|
|
350
328
|
|
|
351
329
|
Archives a page (moves to trash).
|
|
352
330
|
"""
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
page = await client.pages.get(page_id)
|
|
356
|
-
|
|
357
|
-
await page.update(archived=True)
|
|
331
|
+
client = get_client()
|
|
332
|
+
page = await client.pages.get(page_id)
|
|
358
333
|
|
|
359
|
-
|
|
360
|
-
"id": page_id,
|
|
361
|
-
"status": "archived",
|
|
362
|
-
})
|
|
334
|
+
await page.update(archived=True)
|
|
363
335
|
|
|
364
|
-
result =
|
|
336
|
+
result = format_success({
|
|
337
|
+
"id": page_id,
|
|
338
|
+
"status": "archived",
|
|
339
|
+
})
|
|
365
340
|
typer.echo(result)
|
|
366
341
|
|
|
367
342
|
|
|
368
343
|
@app.command()
|
|
369
|
-
def restore(page_id: str) -> None:
|
|
344
|
+
async def restore(page_id: str) -> None:
|
|
370
345
|
"""
|
|
371
346
|
Restore an archived page.
|
|
372
347
|
|
|
373
348
|
Restores a page from the trash/archive.
|
|
374
349
|
"""
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
page = await client.pages.get(page_id)
|
|
350
|
+
client = get_client()
|
|
351
|
+
page = await client.pages.get(page_id)
|
|
378
352
|
|
|
379
|
-
|
|
353
|
+
await page.update(archived=False)
|
|
380
354
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
result = asyncio.run(_restore())
|
|
355
|
+
result = format_success({
|
|
356
|
+
"id": page_id,
|
|
357
|
+
"status": "restored",
|
|
358
|
+
})
|
|
387
359
|
typer.echo(result)
|
|
388
360
|
|
|
389
361
|
|
|
390
362
|
@app.command("create-from-md")
|
|
391
|
-
def create_from_md(
|
|
363
|
+
async def create_from_md(
|
|
392
364
|
file: str = typer.Option(..., "--file", "-f", help="Path to markdown file"),
|
|
393
365
|
parent: str = typer.Option(..., "--parent", "-p", help="Parent database or page ID"),
|
|
394
366
|
title: str = typer.Option(None, "--title", "-t", help="Custom page title (default: first H1 or filename)"),
|
|
@@ -399,74 +371,79 @@ def create_from_md(
|
|
|
399
371
|
|
|
400
372
|
Parses the markdown file and creates a new Notion page with all blocks.
|
|
401
373
|
"""
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
md_title, blocks = parse_markdown_file(file)
|
|
406
|
-
|
|
407
|
-
# Use custom title if provided
|
|
408
|
-
page_title = title or md_title
|
|
409
|
-
|
|
410
|
-
if dry_run:
|
|
411
|
-
# Show what would be created
|
|
412
|
-
return format_success({
|
|
413
|
-
"dry_run": True,
|
|
414
|
-
"file": file,
|
|
415
|
-
"title": page_title,
|
|
416
|
-
"parent": parent,
|
|
417
|
-
"blocks_count": len(blocks),
|
|
418
|
-
"blocks_preview": [
|
|
419
|
-
{
|
|
420
|
-
"type": block.get("type"),
|
|
421
|
-
"preview": str(block.get(block.get("type", {}), {}))[:100]
|
|
422
|
-
}
|
|
423
|
-
for block in blocks[:5] # Show first 5 blocks
|
|
424
|
-
]
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
client = get_client()
|
|
428
|
-
|
|
429
|
-
# Resolve parent
|
|
430
|
-
try:
|
|
431
|
-
parent_obj = await client.databases.get(parent)
|
|
432
|
-
except Exception:
|
|
433
|
-
parent_obj = await client.pages.get(parent)
|
|
374
|
+
try:
|
|
375
|
+
# Parse markdown file
|
|
376
|
+
md_title, blocks = parse_markdown_file(file)
|
|
434
377
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
parent=parent_obj,
|
|
438
|
-
title=page_title,
|
|
439
|
-
)
|
|
378
|
+
# Use custom title if provided
|
|
379
|
+
page_title = title or md_title
|
|
440
380
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
381
|
+
if dry_run:
|
|
382
|
+
# Show what would be created
|
|
383
|
+
result = format_success({
|
|
384
|
+
"dry_run": True,
|
|
385
|
+
"file": file,
|
|
386
|
+
"title": page_title,
|
|
387
|
+
"parent": parent,
|
|
388
|
+
"blocks_count": len(blocks),
|
|
389
|
+
"blocks_preview": [
|
|
390
|
+
{
|
|
391
|
+
"type": block.get("type"),
|
|
392
|
+
"preview": str(block.get(block.get("type", {}), {}))[:100]
|
|
393
|
+
}
|
|
394
|
+
for block in blocks[:5] # Show first 5 blocks
|
|
395
|
+
]
|
|
396
|
+
})
|
|
397
|
+
typer.echo(result)
|
|
398
|
+
return
|
|
445
399
|
|
|
446
|
-
|
|
400
|
+
client = get_client()
|
|
447
401
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
# Continue with other blocks even if one fails
|
|
454
|
-
pass
|
|
402
|
+
# Resolve parent
|
|
403
|
+
try:
|
|
404
|
+
parent_obj = await client.databases.get(parent)
|
|
405
|
+
except Exception:
|
|
406
|
+
parent_obj = await client.pages.get(parent)
|
|
455
407
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
"file": file,
|
|
462
|
-
})
|
|
408
|
+
# Create page
|
|
409
|
+
page = await client.pages.create(
|
|
410
|
+
parent=parent_obj,
|
|
411
|
+
title=page_title,
|
|
412
|
+
)
|
|
463
413
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
except Exception as e:
|
|
469
|
-
return format_error("UNKNOWN_ERROR", str(e), retry=False)
|
|
414
|
+
# Add blocks to page
|
|
415
|
+
if blocks:
|
|
416
|
+
# Use BlockCollection to append blocks
|
|
417
|
+
from better_notion._api.collections import BlockCollection
|
|
470
418
|
|
|
471
|
-
|
|
472
|
-
|
|
419
|
+
blocks_collection = BlockCollection(client.api, parent_id=page.id)
|
|
420
|
+
|
|
421
|
+
# Add blocks one by one (Notion API limitation)
|
|
422
|
+
for block_data in blocks:
|
|
423
|
+
try:
|
|
424
|
+
await blocks_collection.append(block_data)
|
|
425
|
+
except Exception as e:
|
|
426
|
+
# Continue with other blocks even if one fails
|
|
427
|
+
pass
|
|
428
|
+
|
|
429
|
+
result = format_success({
|
|
430
|
+
"id": page.id,
|
|
431
|
+
"title": page_title,
|
|
432
|
+
"url": page.url,
|
|
433
|
+
"blocks_created": len(blocks),
|
|
434
|
+
"file": file,
|
|
435
|
+
})
|
|
436
|
+
typer.echo(result)
|
|
437
|
+
|
|
438
|
+
except FileNotFoundError as e:
|
|
439
|
+
result = format_error("FILE_NOT_FOUND", str(e), retry=False)
|
|
440
|
+
typer.echo(result)
|
|
441
|
+
raise typer.Exit(code=1)
|
|
442
|
+
except ValueError as e:
|
|
443
|
+
result = format_error("INVALID_FILE", str(e), retry=False)
|
|
444
|
+
typer.echo(result)
|
|
445
|
+
raise typer.Exit(code=1)
|
|
446
|
+
except Exception as e:
|
|
447
|
+
result = format_error("UNKNOWN_ERROR", str(e), retry=False)
|
|
448
|
+
typer.echo(result)
|
|
449
|
+
raise typer.Exit(code=1)
|