#!/usr/bin/env python3 """SRS-state preservation check between two versions of an org-drill deck. Extracts every :ID: from each version and reports IDs that disappeared or appeared. Disappeared IDs lose org-drill SRS state (review history, ease, intervals) and are the worst-case bug from a deck rewrite. Appeared IDs are usually fine (new cards added on purpose) but worth surfacing. Exits 0 when clean, 1 when any IDs disappeared or appeared. Usage: flashcard-diff-ids.py """ from __future__ import annotations import re import sys from pathlib import Path CARD_RE = re.compile(r"^\*\*\s+(.+?)\s+:drill:\s*$") ID_RE = re.compile(r"^\s*:ID:\s+(\S+)\s*$") def card_id_map(path: Path) -> dict[str, str]: """Return {id -> heading} for every :drill: card in path.""" result: dict[str, str] = {} lines = path.read_text(encoding="utf-8").splitlines() i = 0 while i < len(lines): m = CARD_RE.match(lines[i]) if m: heading = m.group(1).strip() i += 1 while i < len(lines): line = lines[i] if line.startswith("* ") or CARD_RE.match(line): break mid = ID_RE.match(line) if mid: result[mid.group(1)] = heading break i += 1 continue i += 1 return result def main() -> int: if len(sys.argv) != 3: print(f"usage: {sys.argv[0]} ", file=sys.stderr) return 2 before_path = Path(sys.argv[1]).expanduser().resolve() after_path = Path(sys.argv[2]).expanduser().resolve() for p in (before_path, after_path): if not p.is_file(): print(f"error: {p} not found", file=sys.stderr) return 2 before = card_id_map(before_path) after = card_id_map(after_path) before_ids = set(before) after_ids = set(after) preserved = before_ids & after_ids disappeared = before_ids - after_ids appeared = after_ids - before_ids print(f"flashcard-diff-ids: {before_path.name} → {after_path.name}") print() print(f"IDs in BEFORE: {len(before_ids)}") print(f"IDs in AFTER: {len(after_ids)}") print(f"Preserved: {len(preserved)}") print(f"Disappeared: {len(disappeared)}") print(f"Appeared: {len(appeared)}") print() warnings = 0 if disappeared: warnings += 1 print(f"WARN: {len(disappeared)} card IDs disappeared (SRS state lost)") for cid in sorted(disappeared): print(f" - {cid} (was: {before[cid]!r})") if appeared: warnings += 1 print(f"NOTE: {len(appeared)} new card IDs appeared") for cid in sorted(appeared): print(f" - {cid} (now: {after[cid]!r})") if warnings == 0: print("clean — SRS state preserved") return 0 return 1 if __name__ == "__main__": raise SystemExit(main())