import math import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import pandas as pd from app_utils import ( apply_defect_filters, build_diagnostic_dashboard, classify_panel_zone, calculate_kpis, calculate_spc_metrics, generate_industry_diagnosis, ) class AppUtilsTest(unittest.TestCase): def setUp(self): self.df = pd.DataFrame( { "defect_id": ["D1", "D2", "D3", "D4"], "panel_id": ["P1", "P2", "P2", "P3"], "batch_id": ["B1", "B1", "B2", "B2"], "equipment_id": ["E1", "E1", "E2", "E2"], "seat_id": ["S1", "S2", "S1", "S2"], "timestamp": pd.to_datetime( [ "2026-04-01 00:00:00", "2026-04-01 23:59:59", "2026-04-02 12:00:00", "2026-04-03 00:00:01", ] ), "defect_type": ["划痕", "亮点", "划痕", "暗点"], "severity": ["严重", "轻微", "中等", "严重"], "shift": ["白班", "夜班", "白班", "白班"], "day": ["2026-04-01", "2026-04-01", "2026-04-02", "2026-04-03"], } ) def test_date_filter_includes_full_end_date(self): filtered = apply_defect_filters( self.df, start_date=pd.Timestamp("2026-04-01"), end_date=pd.Timestamp("2026-04-01"), selected_types=["划痕", "亮点", "暗点"], selected_batches=["B1", "B2"], selected_equipment=["E1", "E2"], selected_seats=["S1", "S2"], selected_shift="全部", selected_severity="全部", ) self.assertEqual(["D1", "D2"], filtered["defect_id"].tolist()) def test_kpis_use_same_filter_scope_for_total_panels(self): filtered = apply_defect_filters( self.df, start_date=pd.Timestamp("2026-04-01"), end_date=pd.Timestamp("2026-04-02"), selected_types=["划痕"], selected_batches=["B1", "B2"], selected_equipment=["E1", "E2"], selected_seats=["S1"], selected_shift="全部", selected_severity="全部", ) kpis = calculate_kpis(self.df, filtered) self.assertEqual(2, kpis["total_panels_inspected"]) self.assertEqual(2, kpis["defective_panels"]) self.assertEqual(0.0, kpis["yield_rate"]) def test_spc_metrics_clamp_estimated_rate_to_valid_probability(self): metrics = calculate_spc_metrics(self.df) self.assertTrue(math.isfinite(metrics["p_bar"])) self.assertTrue(math.isfinite(metrics["ucl"])) self.assertTrue(math.isfinite(metrics["lcl"])) self.assertLessEqual(metrics["daily"]["defect_rate"].max(), 1.0) def test_diagnostic_dashboard_ranks_root_cause_candidates(self): dashboard = build_diagnostic_dashboard(self.df) self.assertEqual("严重", dashboard["severity_level"]) self.assertEqual("E1 / S1", dashboard["root_causes"].iloc[0]["根因候选"]) self.assertEqual("划痕", dashboard["top_defect_type"]) self.assertIn("优先排查", dashboard["primary_recommendation"]) def test_diagnostic_dashboard_reports_baseline_lift(self): rows = [] for i in range(10): rows.append( { "defect_id": f"D{i}", "panel_id": f"P{i}", "batch_id": "B1", "equipment_id": "E1", "seat_id": "S-hot" if i < 8 else "S-cold", "timestamp": pd.Timestamp("2026-04-01"), "defect_type": "气泡", "severity": "严重" if i < 2 else "轻微", "shift": "白班", "day": "2026-04-01", } ) df = pd.DataFrame(rows) dashboard = build_diagnostic_dashboard(df) top = dashboard["root_causes"].iloc[0] self.assertEqual("E1 / S-hot", top["根因候选"]) self.assertGreater(top["异常倍数"], 1.0) def test_classify_panel_zone_uses_3c_panel_regions(self): zones = classify_panel_zone( pd.DataFrame( { "x_mm": [2.0, 77.5, 150.0, 80.0], "y_mm": [335.0, 255.0, 170.0, 20.0], "panel_width_mm": [155.0] * 4, "panel_height_mm": [340.0] * 4, } ) ) self.assertIn("角落区", zones.iloc[0]) self.assertIn("FPC/绑定区", zones.iloc[1]) self.assertIn("右边缘区", zones.iloc[2]) self.assertIn("下边缘区", zones.iloc[3]) def test_industry_diagnosis_generates_panel_sop_recommendation(self): rows = [] for i in range(12): rows.append( { "defect_id": f"D{i}", "panel_id": f"P{i}", "batch_id": "B1", "equipment_id": "LAM-A01", "seat_id": "R2C3", "timestamp": pd.Timestamp("2026-04-01"), "defect_type": "气泡", "severity": "严重" if i < 4 else "中等", "x_mm": 5.0 + i * 0.3, "y_mm": 250.0, "panel_width_mm": 155.0, "panel_height_mm": 340.0, "shift": "白班", "day": "2026-04-01", } ) df = pd.DataFrame(rows) dashboard = build_diagnostic_dashboard(df) diagnosis = generate_industry_diagnosis(df, dashboard) self.assertIn("边缘", diagnosis["headline"]) self.assertIn("气泡", diagnosis["headline"]) self.assertTrue(any("贴合" in item for item in diagnosis["recommendations"])) self.assertTrue(any("跨面板重复" in pattern for pattern in diagnosis["patterns"])) if __name__ == "__main__": unittest.main()