gateway/modules/datamodels/datamodelFiles.py
2026-04-10 12:33:27 +02:00

136 lines
5.8 KiB
Python

# 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},
)
mandateId: Optional[str] = Field(
default="",
description="ID of the mandate this file belongs to",
json_schema_extra={"label": "Mandanten-ID", "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={"label": "Feature-Instanz", "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={"label": "Dateiname", "frontend_type": "text", "frontend_readonly": False, "frontend_required": True},
)
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},
)
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},
)
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": {"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={"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"},
)