"""Pull playtest feedback down from the private HF Dataset and summarise it. python scripts/pull_feedback.py # summary to stdout python scripts/pull_feedback.py --out fb.jsonl # also write a merged JSONL Requires LWC_FEEDBACK_DATASET (e.g. "defenestrate/lwc-feedback") and HF_TOKEN. Each feedback submission is stored as its own file under records/YYYY/MM/DD/ because the Space's local disk is ephemeral — see world_compiler/ui/feedback.py. This script globs them back together in timestamp order. """ from __future__ import annotations import argparse import json import os import sys from pathlib import Path def load_records(local_dir: Path) -> list[dict]: records = [] for path in sorted((local_dir / "records").rglob("*.json")): try: records.append(json.loads(path.read_text(encoding="utf-8"))) except (OSError, json.JSONDecodeError) as exc: print(f"skipping unreadable {path.name}: {exc}", file=sys.stderr) records.sort(key=lambda r: r.get("timestamp", "")) return records def summarise(records: list[dict]) -> None: if not records: print("No feedback yet.") return print(f"{len(records)} feedback records\n") by_version: dict[str, int] = {} for r in records: version = f"{r.get('scenario_id', '?')} v{r.get('scenario_version', '?')}" by_version[version] = by_version.get(version, 0) + 1 print("By scenario version:") for version, count in sorted(by_version.items()): print(f" {version}: {count}") print() for r in records: print("=" * 78) print( f"{r.get('timestamp', '?')} {r.get('username', '?')} " f"[{r.get('status', '?')}] talking to: {r.get('character') or '(nobody)'}" ) for student_text, actor_text in r.get("last_exchange") or []: print(f" they asked: {student_text[:160]}") print(f" reply began: {actor_text[:160]}") print(f"COMMENT: {str(r.get('comment', '')).strip()[:600]}") print() def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--out", type=Path, help="write merged JSONL here") parser.add_argument( "--local-dir", type=Path, default=Path("feedback_pull"), help="where to download the dataset snapshot", ) args = parser.parse_args(argv) repo = os.getenv("LWC_FEEDBACK_DATASET") token = os.getenv("HF_TOKEN") if not repo or not token: print( "Set LWC_FEEDBACK_DATASET and HF_TOKEN first.\n" " LWC_FEEDBACK_DATASET the dataset repo id, e.g. defenestrate/lwc-feedback\n" " HF_TOKEN a token with read access to it", file=sys.stderr, ) return 1 from huggingface_hub import snapshot_download snapshot_download( repo_id=repo, repo_type="dataset", local_dir=str(args.local_dir), token=token, ) records = load_records(args.local_dir) summarise(records) if args.out: args.out.parent.mkdir(parents=True, exist_ok=True) with args.out.open("w", encoding="utf-8") as fh: for r in records: fh.write(json.dumps(r, ensure_ascii=False) + "\n") print(f"wrote {len(records)} records -> {args.out}") return 0 if __name__ == "__main__": sys.exit(main())