# 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.attributeUtils import registerModelLabels import uuid import base64 class FileItem(PowerOnModel): id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) mandateId: Optional[str] = Field(default="", description="ID of the mandate this file belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) featureInstanceId: Optional[str] = Field(default="", description="ID of the feature instance this file belongs to", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False, "frontend_fk_source": "/api/features/instances", "frontend_fk_display_field": "label"}) fileName: str = Field(description="Name of the file", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True}) mimeType: str = Field(description="MIME type of the file", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) fileHash: str = Field(description="Hash of the file", json_schema_extra={"frontend_type": "text", "frontend_readonly": True, "frontend_required": False}) fileSize: int = Field(description="Size of the file in bytes", json_schema_extra={"frontend_type": "integer", "frontend_readonly": True, "frontend_required": False}) tags: Optional[List[str]] = Field(default=None, description="Tags for categorization and search", json_schema_extra={"frontend_type": "tags", "frontend_readonly": False, "frontend_required": False}) folderId: Optional[str] = Field(default=None, description="ID of the parent folder", json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": False}) description: Optional[str] = Field(default=None, description="User-provided description of the file", json_schema_extra={"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={"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={"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"}}, ]} ) neutralize: bool = Field( default=False, description="Whether this file should be neutralized before AI processing", json_schema_extra={"frontend_type": "checkbox", "frontend_readonly": False, "frontend_required": False} ) registerModelLabels( "FileItem", {"en": "File Item", "fr": "Élément de fichier"}, { "id": {"en": "ID", "fr": "ID"}, "mandateId": {"en": "Mandate ID", "fr": "ID du mandat"}, "featureInstanceId": {"en": "Feature Instance", "fr": "Instance de fonctionnalité"}, "fileName": {"en": "fileName", "fr": "Nom de fichier"}, "mimeType": {"en": "MIME Type", "fr": "Type MIME"}, "fileHash": {"en": "File Hash", "fr": "Hash du fichier"}, "fileSize": {"en": "File Size", "fr": "Taille du fichier"}, "tags": {"en": "Tags", "fr": "Tags"}, "folderId": {"en": "Folder ID", "fr": "ID du dossier"}, "description": {"en": "Description", "fr": "Description"}, "status": {"en": "Status", "fr": "Statut"}, "scope": {"en": "Scope", "de": "Sichtbarkeit"}, "neutralize": {"en": "Neutralize", "de": "Neutralisieren"}, }, ) class FilePreview(BaseModel): content: Union[str, bytes] = Field(description="File content (text or binary)") mimeType: str = Field(description="MIME type of the file") fileName: str = Field(description="Original fileName") isText: bool = Field(description="Whether the content is text (True) or binary (False)") encoding: Optional[str] = Field(None, description="Text encoding if content is text") size: int = Field(description="Size of the content in bytes") 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 registerModelLabels( "FilePreview", {"en": "File Preview", "fr": "Aperçu du fichier"}, { "content": {"en": "Content", "fr": "Contenu"}, "mimeType": {"en": "MIME Type", "fr": "Type MIME"}, "fileName": {"en": "fileName", "fr": "Nom de fichier"}, "isText": {"en": "Is Text", "fr": "Est du texte"}, "encoding": {"en": "Encoding", "fr": "Encodage"}, "size": {"en": "Size", "fr": "Taille"}, }, ) class FileData(PowerOnModel): id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Primary key") data: str = Field(description="File data content") base64Encoded: bool = Field(description="Whether the data is base64 encoded") registerModelLabels( "FileData", {"en": "File Data", "fr": "Données de fichier"}, { "id": {"en": "ID", "fr": "ID"}, "data": {"en": "Data", "fr": "Données"}, "base64Encoded": {"en": "Base64 Encoded", "fr": "Encodé en Base64"}, }, )