File size: 4,506 Bytes
470b7dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""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"),
    }