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
+238 -10
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import json
import os
from datetime import date
from datetime import date, timedelta
from pathlib import Path
from typing import Any
@@ -12,7 +12,7 @@ import httpx
import pytest
from PySide6.QtCore import QDate, QPoint, Qt
from PySide6.QtGui import QPalette
from PySide6.QtWidgets import QApplication, QLabel, QPushButton, QScrollArea
from PySide6.QtWidgets import QApplication, QLabel, QPushButton, QScrollArea, QWidget
from doctor_workstation.core import PermissionSet
from doctor_workstation.services.api_client import ApiClient
@@ -283,9 +283,10 @@ def test_queue_uses_admin_same_day_contract(
"status": 1,
"start_date": date.today().isoformat(),
"end_date": date.today().isoformat(),
"page_no": 1,
"page_size": 15,
"patient_name": "王小明",
"page_no": 1,
"page_size": 15,
"patient_name": "王小明",
"include_status_counts": 1,
}
]
@@ -296,6 +297,167 @@ def test_queue_uses_admin_same_day_contract(
application.processEvents()
def test_reception_daily_records_use_backend_matrix_contract(
application: QApplication,
immediate_async: None,
) -> None:
today = date.today().isoformat()
tracking_calls: list[tuple[int, str, str]] = []
note_calls: list[int] = []
class Repository:
fail_tracking = False
def list_appointments(self, **_kwargs: Any) -> dict[str, Any]:
return {
"lists": [
{
"id": 71,
"diagnosis_id": 271,
# Production appointment.patient_id is the diagnosis id.
"patient_id": 271,
"patient_name": "日常记录患者",
"status": 1,
"appointment_time": "09:30:00",
}
],
"count": 1,
}
def get_reception(self, appointment_id: int) -> dict[str, Any]:
assert appointment_id == 71
return {
"appointment": {
"id": 71,
"patient_id": 271,
"patient_name": "日常记录患者",
"status": 1,
},
"diagnosis": {
"id": 271,
"patient_id": 971,
"patient_name": "日常记录患者",
"age": 56,
},
"tracking_notes": [
{"note_date": today, "content": "内嵌备注降级数据"}
],
}
def get_tracking_window(
self,
diagnosis_id: int,
*,
start_date: str,
end_date: str,
) -> dict[str, Any]:
tracking_calls.append((diagnosis_id, start_date, end_date))
if self.fail_tracking:
raise RuntimeError("tracking unavailable")
return {
"diagnosis_id": diagnosis_id,
"start_date": start_date,
"end_date": end_date,
"blood_records": [
{
"record_date": today,
"record_time": "08:30:00",
"fasting_blood_sugar": 9.6,
"postprandial_blood_sugar": 11.4,
"other_blood_sugar": 8.5,
"systolic_pressure": 141,
"diastolic_pressure": 90,
"western_medicine": "二甲双胍",
"insulin": "睡前 8U",
"source": 1,
}
],
"diet_records": [
{
"record_date": today,
"breakfast_foods": ["小米粥"],
"lunch_foods": ["杂粮饭"],
"dinner_foods": ["青菜"],
}
],
"exercise_records": [
{
"record_date": today,
"exercise_type": "快走",
"duration": 45,
"intensity_text": "中等",
}
],
}
def list_tracking_notes(self, diagnosis_id: int) -> list[dict[str, Any]]:
note_calls.append(diagnosis_id)
return [{"note_date": today, "content": "睡眠改善,继续随访"}]
repository = Repository()
page = ReceptionPage(repository, PermissionSet([]))
page.refresh()
application.processEvents()
assert [
page.detail_tabs.tabText(index) for index in range(page.detail_tabs.count())
] == ["问诊信息", "检查报告", "用药记录", "日常记录", "随访记录", "健康数据"]
assert tracking_calls == [
(271, (date.today() - timedelta(days=6)).isoformat(), today)
]
assert note_calls == [271]
assert page._selection_context()[-1] == 971
matrix = page.daily_panel.matrix
assert matrix.objectName() == "ReceptionDailyRecordsTable"
assert matrix.rowCount() == 11
assert matrix.columnCount() == 8
assert matrix.horizontalHeaderItem(0).text() == "指标"
assert matrix.horizontalHeaderItem(1).text() == today[5:]
assert [matrix.item(row, 0).text() for row in range(11)] == [
"空腹血糖",
"餐后2h血糖",
"其他血糖",
"血压",
"西药",
"胰岛素",
"早餐",
"午餐",
"晚餐",
"运动",
"跟踪备注",
]
assert matrix.item(0, 1).text() == "9.6 · 自录 ↑"
assert matrix.item(0, 1).data(Qt.ItemDataRole.UserRole)["high"] is True
assert matrix.item(1, 1).text() == "11.4 · 自录 ↑"
assert matrix.item(2, 1).text() == "8.5 · 自录"
assert matrix.item(2, 1).data(Qt.ItemDataRole.UserRole)["high"] is False
assert matrix.item(3, 1).text() == "141/90 · 自录 ↑"
assert matrix.item(4, 1).text() == "二甲双胍"
assert matrix.item(5, 1).text() == "睡前 8U"
assert matrix.item(6, 1).text() == "已记录"
assert matrix.item(9, 1).text() == "45min"
assert matrix.item(10, 1).text() == "睡眠改善,继续随访"
assert "睡眠改善" in page.followup_text.text()
page.daily_panel.range_buttons["30"].click()
assert tracking_calls[-1] == (
271,
(date.today() - timedelta(days=29)).isoformat(),
today,
)
assert page.daily_panel.matrix.columnCount() == 31
preserved = page.daily_panel.matrix.item(0, 1).text()
repository.fail_tracking = True
page.daily_panel.refresh_button.click()
assert page.daily_panel.matrix.item(0, 1).text() == preserved
assert "已保留上次数据" in page.daily_panel.state.label.text()
page.close()
application.processEvents()
def test_queue_date_picker_filters_the_selected_day(
application: QApplication,
immediate_async: None,
@@ -394,6 +556,69 @@ def test_silent_queue_polls_reuse_rows_and_do_not_restart_detail_or_ai(
application.processEvents()
def test_silent_poll_replaces_only_the_queue_row_with_visible_changes(
application: QApplication,
immediate_async: None,
monkeypatch: pytest.MonkeyPatch,
) -> None:
original_queue_row = reception_module.QueueRow
constructed: list[int] = []
class CountingQueueRow(original_queue_row):
def __init__(self, record: Any, parent: QWidget | None = None) -> None:
constructed.append(int(record["id"]))
super().__init__(record, parent)
monkeypatch.setattr(reception_module, "QueueRow", CountingQueueRow)
class Repository:
calls = 0
def list_appointments(self, **_kwargs: Any) -> dict[str, Any]:
self.calls += 1
rows = [
{
"id": index,
"diagnosis_id": 200 + index,
"patient_id": 100 + index,
"patient_name": f"患者{index}",
"clinical_diagnosis": "消渴",
"status": 1,
}
for index in range(1, 4)
]
if self.calls > 1:
rows[1]["clinical_diagnosis"] = "消渴 · 气阴两虚"
return {"lists": rows, "count": 3}
def get_reception(self, appointment_id: int) -> dict[str, Any]:
return {
"appointment": {"id": appointment_id, "patient_id": 100 + appointment_id},
"diagnosis": {
"id": 200 + appointment_id,
"patient_id": 100 + appointment_id,
},
}
page = ReceptionPage(Repository(), PermissionSet([]))
page.refresh()
items = [page.queue_list.item(index) for index in range(3)]
widgets = [page.queue_list.itemWidget(item) for item in items]
assert constructed == [1, 2, 3]
page.refresh(silent=True)
assert all(page.queue_list.item(index) is items[index] for index in range(3))
assert page.queue_list.itemWidget(items[0]) is widgets[0]
assert page.queue_list.itemWidget(items[1]) is not widgets[1]
assert page.queue_list.itemWidget(items[2]) is widgets[2]
assert constructed == [1, 2, 3, 2]
changed_row = page.queue_list.itemWidget(items[1])
assert changed_row.findChild(QLabel, "ReceptionQueueSubline").text() == "消渴 · 气阴两虚"
page.close()
application.processEvents()
def test_timer_poll_does_not_supersede_an_in_flight_queue_request(
application: QApplication,
monkeypatch: pytest.MonkeyPatch,
@@ -905,9 +1130,10 @@ def test_queue_worker_uses_frozen_widget_snapshot(
"status": 1,
"start_date": date.today().isoformat(),
"end_date": date.today().isoformat(),
"page_no": 1,
"page_size": 15,
"patient_name": "甲患者",
"page_no": 1,
"page_size": 15,
"patient_name": "甲患者",
"include_status_counts": 1,
}
]
page.close()
@@ -1094,7 +1320,10 @@ def test_reception_auto_loads_structured_ai_analysis_and_matches_reference_geome
left = page.ai_analysis_card.geometry()
right = page.ai_assistant_card.geometry()
assert 470 <= left.height() <= 520
assert left.height() < 470
assert page.ai_analysis_card.minimumHeight() == 0
assert page.ai_analysis_card.maximumHeight() > 520
assert page.ai_analysis_card.findChildren(QScrollArea) == []
assert left.height() == right.height()
assert 0 <= right.left() - left.right() - 1 <= 2
assert abs(left.width() * 5 - right.width() * 4) <= 10
@@ -1464,7 +1693,6 @@ def test_ai_analysis_dialog_switches_complete_cached_payloads_without_requests(
"qwen 风险项目 1",
"qwen 风险项目 2",
"qwen 风险项目 3",
"qwen 风险项目 4",
]
calls_before_dialog = list(repository.analysis_calls)
page.ai_analysis_expand_button.click()