更新
This commit is contained in:
@@ -12,9 +12,11 @@ from PySide6.QtCore import Qt
|
||||
from PySide6.QtTest import QTest
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QDialog,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QTextBrowser,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
@@ -1506,9 +1508,17 @@ def test_sectioned_reply_becomes_a_structured_report(application: QApplication)
|
||||
titles = [
|
||||
label.text()
|
||||
for label in panel.findChildren(QLabel)
|
||||
if label.objectName() == "AiConsultClinicalSectionTitle"
|
||||
if label.objectName() == "AiConsultReportSectionTitle"
|
||||
]
|
||||
assert titles == ["症状演变与疗效评估", "血糖控制与监测细节", "用药依从性与生活方式"]
|
||||
# 要点不再每句一个方框,而是「小标题 + 正文」两级文字。
|
||||
assert panel.findChildren(ai_consult_module.QFrame, "AiConsultGapRow") == []
|
||||
leads = [
|
||||
label.text()
|
||||
for label in panel.findChildren(QLabel)
|
||||
if label.objectName() == "AiConsultReportLead"
|
||||
]
|
||||
assert leads == ["麻木症状", "皮肤瘙痒", "空腹血糖波动", "西药服用情况"]
|
||||
bubble.deleteLater()
|
||||
|
||||
|
||||
@@ -1522,3 +1532,128 @@ def test_sectioned_reply_becomes_a_structured_report(application: QApplication)
|
||||
)
|
||||
def test_unsectioned_replies_stay_plain_text(reply: str, application: QApplication) -> None:
|
||||
assert ai_consult_module.parse_structured_report(reply) is None
|
||||
|
||||
|
||||
def test_report_points_split_into_a_scannable_label_and_body() -> None:
|
||||
split = ai_consult_module.split_report_lead
|
||||
|
||||
assert split("糖尿病管理缺失: 患者确诊糖尿病3年,空腹血糖为 8.5 mmol/L。") == (
|
||||
"糖尿病管理缺失",
|
||||
"患者确诊糖尿病3年,空腹血糖为 8.5 mmol/L。",
|
||||
)
|
||||
assert split("结论:由于患者当前未使用任何药物,无需复核。")[0] == "结论"
|
||||
# 冒号前是一整句话,或正文太短,都按普通要点整段显示。
|
||||
assert split("尽管无需复核用药,但基于患者病史,以下临床风险点需重点关注。以下为要点:细节")[0] == ""
|
||||
assert split("空腹血糖: 8.5") == ("", "空腹血糖: 8.5")
|
||||
assert split("没有冒号的一条要点") == ("", "没有冒号的一条要点")
|
||||
|
||||
|
||||
def test_report_section_titles_drop_the_number_the_chip_already_shows() -> None:
|
||||
strip = ai_consult_module._strip_leading_ordinal
|
||||
|
||||
assert strip("1. 当前用药状态评估") == "当前用药状态评估"
|
||||
assert strip("二、临床风险与干预提示") == "临床风险与干预提示"
|
||||
assert strip("建议下一步行动") == "建议下一步行动"
|
||||
|
||||
|
||||
def test_report_body_escapes_markup_and_carries_reading_rhythm() -> None:
|
||||
html = ai_consult_module._reading_html('血糖 <7.0 mmol/L 且 "达标" & 稳定')
|
||||
|
||||
assert "line-height" in html
|
||||
assert "<7.0" in html
|
||||
assert "&" in html
|
||||
assert "<7.0" not in html
|
||||
|
||||
|
||||
def test_structured_report_uses_a_readable_column_width(application: QApplication) -> None:
|
||||
reply = (
|
||||
"针对该患者的用药复核评估如下:\n\n"
|
||||
"### 1. 当前用药状态评估\n"
|
||||
"- 无当前处方药物: 病例数据中明确记录患者目前没有服药,系统内也没有有效处方记录。\n"
|
||||
"- 结论: 患者当前未使用任何药物,不存在药物相互作用或配伍禁忌风险。\n"
|
||||
"### 2. 临床风险与干预提示\n"
|
||||
"- 糖尿病管理缺失: 患者确诊糖尿病3年,空腹血糖高于一般控制目标且未接受药物治疗。\n"
|
||||
"重要提示: 本分析不能替代执业医师的面诊与完整病历评估。\n"
|
||||
)
|
||||
bubble = ai_consult_module._ChatBubble(role="ai", text=reply, time_text="千问 · 16:32")
|
||||
|
||||
assert bubble.finalize_clinical_analysis() is True
|
||||
panel = bubble.findChild(ai_consult_module._StructuredReportPanel)
|
||||
assert panel is not None
|
||||
# 报告收窄到易读行宽,而不是继续用多栏面板的 1080。
|
||||
assert panel.PREFERRED_MAX_WIDTH == 880
|
||||
assert bubble._bubble_frame.maximumWidth() == 880
|
||||
sections = panel.findChildren(ai_consult_module.QFrame, "AiConsultReportSection")
|
||||
assert len(sections) == 2
|
||||
assert panel.findChildren(ai_consult_module.QFrame, "AiConsultGapRow") == []
|
||||
bubble.deleteLater()
|
||||
|
||||
|
||||
def _fitted_bubble(reply: str, width: int = 900) -> Any:
|
||||
host = QDialog()
|
||||
host.setObjectName("AiConsultDialog")
|
||||
host.setStyleSheet(ai_consult_module.AI_CONSULT_QSS)
|
||||
layout = QVBoxLayout(host)
|
||||
layout.setContentsMargins(12, 12, 12, 12)
|
||||
bubble = ai_consult_module._ChatBubble(role="ai", text=reply, time_text="千问 · 16:32")
|
||||
assert bubble.finalize_clinical_analysis() is True
|
||||
layout.addWidget(bubble)
|
||||
layout.addStretch(1)
|
||||
host.setFixedWidth(width)
|
||||
host.show()
|
||||
for _ in range(12):
|
||||
QApplication.processEvents()
|
||||
host.adjustSize()
|
||||
for _ in range(6):
|
||||
QApplication.processEvents()
|
||||
return host, bubble
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"reply",
|
||||
[
|
||||
LONG_CLINICAL_REPLY,
|
||||
(
|
||||
"针对该患者的用药复核评估如下:\n\n"
|
||||
"### 1. 当前用药状态评估\n"
|
||||
"- 无当前处方药物: 病例数据中明确记录患者目前没有服药,系统内也没有有效处方记录,"
|
||||
"因此不存在药物相互作用或配伍禁忌风险,无需再做安全性复核。\n"
|
||||
"### 2. 临床风险与干预提示\n"
|
||||
"- 糖尿病管理缺失: 患者确诊糖尿病3年,空腹血糖高于一般控制目标且未接受任何药物治疗,"
|
||||
"存在长期高血糖导致微血管及大血管并发症的风险,需要尽快评估。\n"
|
||||
),
|
||||
],
|
||||
ids=["clinical", "structured"],
|
||||
)
|
||||
def test_report_bubbles_report_the_height_they_actually_paint(
|
||||
reply: str,
|
||||
application: QApplication,
|
||||
) -> None:
|
||||
"""否则聊天区会按高估的高度撑出滚动空白,打开就是一片空白要往上滑。"""
|
||||
|
||||
host, bubble = _fitted_bubble(reply)
|
||||
|
||||
assert bubble.height() > 0
|
||||
assert abs(bubble.sizeHint().height() - bubble.height()) <= 2
|
||||
host.close()
|
||||
host.deleteLater()
|
||||
|
||||
|
||||
def test_risk_block_uses_the_red_alert_palette() -> None:
|
||||
qss = ai_consult_module.AI_CONSULT_QSS
|
||||
|
||||
risk_card = qss.split("QFrame#AiConsultRiskCard {", 1)[1].split("}", 1)[0]
|
||||
assert "#FEF3F2" in risk_card
|
||||
assert "#F1B35C" not in risk_card # 旧的橙色描边
|
||||
marker = qss.split("QLabel#AiConsultRiskMarker {", 1)[1].split("}", 1)[0]
|
||||
assert "#C0392B" in marker
|
||||
|
||||
|
||||
def test_clinical_bodies_are_no_longer_rendered_at_eleven_pixels() -> None:
|
||||
qss = ai_consult_module.AI_CONSULT_QSS
|
||||
|
||||
body = qss.split("QLabel#AiConsultRiskBody {\n color: #46557A;", 1)
|
||||
assert len(body) == 2 or "font-size: 13px" in qss
|
||||
block = qss.split("QLabel#AiConsultClinicalBody,", 1)[1].split("}", 1)[0]
|
||||
assert "font-size: 13px" in block
|
||||
assert "font-size: 11px" not in block
|
||||
|
||||
Reference in New Issue
Block a user