from dataclasses import dataclass from typing import Tuple from serato_doctor.models.reference import TrackReference from serato_doctor.models.track import DiskTrack @dataclass(frozen=True) class MatchEvidence: """One explainable scoring decision for a candidate track.""" field: str matched: bool points: int max_points: int explanation: str @dataclass(frozen=True) class TrackMatch: """A ranked candidate backed by explicit, inspectable evidence.""" reference: TrackReference track: DiskTrack evidence: Tuple[MatchEvidence, ...] @property def score(self) -> int: return sum(item.points for item in self.evidence) @property def max_score(self) -> int: return sum(item.max_points for item in self.evidence) @property def score_percent(self) -> float: if not self.max_score: return 0.0 return round(self.score / self.max_score * 100, 1)