from __future__ import annotations import unittest from pathlib import Path import pandas as pd from engin.charts import cylinder_spectrum, deviation_chart, engine_heatmap from engin.explainability import ( analyze_engine, explain_cylinder, rank_cylinders, summarize_engine, ) from engin.service import DiagnosisResult ROOT = Path(__file__).resolve().parents[1] class ExplainabilityTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: measurements = pd.read_csv(ROOT / "test.csv") submission = pd.read_csv(ROOT / "predictions.csv") diagnostics = pd.read_csv(ROOT / "prediction_diagnostics.csv") cls.result = DiagnosisResult(measurements, submission, diagnostics) cls.engine_id = diagnostics[diagnostics["label"].ne("ok")]["engine_id"].iloc[0] cls.analysis = analyze_engine(cls.result, cls.engine_id) def test_analysis_aligns_every_cylinder_and_frequency(self) -> None: self.assertEqual(self.analysis.spectra.shape, self.analysis.reference.shape) self.assertEqual(self.analysis.spectra.shape[1], 21) self.assertEqual(len(self.analysis.diagnostics), len(self.analysis.measurements)) def test_health_summary_and_ranking_are_bounded(self) -> None: summary = summarize_engine(self.analysis) ranking = rank_cylinders(self.analysis) self.assertEqual(len(ranking), summary.cylinders) self.assertTrue(ranking["triage_level"].is_monotonic_decreasing) self.assertIn(summary.highest_severity, {"nie_dotyczy", "male", "srednie", "duze"}) def test_unknown_only_engine_requires_verification(self) -> None: engine_id = "unknown_only" measurements = self.analysis.measurements.copy() measurements["engine_id"] = engine_id diagnostics = self.analysis.diagnostics.copy() diagnostics["engine_id"] = engine_id diagnostics["label"] = "ok" diagnostics["severity"] = "nie_dotyczy" diagnostics.loc[0, "label"] = "unknown" result = DiagnosisResult(measurements, diagnostics[["engine_id", "cylinder", "label", "severity"]], diagnostics) summary = summarize_engine(analyze_engine(result, engine_id)) self.assertEqual(summary.status, "WYMAGA WERYFIKACJI") self.assertEqual(summary.status_tone, "unknown") def test_cylinder_explanation_contains_actionable_evidence(self) -> None: cylinder = int(rank_cylinders(self.analysis).iloc[0]["cylinder"]) explanation = explain_cylinder(self.analysis, cylinder) self.assertEqual(len(explanation.top_frequencies), 3) self.assertIn("mV", explanation.reason) self.assertGreater(len(explanation.recommendation), 20) def test_all_chart_factories_return_populated_figures(self) -> None: cylinder = int(self.analysis.measurements["cylinder"].iloc[0]) compared = self.analysis.measurements["cylinder"].astype(int).head(4).tolist() heatmap = engine_heatmap(self.analysis) self.assertEqual(len(heatmap.data), 1) self.assertEqual(heatmap.layout.height, 360) self.assertEqual(len(cylinder_spectrum(self.analysis, cylinder).data), 2) self.assertEqual( len(cylinder_spectrum(self.analysis, cylinder, compared).data), 5, ) self.assertEqual(len(deviation_chart(self.analysis, cylinder).data), 1)