scrub / scrub_key_import.py
solidprivacy-nl
Add Scrub Key import helper
470b7dd
Raw
History Blame
4.51 kB
"""Pure Scrub Key import/reload helpers for SolidPrivacy Scrub.
This module prepares v13.2 import/reload without touching the Streamlit UI. It
parses a previously downloaded Scrub Key JSON string, validates it with the v13.0
model, and normalises the mappings into review-table-like rows for later UI and
reinsert work.
The helpers deliberately avoid file-system writes, server-side storage, cloud
processing and UI side effects.
"""
from __future__ import annotations
import json
from collections.abc import Mapping
from typing import Any
from scrub_key import scrub_key_from_json, validate_scrub_key
IMPORT_PRIVACY_WARNING = (
"Een Scrub Key maakt vervangen waarden lokaal herleidbaar. "
"Gebruik dit bestand alleen lokaal en deel het niet met AI-diensten of derden "
"tenzij dat bewust en toegestaan is."
)
def _text(value: Any) -> str:
return str(value or "").strip()
def _validation_errors_for_text(json_text: Any) -> tuple[dict[str, Any] | None, list[str]]:
"""Parse and validate Scrub Key JSON text with safe user-facing errors."""
if not isinstance(json_text, str) or _text(json_text) == "":
return None, ["Scrub Key JSON ontbreekt of is leeg."]
try:
scrub_key = scrub_key_from_json(json_text)
except json.JSONDecodeError:
return None, ["Scrub Key JSON is geen geldige JSON."]
except ValueError as exc:
return None, [f"Scrub Key JSON heeft een ongeldig hoofdformaat: {exc}"]
validation_issues = validate_scrub_key(scrub_key)
if validation_issues:
return scrub_key, [f"Scrub Key is niet geldig: {issue}" for issue in validation_issues]
return scrub_key, []
def validate_scrub_key_import_text(json_text: Any) -> list[str]:
"""Return safe validation errors for a candidate Scrub Key JSON string."""
_scrub_key, errors = _validation_errors_for_text(json_text)
return errors
def normalise_scrub_key_items(scrub_key: Mapping[str, Any]) -> list[dict[str, Any]]:
"""Convert Scrub Key items to review-table-like mapping rows.
The normalised rows are intentionally simple and deterministic. They can be
reused later by UI import/reload and deterministic reinsert helpers.
"""
items = scrub_key.get("items")
if not isinstance(items, list):
return []
rows: list[dict[str, Any]] = []
for item in items:
if not isinstance(item, Mapping):
continue
rows.append(
{
"find": _text(item.get("original_value")),
"replace_with": _text(item.get("placeholder")),
"original_value": _text(item.get("original_value")),
"placeholder": _text(item.get("placeholder")),
"entity_type": _text(item.get("entity_type")),
"type_label": _text(item.get("type_label")),
"source": _text(item.get("source")),
"review_status": _text(item.get("review_status")),
"include": item.get("include_state") == "included",
"include_state": _text(item.get("include_state")),
"timestamp": _text(item.get("timestamp")),
"document_label": _text(item.get("document_label"))
or _text(scrub_key.get("document_label")),
}
)
return rows
def build_scrub_key_import_result(json_text: Any) -> dict[str, Any]:
"""Parse, validate and normalise a Scrub Key JSON string.
The return shape is deliberately UI-friendly while staying pure and
side-effect free. `ok=True` means the Scrub Key is structurally valid and the
`mapping_rows` can be used by later import/reload or reinsert work.
"""
scrub_key, errors = _validation_errors_for_text(json_text)
if errors:
return {
"ok": False,
"errors": errors,
"warnings": [IMPORT_PRIVACY_WARNING],
"scrub_key": scrub_key,
"mapping_rows": [],
"item_count": 0,
"reversible": False,
}
assert scrub_key is not None
mapping_rows = normalise_scrub_key_items(scrub_key)
return {
"ok": True,
"errors": [],
"warnings": [IMPORT_PRIVACY_WARNING],
"scrub_key": scrub_key,
"mapping_rows": mapping_rows,
"item_count": len(mapping_rows),
"reversible": scrub_key.get("reversible") is True,
"privacy_model": scrub_key.get("privacy_model"),
"document_label": scrub_key.get("document_label"),
}