63 lines
2.5 KiB
Python
63 lines
2.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Snapshot a book's character-library records to a JSON file for later comparison.
|
|
|
|
python3 scripts/backup_characters.py "<book title>" characters_auto.json
|
|
python3 scripts/backup_characters.py "<book title>" characters_manual.json --base-url http://127.0.0.1:7890
|
|
|
|
Matches records the same way the app does: exact `book` field OR `book`
|
|
present in the comma-separated `tags` field (case-insensitive) — this mirrors
|
|
`_characters_for_title` in routes/reader.py / clGetAllByTagOrBook in
|
|
characters-library.js, so the snapshot contains exactly what the UI would
|
|
show for that book.
|
|
|
|
Run this once right after "Define Characters" (step 5) for each version you
|
|
want to compare — e.g. once for the automatic pass, once for the manually
|
|
corrected KONTROLLE version — before either one gets overwritten by a later
|
|
run. compare_characters.py then diffs two such snapshots.
|
|
"""
|
|
import argparse
|
|
import json
|
|
import sys
|
|
|
|
import requests
|
|
|
|
|
|
def characters_for_title(all_chars: list[dict], title: str) -> list[dict]:
|
|
key = title.strip().lower()
|
|
out = []
|
|
for rec in all_chars:
|
|
book = str(rec.get("book") or "").strip().lower()
|
|
tags = [t.strip().lower() for t in str(rec.get("tags") or "").split(",")]
|
|
if book == key or key in tags:
|
|
out.append(rec)
|
|
return out
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
ap.add_argument("book", help="Book title as it appears in the character's `book` field / tags")
|
|
ap.add_argument("outfile", help="Path to write the JSON snapshot to")
|
|
ap.add_argument("--base-url", default="http://127.0.0.1:7890")
|
|
args = ap.parse_args()
|
|
|
|
resp = requests.get(f"{args.base_url}/api/characters", timeout=60)
|
|
resp.raise_for_status()
|
|
all_chars = resp.json()
|
|
if isinstance(all_chars, dict):
|
|
all_chars = all_chars.get("characters") or all_chars.get("items") or []
|
|
|
|
matched = characters_for_title(all_chars, args.book)
|
|
if not matched:
|
|
print(f"No characters found for book {args.book!r}. Known books: "
|
|
f"{sorted({str(c.get('book') or '') for c in all_chars} - {''})}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
matched.sort(key=lambda r: str(r.get("name") or "").lower())
|
|
with open(args.outfile, "w", encoding="utf-8") as f:
|
|
json.dump(matched, f, ensure_ascii=False, indent=2)
|
|
print(f"Wrote {len(matched)} characters for {args.book!r} -> {args.outfile}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|