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