update
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
Pop-up bei neuer Empfangs-Nachricht — Backup
|
||||
=============================================
|
||||
|
||||
Geaendert wurden (Stand nach Fix):
|
||||
- web/empfang.html
|
||||
- aza_empfang_webview.py
|
||||
|
||||
Nicht geaendert (zur Sicherheit mitgesichert):
|
||||
- basis14.py (Admin-Seite: das Auto-Open-Verhalten existiert bereits;
|
||||
_empfang_background_poll, _empfang_maybe_autopopup_from_poll,
|
||||
_empfang_open_webview_singleton, _empfang_webview_try_bring_to_front;
|
||||
gesteuert ueber Hauptmenue-Haekchen "Chat-Empfang"
|
||||
= self._autotext_data["empfang_auto_open"]).
|
||||
|
||||
Rollback (PowerShell, im Projektroot "AzA march 2026"):
|
||||
|
||||
$b = "backups_empfang_new_message_popup_fix_20260509_211902"
|
||||
Copy-Item -LiteralPath "$b\empfang.html" -Destination "web\empfang.html" -Force
|
||||
Copy-Item -LiteralPath "$b\aza_empfang_webview.py" -Destination "aza_empfang_webview.py" -Force
|
||||
|
||||
Was wurde fachlich geaendert
|
||||
---------------------------
|
||||
1) web/empfang.html
|
||||
- Neue Variablen: previousMsgIds (Set), _newMsgFlashTimer/_newMsgOrigTitle.
|
||||
- In loadMessages(): nach buildThreads() pro Tick echte neue Nachrichten
|
||||
ermitteln (Differenz zu previousMsgIds). Eigene werden ausgeschlossen
|
||||
ueber:
|
||||
(a) extras.sender_user_id === currentSession.user_id
|
||||
(b) Absender-Name beginnt mit currentSession.display_name
|
||||
(c) m.id === _dmV2State.lastSentMsgId
|
||||
(d) justSentReply
|
||||
Nur bei Vorhandensein eines fremden Treffers wird notifyNewIncomingMessage
|
||||
ausgeloest. Bei erstem Laden (previousMsgIds.size === 0) passiert nichts.
|
||||
- Reset auf Logout-Pfad (gleiche Stelle wie previousThreadIds).
|
||||
- notifyNewIncomingMessage:
|
||||
* Titel-Flash (visueller Hinweis im Browser-Tab),
|
||||
* window.focus() (best-effort),
|
||||
* window.pywebview.api.bring_to_front() (nur in der Hülle wirksam).
|
||||
- Stop-Flash bei document focus / visibility visible.
|
||||
- Es werden KEINE Chat-/Patientendaten geloggt; nur eine gekuerzte msg-id
|
||||
(8 Zeichen) als console.debug.
|
||||
|
||||
2) aza_empfang_webview.py
|
||||
- EmpfangWebviewApi.bring_to_front(): pywebview restore() + Win32
|
||||
FindWindowW("AzA-Empfang") + ShowWindow(SW_RESTORE) + SetForegroundWindow.
|
||||
- Idempotent, keine Datenverarbeitung.
|
||||
|
||||
Hinweis zum Admin-Seite-Verhalten (basis14.py)
|
||||
---------------------------------------------
|
||||
Damit beim Arzt/Admin die Empfang-Huelle bei neuen Empfangs-Nachrichten
|
||||
nach vorne kommt, muss im Hauptfenster das Haekchen "Chat-Empfang" gesetzt
|
||||
sein (Pref-Schluessel: empfang_auto_open). Der Mechanismus existiert
|
||||
bereits und wurde nicht angefasst.
|
||||
@@ -0,0 +1,178 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
AzA Empfang Web-Huelle: eigener Desktop-Prozess (pywebview).
|
||||
|
||||
Wird vom Desktop per subprocess gestartet, damit keine GUI-Kollision mit Tkinter entsteht.
|
||||
Argument: erste Start-URL (z.B. GET /empfang/shell/launch?token=...).
|
||||
|
||||
WebView2-Profil: pywebview nutzt standardmaessig ``private_mode=True`` (ephemeral) und
|
||||
speichert keine Site-Permissions zwischen Sessions. Für die Huelle setzen wir einen
|
||||
festen ``storage_path`` unter %APPDATA%\\AzA\\EmpfangWebView und ``private_mode=False``,
|
||||
damit z. B. Mikrofon-Erlaubnis für die App-URL persistiert (wie im normalen Edge-Profil),
|
||||
ohne den Systembrowser zu verwenden.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _empfang_shell_icon_path() -> str | None:
|
||||
"""Windows/pywebview: WinForms laedt Fenstericon aus .ico neben diesem Skript."""
|
||||
p = Path(__file__).resolve().parent / "logo.ico"
|
||||
return str(p) if p.is_file() else None
|
||||
|
||||
|
||||
def _empfang_webview_storage_dir() -> str:
|
||||
"""Eigener User-Data-Ordner, isoliert vom normalen Browser."""
|
||||
appdata = (os.environ.get("APPDATA") or "").strip()
|
||||
if appdata:
|
||||
return str(Path(appdata) / "AzA" / "EmpfangWebView")
|
||||
return str(Path.home() / ".aza_empfang_webview")
|
||||
|
||||
|
||||
class EmpfangWebviewApi:
|
||||
"""pywebview JS-API: globaler Patienten-Pinsel (Desktop), ohne Server/URL."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._window = None
|
||||
|
||||
def bind_window(self, window) -> None:
|
||||
self._window = window
|
||||
|
||||
@staticmethod
|
||||
def _pinsel_eval_js(win, js_code: str, label: str) -> None:
|
||||
"""Lokal nur stdout/stderr; keine Clipboard-/Patient-Inhalte loggen."""
|
||||
try:
|
||||
win.evaluate_js(js_code)
|
||||
print(f"[pinsel] evaluate_js called ({label})", flush=True)
|
||||
except Exception as exc:
|
||||
print(
|
||||
f"[pinsel] evaluate_js failed ({label}): {type(exc).__name__}: {exc}",
|
||||
flush=True,
|
||||
)
|
||||
try:
|
||||
win.evaluate_js(
|
||||
"try{if(window.shellPinselDiagSet)window.shellPinselDiagSet('Pinsel: Fehler: evaluate_js_failed');}catch(_e){}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def start_patient_nr_pick(self) -> dict:
|
||||
from aza_empfang_smart_pick import (
|
||||
compact_and_validate_patient_nr_pick_text,
|
||||
start_global_patient_nr_pick,
|
||||
)
|
||||
|
||||
print("[pinsel] api called", flush=True)
|
||||
win = self._window
|
||||
print(f"[pinsel] window bound {win is not None}", flush=True)
|
||||
if win is None:
|
||||
return {"ok": False, "reason": "no_window"}
|
||||
|
||||
def js_reset() -> None:
|
||||
self._pinsel_eval_js(
|
||||
win,
|
||||
"try{window.shellFinalizeGlobalPickMechanismUi&&window.shellFinalizeGlobalPickMechanismUi();}catch(_e){}",
|
||||
"shellFinalizeGlobalPickMechanismUi",
|
||||
)
|
||||
|
||||
def on_clipboard(raw: str) -> None:
|
||||
nn = compact_and_validate_patient_nr_pick_text(raw)
|
||||
if not nn:
|
||||
print("[pinsel] validated_rejected", flush=True)
|
||||
self._pinsel_eval_js(
|
||||
win,
|
||||
"try{window.shellReceivePatientNrPickInvalid&&window.shellReceivePatientNrPickInvalid();}catch(_e){}",
|
||||
"shellReceivePatientNrPickInvalid",
|
||||
)
|
||||
return
|
||||
print("[pinsel] validated_ok", flush=True)
|
||||
self._pinsel_eval_js(
|
||||
win,
|
||||
"try{window.shellReceivePatientNrFromDesktop(%s);}catch(_e){}"
|
||||
% json.dumps(nn),
|
||||
"shellReceivePatientNrFromDesktop",
|
||||
)
|
||||
|
||||
print("[pinsel] starting_global_pick", flush=True)
|
||||
rc = start_global_patient_nr_pick(
|
||||
on_clipboard_pick=on_clipboard,
|
||||
reset_pick_ui=js_reset,
|
||||
owner_tk=None,
|
||||
schedule_ui=None,
|
||||
diagnostics=True,
|
||||
)
|
||||
print(f"[pinsel] start_global_patient_nr_pick rc={rc}", flush=True)
|
||||
if rc == "cancelled":
|
||||
return {"ok": True, "cancelled": True}
|
||||
if rc == "unavailable":
|
||||
self._pinsel_eval_js(
|
||||
win,
|
||||
"try{window.shellGlobalPinselResetUi&&window.shellGlobalPinselResetUi();}catch(_e){}",
|
||||
"shellGlobalPinselResetUi(unavailable)",
|
||||
)
|
||||
return {"ok": False, "reason": "no_input_hook"}
|
||||
return {"ok": True}
|
||||
|
||||
def cancel_patient_nr_pick(self) -> dict:
|
||||
from aza_empfang_smart_pick import stop_global_patient_nr_pick
|
||||
|
||||
print("[pinsel] cancel_patient_nr_pick called", flush=True)
|
||||
stop_global_patient_nr_pick()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
argv = argv if argv is not None else sys.argv[1:]
|
||||
if not argv or not argv[0].strip():
|
||||
print(
|
||||
"Usage:\n"
|
||||
' python aza_empfang_webview.py "https://host/empfang/shell/launch?token=..."',
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
url = argv[0].strip()
|
||||
w = int(argv[1]) if len(argv) > 1 and str(argv[1]).isdigit() else 1180
|
||||
h = int(argv[2]) if len(argv) > 2 and str(argv[2]).isdigit() else 820
|
||||
|
||||
storage_path = _empfang_webview_storage_dir()
|
||||
try:
|
||||
Path(storage_path).mkdir(parents=True, exist_ok=True)
|
||||
except OSError as exc:
|
||||
print(
|
||||
f"WebView-Profil-Ordner konnte nicht angelegt werden ({storage_path}): {exc}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 13
|
||||
|
||||
try:
|
||||
import webview # noqa: WPS433 (runtime dependency)
|
||||
except ImportError:
|
||||
print(
|
||||
"pywebview fehlt. Bitte installieren:\n"
|
||||
" pip install pywebview>=5",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 11
|
||||
|
||||
api = EmpfangWebviewApi()
|
||||
start_kw = {"storage_path": storage_path, "private_mode": False}
|
||||
_ico = _empfang_shell_icon_path()
|
||||
if _ico:
|
||||
start_kw["icon"] = _ico
|
||||
try:
|
||||
win = webview.create_window("AzA-Empfang", url, width=w, height=h, js_api=api)
|
||||
api.bind_window(win)
|
||||
webview.start(**start_kw)
|
||||
return 0
|
||||
except Exception as exc:
|
||||
print(str(exc), file=sys.stderr)
|
||||
return 12
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user