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