from datetime import datetime, UTC from enum import Enum from typing import Optional, Any, List from pydantic import BaseModel, computed_field, Field, model_validator class AssetContentType(str, Enum): IMAGE = 'image' PROMPT = 'prompt' VIDEO = 'video' class AssetType(str, Enum): UPLOADED = 'uploaded' GENERATED = 'generated' INSPIRATION = 'inspiration' class Asset(BaseModel): id: Optional[str] = None name: str type: AssetType = AssetType.GENERATED content_type: AssetContentType = AssetContentType.IMAGE linked_char_id: Optional[str] = None data: Optional[bytes] = None tg_doc_file_id: Optional[str] = None tg_photo_file_id: Optional[str] = None minio_object_name: Optional[str] = None minio_bucket: Optional[str] = None minio_thumbnail_object_name: Optional[str] = None thumbnail: Optional[bytes] = None tags: List[str] = [] created_by: Optional[str] = None project_id: Optional[str] = None is_deleted: bool = False created_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) @model_validator(mode='before') @classmethod def check_legacy_type(cls, data: Any) -> Any: if isinstance(data, dict): # Если поле type содержит старые значения ("image", "prompt"), # переносим их в content_type, а type ставим по умолчанию (GENERATED) # или пытаемся угадать. # Но по задаче мы дефолтим в GENERATED, и script'ом поправим. raw_type = data.get('type') if raw_type in ['image', 'prompt']: data['content_type'] = raw_type # Если в базе нет нового поля type, оно встанет в default=GENERATED # Чтобы не вызывало ошибку валидации AssetType, меняем его или удаляем, # полагаясь на default. # Но если мы просто удалим, поле type примет дефолтное значение. # Однако, если мы хотим явно отличить, можно ничего не делать, # но тогда валидация поля `type` упадет, т.к. "image" != "generated". # Поэтому удаляем старое значение из type, чтобы сработал дефолт. if 'type' in data: del data['type'] # Если content_type нет в данных (легаси), пытаемся его восстановить из удалённого type # (выше мы его переложили). return data # --- CALCULATED FIELD --- @computed_field @property def url(self) -> str: """ Это поле автоматически вычислится и попадет в model_dump() / .json() """ if self.id: return f"/assets/{self.id}" return ""