"""Evaluate the transparent vehicle damage scoring heuristic.""" from __future__ import annotations import json import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[1] sys.path.append(str(PROJECT_ROOT)) from app.damage_model import DAMAGE_WEIGHTS, analyze_manual_damage REPORT_PATH = PROJECT_ROOT / "reports/damage_scoring_evaluation.md" JSON_PATH = PROJECT_ROOT / "reports/damage_scoring_examples.json" EVALUATION_CASES = [ { "case_id": "no_damage", "base_price_eur": 15_000, "labels": [], "expected_interpretation": "No visible damage, no price adjustment.", }, { "case_id": "minor_scratch", "base_price_eur": 15_000, "labels": ["scratch"], "expected_interpretation": "Minor visible damage.", }, { "case_id": "scratch_and_dent", "base_price_eur": 15_000, "labels": ["scratch", "dent"], "expected_interpretation": "Small to moderate exterior damage.", }, { "case_id": "lamp_and_glass_damage", "base_price_eur": 20_000, "labels": ["lamp broken", "glass shatter"], "expected_interpretation": "More serious visible damage with stronger adjustment.", }, { "case_id": "multiple_severe_damages", "base_price_eur": 30_000, "labels": ["dent", "crack", "glass shatter", "lamp broken"], "expected_interpretation": "Severe damage capped by maximum damage score.", }, ] def evaluate_cases() -> list[dict]: """Run deterministic scoring examples.""" rows = [] for case in EVALUATION_CASES: analysis = analyze_manual_damage( labels=case["labels"], base_price_eur=case["base_price_eur"], ) rows.append( { "case_id": case["case_id"], "base_price_eur": case["base_price_eur"], "labels": case["labels"], "damage_score": analysis.damage_score, "recommended_discount_eur": analysis.recommended_discount_eur, "adjusted_price_eur": analysis.adjusted_price_eur, "expected_interpretation": case["expected_interpretation"], } ) return rows def write_report(rows: list[dict]) -> None: """Write markdown and JSON evaluation artifacts.""" REPORT_PATH.parent.mkdir(parents=True, exist_ok=True) JSON_PATH.write_text(json.dumps(rows, indent=2), encoding="utf-8") lines = [ "# Damage Scoring Evaluation", "", "The current computer vision integration converts detected damage labels into a transparent damage score. The score is a heuristic price adjustment, not a certified repair-cost estimate.", "", "## Damage Weights", "", "| Damage label | Weight |", "|---|---:|", ] for label, weight in DAMAGE_WEIGHTS.items(): if weight > 0: lines.append(f"| {label} | {weight:.2f} |") lines.extend( [ "", "## Deterministic Evaluation Cases", "", "| Case | Labels | Base price | Damage score | Discount | Adjusted price | Interpretation |", "|---|---|---:|---:|---:|---:|---|", ] ) for row in rows: labels = ", ".join(row["labels"]) if row["labels"] else "none" lines.append( f"| {row['case_id']} | {labels} | EUR {row['base_price_eur']:,.0f} | " f"{row['damage_score']:.3f} | EUR {row['recommended_discount_eur']:,.0f} | " f"EUR {row['adjusted_price_eur']:,.0f} | {row['expected_interpretation']} |" ) lines.extend( [ "", "## Interpretation", "", "The scoring logic behaves monotonically: more severe and more numerous damage labels produce a larger discount. The score is capped at 0.35 to avoid unrealistic automatic reductions. In the final app, a user-facing note must explain that this adjustment is based only on visible image evidence and should not replace a professional inspection.", ] ) REPORT_PATH.write_text("\n".join(lines), encoding="utf-8") def main() -> None: rows = evaluate_cases() write_report(rows) print(f"Wrote {REPORT_PATH}") print(f"Wrote {JSON_PATH}") if __name__ == "__main__": main()