"""Render deterministic diagnosis readonly, edit, and view-only references.""" from __future__ import annotations import os from pathlib import Path from typing import Any os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtGui import QFont, QFontDatabase from PySide6.QtWidgets import QApplication from doctor_workstation.core import PermissionSet from doctor_workstation.ui.diagnosis_editors import DailyRecordEditorDialog from doctor_workstation.ui.diagnosis_media import RecordingPlayerDialog from doctor_workstation.ui.dialogs import diagnosis as diagnosis_module from doctor_workstation.ui.dialogs.diagnosis import DiagnosisDialog from doctor_workstation.ui.dialogs.prescription import PrescriptionEditorDialog def _detail(*, locked: bool) -> dict[str, Any]: diagnosis: dict[str, Any] = { "id": 501, "patient_id": 1501, "patient_name": "林晓岚", "phone": "13800138000", "id_card": "110105199203071234", "gender": 0, "age": 34, "marital_status": 1, "height": 162, "weight": 54.5, "region": "浙江省杭州市", "systolic_pressure": 146, "diastolic_pressure": 92, "fasting_blood_sugar": 8.2, "diagnosis_type": "follow_up", "diagnosis_date": "2026-08-10", "status_desc": "诊疗中", "channel_name": "健康顾问转介", "statistical_visit_card": "第 4 次", "current_medications": "二甲双胍", "diabetes_discovery_year": "2019 年", "local_hospital_diagnosis": ["2 型糖尿病", "高血压"], "local_hospital_name": "杭州市第一人民医院", "oral_condition": "口干", "water_intake": "约 2200 ml", "weight_change": "近月下降 1 kg", "fatty_liver_degree": "轻度", "diet_condition": ["偏甜", "夜宵"], "body_feeling": ["乏力", "四肢沉重"], "sleep_condition": "入睡稍慢", "eye_condition": "偶有干涩", "head_feeling": "午后头昏", "sweat_condition": "易出汗", "skin_condition": "皮肤偏干", "urine_condition": "夜尿 2 次", "stool_condition": "每日一次", "kidney_condition": "腰酸", "present_illness": "口渴、乏力反复半年,近期血糖波动。", "past_history": "高血压病史 5 年。", "trauma_history": 0, "surgery_history": 0, "allergy_history": 1, "family_history": 1, "pregnancy_history": 0, "remark": "建议持续记录空腹及餐后血糖。", "clinical_diagnosis": "气阴两虚", "prescription_opinion": "益气养阴,兼顾活血。", "has_prescription": 1, "unserved_days": 4, "patient_basic_locked": locked, "can_edit_patient_basic": not locked, "latest_prescription_order": { "id": "RX-240810-09", "fulfillment_status_text": "待配药", }, } return { "diagnosis": diagnosis, "patient": { "id": 1501, "patient_name": "林晓岚", "phone": "13800138000", "id_card": "110105199203071234", "gender": 0, "age": 34, }, "appointment": { "id": 2501, "appointment_date": "2026-08-10", "appointment_time": "09:00-09:30", "period_text": "上午", "doctor_name": "陈医生", "assistant_name": "赵医助", "status_desc": "已到诊", "remark": "复诊评估", }, } class ScreenshotRepository: """No-network data source for visual artifact generation.""" def __init__(self, *, locked: bool) -> None: self.detail = _detail(locked=locked) def get_diagnosis_detail(self, diagnosis_id: int, **_kwargs: Any) -> dict[str, Any]: if diagnosis_id != 501: raise LookupError(diagnosis_id) return self.detail diagnosis_readonly_detail = get_diagnosis_detail patient_detail = get_diagnosis_detail def get_dictionary(self, dictionary_type: str) -> list[dict[str, Any]]: if dictionary_type == "diagnosis_type": return [ {"name": "初诊", "value": "first_visit"}, {"name": "复诊", "value": "follow_up"}, {"name": "会诊", "value": "consultation"}, ] if dictionary_type == "appetite": return [ {"name": "口干", "value": "口干"}, {"name": "口苦", "value": "口苦"}, ] return [] def get_doctor_notes(self, diagnosis_id: int) -> list[dict[str, Any]]: if diagnosis_id != 501: raise LookupError(diagnosis_id) return [ { "id": 7001, "create_time": "2026-08-10 09:20", "doctor_name": "陈医生", "content": "面色稍淡,舌淡红,苔薄白。", "tongue_images": [{"url": "tongue.jpg"}], "report_files": [ { "name": "近期血糖趋势.pdf", "url": "https://media.example.invalid/report.pdf", } ], } ] def get_tracking_window( self, diagnosis_id: int, *, start_date: str = "", end_date: str = "", ) -> dict[str, Any]: if diagnosis_id != 501: raise LookupError(diagnosis_id) del start_date, end_date return { "blood_records": [ { "id": 6101, "diagnosis_id": 501, "patient_id": 1501, "record_date": "2026-08-10", "fasting_blood_sugar": 8.2, "systolic_pressure": 146, "source": 1, }, { "id": 6102, "diagnosis_id": 501, "patient_id": 1501, "record_date": "2026-08-10", "postprandial_blood_sugar": 12.4, "diastolic_pressure": 92, "western_medicine": "二甲双胍", }, { "id": 6103, "diagnosis_id": 501, "patient_id": 1501, "record_date": "2026-08-09", "fasting_blood_sugar": 7.6, "postprandial_blood_sugar": 10.8, }, ], "diet_records": [ { "id": 6201, "diagnosis_id": 501, "patient_id": 1501, "record_date": "2026-08-10", "breakfast_foods": "燕麦、鸡蛋", "lunch_foods": "杂粮饭", } ], "exercise_records": [ { "id": 6301, "diagnosis_id": 501, "patient_id": 1501, "record_date": "2026-08-09", "exercise_type": "散步", "duration": 35, "intensity": 2, } ], } def list_tracking_notes(self, diagnosis_id: int) -> list[dict[str, Any]]: if diagnosis_id != 501: raise LookupError(diagnosis_id) return [{"note_date": "2026-08-10", "content": "饭后散步,继续观察。"}] def list_diagnosis_todos(self, diagnosis_id: int, **_kwargs: Any) -> dict[str, Any]: if diagnosis_id != 501: raise LookupError(diagnosis_id) return { "lists": [ { "remind_time_text": "2026-08-12 09:00", "content": "回访复测餐后血糖", "status": 0, "status_text": "待执行", "creator_name": "陈医生", "can_cancel": True, "id": 7101, } ], "count": 1, } def list_prescriptions_by_diagnosis(self, diagnosis_id: int) -> list[dict[str, Any]]: if diagnosis_id != 501: raise LookupError(diagnosis_id) return [ { "id": 901, "diagnosis_id": 501, "prescription_date": "2026-08-02", "global_visit_sequence": 4, "prescription_type_text": "中药", "prescription_summary": "玉泉丸加减", "doctor_name": "陈医生", "status_text": "已审核", } ] def appointment_history(self, **_kwargs: Any) -> dict[str, Any]: return { "lists": [ { "id": 2501, "status_desc": "已到诊", "patient_name": "林晓岚", "patient_phone": "13800138000", "doctor_name": "陈医生", "assistant_name": "赵医助", "appointment_date": "2026-08-10", "appointment_time": "09:00-09:30", "appointment_type_text": "复诊", "channel_name": "健康顾问", "is_confirmed": 1, "has_prescription": 1, "remark": "复诊评估", "create_time": "2026-08-08 11:20", } ] } def assign_history(self, **_kwargs: Any) -> dict[str, Any]: return { "lists": [ { "create_time": "2026-08-08 11:22", "from_assistant_name": "王医助", "to_assistant_name": "赵医助", "is_inherit": 1, "snapshot_order_creator_name": "王医助", "snapshot_order_create_time": "2026-08-01 10:00", "operator_name": "陈医生", "operator_account": "doctor.chen", "ip": "10.1.2.8", } ] } def list_prescription_orders(self, **_kwargs: Any) -> dict[str, Any]: return { "lists": [ { "id": 801, "order_no": "RX-240810-09", "global_visit_sequence": 4, "statistics_count": 1, "amount": "¥ 286.00", "doctor_name": "陈医生", "assistant_name": "赵医助", "fulfillment_status_text": "待配药", "create_time": "2026-08-10 09:24", } ], "count": 1, } def get_prescription_order(self, order_id: int) -> dict[str, Any]: if order_id != 801: raise LookupError(order_id) return { "id": order_id, "order_no": "RX-240810-09", "patient_name": "林晓岚", "amount": 286, "fulfillment_status_text": "待配药", } def add_blood_record(self, payload: dict[str, Any]) -> dict[str, Any]: return {"id": 7201, **payload} def add_diet_record(self, payload: dict[str, Any]) -> dict[str, Any]: return {"id": 7202, **payload} def add_exercise_record(self, payload: dict[str, Any]) -> dict[str, Any]: return {"id": 7203, **payload} def update_blood_record( self, record: dict[str, Any], changes: dict[str, Any] | None = None ) -> dict[str, Any]: return {**record, **(changes or {})} def update_diet_record( self, record: dict[str, Any], changes: dict[str, Any] | None = None ) -> dict[str, Any]: return {**record, **(changes or {})} def update_exercise_record( self, record: dict[str, Any], changes: dict[str, Any] | None = None ) -> dict[str, Any]: return {**record, **(changes or {})} def add_tracking_note(self, diagnosis_id: int, content: str) -> dict[str, Any]: return {"id": 7204, "diagnosis_id": diagnosis_id, "content": content} def add_diagnosis_todo( self, diagnosis_id: int, content: str, remind_time: int ) -> dict[str, Any]: return { "id": 7205, "diagnosis_id": diagnosis_id, "content": content, "remind_time": remind_time, } def cancel_diagnosis_todo(self, todo_id: int) -> dict[str, Any]: return {"id": todo_id, "status": 2} def add_doctor_note(self, diagnosis_id: int, content: str = "", **_kwargs: Any) -> Any: return {"id": 7206, "diagnosis_id": diagnosis_id, "content": content} def delete_doctor_note_image(self, note_id: int, image_type: str, image_path: str) -> Any: return {"note_id": note_id, "image_type": image_type, "image_path": image_path} def upload_material(self, path: str, material_type: str) -> str: return f"https://media.example.invalid/{material_type}/{Path(path).name}" def create_prescription(self, prescription: dict[str, Any]) -> Any: return {"id": 7207, **prescription} def set_revisit_slot_start_offset(self, diagnosis_id: int, offset: int) -> Any: return {"id": diagnosis_id, "revisit_slot_start_offset": offset} def list_call_records(self, diagnosis_id: int) -> list[dict[str, Any]]: return [ { "id": 7208, "diagnosis_id": diagnosis_id, "recording_urls_list": [ "https://media.example.invalid/replay.mp4", "https://media.example.invalid/replay-backup.m3u8", "https://media.example.invalid/replay.webm", ], "start_time_text": "2026-08-10 09:10:00", "end_time_text": "2026-08-10 09:22:00", "call_type_text": "视频", "room_id": "demo-room-501", "duration_text": "12分00秒", "status_text": "已结束", "recording_status_text": "录制完成", } ] def upload_call_recording( self, path: str, diagnosis_id: int, *, call_record_id: int | None = None ) -> dict[str, Any]: return { "diagnosis_id": diagnosis_id, "call_record_id": call_record_id, "file_url": Path(path).name, } def list_im_chat_messages( self, diagnosis_id: int, *, only_archived: bool = True ) -> dict[str, Any]: del diagnosis_id return { "only_archived": only_archived, "lists": [ { "msg_id": "chat-1", "msg_type": "text", "text": "今天空腹血糖 5.8。", "is_from_doctor": False, "time": "2026-08-10 08:30", }, { "msg_id": "chat-2", "msg_type": "image", "image_url": "https://media.example.invalid/blood.jpg", "is_from_doctor": False, "time": "2026-08-10 08:31", }, { "msg_id": "chat-3", "msg_type": "file", "file_url": "https://media.example.invalid/report.pdf", "file_name": "复查报告.pdf", "is_from_doctor": True, "from_staff_name": "陈医生", "time": "2026-08-10 08:40", }, ], } def sync_im_chat_messages(self, diagnosis_id: int) -> dict[str, Any]: return {"diagnosis_id": diagnosis_id, "queued": True} class EmptyScreenshotRepository(ScreenshotRepository): def get_doctor_notes(self, diagnosis_id: int) -> list[dict[str, Any]]: del diagnosis_id return [] def list_prescriptions_by_diagnosis(self, diagnosis_id: int) -> list[dict[str, Any]]: del diagnosis_id return [] def list_prescription_orders(self, **_kwargs: Any) -> dict[str, Any]: return {"lists": [], "count": 0} class FailingScreenshotRepository(ScreenshotRepository): def get_diagnosis_detail(self, diagnosis_id: int, **_kwargs: Any) -> dict[str, Any]: del diagnosis_id raise RuntimeError("诊单详情加载失败,请检查网络后重试") 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) except Exception as error: if on_error: on_error(error) else: if on_success: on_success(result) finally: if on_finished: on_finished() return object() def render() -> list[Path]: app = QApplication.instance() or QApplication([]) font_path = Path("C:/Windows/Fonts/msyh.ttc") if font_path.is_file(): font_id = QFontDatabase.addApplicationFont(str(font_path)) families = QFontDatabase.applicationFontFamilies(font_id) if families: app.setFont(QFont(families[0], 9)) diagnosis_module.run_async = _run_immediately root = Path(__file__).resolve().parents[1] output = root / "artifacts" / "diagnosis_visual" output.mkdir(parents=True, exist_ok=True) rendered: list[Path] = [] for width, height in ((1024, 640), (1440, 900)): for mode in ("readonly", "edit", "viewonly"): dialog = DiagnosisDialog( ScreenshotRepository(locked=mode == "viewonly"), permissions=PermissionSet(["*"]), ) dialog.resize(width, height) if mode == "readonly": dialog.open_for(501, editable=False) elif mode == "edit": dialog.open_for(501, editable=True) else: dialog.open_view_only(501) for _ in range(8): app.processEvents() if mode == "readonly": dialog.readonly_scroll.verticalScrollBar().setValue(0) else: dialog.tabs.setCurrentIndex(0) app.processEvents() path = output / f"diagnosis_{mode}_{width}x{height}.png" if not dialog.grab().save(str(path), "PNG"): raise RuntimeError(f"failed to save {path}") rendered.append(path) dialog.close() app.processEvents() def save_state(dialog: DiagnosisDialog, name: str, *, close: bool = True) -> None: for _ in range(8): app.processEvents() path = output / f"diagnosis_state_{name}_1024x640.png" if not dialog.grab().save(str(path), "PNG"): raise RuntimeError(f"failed to save {path}") rendered.append(path) if close: dialog.close() app.processEvents() def save_widget(widget: Any, name: str) -> None: widget.show() for _ in range(6): app.processEvents() path = output / f"diagnosis_state_{name}.png" if not widget.grab().save(str(path), "PNG"): raise RuntimeError(f"failed to save {path}") rendered.append(path) widget.close() app.processEvents() pending: list[dict[str, Any]] = [] def queue_async(function: Any, **options: Any) -> object: pending.append({"function": function, **options}) return object() diagnosis_module.run_async = queue_async loading = DiagnosisDialog(ScreenshotRepository(locked=False), permissions=PermissionSet(["*"])) loading.resize(1024, 640) loading.open_for(501, editable=True) save_state(loading, "loading") diagnosis_module.run_async = _run_immediately error = DiagnosisDialog( FailingScreenshotRepository(locked=False), permissions=PermissionSet(["*"]) ) error.resize(1024, 640) error.open_for(501, editable=True, seed=_detail(locked=False)) save_state(error, "error") empty = DiagnosisDialog( EmptyScreenshotRepository(locked=False), permissions=PermissionSet(["*"]) ) empty.resize(1024, 640) empty.open_for(501, editable=True) empty_index = next( index for index in range(empty.tabs.count()) if empty.tabs.tabBar().tabData(index) == "prescription" ) empty.tabs.setCurrentIndex(empty_index) save_state(empty, "empty") permission = DiagnosisDialog( ScreenshotRepository(locked=True), permissions=PermissionSet(["tcm.diagnosis/readonlyDetail"]), ) permission.resize(1024, 640) permission.open_view_only(501) save_state(permission, "permission") daily = DiagnosisDialog(ScreenshotRepository(locked=False), permissions=PermissionSet(["*"])) daily.resize(1024, 640) daily.open_for(501, editable=True) daily_index = next( index for index in range(daily.tabs.count()) if daily.tabs.tabBar().tabData(index) == "daily" ) daily.tabs.setCurrentIndex(daily_index) save_state(daily, "daily", close=False) daily_page = daily._tab_pages["daily"] daily_page.verticalScrollBar().setValue(daily_page.verticalScrollBar().maximum()) save_state(daily, "daily_lower") focus = DiagnosisDialog(ScreenshotRepository(locked=False), permissions=PermissionSet(["*"])) focus.resize(1024, 640) focus.open_for(501, editable=True) focus.edit_fields["patient_name"].setFocus() save_state(focus, "focus") save_states = DiagnosisDialog( ScreenshotRepository(locked=False), permissions=PermissionSet(["*"]) ) save_states.resize(1024, 640) save_states.open_for(501, editable=True) save_states.save_button.set_state("loading") save_state(save_states, "save_loading", close=False) save_states.save_button.set_state("success") save_state(save_states, "save_success", close=False) save_states.save_button.set_state("error") save_state(save_states, "save_failure") for tab_key, state_name in ( ("notes", "notes_actions"), ("video", "video_replay"), ("chat", "chat_archive"), ("orders", "order_offset"), ): dialog = DiagnosisDialog( ScreenshotRepository(locked=False), permissions=PermissionSet(["*"]) ) dialog.resize(1024, 640) dialog.open_for(501, editable=True) index = next( item for item in range(dialog.tabs.count()) if dialog.tabs.tabBar().tabData(item) == tab_key ) dialog.tabs.setCurrentIndex(index) save_state(dialog, state_name, close=False) if tab_key == "orders": order_detail = dialog._build_order_detail_dialog( dialog.repository.get_prescription_order(801), 801 ) save_widget(order_detail, "order_detail_640x540") elif tab_key == "video": video_table = dialog._table_registry["video"][1] video_table.horizontalScrollBar().setValue(video_table.horizontalScrollBar().maximum()) save_state(dialog, "video_upload_action", close=False) player = RecordingPlayerDialog( "https://media.example.invalid/replay.mp4", parent=dialog ) save_widget(player, "video_player_820x560") dialog.close() app.processEvents() daily_editor = DailyRecordEditorDialog( "blood", { "id": 6101, "diagnosis_id": 501, "patient_id": 1501, "record_date": "2026-08-10", "record_time": "08:20", "fasting_blood_sugar": 8.2, "postprandial_blood_sugar": 12.4, "systolic_pressure": 146, "diastolic_pressure": 92, "western_medicine": "二甲双胍", "remark": "继续观察餐后波动。", }, ) daily_editor.setStyleSheet(diagnosis_module.DIAGNOSIS_QSS) save_widget(daily_editor, "daily_blood_edit_650x620") prescription = PrescriptionEditorDialog( ScreenshotRepository(locked=False), { "diagnosis_id": 501, "appointment_id": 2501, "patient_name": "林晓岚", "gender": 0, "age": 34, "visit_no": "1K00002501", "tongue": "舌淡红、苔薄白", "pulse": "脉细", "clinical_diagnosis": "气阴两虚", "doctor_name": "陈医生", }, mode="add", current_user={"id": 1, "name": "陈医生"}, ) prescription.setObjectName("DiagnosisPrescriptionEditor") prescription.setStyleSheet(diagnosis_module.DIAGNOSIS_QSS) save_widget(prescription, "prescription_editor_920x780") return rendered if __name__ == "__main__": for path in render(): print(path)