"""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)