"""Item-by-item comparison of two checker result sets (same order expected). usage: python compare_checks.py [mapping] mapping default: legacy_verify_*.json in A <-> G1..G5.json in B. Compares check id, result, expected, actual and detail; item text differences are listed separately (wording may differ where values became parameters). """ import json import sys from pathlib import Path PAIRS = [("legacy_verify_rhino.json", "G1.json"), ("legacy_verify_rebar.json", "G2.json"), ("legacy_verify_footing_rebar.json", "G3.json"), ("legacy_verify_coping_rebar.json", "G4.json"), ("legacy_verify_bearing_plates.json", "G5.json")] DETAIL_FREE = ("object counts",) # detail wording changed (json.dumps -> str), value compared via result def compare(a, b): ra, rb = a["results"], b["results"] value_diffs, name_diffs = [], [] for i, (x, y) in enumerate(zip(ra, rb)): for k in ("check", "result", "expected", "actual", "tol"): if k in x or k in y: vx, vy = x.get(k), y.get(k) if isinstance(vx, (int, float)) and isinstance(vy, (int, float)): if abs(vx - vy) > 1e-9 + (1e-6 * abs(vx) if k == "tol" else 0): value_diffs.append((i, x["item"], k, vx, vy)) elif vx != vy: value_diffs.append((i, x["item"], k, vx, vy)) if x.get("detail") != y.get("detail") and not x["item"].startswith(DETAIL_FREE): value_diffs.append((i, x["item"], "detail", x.get("detail"), y.get("detail"))) if x["item"] != y["item"]: name_diffs.append((i, x["item"], y["item"])) return {"n": [len(ra), len(rb)], "value_diffs": value_diffs, "name_diffs": name_diffs, "same": len(ra) == len(rb) and not value_diffs} if __name__ == "__main__": A, B = Path(sys.argv[1]), Path(sys.argv[2]) ok = True for fa, fb in PAIRS: r = compare(json.loads((A / fa).read_text(encoding="utf-8")), json.loads((B / fb).read_text(encoding="utf-8"))) ok &= r["same"] print(f"{fb}: n={r['n']} same={r['same']} value_diffs={len(r['value_diffs'])} name_diffs={len(r['name_diffs'])}") for d in r["value_diffs"][:8]: print(" VALUE", d) for d in r["name_diffs"]: print(" NAME ", d) sys.exit(0 if ok else 1)