120 lines
3.7 KiB
Python
120 lines
3.7 KiB
Python
"""Render patient-level AI report layout regressions with the offscreen Qt backend."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
|
|
from PySide6.QtGui import QFontDatabase
|
|
from PySide6.QtWidgets import QApplication, QLabel, QScrollArea
|
|
|
|
from doctor_workstation.ui.pages.reception import _ReceptionAiAnalysisDialog
|
|
|
|
|
|
def _report_payload(model_key: str) -> dict[str, object]:
|
|
model_label = "OpenAI" if model_key == "openai" else "千问"
|
|
return {
|
|
"model_key": model_key,
|
|
"model_label": model_label,
|
|
"generated_at": "2026-08-17 10:20:00",
|
|
"diagnosis_advice": [
|
|
"2型糖尿病,HbA1c 7.5%,近期空腹血糖仍有波动。",
|
|
"建议:1. 监测空腹血糖 2. 记录餐后2小时血糖 3. 复核低血糖症状",
|
|
r"保留患者原始报告结构。\n结合复诊记录动态调整随访频率。",
|
|
],
|
|
"risk_assessment": [
|
|
{"label": "低血糖", "level": "high"},
|
|
{"label": "依从性风险", "level": "medium"},
|
|
{"label": "并发症筛查延误风险", "level": "low"},
|
|
{"label": "复诊中断风险", "level": "medium"},
|
|
{
|
|
"label": "肾功能变化可能影响二甲双胍方案,需要结合复查结果持续评估。",
|
|
"level": "high",
|
|
},
|
|
{"label": "饮食波动风险", "level": "low"},
|
|
],
|
|
"treatment_advice": [
|
|
r"二甲双胍 0.5g,每日2次。\n复查肾功能后再评估剂量。",
|
|
"继续糖尿病饮食教育,并记录运动后的血糖变化。",
|
|
"如出现心悸、出汗或意识异常,及时复测血糖并按流程处置。",
|
|
],
|
|
}
|
|
|
|
|
|
def _render(
|
|
app: QApplication,
|
|
output: Path,
|
|
*,
|
|
width: int,
|
|
height: int,
|
|
) -> None:
|
|
histories = {
|
|
"qwen": [_report_payload("qwen")],
|
|
"openai": [_report_payload("openai")],
|
|
}
|
|
dialog = _ReceptionAiAnalysisDialog(histories, preferred_model="qwen")
|
|
dialog.resize(width, height)
|
|
dialog.show()
|
|
for _index in range(3):
|
|
app.processEvents()
|
|
|
|
pixmap = dialog.grab()
|
|
if pixmap.width() != width or pixmap.height() != height:
|
|
raise RuntimeError(
|
|
f"unexpected render size: {pixmap.width()}x{pixmap.height()} "
|
|
f"(expected {width}x{height})"
|
|
)
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
if not pixmap.save(str(output), "PNG"):
|
|
raise RuntimeError(f"failed to save {output}")
|
|
|
|
scrolls = dialog.findChildren(QScrollArea)
|
|
risks = [
|
|
label
|
|
for label in dialog.findChildren(QLabel)
|
|
if label.property("dialogAiRisk")
|
|
]
|
|
print(
|
|
"PATIENT_AI_LAYOUT",
|
|
f"{width}x{height}",
|
|
f"scrolls={len(scrolls)}",
|
|
f"horizontal_max={dialog.scroll_area.horizontalScrollBar().maximum()}",
|
|
f"risk_rows={len({label.y() for label in risks})}",
|
|
f"body_height={dialog.scroll_area.widget().height()}",
|
|
)
|
|
print(output)
|
|
dialog.close()
|
|
app.processEvents()
|
|
|
|
|
|
def main() -> int:
|
|
app = QApplication.instance() or QApplication([])
|
|
font_path = Path(r"C:\Windows\Fonts\msyh.ttc")
|
|
if font_path.is_file():
|
|
QFontDatabase.addApplicationFont(str(font_path))
|
|
|
|
output_dir = (
|
|
Path(__file__).resolve().parents[1]
|
|
/ "artifacts"
|
|
/ "patient_ai_report_layout"
|
|
)
|
|
_render(
|
|
app,
|
|
output_dir / "patient_ai_report_920x760.png",
|
|
width=920,
|
|
height=760,
|
|
)
|
|
_render(
|
|
app,
|
|
output_dir / "patient_ai_report_720x560.png",
|
|
width=720,
|
|
height=560,
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|