110 lines
3.0 KiB
Python
110 lines
3.0 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import getpass
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||
|
|
if str(PROJECT_ROOT) not in sys.path:
|
||
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
||
|
|
|
||
|
|
from app.db import SessionLocal, configure_database
|
||
|
|
from app.notion_import import (
|
||
|
|
apply_import,
|
||
|
|
extract_page_id,
|
||
|
|
fetch_page_blocks,
|
||
|
|
parse_notion_blocks,
|
||
|
|
print_summary,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
parser = argparse.ArgumentParser(description="一次性导入 Notion 搬家记录到当前 SQLite 数据库")
|
||
|
|
parser.add_argument("--dry-run", action="store_true", help="只解析,不写数据库")
|
||
|
|
parser.add_argument("--apply", action="store_true", help="真正写入数据库")
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
mode = _resolve_mode(args)
|
||
|
|
|
||
|
|
token = getpass.getpass("请输入 Notion API token: ").strip()
|
||
|
|
if not token:
|
||
|
|
print("未输入 token,已退出")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
page_url = input("请输入 Notion 页面完整 URL: ").strip()
|
||
|
|
if not page_url:
|
||
|
|
print("未输入页面 URL,已退出")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
try:
|
||
|
|
page_id = extract_page_id(page_url)
|
||
|
|
except ValueError as exc:
|
||
|
|
print(f"页面 URL 无法识别: {exc}")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
print()
|
||
|
|
print(f"正在读取 Notion page: {page_id}")
|
||
|
|
try:
|
||
|
|
blocks = fetch_page_blocks(token, page_id)
|
||
|
|
except Exception as exc:
|
||
|
|
print(f"读取 Notion page 失败: {exc}")
|
||
|
|
return 1
|
||
|
|
|
||
|
|
print(f"已读取顶层及嵌套 blocks,总数约 {count_blocks(blocks)} 个")
|
||
|
|
print("正在解析页面结构...")
|
||
|
|
summary = parse_notion_blocks(blocks)
|
||
|
|
print_summary(summary)
|
||
|
|
|
||
|
|
if mode == "dry-run":
|
||
|
|
print()
|
||
|
|
print("dry-run 完成,未写入数据库。")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
print()
|
||
|
|
print("这是一次性导入脚本,不建议在同一数据库上重复执行。")
|
||
|
|
print("建议先备份当前 SQLite 数据库,再继续。")
|
||
|
|
confirmed = input("确认执行导入?输入 yes 继续: ").strip().lower()
|
||
|
|
if confirmed != "yes":
|
||
|
|
print("已取消导入。")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
configure_database()
|
||
|
|
db = SessionLocal()
|
||
|
|
try:
|
||
|
|
counts = apply_import(summary, db)
|
||
|
|
except Exception as exc:
|
||
|
|
db.rollback()
|
||
|
|
print(f"导入失败,已回滚: {exc}")
|
||
|
|
return 1
|
||
|
|
finally:
|
||
|
|
db.close()
|
||
|
|
|
||
|
|
print()
|
||
|
|
print("导入完成")
|
||
|
|
print(f"- 写入 Box: {counts['boxes']}")
|
||
|
|
print(f"- 写入 Item: {counts['items']}")
|
||
|
|
print(f"- 写入 SubItem: {counts['subitems']}")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
def _resolve_mode(args: argparse.Namespace) -> str:
|
||
|
|
if args.apply and args.dry_run:
|
||
|
|
raise SystemExit("请只选择一种模式:--dry-run 或 --apply")
|
||
|
|
if args.apply:
|
||
|
|
return "apply"
|
||
|
|
return "dry-run"
|
||
|
|
|
||
|
|
|
||
|
|
def count_blocks(blocks: list[dict]) -> int:
|
||
|
|
total = 0
|
||
|
|
for block in blocks:
|
||
|
|
total += 1
|
||
|
|
total += count_blocks(block.get("_children", []))
|
||
|
|
return total
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|