""" validate.py — Compare extracted CSV/JSON against ground truth. Prints per-field accuracy and a detailed diff. """ import json, sys, os from difflib import SequenceMatcher def similarity(a: str, b: str) -> float: return SequenceMatcher(None, a.lower().strip(), b.lower().strip()).ratio() def main(gt_path: str, extracted_path: str): with open(gt_path) as f: gt = json.load(f) with open(extracted_path) as f: ext = json.load(f)["rows"] FIELDS = ["begin_time", "end_time", "patient_name", "dob"] total = {k: 0 for k in FIELDS} exact = {k: 0 for k in FIELDS} fuzzy_sum = {k: 0.0 for k in FIELDS} matched = 0 print(f"Ground truth rows: {len(gt)} Extracted rows: {len(ext)}\n") print(f"{'PG':>3} {'FIELD':<15} {'EXPECTED':<28} {'GOT':<28} {'SIM':>5} {'OK':>3}") print("─" * 90) for g in gt: # find matching row by page + approximate begin_time candidates = [e for e in ext if e["page"] == g["page"]] best, best_sim = None, 0 for c in candidates: s = similarity(c["begin_time"], g["begin_time"]) s += similarity(c["patient_name"], g["patient_name"]) if s > best_sim: best_sim = s best = c if best is None: print(f" p{g['page']} — NO MATCH for {g['begin_time']} {g['patient_name']}") for k in FIELDS: total[k] += 1 continue matched += 1 for k in FIELDS: total[k] += 1 sim = similarity(str(best.get(k, "")), str(g[k])) fuzzy_sum[k] += sim ok = sim >= 0.85 if ok: exact[k] += 1 flag = "✓" if ok else "✗" if not ok: # only print mismatches print(f"{g['page']:>3} {k:<15} {str(g[k]):<28} {str(best.get(k,'')):28} {sim:5.2f} {flag:>3}") print("\n" + "=" * 60) print(f"{'FIELD':<15} {'EXACT':>6} {'TOTAL':>6} {'ACC':>7} {'FUZZY_AVG':>9}") print("─" * 60) for k in FIELDS: acc = exact[k] / total[k] * 100 if total[k] else 0 favg = fuzzy_sum[k] / total[k] if total[k] else 0 print(f"{k:<15} {exact[k]:>6} {total[k]:>6} {acc:>6.1f}% {favg:>9.3f}") overall = sum(exact.values()) / sum(total.values()) * 100 if sum(total.values()) else 0 print(f"\nOverall field accuracy : {overall:.1f}%") print(f"Row match rate : {matched}/{len(gt)}") if __name__ == "__main__": gt = sys.argv[1] if len(sys.argv) > 1 else "./test_pdfs/ground_truth.json" ext = sys.argv[2] if len(sys.argv) > 2 else "./output/schedule_extracted.json" main(gt, ext)