"""Portable JSON documents for Writing and Interaction profiles. Not a journal backup.""" from __future__ import annotations from datetime import datetime, timezone from dialogue_store import StoreError FORMAT_VERSION = 1 KIND_WRITING = "kansho.writing_profile" KIND_INTERACTION = "kansho.interaction_profile" KIND_BUNDLE = "kansho.profiles" ORIGIN_WRITING = {"inferred", "manual", "imported", "accepted_suggestion", "initial_build"} ORIGIN_INTERACTION = {"explicit", "manual", "accepted_suggestion"} def exported_at() -> str: return datetime.now(timezone.utc).replace(microsecond=0).isoformat() def as_document(raw) -> dict: if isinstance(raw, str): import json try: raw = json.loads(raw) except json.JSONDecodeError as exc: raise StoreError("invalid_document", "JSON ist ungültig.") from exc if not isinstance(raw, dict): raise StoreError("invalid_document", "Profil-Dokument muss ein JSON-Objekt sein.") version = raw.get("format_version", FORMAT_VERSION) try: version = int(version) except (TypeError, ValueError) as exc: raise StoreError("unsupported_format", "format_version fehlt oder ist ungültig.") from exc if version != FORMAT_VERSION: raise StoreError("unsupported_format", f"format_version {version} wird nicht unterstützt.") return raw def writing_payload(document: dict) -> dict: data = as_document(document) kind = (data.get("kind") or "").strip() if kind == KIND_BUNDLE: nested = data.get("writing") if not isinstance(nested, dict): raise StoreError("invalid_document", "Sammeldokument enthält kein Writing Profile.") return nested if kind != KIND_WRITING: raise StoreError("invalid_document", "Das ist kein Writing-Profile-Dokument.") return data def interaction_payload(document: dict) -> dict: data = as_document(document) kind = (data.get("kind") or "").strip() if kind == KIND_BUNDLE: nested = data.get("interaction") if not isinstance(nested, dict): raise StoreError("invalid_document", "Sammeldokument enthält kein Interaction Profile.") return nested if kind != KIND_INTERACTION: raise StoreError("invalid_document", "Das ist kein Interaction-Profile-Dokument.") return data def export_bundle(profile_id: str) -> dict: from interaction_profile_store import export_document as export_interaction from writing_profile_store import export_document as export_writing writing = export_writing(profile_id) interaction = export_interaction(profile_id) writing.pop("kind", None) writing.pop("format_version", None) writing.pop("exported_at", None) interaction.pop("kind", None) interaction.pop("format_version", None) interaction.pop("exported_at", None) return { "kind": KIND_BUNDLE, "format_version": FORMAT_VERSION, "exported_at": exported_at(), "writing": writing, "interaction": interaction, } def restore_bundle(profile_id: str, document: dict) -> dict: data = as_document(document) kind = (data.get("kind") or "").strip() if kind != KIND_BUNDLE: raise StoreError("invalid_document", "Das ist kein Sammeldokument beider Profile.") from interaction_profile_store import restore_document as restore_interaction from writing_profile_store import restore_document as restore_writing writing = restore_writing(profile_id, data) interaction = restore_interaction(profile_id, data) return {"writing": writing, "interaction": interaction}