66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""起点/番茄人工导出导入与归因对比。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from muse.store import connect, default_db_path
|
|
|
|
|
|
def import_ranking(path: str | Path, *, sqlite_path: str | Path | None = None) -> dict[str, Any]:
|
|
source = Path(path)
|
|
rows = []
|
|
with source.open(encoding="utf-8") as handle:
|
|
reader = csv.DictReader(handle)
|
|
for row in reader:
|
|
rows.append(
|
|
{
|
|
"platform": row.get("platform") or row.get("平台"),
|
|
"title": row.get("title") or row.get("书名"),
|
|
"rank": int(row.get("rank") or row.get("名次") or 0),
|
|
"votes": int(row.get("votes") or row.get("月票") or row.get("追读") or 0),
|
|
"date": row.get("date") or row.get("日期"),
|
|
}
|
|
)
|
|
if not rows:
|
|
raise ValueError("导入表为空")
|
|
with connect(sqlite_path or default_db_path()) as conn:
|
|
runs = [
|
|
dict(row)
|
|
for row in conn.execute(
|
|
"SELECT id, kind, skill_set_hash, created_at FROM runs ORDER BY created_at DESC LIMIT 20"
|
|
)
|
|
]
|
|
report = {
|
|
"imported": len(rows),
|
|
"top": rows[0],
|
|
"runs_considered": len(runs),
|
|
"attribution": [
|
|
{
|
|
"title": item["title"],
|
|
"platform": item["platform"],
|
|
"rank": item["rank"],
|
|
"nearest_run": runs[0]["id"] if runs else None,
|
|
"skill_set_hash": runs[0]["skill_set_hash"] if runs else None,
|
|
}
|
|
for item in rows[:5]
|
|
],
|
|
}
|
|
return report
|
|
|
|
|
|
def write_attribution_report(report: dict[str, Any], dest: str | Path) -> Path:
|
|
target = Path(dest)
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
lines = ["# 榜单归因", ""]
|
|
lines.append(f"导入 {report['imported']} 行。")
|
|
for item in report["attribution"]:
|
|
lines.append(
|
|
f"- {item['platform']}《{item['title']}》第 {item['rank']} 名 → run `{item['nearest_run']}` skill_set `{item['skill_set_hash']}`"
|
|
)
|
|
target.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
return target
|