Integrate RC1 fusion model routing
This commit is contained in:
+72
-26
@@ -28,10 +28,21 @@ from app.prediction import (
|
||||
)
|
||||
|
||||
|
||||
class DummyCurve:
|
||||
def __init__(self, x: list[float], y: list[float]) -> None:
|
||||
self.x = x
|
||||
self.y = y
|
||||
def dummy_prediction(
|
||||
pipe_id: str = "P001",
|
||||
pipe_age: list[float] | None = None,
|
||||
health_state: list[float] | None = None,
|
||||
current_age: float = 12.0,
|
||||
current_health_state: float = 0.4,
|
||||
) -> dict:
|
||||
return {
|
||||
"ID": pipe_id,
|
||||
"current_age": current_age,
|
||||
"current_health_state": current_health_state,
|
||||
"current_health_grade": "II级",
|
||||
"pipe_age": pipe_age or [1, 10, 12],
|
||||
"health_state": health_state or [0.9, 0.7, 0.4],
|
||||
}
|
||||
|
||||
|
||||
class PredictionHelpersTest(unittest.TestCase):
|
||||
@@ -91,7 +102,7 @@ class PredictionHelpersTest(unittest.TestCase):
|
||||
template = workbook.active
|
||||
template.title = "Template"
|
||||
template.append(INPUT_COLUMNS)
|
||||
template.append(["001", 5, 0, 5, 1, 100, 1.2, 0.4, 20, 800, 1, "=缺陷计算!E2", "=缺陷计算!J2"])
|
||||
template.append(["001", 5, 1, 100, 1, 1.2, 0.4, 20, 800, "=缺陷计算!E2", "=缺陷计算!J2"])
|
||||
|
||||
detail = workbook.create_sheet("缺陷计算")
|
||||
detail.append([
|
||||
@@ -114,48 +125,79 @@ class PredictionHelpersTest(unittest.TestCase):
|
||||
self.assertAlmostEqual(float(df.loc[0, "结构缺陷"]), 3.8)
|
||||
self.assertAlmostEqual(float(df.loc[0, "功能缺陷"]), 1.6)
|
||||
|
||||
def test_prepare_model_features_maps_material_aliases_to_codes(self) -> None:
|
||||
def test_prepare_model_features_maps_material_aliases_to_rc1_names(self) -> None:
|
||||
rows = []
|
||||
for value in ["镀锌", "2-钢塑", 13]:
|
||||
for value in ["镀锌", "2-钢塑", 11]:
|
||||
row = {feature: 1 for feature in FEATURES}
|
||||
row["管材"] = value
|
||||
row[ID_COLUMN] = "P001"
|
||||
row[PIPE_AGE_COLUMN] = 12
|
||||
rows.append(row)
|
||||
df = pd.DataFrame(rows)
|
||||
|
||||
x_test = prepare_model_features(df)
|
||||
|
||||
self.assertEqual(x_test["管材"].tolist(), [1, 2, 13])
|
||||
self.assertEqual(x_test["Material"].tolist(), ["镀锌", "钢塑", "钢管"])
|
||||
|
||||
def test_prepare_model_features_rejects_invalid_material_alias(self) -> None:
|
||||
row = {feature: 1 for feature in FEATURES}
|
||||
row["管材"] = "未知管材"
|
||||
row[ID_COLUMN] = "P001"
|
||||
row[PIPE_AGE_COLUMN] = 12
|
||||
df = pd.DataFrame([row])
|
||||
|
||||
with self.assertRaises(PredictionError) as ctx:
|
||||
prepare_model_features(df)
|
||||
|
||||
self.assertIn("管材编码无效", ctx.exception.message)
|
||||
self.assertIn("管材超出RC1支持范围", ctx.exception.message)
|
||||
|
||||
def test_prepare_model_features_rejects_unsupported_legacy_material_code(self) -> None:
|
||||
row = {feature: 1 for feature in FEATURES}
|
||||
row["管材"] = 13
|
||||
row[ID_COLUMN] = "P001"
|
||||
row[PIPE_AGE_COLUMN] = 12
|
||||
df = pd.DataFrame([row])
|
||||
|
||||
with self.assertRaises(PredictionError) as ctx:
|
||||
prepare_model_features(df)
|
||||
|
||||
self.assertIn("管材超出RC1支持范围", ctx.exception.message)
|
||||
|
||||
def test_prepare_model_features_maps_location_aliases_to_rc1_names(self) -> None:
|
||||
row = {feature: 1 for feature in FEATURES}
|
||||
row["管材"] = 5
|
||||
row["位置"] = "2-行人道"
|
||||
row[ID_COLUMN] = "P001"
|
||||
row[PIPE_AGE_COLUMN] = 12
|
||||
df = pd.DataFrame([row])
|
||||
|
||||
x_test = prepare_model_features(df)
|
||||
|
||||
self.assertEqual(x_test["Location"].tolist(), ["行人道"])
|
||||
|
||||
def test_prediction_workbook_keeps_sample_data_in_one_sheet(self) -> None:
|
||||
curves = [
|
||||
DummyCurve([1, 2], [0.9, 0.7]),
|
||||
DummyCurve([1, 2], [0.8, 0.6]),
|
||||
predictions = [
|
||||
dummy_prediction("P001", [1, 2], [0.9, 0.7], 10, 0.7),
|
||||
dummy_prediction("P002", [1, 2], [0.8, 0.6], 12, 0.6),
|
||||
]
|
||||
summary_rows = [{"pipe_id": "P001"}, {"pipe_id": "P002"}]
|
||||
summary_sheet_rows = [
|
||||
{ID_COLUMN: "P001", PIPE_AGE_COLUMN: "10 年", "健康风险值": 0.3},
|
||||
{ID_COLUMN: "P002", PIPE_AGE_COLUMN: "12 年", "健康风险值": 0.4},
|
||||
{ID_COLUMN: "P001", PIPE_AGE_COLUMN: "10 年", "当前健康状态": 0.7, "健康等级": "IV级", "预计剩余寿命(年)": ">63"},
|
||||
{ID_COLUMN: "P002", PIPE_AGE_COLUMN: "12 年", "当前健康状态": 0.6, "健康等级": "III级", "预计剩余寿命(年)": ">61"},
|
||||
]
|
||||
|
||||
with TemporaryDirectory() as temp_dir:
|
||||
output_path = Path(temp_dir) / "prediction.xlsx"
|
||||
write_prediction_workbook(output_path, curves, summary_rows, summary_sheet_rows)
|
||||
write_prediction_workbook(output_path, predictions, summary_rows, summary_sheet_rows)
|
||||
|
||||
workbook = pd.ExcelFile(output_path)
|
||||
self.assertEqual(workbook.sheet_names, ["结果摘要", "样本数据"])
|
||||
|
||||
summary_data = pd.read_excel(output_path, sheet_name="结果摘要")
|
||||
self.assertEqual(summary_data.columns.tolist(), [ID_COLUMN, PIPE_AGE_COLUMN, "健康风险值"])
|
||||
self.assertEqual(
|
||||
summary_data.columns.tolist(),
|
||||
[ID_COLUMN, PIPE_AGE_COLUMN, "当前健康状态", "健康等级", "预计剩余寿命(年)"],
|
||||
)
|
||||
|
||||
sample_data = pd.read_excel(output_path, sheet_name="样本数据")
|
||||
self.assertEqual(sample_data.columns.tolist(), ["管龄(年)", "P001", "P002"])
|
||||
@@ -171,13 +213,15 @@ class PredictionHelpersTest(unittest.TestCase):
|
||||
self.assertEqual(sample_worksheet["B2"].number_format, "0.0%")
|
||||
|
||||
def test_prediction_workbook_writes_pipe_ids_as_excel_text(self) -> None:
|
||||
curves = [DummyCurve([1], [0.9])]
|
||||
predictions = [dummy_prediction("00123", [1], [0.9], 10, 0.9)]
|
||||
summary_rows = [{"pipe_id": "00123"}]
|
||||
summary_sheet_rows = [{ID_COLUMN: "00123", PIPE_AGE_COLUMN: "10 年", "健康风险值": 0.1}]
|
||||
summary_sheet_rows = [
|
||||
{ID_COLUMN: "00123", PIPE_AGE_COLUMN: "10 年", "当前健康状态": 0.9, "健康等级": "V级", "预计剩余寿命(年)": ">63"}
|
||||
]
|
||||
|
||||
with TemporaryDirectory() as temp_dir:
|
||||
output_path = Path(temp_dir) / "prediction.xlsx"
|
||||
write_prediction_workbook(output_path, curves, summary_rows, summary_sheet_rows)
|
||||
write_prediction_workbook(output_path, predictions, summary_rows, summary_sheet_rows)
|
||||
|
||||
workbook = load_workbook(output_path)
|
||||
summary_worksheet = workbook["结果摘要"]
|
||||
@@ -200,18 +244,17 @@ class PredictionHelpersTest(unittest.TestCase):
|
||||
patch("app.prediction.plt.figtext") as figtext,
|
||||
patch("app.prediction.plt.title") as title,
|
||||
):
|
||||
summary_rows, _ = render_survival_chart(df, [DummyCurve([1, 10, 12], [0.9, 0.7, 0.4])], output_path)
|
||||
summary_rows, _ = render_survival_chart([dummy_prediction()], output_path)
|
||||
|
||||
self.assertAlmostEqual(summary_rows[0]["health_probability"], 0.4)
|
||||
self.assertAlmostEqual(summary_rows[0]["health_risk"], 0.6)
|
||||
self.assertAlmostEqual(summary_rows[0]["health_state"], 0.4)
|
||||
self.assertEqual(summary_rows[0]["grade_label"], "II级")
|
||||
|
||||
xlabel.assert_called_once()
|
||||
self.assertEqual(xlabel.call_args.args[0], "管龄(年)")
|
||||
ylabel.assert_called_once()
|
||||
self.assertEqual(ylabel.call_args.args[0], "健康风险")
|
||||
self.assertEqual(ylabel.call_args.args[0], "管道健康状态")
|
||||
title.assert_called_once()
|
||||
self.assertEqual(title.call_args.args[0], "管道的剩余寿命分析图")
|
||||
self.assertEqual(title.call_args.args[0], "供水管道健康状态曲线")
|
||||
figtext.assert_called_once()
|
||||
self.assertIn(f"前{CHART_DISPLAY_LIMIT}条管道的示例数据", figtext.call_args.args[2])
|
||||
|
||||
@@ -223,12 +266,15 @@ class PredictionHelpersTest(unittest.TestCase):
|
||||
PIPE_AGE_COLUMN: [12] * sample_count,
|
||||
}
|
||||
)
|
||||
curves = [DummyCurve([1, 10, 12], [0.9, 0.7, 0.4]) for _ in range(sample_count)]
|
||||
predictions = [
|
||||
dummy_prediction(f"P{i:03d}", [1, 10, 12], [0.9, 0.7, 0.4])
|
||||
for i in range(sample_count)
|
||||
]
|
||||
|
||||
with TemporaryDirectory() as temp_dir:
|
||||
output_path = Path(temp_dir) / "chart.png"
|
||||
with patch("app.prediction.plt.step") as step, patch("app.prediction.plt.legend"):
|
||||
summary_rows, summary_sheet_rows = render_survival_chart(df, curves, output_path)
|
||||
summary_rows, summary_sheet_rows = render_survival_chart(predictions, output_path)
|
||||
|
||||
self.assertEqual(step.call_count, CHART_DISPLAY_LIMIT)
|
||||
self.assertEqual(len(summary_rows), sample_count)
|
||||
|
||||
Reference in New Issue
Block a user