"""Render the Notes, Chat, and Daily-lower parity states without external network.""" from __future__ import annotations import os from pathlib import Path from typing import Any os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtCore import QBuffer, QIODevice, QPointF, Qt, QTimer from PySide6.QtGui import QColor, QFont, QFontDatabase, QImage, QPainter, QPen from PySide6.QtWidgets import QApplication from render_diagnosis_detail_visual import ScreenshotRepository, _run_immediately from doctor_workstation.core import PermissionSet from doctor_workstation.ui import diagnosis_drawer from doctor_workstation.ui.dialogs import diagnosis as diagnosis_module from doctor_workstation.ui.dialogs.diagnosis import DiagnosisDialog class MediaScreenshotRepository(ScreenshotRepository): """Use the established artifact fixture with production-shaped media URLs.""" def get_doctor_notes(self, diagnosis_id: int) -> list[dict[str, Any]]: notes = super().get_doctor_notes(diagnosis_id) notes[0]["tongue_images"] = [ {"url": "https://media.example.invalid/diagnosis/501/tongue-7001.jpg"} ] return notes def _image_bytes(kind: str) -> bytes: if kind == "tongue": image = QImage(240, 180, QImage.Format.Format_ARGB32) image.fill(QColor("#F4E3D8")) painter = QPainter(image) painter.setRenderHint(QPainter.RenderHint.Antialiasing) painter.setPen(QPen(QColor("#D1A08F"), 3)) painter.setBrush(QColor("#D77F83")) painter.drawEllipse(52, 18, 136, 162) painter.setPen(QPen(QColor("#B75F65"), 2)) painter.drawLine(120, 42, 120, 148) painter.setPen(QPen(QColor("#F7D8D2"), 10, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) painter.drawLine(91, 55, 103, 132) painter.drawLine(149, 55, 137, 132) painter.end() else: image = QImage(640, 360, QImage.Format.Format_ARGB32) image.fill(QColor("#F8FAFC")) painter = QPainter(image) painter.setRenderHint(QPainter.RenderHint.Antialiasing) painter.setPen(QPen(QColor("#DCE3EC"), 1)) for x in range(48, 610, 70): painter.drawLine(x, 36, x, 316) for y in range(56, 310, 52): painter.drawLine(42, y, 606, y) points = [ QPointF(48, 250), QPointF(125, 226), QPointF(205, 238), QPointF(285, 168), QPointF(365, 190), QPointF(445, 112), QPointF(525, 142), QPointF(602, 82), ] painter.setPen(QPen(QColor("#0F766E"), 7, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap)) for start, end in zip(points, points[1:], strict=False): painter.drawLine(start, end) painter.setPen(QPen(QColor("#FFFFFF"), 3)) painter.setBrush(QColor("#0F766E")) for point in points: painter.drawEllipse(point, 8, 8) painter.end() buffer = QBuffer() if not buffer.open(QIODevice.OpenModeFlag.WriteOnly) or not image.save(buffer, "PNG"): raise RuntimeError("failed to encode deterministic visual image") return bytes(buffer.data()) TONGUE_IMAGE = _image_bytes("tongue") CHAT_IMAGE = _image_bytes("chat") def _offline_remote_load(self: diagnosis_drawer._RemoteImageButton, source: str) -> None: """Preserve asynchronous completion while replacing only external transport.""" self._source = str(source).strip() generation = self._invalidate_request() self.setToolTip(self._source) self._show_loading() payload = TONGUE_IMAGE if self.objectName() == "DiagnosisTongueThumb" else CHAT_IMAGE QTimer.singleShot(0, lambda: self._apply_payload(payload, generation)) def _prepare_app() -> QApplication: app = QApplication.instance() or QApplication([]) app.setFont(QFont("Microsoft YaHei", 9)) 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)) return app def _open_tab(app: QApplication, key: str) -> DiagnosisDialog: dialog = DiagnosisDialog( MediaScreenshotRepository(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) == key ) dialog.tabs.setCurrentIndex(index) for _ in range(10): app.processEvents() return dialog def render() -> list[Path]: app = _prepare_app() diagnosis_module.run_async = _run_immediately diagnosis_drawer._RemoteImageButton.load_url = _offline_remote_load output = Path(__file__).resolve().parents[1] / "artifacts" / "diagnosis_visual" output.mkdir(parents=True, exist_ok=True) rendered: list[Path] = [] for tab_key, state_name in (("notes", "notes_actions"), ("chat", "chat_archive")): dialog = _open_tab(app, tab_key) path = output / f"diagnosis_state_{state_name}_1024x640.png" if not dialog.grab().save(str(path), "PNG"): raise RuntimeError(f"failed to save {path}") rendered.append(path) dialog.close() app.processEvents() daily = _open_tab(app, "daily") page = daily._tab_pages["daily"] page.verticalScrollBar().setValue(page.verticalScrollBar().maximum()) for _ in range(4): app.processEvents() daily_path = output / "diagnosis_state_daily_lower_1024x640.png" if not daily.grab().save(str(daily_path), "PNG"): raise RuntimeError(f"failed to save {daily_path}") rendered.append(daily_path) daily.close() app.processEvents() return rendered if __name__ == "__main__": for rendered_path in render(): print(rendered_path)