#!/usr/bin/env python3 """Apply corrections.json exported from review.html. python3 apply_corrections.py ~/Downloads/corrections.json [--dry-run] Moves each crop to the folder you chose. "discard" goes to labelling//discard/, which the sweep ignores — nothing is deleted, so a misclick is recoverable. Refuses to move a file it cannot find exactly once, rather than guessing: a half-applied correction set would put a crop in two folders and quietly duplicate a label. """ import sys, json, glob, os, shutil if len(sys.argv) < 2: sys.exit(__doc__) path = sys.argv[1] DRY = "--dry-run" in sys.argv corr = json.load(open(path)) if not corr: sys.exit("no corrections in that file") moved = skipped = 0 for fname, c in corr.items(): clip, to = c["clip"], c["to"] hits = glob.glob(f"labelling/{clip}/**/{fname}", recursive=True) if len(hits) != 1: print(f"[skip] {fname}: found {len(hits)} copies, expected 1") skipped += 1 continue src = hits[0] dst_dir = f"labelling/{clip}/{to}" dst = f"{dst_dir}/{fname}" if os.path.abspath(src) == os.path.abspath(dst): continue print(f"{'would move' if DRY else 'move'} {c['from']} -> {to}: {fname}") if not DRY: os.makedirs(dst_dir, exist_ok=True) shutil.move(src, dst) moved += 1 print(f"\n{moved} moved, {skipped} skipped{' (dry run)' if DRY else ''}") if not DRY and moved: print("re-run verify_labels.py to confirm the set is still consistent")