135 lines
5.0 KiB
Python
135 lines
5.0 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
query_tasks.py — 列出 Holy Crab 所有任务
|
||
|
|
用法: python3 query_tasks.py [任务ID 关键词]
|
||
|
|
|
||
|
|
Examples:
|
||
|
|
python3 query_tasks.py
|
||
|
|
python3 query_tasks.py HC-20260727-ABC12
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||
|
|
DEFAULT_BACKEND = "http://localhost:8000"
|
||
|
|
|
||
|
|
|
||
|
|
def load_cookie() -> str | None:
|
||
|
|
# 优先级:环境变量 > ~/.holy_crab_env > ~/.holy_crab_cookie
|
||
|
|
env = os.environ.get("HC_SESSION")
|
||
|
|
if env:
|
||
|
|
return env
|
||
|
|
env_file = Path.home() / ".holy_crab_env"
|
||
|
|
if env_file.exists():
|
||
|
|
for line in env_file.read_text().strip().splitlines():
|
||
|
|
line = line.strip()
|
||
|
|
if line.startswith("HC_SESSION=") or line.startswith("hc_session="):
|
||
|
|
val = line.split("=", 1)[1].strip().strip('"').strip("'")
|
||
|
|
if val:
|
||
|
|
return val
|
||
|
|
cookie_file = Path.home() / ".holy_crab_cookie"
|
||
|
|
if cookie_file.exists():
|
||
|
|
return cookie_file.read_text().strip()
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def api_get(path: str, params: dict | None = None) -> dict:
|
||
|
|
backend = os.environ.get("HC_BACKEND_URL", DEFAULT_BACKEND).rstrip("/")
|
||
|
|
url = f"{backend}/api/v1{path}"
|
||
|
|
cookie = load_cookie()
|
||
|
|
headers = {}
|
||
|
|
if cookie:
|
||
|
|
headers["Cookie"] = f"hc_session={cookie}"
|
||
|
|
|
||
|
|
import urllib.request
|
||
|
|
req = urllib.request.Request(url, headers=headers)
|
||
|
|
if params:
|
||
|
|
import urllib.parse
|
||
|
|
req.full_url = f"{url}?{urllib.parse.urlencode(params)}"
|
||
|
|
try:
|
||
|
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
||
|
|
return json.loads(resp.read().decode("utf-8"))
|
||
|
|
except urllib.error.HTTPError as exc:
|
||
|
|
body = exc.read().decode("utf-8", errors="replace")
|
||
|
|
try:
|
||
|
|
err = json.loads(body)
|
||
|
|
print(f"❌ API错误 {exc.code}: {err}", file=sys.stderr)
|
||
|
|
except Exception:
|
||
|
|
print(f"❌ HTTP {exc.code}: {body[:300]}", file=sys.stderr)
|
||
|
|
sys.exit(1)
|
||
|
|
|
||
|
|
|
||
|
|
def format_status(status: str) -> str:
|
||
|
|
mapping = {
|
||
|
|
"completed": "✅ 完成",
|
||
|
|
"failed": "❌ 失败",
|
||
|
|
"cancelled": "🚫 取消",
|
||
|
|
"partial": "⚠️ 部分",
|
||
|
|
"running": "⏳ 进行中",
|
||
|
|
"waiting": "⏳ 等待中",
|
||
|
|
}
|
||
|
|
return mapping.get(status, status)
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> None:
|
||
|
|
task_id_filter = sys.argv[1] if len(sys.argv) > 1 else None
|
||
|
|
|
||
|
|
if task_id_filter:
|
||
|
|
# 精确查单个任务
|
||
|
|
data = api_get(f"/tasks/{task_id_filter}")
|
||
|
|
task = data.get("data", {})
|
||
|
|
print(f"\n{'─'*60}")
|
||
|
|
print(f" 任务ID : {task.get('task_id')}")
|
||
|
|
print(f" 名称 : {task.get('name')}")
|
||
|
|
print(f" 关键词 : {', '.join(task.get('keywords', []))}")
|
||
|
|
print(f" 状态 : {format_status(task.get('status'))}")
|
||
|
|
print(f" 进度 : {task.get('progress_percent')}%")
|
||
|
|
print(f" 模式 : {task.get('mode')}")
|
||
|
|
print(f" 创建时间 : {task.get('created_at')}")
|
||
|
|
print(f" 完成时间 : {task.get('completed_at') or '—'}")
|
||
|
|
if task.get("error"):
|
||
|
|
print(f" 错误 : [{task['error'].get('code')}] {task['error'].get('message')}")
|
||
|
|
# 检查 extracted
|
||
|
|
result = task.get("result", {})
|
||
|
|
extracted = result.get("extracted")
|
||
|
|
if extracted:
|
||
|
|
meta = extracted.get("提取元数据", {})
|
||
|
|
stats = meta.get("召回统计", {})
|
||
|
|
print(f"\n 📊 数据清洗结果:")
|
||
|
|
print(f" schema : {extracted.get('schema_version')}")
|
||
|
|
print(f" 标签总数 : {stats.get('标签总数', 'N/A')}")
|
||
|
|
print(f" 召回总数 : {stats.get('实际召回内容总数', 'N/A')}")
|
||
|
|
print(f" 榜单数量 : {len(extracted.get('榜单', []))}")
|
||
|
|
print(f" 产品数量 : {len(extracted.get('产品详情', []))}")
|
||
|
|
src = extracted.get("参考来源笔记总量", {})
|
||
|
|
print(f" 笔记总量 : {src.get('原始文本', 'N/A')}")
|
||
|
|
else:
|
||
|
|
print(f"\n ⏳ 任务尚未完成或无 extracted 数据")
|
||
|
|
print(f"{'─'*60}\n")
|
||
|
|
else:
|
||
|
|
# 列表
|
||
|
|
data = api_get("/tasks", {"page_size": 50})
|
||
|
|
items = data.get("data", {}).get("items", [])
|
||
|
|
total = data.get("data", {}).get("total", 0)
|
||
|
|
print(f"\n{'─'*60}")
|
||
|
|
print(f" Holy Crab 任务列表 (共 {total} 个)")
|
||
|
|
print(f"{'─'*60}")
|
||
|
|
print(f" {'任务ID':<22} {'状态':<14} {'关键词':<16} {'创建时间':<12} {'文件'}")
|
||
|
|
print(f" {'─'*60}")
|
||
|
|
for t in items:
|
||
|
|
keywords = ",".join(t.get("keywords", [])[:2])
|
||
|
|
if len(",".join(t.get("keywords", []))) > 16:
|
||
|
|
keywords += "..."
|
||
|
|
status = format_status(t.get("status", ""))
|
||
|
|
files = len(t.get("files", []))
|
||
|
|
ts = (t.get("created_at") or "")[:10]
|
||
|
|
print(f" {t.get('task_id'):<22} {status:<14} {keywords:<16} {ts:<12} {files}f")
|
||
|
|
print(f"{'─'*60}\n")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|