Kansho/backend/profile_documents.py
2026-08-25 13:57:23 +02:00

98 lines
3.6 KiB
Python

"""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}