test_production_modules.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import unittest
  2. import pandas as pd
  3. from defect_analysis.data_quality import build_data_quality_report
  4. from defect_analysis.root_cause import build_extended_root_causes
  5. from defect_analysis.schemas import (
  6. CORE_REQUIRED_COLUMNS,
  7. TEMPLATE_COLUMNS,
  8. get_missing_required_columns,
  9. normalize_defect_schema,
  10. )
  11. class ProductionModulesTest(unittest.TestCase):
  12. def test_schema_exposes_core_and_template_columns(self):
  13. self.assertIn("defect_id", CORE_REQUIRED_COLUMNS)
  14. self.assertIn("lam_fixture_id", TEMPLATE_COLUMNS)
  15. self.assertIn("material_lot_oca", TEMPLATE_COLUMNS)
  16. def test_schema_normalization_keeps_legacy_csv_compatible(self):
  17. legacy = pd.DataFrame(
  18. {
  19. "defect_id": ["D1"],
  20. "panel_id": ["P1"],
  21. "batch_id": ["B1"],
  22. "equipment_id": ["LAM-A01"],
  23. "seat_id": ["R1C1"],
  24. "inspection_station": ["AOI-1"],
  25. "timestamp": [pd.Timestamp("2026-04-01 08:00:00")],
  26. "defect_type": ["划痕"],
  27. "severity": ["严重"],
  28. "x_mm": [10.0],
  29. "y_mm": [20.0],
  30. "panel_width_mm": [155.0],
  31. "panel_height_mm": [340.0],
  32. "hour": [8],
  33. "shift": ["白班"],
  34. "day": ["2026-04-01"],
  35. }
  36. )
  37. normalized = normalize_defect_schema(legacy)
  38. self.assertEqual([], get_missing_required_columns(normalized))
  39. self.assertEqual("point", normalized.loc[0, "defect_geometry_type"])
  40. self.assertEqual("LAM-A01", normalized.loc[0, "lam_equipment_id"])
  41. def test_data_quality_report_flags_invalid_coordinates_and_traceability(self):
  42. df = normalize_defect_schema(
  43. pd.DataFrame(
  44. {
  45. "defect_id": ["D1", "D2"],
  46. "panel_id": ["P1", ""],
  47. "batch_id": ["B1", "B1"],
  48. "equipment_id": ["LAM-A01", "LAM-A01"],
  49. "seat_id": ["R1C1", "R1C2"],
  50. "inspection_station": ["AOI-1", "AOI-1"],
  51. "timestamp": [pd.Timestamp("2026-04-01"), pd.Timestamp("2026-04-01")],
  52. "defect_type": ["划痕", "未知缺陷"],
  53. "severity": ["严重", "轻微"],
  54. "x_mm": [10.0, 999.0],
  55. "y_mm": [20.0, 20.0],
  56. "panel_width_mm": [155.0, 155.0],
  57. "panel_height_mm": [340.0, 340.0],
  58. "hour": [8, 8],
  59. "shift": ["白班", "白班"],
  60. "day": ["2026-04-01", "2026-04-01"],
  61. "lam_fixture_id": ["FIX-1", ""],
  62. }
  63. )
  64. )
  65. report = build_data_quality_report(df)
  66. self.assertLess(report["score"], 100)
  67. self.assertLess(report["coordinate_valid_rate"], 1.0)
  68. self.assertLess(report["traceability_rate"], 1.0)
  69. self.assertTrue(any("坐标" in issue for issue in report["issues"]))
  70. def test_data_quality_report_handles_missing_columns(self):
  71. report = build_data_quality_report(pd.DataFrame({"defect_id": ["D1"]}))
  72. self.assertLess(report["score"], 100)
  73. self.assertEqual(0.0, report["coordinate_valid_rate"])
  74. self.assertTrue(report["issues"])
  75. def test_root_cause_module_returns_extended_candidates(self):
  76. rows = []
  77. for i in range(10):
  78. rows.append(
  79. {
  80. "defect_id": f"D{i}",
  81. "panel_id": f"P{i}",
  82. "batch_id": "B1",
  83. "equipment_id": "LAM-A01",
  84. "seat_id": "R1C1",
  85. "inspection_station": "AOI-1",
  86. "timestamp": pd.Timestamp("2026-04-01"),
  87. "defect_type": "划痕",
  88. "severity": "严重" if i < 3 else "轻微",
  89. "x_mm": 10.0,
  90. "y_mm": 20.0,
  91. "panel_width_mm": 155.0,
  92. "panel_height_mm": 340.0,
  93. "hour": 8,
  94. "shift": "白班",
  95. "day": "2026-04-01",
  96. "lam_fixture_id": "FIX-HOT" if i < 8 else "FIX-OK",
  97. }
  98. )
  99. df = normalize_defect_schema(pd.DataFrame(rows))
  100. candidates = build_extended_root_causes(df, dimensions=["lam_fixture_id"])
  101. self.assertEqual("lam_fixture_id", candidates.iloc[0]["维度"])
  102. self.assertEqual("FIX-HOT", candidates.iloc[0]["候选值"])
  103. self.assertGreater(candidates.iloc[0]["异常倍数"], 1.0)
  104. def test_root_cause_empty_dimensions_do_not_fallback_to_defaults(self):
  105. df = normalize_defect_schema(
  106. pd.DataFrame(
  107. {
  108. "defect_id": ["D1"],
  109. "panel_id": ["P1"],
  110. "batch_id": ["B1"],
  111. "equipment_id": ["LAM-A01"],
  112. "seat_id": ["R1C1"],
  113. "inspection_station": ["AOI-1"],
  114. "timestamp": [pd.Timestamp("2026-04-01")],
  115. "defect_type": ["划痕"],
  116. "severity": ["严重"],
  117. "x_mm": [10.0],
  118. "y_mm": [20.0],
  119. "panel_width_mm": [155.0],
  120. "panel_height_mm": [340.0],
  121. "hour": [8],
  122. "shift": ["白班"],
  123. "day": ["2026-04-01"],
  124. "lam_fixture_id": ["FIX-1"],
  125. }
  126. )
  127. )
  128. candidates = build_extended_root_causes(df, dimensions=[])
  129. self.assertTrue(candidates.empty)
  130. if __name__ == "__main__":
  131. unittest.main()