# Copyright (c) 2025 Patrick Motsch # All rights reserved. """Neutralizer models: DataNeutraliserConfig and DataNeutralizerAttributes.""" import uuid from enum import Enum from typing import Optional from pydantic import BaseModel, Field from modules.datamodels.datamodelBase import PowerOnModel from modules.shared.attributeUtils import registerModelLabels class DataScope(str, Enum): PERSONAL = "personal" FEATURE_INSTANCE = "featureInstance" MANDATE = "mandate" GLOBAL = "global" class DataNeutraliserConfig(PowerOnModel): id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the configuration", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) mandateId: str = Field(description="ID of the mandate this configuration belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) featureInstanceId: str = Field(description="ID of the feature instance this configuration belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) userId: str = Field(description="ID of the user who created this configuration", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) enabled: bool = Field(default=True, description="Whether data neutralization is enabled", json_schema_extra={"frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False}) scope: str = Field(default="personal", description="Data visibility scope: personal, featureInstance, mandate, global", json_schema_extra={"frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": [ {"value": "personal", "label": {"en": "Personal", "de": "Persönlich"}}, {"value": "featureInstance", "label": {"en": "Feature Instance", "de": "Feature-Instanz"}}, {"value": "mandate", "label": {"en": "Mandate", "de": "Mandant"}}, {"value": "global", "label": {"en": "Global", "de": "Global"}}, ]}) neutralizationStatus: str = Field(default="not_required", description="Status of neutralization: pending, completed, failed, not_required", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) namesToParse: str = Field(default="", description="Multiline list of names to parse for neutralization", json_schema_extra={"frontend_type": "textarea", "frontend_readonly": False, "frontend_required": False}) sharepointSourcePath: str = Field(default="", description="SharePoint path to read files for neutralization", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False}) sharepointTargetPath: str = Field(default="", description="SharePoint path to store neutralized files", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False}) registerModelLabels( "DataNeutraliserConfig", {"en": "Data Neutralization Config", "fr": "Configuration de neutralisation des données"}, { "id": {"en": "ID", "fr": "ID"}, "mandateId": {"en": "Mandate ID", "fr": "ID de mandat"}, "featureInstanceId": {"en": "Feature Instance ID", "fr": "ID de l'instance de fonctionnalité"}, "userId": {"en": "User ID", "fr": "ID utilisateur"}, "enabled": {"en": "Enabled", "fr": "Activé"}, "scope": {"en": "Scope", "fr": "Portée"}, "neutralizationStatus": {"en": "Neutralization Status", "fr": "Statut de neutralisation"}, "namesToParse": {"en": "Names to Parse", "fr": "Noms à analyser"}, "sharepointSourcePath": {"en": "Source Path", "fr": "Chemin source"}, "sharepointTargetPath": {"en": "Target Path", "fr": "Chemin cible"}, }, ) class DataNeutralizerAttributes(BaseModel): id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique ID of the attribute mapping (used as UID in neutralized files)", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) mandateId: str = Field(description="ID of the mandate this attribute belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) featureInstanceId: str = Field(description="ID of the feature instance this attribute belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) userId: str = Field(description="ID of the user who created this attribute", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) originalText: str = Field(description="Original text that was neutralized", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) fileId: Optional[str] = Field(default=None, description="ID of the file this attribute belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) patternType: str = Field(description="Type of pattern that matched (email, phone, name, etc.)", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": True}) class DataNeutralizationSnapshot(BaseModel): """Stores the full neutralized text (with embedded placeholders) per source.""" id: str = Field(default_factory=lambda: str(uuid.uuid4())) mandateId: str = Field(description="Mandate scope") featureInstanceId: str = Field(default="", description="Feature instance scope") userId: str = Field(description="User who triggered neutralization") sourceLabel: str = Field(description="Human label, e.g. 'Prompt', 'Kontext', 'Nachricht 3'") neutralizedText: str = Field(description="Full text with [type.uuid] placeholders embedded") placeholderCount: int = Field(default=0, description="Number of placeholders in the text") registerModelLabels( "DataNeutralizerAttributes", {"en": "Neutralized Data Attribute", "fr": "Attribut de données neutralisées"}, { "id": {"en": "ID", "fr": "ID"}, "mandateId": {"en": "Mandate ID", "fr": "ID de mandat"}, "featureInstanceId": {"en": "Feature Instance ID", "fr": "ID de l'instance de fonctionnalité"}, "userId": {"en": "User ID", "fr": "ID utilisateur"}, "originalText": {"en": "Original Text", "fr": "Texte original"}, "fileId": {"en": "File ID", "fr": "ID de fichier"}, "patternType": {"en": "Pattern Type", "fr": "Type de modèle"}, }, ) registerModelLabels( "DataNeutralizationSnapshot", {"en": "Neutralization Snapshot", "de": "Neutralisierungs-Snapshot"}, { "id": {"en": "ID"}, "mandateId": {"en": "Mandate ID"}, "featureInstanceId": {"en": "Feature Instance ID"}, "userId": {"en": "User ID"}, "sourceLabel": {"en": "Source", "de": "Quelle"}, "neutralizedText": {"en": "Neutralized Text", "de": "Neutralisierter Text"}, "placeholderCount": {"en": "Placeholders", "de": "Platzhalter"}, }, )