Spaces:
Sleeping
Sleeping
File size: 4,381 Bytes
ca03167 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """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()
|