This commit is contained in:
Your Name
2026-08-18 14:08:38 +08:00
parent 8b9df1154c
commit bc1228a310
77 changed files with 10763 additions and 1181 deletions
+188 -1
View File
@@ -3,13 +3,14 @@
from __future__ import annotations
import os
from copy import deepcopy
from datetime import date
from typing import Any
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
import pytest
from PySide6.QtWidgets import QApplication
from PySide6.QtWidgets import QApplication, QLabel, QScrollArea
from doctor_workstation.core import PermissionSet
from doctor_workstation.services.mock_repository import DemoDoctorRepository
@@ -18,7 +19,9 @@ from doctor_workstation.ui.pages import reception as reception_module
from doctor_workstation.ui.pages.reception import (
AI_MEDICAL_DISCLAIMER,
ReceptionPage,
_ai_narrative_text,
_generated_patient_report,
_normalize_patient_report,
_patient_report_rows,
_ReceptionAiAnalysisDialog,
)
@@ -596,3 +599,187 @@ def test_patient_ai_disclaimer_remains_the_unified_text() -> None:
"仅供临床辅助参考,不可替代医生诊断,不得直接用于开方、用药调整或其他医疗决策。"
"系统未对舌像、报告附件或视频画面进行视觉诊断;仅分析已录入、归档或转写的文字及附件元数据。"
)
def test_ai_narrative_formatter_preserves_lists_arrays_and_medical_numbers() -> None:
diagnosis: list[Any] = [
"2型糖尿病,HbA1c 7.5%,当前控制未达标。",
{"text": r"二甲双胍 0.5g,每日2次。\n复查肾功能。"},
"建议:1. 监测空腹血糖 2. 记录餐后2小时血糖",
]
original = deepcopy(diagnosis)
rendered = _ai_narrative_text(diagnosis)
assert diagnosis == original
assert rendered == _ai_narrative_text(rendered)
assert rendered.splitlines() == [
"• 2型糖尿病,HbA1c 7.5%,当前控制未达标。",
"• 二甲双胍 0.5g,每日2次。",
"复查肾功能。",
"• 建议:",
"1. 监测空腹血糖",
"2. 记录餐后2小时血糖",
]
assert "7.5%" in rendered
assert "0.5g" in rendered
assert "2型糖尿病" in rendered
assert "7.\n5" not in rendered
assert "0.\n5" not in rendered
assert "2\n型糖尿病" not in rendered
payload = {
"model_key": "qwen",
"diagnosis_advice": diagnosis,
"treatment_advice": ["控制总热量", "规律复诊"],
"risk_assessment": ["低血糖风险", {"label": "依从性风险", "level": "medium"}],
}
payload_before = deepcopy(payload)
normalized = _normalize_patient_report(payload)
assert payload == payload_before
assert normalized is not None
assert normalized["diagnosis_advice"] == rendered
assert normalized["treatment_advice"] == "• 控制总热量\n• 规律复诊"
assert normalized["risk_assessment"] == [
{"label": "低血糖风险", "level": "low"},
{"label": "依从性风险", "level": "medium"},
]
def test_patient_report_dialog_uses_one_scroll_owner_and_wrapped_risk_flow(
application: QApplication,
) -> None:
long_risk = (
"这是一个需要换行展示的较长风险项目,用于验证标签不会超出正文区域,"
"并且能够在流式布局中可靠折行。"
)
payload = {
"model_key": "qwen",
"model_label": "千问",
"generated_at": "2026-08-17 10:20:00",
"diagnosis_advice": [
"2型糖尿病,HbA1c 7.5%,建议继续分层监测。",
"1. 监测空腹血糖 2. 记录餐后2小时血糖",
]
* 10
+ ["[诊断末尾]"],
"risk_assessment": [
{"label": "低血糖", "level": "high"},
{"label": "依从性风险", "level": "medium"},
{"label": "并发症筛查延误风险", "level": "low"},
{"label": "复诊中断风险", "level": "medium"},
{"label": long_risk, "level": "high"},
{"label": "饮食波动风险", "level": "low"},
],
"treatment_advice": [r"二甲双胍 0.5g,每日2次。\n复查肾功能。"] * 12
+ ["[治疗末尾]"],
}
dialog = _ReceptionAiAnalysisDialog({"qwen": [payload]})
dialog.resize(720, 560)
dialog.show()
application.processEvents()
assert dialog.minimumWidth() == 720
assert dialog.minimumHeight() == 560
scrolls = dialog.findChildren(QScrollArea)
assert scrolls == [dialog.scroll_area]
assert dialog.scroll_area.horizontalScrollBar().maximum() == 0
assert dialog.scroll_area.verticalScrollBar().maximum() > 0
body = dialog.scroll_area.widget()
assert body is not None and body.layout() is not None
assert body.height() <= max(
dialog.scroll_area.viewport().height(),
body.layout().sizeHint().height(),
) + 40
assert dialog.diagnosis_label.text().endswith("[诊断末尾]")
assert dialog.treatment_label.text().endswith("[治疗末尾]")
assert "7.5%" in dialog.diagnosis_label.text()
assert "0.5g" in dialog.treatment_label.text()
risk_labels = [
label
for label in dialog.findChildren(QLabel)
if label.property("dialogAiRisk")
]
assert len(risk_labels) == 6
assert len({label.y() for label in risk_labels}) >= 2
short_risk = risk_labels[0]
wrapped_risk = next(label for label in risk_labels if label.text() == long_risk)
assert short_risk.width() < dialog.risk_items.width() // 2
assert wrapped_risk.width() <= 340
assert wrapped_risk.height() > short_risk.height()
assert max(label.y() + label.height() for label in risk_labels) <= dialog.risk_items.height()
dialog.close()
application.processEvents()
def test_reception_ai_card_is_compact_preview_without_nested_scroll(
application: QApplication,
) -> None:
payload = {
"diagnosis_advice": ["2型糖尿病,HbA1c 7.5%,需要继续监测。"] * 12,
"risk_assessment": [
{"label": "低血糖", "level": "high"},
{"label": "依从性风险", "level": "medium"},
{
"label": "这是一个需要在紧凑卡片内自行换行而不能向右溢出的长风险项目。",
"level": "low",
},
{"label": "复诊中断", "level": "medium"},
{"label": "饮食波动", "level": "low"},
{"label": "并发症筛查延误", "level": "high"},
],
"treatment_advice": ["二甲双胍 0.5g,每日2次。"] * 10,
"model_key": "qwen",
"model_label": "千问",
}
payload_before = deepcopy(payload)
page = ReceptionPage(object(), PermissionSet([]))
page._render_ai_analysis_payload(payload, "qwen")
page.ai_analysis_stack.setCurrentWidget(page.ai_analysis_content_page)
page.detail_stack.setCurrentIndex(1)
page.resize(1494, 832)
page.show()
application.processEvents()
assert payload == payload_before
assert not isinstance(page.ai_analysis_content_page, QScrollArea)
assert page.ai_analysis_card.findChildren(QScrollArea) == []
assert page.ai_analysis_card.minimumHeight() < 470
assert page.ai_analysis_card.maximumHeight() > 520
assert page.ai_analysis_card.sizeHint().height() < 470
assert page.ai_summary_label.fullText() == _ai_narrative_text(
payload["diagnosis_advice"]
)
assert page.ai_treatment_label.fullText() == _ai_narrative_text(
payload["treatment_advice"]
)
assert page.ai_summary_label.text().count("\n") + 1 == 3
assert page.ai_treatment_label.text().count("\n") + 1 == 2
assert page.ai_summary_label.text().endswith("")
assert page.ai_treatment_label.text().endswith("")
chips = [
label
for label in page.ai_risk_chip_host.findChildren(QLabel)
if label.property("receptionRiskChip")
]
overflow = [
label
for label in page.ai_risk_chip_host.findChildren(QLabel)
if label.property("receptionRiskOverflow")
]
assert len(chips) == 3
assert [label.text() for label in overflow] == ["+3 项"]
assert max(label.x() + label.width() for label in [*chips, *overflow]) <= (
page.ai_risk_chip_host.width()
)
assert max(label.y() + label.height() for label in [*chips, *overflow]) <= (
page.ai_risk_chip_host.height()
)
assert page.ai_risk_label.text().count("") == 5
page.close()
application.processEvents()