"""Replace records of a base JSONL with re-captured records (matched by id), keeping the base order. Image paths of the replacements are rewritten relative to the output file's directory. usage: python scripts/swap_records.py base.jsonl replacements.jsonl out.jsonl [--only-substr settings_destructive] """ import json, os, sys def load(p): with open(p, encoding="utf-8") as f: return [json.loads(l) for l in f if l.strip()] def main(base, repl, dst, only=None): rep = {r["id"]: r for r in load(repl)} rdir, odir = os.path.dirname(os.path.abspath(repl)), os.path.dirname(os.path.abspath(dst)) n = swapped = 0 used, unmatched = set(), [] with open(dst, "w", encoding="utf-8", newline="\n") as out: for r in load(base): n += 1 if r["id"] in rep: new = json.loads(json.dumps(rep[r["id"]])) new["images"] = [os.path.relpath(os.path.join(rdir, im), odir).replace("\\", "/") for im in new["images"]] missing = [im for im in new["images"] if not os.path.exists(os.path.join(odir, im))] if missing: sys.exit(f"missing image {missing}") new["meta"]["swapped_from"] = os.path.relpath(repl, odir).replace("\\", "/") r = new used.add(r["id"]) swapped += 1 elif only and only in r["id"]: unmatched.append(r["id"]) out.write(json.dumps(r, ensure_ascii=False) + "\n") print(f"{n} records, swapped={swapped}, replacements unused={len(rep) - len(used)}, " f"base '{only}' not swapped={len(unmatched)} {unmatched[:5]} -> {dst}") if __name__ == "__main__": a = sys.argv[1:] only = None if "--only-substr" in a: i = a.index("--only-substr"); only = a[i + 1]; del a[i:i + 2] main(*a, only=only)