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