This commit is contained in:
Your Name
2026-09-01 11:26:47 +08:00
parent 5bd5eae62d
commit 398f9f3726
357 changed files with 733 additions and 343 deletions
@@ -9,7 +9,7 @@ from math import ceil
from typing import Any
from PySide6.QtCore import QDateTime, QModelIndex, QRectF, QSize, Qt
from PySide6.QtGui import QColor, QFont, QIcon, QPainter, QPen, QPixmap, QTextDocument, QTextOption
from PySide6.QtGui import QColor, QFont, QIcon, QPainter, QPen, QTextDocument, QTextOption
from PySide6.QtWidgets import (
QComboBox,
QDateTimeEdit,
@@ -398,7 +398,10 @@ def _order_warnings(row: Any) -> list[str]:
return []
herbs = get_value(source, "herbs", None)
herbs = herbs if isinstance(herbs, (list, tuple)) else []
names = [str(get_value(herb, "name", "") or "").strip() for herb in herbs]
names = []
for herb in herbs:
raw_name = get_value(herb, "name", "")
names.append(str("" if raw_name is None else raw_name).strip())
names = [name for name in names if name]
if not names:
return ["请开方,当前处方药材为空白"]
+197
View File
@@ -0,0 +1,197 @@
from __future__ import annotations
import os
from types import SimpleNamespace
from typing import Any
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
import pytest
from PySide6.QtCore import Qt
from PySide6.QtGui import QImage
from PySide6.QtWidgets import QApplication, QStyleOptionViewItem
from doctor_workstation.ui.pages import prescriptions as prescriptions_module
from doctor_workstation.ui.pages.prescriptions import PrescriptionsPage, _order_warnings
@pytest.fixture(scope="module")
def application() -> QApplication:
return QApplication.instance() or QApplication([])
@pytest.fixture(autouse=True)
def immediate_async(monkeypatch: pytest.MonkeyPatch) -> None:
def run_immediately(
function: Any,
*args: Any,
on_success: Any = None,
on_error: Any = None,
on_finished: Any = None,
**kwargs: Any,
) -> object:
try:
result = function(*args, **kwargs)
except Exception as error:
if on_error is not None:
on_error(error)
else:
if on_success is not None:
on_success(result)
finally:
if on_finished is not None:
on_finished()
return object()
monkeypatch.setattr(prescriptions_module, "run_async", run_immediately)
def row(
record_id: int,
number: str,
*,
has_order: Any,
herbs: Any,
) -> dict[str, Any]:
return {
"id": record_id,
"sn": number,
"patient_name": "列表提示测试患者",
"gender": 1,
"age": 45,
"prescription_type": "浓缩水丸",
"is_system_auto": 1,
"audit_status": 0,
"void_status": 0,
"has_prescription_order": has_order,
"herbs": herbs,
"doctor_name": "测试医生",
"assistant_name": "测试医助",
"create_time": "2026-09-01 09:30:00",
}
class Repository:
def __init__(self, rows: list[dict[str, Any]]) -> None:
self.rows = rows
def list_diagnosis_doctors(self) -> list[Any]:
return []
def list_prescriptions(self, **_filters: Any) -> dict[str, Any]:
return {"lists": self.rows, "count": len(self.rows)}
def test_order_warnings_exactly_match_pc_rules() -> None:
assert _order_warnings(row(1, "RX-1", has_order=0, herbs=[])) == []
assert _order_warnings(row(2, "RX-2", has_order=1, herbs=[])) == ["请开方,当前处方药材为空白"]
assert _order_warnings(
row(3, "RX-3", has_order="1.0", herbs=[{}, {"name": ""}, {"name": " "}])
) == ["请开方,当前处方药材为空白"]
assert _order_warnings(
row(
4,
"RX-4",
has_order=1,
herbs=[{"name": "黄芪"}, {"name": " 黄 芪 "}, {"name": "党参"}],
)
) == ["已有关联业务订单,当前处方存在重复药材:黄 芪"]
# The PC implementation only reads the canonical `name` field.
assert _order_warnings(row(5, "RX-5", has_order=1, herbs=[{"medicine_name": "黄芪"}])) == [
"请开方,当前处方药材为空白"
]
def test_number_column_renders_sn_id_and_visible_warning_with_dynamic_height(
application: QApplication,
) -> None:
rows = [
row(11, "RX-NORMAL", has_order=0, herbs=[]),
row(22, "RX-BLANK", has_order=1, herbs=[{"name": ""}]),
row(
33,
"RX-DUPLICATE",
has_order=1,
herbs=[{"name": "黄芪"}, {"name": "黄 芪"}],
),
]
page = PrescriptionsPage(Repository(rows), {"*"}, SimpleNamespace(id=7, name="测试医生"))
page.resize(1366, 768)
page.show()
page.refresh()
for _ in range(6):
application.processEvents()
rendered: dict[int, tuple[int, str, str]] = {}
for visual_row in range(page.table.rowCount()):
item = page.table.item(visual_row, 1)
source = item.data(Qt.ItemDataRole.UserRole)
rendered[source["id"]] = (
page.table.rowHeight(visual_row),
item.toolTip(),
item.data(Qt.ItemDataRole.AccessibleTextRole),
)
normal_height, normal_tip, normal_accessible = rendered[11]
blank_height, blank_tip, blank_accessible = rendered[22]
duplicate_height, duplicate_tip, _duplicate_accessible = rendered[33]
assert normal_tip == normal_accessible == "RX-NORMAL\nID: 11"
assert "请开方,当前处方药材为空白" in blank_tip == blank_accessible
assert "已有关联业务订单,当前处方存在重复药材:黄 芪" in duplicate_tip
assert blank_height > normal_height
assert duplicate_height > normal_height
assert page.table.columnWidth(1) >= 250
image = page.table.viewport().grab().toImage().convertToFormat(QImage.Format.Format_RGB32)
red_pixels = 0
for y in range(image.height()):
for x in range(image.width()):
color = image.pixelColor(x, y)
if color.red() > 170 and color.green() < 90 and color.blue() < 100:
red_pixels += 1
assert red_pixels > 40
page.close()
application.processEvents()
def test_number_warning_survives_sort_and_column_resize(application: QApplication) -> None:
rows = [
row(41, "RX-Z", has_order=1, herbs=[]),
row(42, "RX-A", has_order=0, herbs=[{"name": "黄芪"}]),
]
page = PrescriptionsPage(Repository(rows), {"*"}, SimpleNamespace(id=7, name="测试医生"))
page.resize(1200, 700)
page.show()
page.refresh()
page.table.sortItems(1, Qt.SortOrder.AscendingOrder)
page.table.setColumnWidth(1, 190)
for _ in range(5):
application.processEvents()
assert page.table.item(0, 1).text() == "RX-A"
assert page.table.item(1, 1).text() == "RX-Z"
assert page.table.item(1, 1).data(Qt.ItemDataRole.UserRole)["id"] == 41
assert "请开方,当前处方药材为空白" in page.table.item(1, 1).toolTip()
assert page.table.rowHeight(1) > page.table.rowHeight(0)
page.close()
application.processEvents()
def test_number_html_is_escaped_but_remains_readable(application: QApplication) -> None:
unsafe_number = "RX-<b>NOT HTML</b>"
page = PrescriptionsPage(
Repository([row(51, unsafe_number, has_order=1, herbs=[])]),
{"*"},
SimpleNamespace(id=7, name="测试医生"),
)
page.refresh()
delegate = page.table.itemDelegateForColumn(1)
option = QStyleOptionViewItem()
option.initFrom(page.table)
index = page.table.model().index(0, 1)
plain_text = delegate.document(option, index, page.table.columnWidth(1)).toPlainText()
assert unsafe_number in plain_text
assert "ID: 51" in plain_text
assert "请开方,当前处方药材为空白" in plain_text
page.close()
application.processEvents()