# Copyright (c) 2025 Patrick Motsch # All rights reserved. """File-related datamodels: FileItem, FilePreview, FileData.""" from typing import Dict, Any, List, Optional, Union from pydantic import BaseModel, Field from modules.datamodels.datamodelBase import PowerOnModel from modules.shared.i18nRegistry import i18nModel import uuid import base64 @i18nModel("Datei") class FileItem(PowerOnModel): """Metadaten einer gespeicherten Datei.""" id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"label": "ID", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False}, ) fileName: str = Field( description="Name of the file", json_schema_extra={"label": "Dateiname", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True}, ) mandateId: Optional[str] = Field( default="", description="ID of the mandate this file belongs to", json_schema_extra={ "label": "Mandant", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "fk_model": "Mandate", "fk_label_field": "label", "fk_target": {"db": "poweron_app", "table": "Mandate"}, }, ) featureInstanceId: Optional[str] = Field( default="", description="ID of the feature instance this file belongs to", json_schema_extra={ "label": "Feature-Instanz", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "fk_model": "FeatureInstance", "fk_label_field": "label", "fk_target": {"db": "poweron_app", "table": "FeatureInstance"}, }, ) mimeType: str = Field( description="MIME type of the file", json_schema_extra={"label": "MIME-Typ", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False}, ) fileHash: str = Field( description="Hash of the file", json_schema_extra={"label": "Datei-Hash", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False}, ) fileSize: int = Field( description="Size of the file in bytes", json_schema_extra={ "label": "Dateigroesse", "frontend_type": "integer", "frontend_readonly": True, "frontend_required": False, # Auto-scale byte units (B / KB / MB / GB / TB), right-aligned in tables. "frontend_format": "R:b", }, ) tags: Optional[List[str]] = Field( default=None, description="Tags for categorization and search", json_schema_extra={"label": "Tags", "frontend_type": "tags", "frontend_readonly": False, "frontend_required": False}, ) folderId: Optional[str] = Field( default=None, description="ID of the parent folder", json_schema_extra={ "label": "Ordner-ID", "frontend_type": "text", "frontend_readonly": False, "frontend_required": False, "fk_target": {"db": "poweron_management", "table": "FileFolder"}, }, ) description: Optional[str] = Field( default=None, description="User-provided description of the file", json_schema_extra={"label": "Beschreibung", "frontend_type": "textarea", "frontend_readonly": False, "frontend_required": False}, ) status: Optional[str] = Field( default=None, description="Processing status: pending, extracted, embedding, indexed, failed", json_schema_extra={"label": "Status", "frontend_type": "text", "frontend_readonly": True, "frontend_required": False}, ) scope: str = Field( default="personal", description="Data visibility scope: personal, featureInstance, mandate, global", json_schema_extra={"label": "Sichtbarkeit", "frontend_type": "select", "frontend_readonly": False, "frontend_required": False, "frontend_options": [ {"value": "personal", "label": "Persönlich"}, {"value": "featureInstance", "label": "Feature-Instanz"}, {"value": "mandate", "label": "Mandant"}, {"value": "global", "label": "Global"}, ]}, ) neutralize: bool = Field( default=False, description="Whether this file should be neutralized before AI processing", json_schema_extra={"label": "Neutralisieren", "frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False}, ) @i18nModel("Datei-Vorschau") class FilePreview(BaseModel): """Vorschau-Inhalt einer Datei fuer die Anzeige.""" content: Union[str, bytes] = Field( description="File content (text or binary)", json_schema_extra={"label": "Inhalt"}, ) mimeType: str = Field( description="MIME type of the file", json_schema_extra={"label": "MIME-Typ"}, ) fileName: str = Field( description="Original fileName", json_schema_extra={"label": "Dateiname"}, ) isText: bool = Field( description="Whether the content is text (True) or binary (False)", json_schema_extra={"label": "Ist Text"}, ) encoding: Optional[str] = Field( None, description="Text encoding if content is text", json_schema_extra={"label": "Kodierung"}, ) size: int = Field( description="Size of the content in bytes", json_schema_extra={"label": "Groesse"}, ) def toDictWithBase64Encoding(self) -> Dict[str, Any]: """Convert to dictionary with base64 encoding for binary content.""" data = self.model_dump() if isinstance(data.get("content"), bytes): data["content"] = base64.b64encode(data["content"]).decode("utf-8") return data @i18nModel("Dateidaten") class FileData(PowerOnModel): """Rohdaten einer Datei (z.B. Base64).""" id: str = Field( default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"label": "ID"}, ) data: str = Field( description="File data content", json_schema_extra={"label": "Daten"}, ) base64Encoded: bool = Field( description="Whether the data is base64 encoded", json_schema_extra={"label": "Base64-kodiert"}, )