"""Service Conversations (médical, cœur RGPD). Rapatriement des conversations cliniques : `conversations_export` (RPC file-drop NDJSON sur le device) → `adb pull` → archive **chiffrée** dans le store → **`export_purge` immédiat** (l'export est PLAINTEXT MVP côté app §écarts, donc ne doit pas traîner sur le stockage device). Puis lecture / export JSON / purge RGPD depuis l'archive centrale. """ from __future__ import annotations import json import os import tempfile from typing import Any from kazeia_central.core.adb import Adb from kazeia_central.core.provider import ProviderClient from kazeia_central.core.store import Store def collect(adb: Adb, store: Store, serial: str, *, profile_id: str | None = None, since: int | None = None, until: int | None = None, now: int, actor: str | None = None) -> dict[str, Any]: """Exporte (device) → pull → archive chiffrée (dédoublonnée) → purge device immédiate.""" c = ProviderClient(adb, serial) res = c.conversations_export(profile_id=profile_id, since=since, until=until) path = res.get("path") exported = int(res.get("count") or 0) archived = 0 if path and exported: with tempfile.TemporaryDirectory() as td: local = os.path.join(td, "export.ndjson") adb.pull(path, local, serial=serial) turns = [] with open(local, encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue obj = json.loads(line) if "_meta" in obj: # ligne d'en-tête {"_meta": {...}} continue turns.append(obj) archived = store.archive_turns(serial, turns, now=now) # PLAINTEXT MVP : purger le fichier device tout de suite (pas de PII qui traîne). purged = False if path: try: c.export_purge(path) purged = True except Exception: pass store.audit("conversations_collect", actor=actor, target=serial, detail={"exported": exported, "archived": archived, "purged": purged}, now=now) return {"exported": exported, "archived": archived, "purged": purged} def sessions(store: Store, serial: str) -> list[dict[str, Any]]: return store.archived_sessions(serial) def session_turns(store: Store, serial: str, session_id: str) -> list[dict[str, Any]]: return store.session_turns(serial, session_id) def export_session(store: Store, serial: str, session_id: str) -> dict[str, Any]: """Bundle JSON exportable d'une session (déchiffré).""" turns = store.session_turns(serial, session_id) return {"serial": serial, "session_id": session_id, "turn_count": len(turns), "profile_id": turns[0]["profile_id"] if turns else None, "turns": turns} def purge(store: Store, serial: str, *, session_id: str | None = None, profile_id: str | None = None, before: int | None = None, now: int, actor: str | None = None) -> dict[str, Any]: n = store.purge_turns(serial, session_id=session_id, profile_id=profile_id, before=before) store.audit("conversations_purge", actor=actor, target=serial, detail={"session_id": session_id, "profile_id": profile_id, "before": before, "deleted": n}, now=now) return {"deleted": n}