This commit is contained in:
2026-06-13 22:47:31 +02:00
parent add3da5177
commit d1446fc452
8032 changed files with 2650751 additions and 1551 deletions

View File

@@ -29,9 +29,13 @@ CATEGORY_TO_STORE = {
"person": "personen",
"correction": "begriffe",
"practice_term": "begriffe",
"diktatblock": "diktatblocks",
"signature": "signatur",
}
STORE_TO_CATEGORY = {v: k for k, v in CATEGORY_TO_STORE.items()}
# Mehrere UI-Kategorien koennen auf dieselbe Store-Kategorie zeigen.
STORE_TO_CATEGORY["begriffe"] = "medical_term"
CATEGORY_LABELS = {
"medication": "Medikamente",
@@ -42,10 +46,13 @@ CATEGORY_LABELS = {
"correction": "Korrekturen",
"practice_term": "Praxisbegriffe",
"doku_prompt": "Doku-Prompts",
"diktatblock": "Diktatblock",
"signature": "Signatur",
}
PRIVATE_UI_CATEGORIES = (
"medication", "medical_term", "person", "correction",
"diktatblock", "signature",
)
PUBLIC_UI_CATEGORIES = ("medication", "medical_term", "correction")
@@ -170,6 +177,52 @@ def save_public_cache(items: List[Dict[str, Any]]) -> None:
pass
def merge_server_items_into_public_cache(server_items: List[Dict[str, Any]]) -> bool:
"""Fügt/aktualisiert serverseitige Public-Einträge im lokalen Cache (ohne kuratierte AzA-Terms)."""
if not server_items:
return False
cached = _load_public_cache()
by_id: Dict[str, Dict[str, Any]] = {}
for it in cached:
if not isinstance(it, dict):
continue
iid = str(it.get("item_id") or it.get("id") or "").strip()
if iid:
by_id[iid] = it
changed = False
for it in server_items:
if not isinstance(it, dict):
continue
iid = str(it.get("item_id") or it.get("id") or "").strip()
if not iid:
continue
entry = dict(it)
entry.setdefault("scope", "public")
entry.setdefault("source", "doctor_published")
prev = by_id.get(iid)
if prev and int(prev.get("revision") or 0) > int(entry.get("revision") or 0):
continue
by_id[iid] = entry
changed = True
if changed:
save_public_cache(list(by_id.values()))
return changed
def sync_public_library_from_server(app) -> Optional[str]:
"""Lädt Public-Bibliothek vom Server in den lokalen Cache. Fehler-Tags wie Doku-Sync."""
try:
from aza_bibliothek_sync import fetch_public_library_from_server_result
items, err = fetch_public_library_from_server_result(app)
if err:
return err
if items:
merge_server_items_into_public_cache(items)
return None
except Exception:
return "__CONN_ERROR__"
def adopt_public_entry(entry: dict) -> Tuple[bool, str]:
"""Übernimmt öffentlichen Eintrag als private Kopie in korrekturen.json."""
from aza_persistence import load_korrekturen, save_korrekturen, add_korrektur_to_bibliothek
@@ -363,3 +416,82 @@ def serialize_term_for_sync(entry: dict) -> dict:
"source": entry.get("source", ""),
},
}
def _diktatblock_aliases(korrekturen: dict) -> dict[str, list[str]]:
meta = korrekturen.get("_metadata") if isinstance(korrekturen.get("_metadata"), dict) else {}
raw = meta.get("diktatblock_aliases") if isinstance(meta, dict) else {}
out: dict[str, list[str]] = {}
if not isinstance(raw, dict):
return out
for name, aliases in raw.items():
if not name:
continue
if isinstance(aliases, str):
aliases = [aliases]
if isinstance(aliases, (list, tuple, set)):
out[str(name)] = [str(a).strip() for a in aliases if str(a).strip()]
return out
def apply_diktat_block_commands(text: str, korrekturen: dict | None = None) -> tuple[str, list[tuple[str, str]]]:
"""Ersetzt gesprochene ``Block <Name>``-Befehle durch Bibliotheksinhalte (exakt, nicht rekursiv)."""
from aza_persistence import load_korrekturen
data = korrekturen if isinstance(korrekturen, dict) else load_korrekturen()
blocks = data.get("diktatblocks") if isinstance(data.get("diktatblocks"), dict) else {}
inactive = data.get("_inactive") if isinstance(data.get("_inactive"), dict) else {}
inact_set = set(inactive.get("diktatblocks") or [])
aliases = _diktatblock_aliases(data)
result = text or ""
applied: list[tuple[str, str]] = []
def _replace_block(name: str, content: str) -> None:
nonlocal result
if not name or not content or name in inact_set:
return
pat = r"(?<!\w)Block\s+" + re.escape(name) + r"(?!\w)"
if re.search(pat, result, flags=re.IGNORECASE):
result = re.sub(pat, content, result, flags=re.IGNORECASE)
applied.append((f"Block {name}", content))
for name, content in blocks.items():
if not name or str(name).startswith("_"):
continue
_replace_block(str(name), str(content or ""))
for alias in aliases.get(str(name), []):
pat = r"(?<!\w)Block\s+" + re.escape(alias) + r"(?!\w)"
if re.search(pat, result, flags=re.IGNORECASE):
block_content = str(blocks.get(name) or "")
if block_content:
result = re.sub(pat, block_content, result, flags=re.IGNORECASE)
applied.append((f"Block {alias}", block_content))
return result, applied
def resolve_signature_text(
korrekturen: dict | None = None,
*,
user_profile: dict | None = None,
) -> str:
"""Signatur fuer Diktat: Bibliothek Signatur > Profil > display_name (kein erfundener Titel)."""
from aza_persistence import load_korrekturen, load_user_profile
data = korrekturen if isinstance(korrekturen, dict) else load_korrekturen()
prof = user_profile if isinstance(user_profile, dict) else load_user_profile()
sig_map = data.get("signatur") if isinstance(data.get("signatur"), dict) else {}
inactive = data.get("_inactive") if isinstance(data.get("_inactive"), dict) else {}
inact_set = set(inactive.get("signatur") or [])
for _key, val in sig_map.items():
if not _key or _key in inact_set:
continue
s = str(val or "").strip()
if s:
return s
for fld in ("professional_name", "signatur_name", "empfang_display_name"):
s = str((prof or {}).get(fld) or "").strip()
if s:
return s
return str((prof or {}).get("empfang_display_name") or (prof or {}).get("name") or "").strip()