81 lines
3.4 KiB
Python
81 lines
3.4 KiB
Python
"""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}
|