from sqlalchemy import Column, Integer, String, ForeignKey, Text, DateTime, Boolean from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import relationship from sqlalchemy.sql import func import enum import json from typing import Any, Optional from app.database import Base class AssetType(str, enum.Enum): IMAGE = "image" AUDIO = "audio" VIDEO = "video" DOCUMENT = "document" MODEL_3D = "model_3d" OTHER = "other" class Asset(Base): __tablename__ = "assets" id = Column(Integer, primary_key=True, index=True) name = Column(String(255), nullable=False) description = Column(Text, nullable=True) asset_type = Column( ENUM(AssetType, name="asset_type", create_type=False), nullable=False, ) file_path = Column(String(512), nullable=False) file_size = Column(Integer, nullable=True) mime_type = Column(String(128), nullable=True) metadata_json = Column(Text, nullable=True) world_id = Column(Integer, ForeignKey("worlds.id"), nullable=True) owner_id = Column(Integer, ForeignKey("users.id"), nullable=False) is_public = Column(Boolean, default=False, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) owner = relationship("User", back_populates="assets") world = relationship("World", back_populates="assets") # ------------------------------------------------------------------ # Computed properties # ------------------------------------------------------------------ @property def has_gltf_metadata(self) -> bool: """Return True when metadata_json contains glTF-specific keys. glTF metadata is considered present when the stored JSON contains at least one of the keys that *extract_gltf_metadata* produces (mesh_count, vertex_count, has_animation, texture_refs, bounding_box_min, bounding_box_max). """ if not self.metadata_json: return False try: data: dict[str, Any] = json.loads(self.metadata_json) except (json.JSONDecodeError, TypeError): return False gltf_keys = { "mesh_count", "vertex_count", "has_animation", "texture_refs", "bounding_box_min", "bounding_box_max", } return bool(gltf_keys & data.keys())