feat: align prediction report with guidance

This commit is contained in:
2026-07-20 20:50:37 +08:00
parent 1223227ea9
commit fac2a2a2d2
4 changed files with 39 additions and 19 deletions
+28 -11
View File
@@ -18,8 +18,10 @@ from app.prediction import (
REMAINING_LIFE_COLUMN,
PredictionError,
estimate_remaining_life,
format_years,
grade_info,
interpolate_probability,
make_analysis_text,
prepare_model_features,
read_input_file,
render_survival_chart,
@@ -60,6 +62,18 @@ class PredictionHelpersTest(unittest.TestCase):
def test_probability_helpers(self) -> None:
self.assertEqual(interpolate_probability([1, 5, 10], [0.9, 0.8, 0.6], 6), 0.6)
self.assertEqual(estimate_remaining_life([1, 5, 10], [0.9, 0.4, 0.2]), 5.0)
self.assertEqual(format_years(None, 10, 73), 63.0)
def test_analysis_text_displays_health_state_as_decimal(self) -> None:
text = make_analysis_text(
[
{"pipe_id": "P001", "health_state": 0.734, "grade_label": "IV级"},
{"pipe_id": "P002", "health_state": 0.2, "grade_label": "I级"},
]
)
self.assertIn("0.73", text)
self.assertNotIn("%", text)
def test_grade_boundaries(self) -> None:
self.assertEqual(grade_info(0.2)[0], "I级")
@@ -79,13 +93,12 @@ class PredictionHelpersTest(unittest.TestCase):
validate_input_frame(df)
def test_example_workbook_columns_match_backend_contract(self) -> None:
def test_example_workbook_contains_backend_contract_columns(self) -> None:
workbook = load_workbook(Path(__file__).resolve().parents[1] / "example.xlsx", read_only=True)
worksheet = workbook["Template"]
headers = [cell.value for cell in next(worksheet.iter_rows(min_row=1, max_row=1))]
self.assertEqual(len(headers), len(INPUT_COLUMNS))
self.assertEqual(set(headers), set(INPUT_COLUMNS))
self.assertTrue(set(INPUT_COLUMNS).issubset(headers))
def test_read_input_file_preserves_text_pipe_ids(self) -> None:
with TemporaryDirectory() as temp_dir:
@@ -183,8 +196,8 @@ class PredictionHelpersTest(unittest.TestCase):
]
summary_rows = [{"pipe_id": "P001"}, {"pipe_id": "P002"}]
summary_sheet_rows = [
{ID_COLUMN: "P001", PIPE_AGE_COLUMN: "10 年", "当前健康状态": 0.7, "健康等级": "IV级", REMAINING_LIFE_COLUMN: ">63"},
{ID_COLUMN: "P002", PIPE_AGE_COLUMN: "12 年", "当前健康状态": 0.6, "健康等级": "III级", REMAINING_LIFE_COLUMN: ">61"},
{ID_COLUMN: "P001", PIPE_AGE_COLUMN: 10, "当前健康状态": 0.7, "健康等级": "IV级", REMAINING_LIFE_COLUMN: 63},
{ID_COLUMN: "P002", PIPE_AGE_COLUMN: 12, "当前健康状态": 0.6, "健康等级": "III级", REMAINING_LIFE_COLUMN: 61},
]
with TemporaryDirectory() as temp_dir:
@@ -199,6 +212,8 @@ class PredictionHelpersTest(unittest.TestCase):
summary_data.columns.tolist(),
[ID_COLUMN, PIPE_AGE_COLUMN, "当前健康状态", "健康等级", REMAINING_LIFE_COLUMN],
)
self.assertEqual(summary_data[PIPE_AGE_COLUMN].tolist(), [10, 12])
self.assertEqual(summary_data[REMAINING_LIFE_COLUMN].tolist(), [63, 61])
sample_data = pd.read_excel(output_path, sheet_name="样本数据")
self.assertEqual(sample_data.columns.tolist(), ["管龄(年)", "P001", "P002"])
@@ -209,15 +224,19 @@ class PredictionHelpersTest(unittest.TestCase):
openpyxl_workbook = load_workbook(output_path)
sample_worksheet = openpyxl_workbook["样本数据"]
summary_worksheet = openpyxl_workbook["结果摘要"]
self.assertEqual(sample_worksheet.freeze_panes, "A2")
self.assertEqual(sample_worksheet.auto_filter.ref, "A1:C3")
self.assertEqual(sample_worksheet["B2"].number_format, "0.0%")
self.assertEqual(summary_worksheet["C2"].number_format, "0.00")
self.assertEqual(sample_worksheet["B2"].number_format, "0.00")
self.assertIn("73减去当前管龄", summary_worksheet["E1"].value)
self.assertNotIn("73 - 当前管龄", summary_worksheet["E1"].value)
def test_prediction_workbook_writes_pipe_ids_as_excel_text(self) -> None:
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.9, "健康等级": "V级", REMAINING_LIFE_COLUMN: ">63"}
{ID_COLUMN: "00123", PIPE_AGE_COLUMN: 10, "当前健康状态": 0.9, "健康等级": "V级", REMAINING_LIFE_COLUMN: 63}
]
with TemporaryDirectory() as temp_dir:
@@ -243,19 +262,17 @@ class PredictionHelpersTest(unittest.TestCase):
patch("app.prediction.plt.xlabel") as xlabel,
patch("app.prediction.plt.ylabel") as ylabel,
patch("app.prediction.plt.figtext") as figtext,
patch("app.prediction.plt.title") as title,
):
summary_rows, _ = render_survival_chart([dummy_prediction()], output_path)
summary_rows, summary_sheet_rows = render_survival_chart([dummy_prediction()], output_path)
self.assertAlmostEqual(summary_rows[0]["health_state"], 0.4)
self.assertEqual(summary_rows[0]["grade_label"], "II级")
self.assertEqual(summary_sheet_rows[0][PIPE_AGE_COLUMN], 12.0)
xlabel.assert_called_once()
self.assertEqual(xlabel.call_args.args[0], "管龄(年)")
ylabel.assert_called_once()
self.assertEqual(ylabel.call_args.args[0], "管道健康状态")
title.assert_called_once()
self.assertEqual(title.call_args.args[0], "供水管道健康状态曲线")
figtext.assert_called_once()
self.assertIn(f"{CHART_DISPLAY_LIMIT}条管道的示例数据", figtext.call_args.args[2])