FastAPI app that parses CMM inspection reports (PDF/Excel/CSV), computes SPC metrics (Cp/Cpk/Pp/Ppk, control limits, Shapiro-Wilk), generates interactive Plotly charts, and provides AI-powered quality summaries via Azure OpenAI with graceful fallback. Includes 21 passing tests covering parsers, SPC calculations, and API endpoints.
85 lines
2.4 KiB
Python
85 lines
2.4 KiB
Python
from app.analysis.spc import SPCResult, calculate_spc
|
|
from app.parsers.models import MeasurementRecord
|
|
|
|
|
|
def _make_records(name: str, nominal: float, tol: float, actuals: list[float]):
|
|
return [
|
|
MeasurementRecord(
|
|
feature_name=name,
|
|
nominal=nominal,
|
|
tolerance_plus=tol,
|
|
tolerance_minus=-tol,
|
|
actual=a,
|
|
deviation=a - nominal,
|
|
)
|
|
for a in actuals
|
|
]
|
|
|
|
|
|
def test_single_measurement_returns_none_indices():
|
|
records = _make_records("D1", 10.0, 0.05, [10.01])
|
|
results = calculate_spc(records)
|
|
assert len(results) == 1
|
|
r = results[0]
|
|
assert r.cp is None
|
|
assert r.cpk is None
|
|
assert r.pp is None
|
|
assert r.ppk is None
|
|
|
|
|
|
def test_basic_spc_calculation():
|
|
actuals = [10.01, 10.02, 9.99, 10.00, 10.03, 9.98, 10.01, 10.02, 9.99, 10.00]
|
|
records = _make_records("D1", 10.0, 0.05, actuals)
|
|
results = calculate_spc(records)
|
|
|
|
assert len(results) == 1
|
|
r = results[0]
|
|
assert r.n == 10
|
|
assert r.pp is not None
|
|
assert r.ppk is not None
|
|
assert r.cp is not None
|
|
assert r.cpk is not None
|
|
assert r.pp > 0
|
|
assert r.cpk > 0
|
|
assert r.out_of_spec_count == 0
|
|
|
|
|
|
def test_out_of_spec_count():
|
|
actuals = [10.0, 10.06, 9.94, 10.0, 10.0] # 10.06 and 9.94 outside ±0.05
|
|
records = _make_records("D1", 10.0, 0.05, actuals)
|
|
results = calculate_spc(records)
|
|
assert results[0].out_of_spec_count == 2
|
|
|
|
|
|
def test_multiple_features():
|
|
records = (
|
|
_make_records("D1", 10.0, 0.05, [10.01, 10.02, 9.99])
|
|
+ _make_records("D2", 20.0, 0.10, [20.05, 19.95, 20.01])
|
|
)
|
|
results = calculate_spc(records)
|
|
assert len(results) == 2
|
|
names = {r.feature_name for r in results}
|
|
assert names == {"D1", "D2"}
|
|
|
|
|
|
def test_shapiro_not_computed_for_small_n():
|
|
records = _make_records("D1", 10.0, 0.05, [10.01, 10.02])
|
|
results = calculate_spc(records)
|
|
assert results[0].shapiro_p is None
|
|
|
|
|
|
def test_shapiro_computed_for_n_ge_3():
|
|
records = _make_records("D1", 10.0, 0.05, [10.01, 10.02, 9.99])
|
|
results = calculate_spc(records)
|
|
assert results[0].shapiro_p is not None
|
|
|
|
|
|
def test_to_dict():
|
|
records = _make_records("D1", 10.0, 0.05, [10.01, 10.02, 9.99, 10.00, 10.03])
|
|
results = calculate_spc(records)
|
|
d = results[0].to_dict()
|
|
assert "feature_name" in d
|
|
assert "cpk" in d
|
|
assert "values" in d
|
|
assert isinstance(d["values"], list)
|