| """ |
| Explainer — turns a ConfidenceScore or UnifiedFaceReport into a |
| multi-line human-readable string for display in the UI / logs. |
| """ |
|
|
| from __future__ import annotations |
|
|
| from models.reports import ( |
| ConfidenceScore, |
| ConflictReport, |
| UnifiedFaceReport, |
| ) |
|
|
|
|
| class Explainer: |
| """Renders confidence + report as human-readable text.""" |
|
|
| @staticmethod |
| def explain_score(score: ConfidenceScore) -> str: |
| lines = [f"Overall confidence: {score.overall:.2%} ({score.method})"] |
| if score.explanation: |
| lines.append(f" {score.explanation}") |
| if score.components: |
| lines.append(" Components:") |
| for k, v in score.components.items(): |
| lines.append(f" - {k}: {v:.3f}" if isinstance(v, float) else f" - {k}: {v}") |
| return "\n".join(lines) |
|
|
| @staticmethod |
| def explain_conflicts(conflicts: list[ConflictReport]) -> str: |
| if not conflicts: |
| return "No conflicts detected." |
| lines = [f"{len(conflicts)} conflict(s) detected:"] |
| for c in conflicts: |
| lines.append( |
| f" [{c.severity.upper()}] {c.kind}: {c.description} " |
| f"(providers: {', '.join(c.providers)})" |
| ) |
| return "\n".join(lines) |
|
|
| @staticmethod |
| def explain_report(report: UnifiedFaceReport) -> str: |
| m = report.metadata |
| lines = [ |
| f"Job {m.job_id} — {m.total_elapsed_ms:.0f}ms total", |
| f" Providers invoked: {', '.join(m.providers_invoked) or 'none'}", |
| f" Succeeded: {', '.join(m.providers_succeeded) or 'none'}", |
| f" Failed: {', '.join(m.providers_failed) or 'none'}", |
| f" Detections: {len(report.detections)}", |
| f" Matches: {len(report.matches)}", |
| f" Scraped images: {len(report.scraped_images)}", |
| f" Reverse matches: {len(report.reverse_matches)}", |
| f" Image analyses: {len(report.image_analyses)}", |
| f" Metadata extractions: {len(report.metadata_extractions)}", |
| f" Forensics: {len(report.forensics)}", |
| f" Conflicts: {len(report.conflicts)}", |
| ] |
| if m.limitations: |
| lines.append(" Limitations:") |
| for lim in m.limitations: |
| lines.append(f" - {lim}") |
| if report.overall_confidence: |
| lines.append("") |
| lines.append(Explainer.explain_score(report.overall_confidence)) |
| if report.conflicts: |
| lines.append("") |
| lines.append(Explainer.explain_conflicts(report.conflicts)) |
| return "\n".join(lines) |
|
|