from sqlalchemy import Column, Integer, String, Float, DateTime, Text, Boolean, Table, ForeignKey from sqlalchemy.orm import relationship from datetime import datetime, timezone from app.database import Base # Many-to-many association tables model_tags = Table( 'model_tags', Base.metadata, Column('model_id', Integer, ForeignKey('models.id'), primary_key=True), Column('tag_id', Integer, ForeignKey('tags.id'), primary_key=True) ) collection_models = Table( 'collection_models', Base.metadata, Column('collection_id', Integer, ForeignKey('collections.id'), primary_key=True), Column('model_id', Integer, ForeignKey('models.id'), primary_key=True) ) class Tag(Base): __tablename__ = "tags" id = Column(Integer, primary_key=True, index=True) name = Column(String, unique=True, nullable=False, index=True) models = relationship("Model3D", secondary=model_tags, back_populates="tags") class Model3D(Base): __tablename__ = "models" id = Column(Integer, primary_key=True, index=True) title = Column(String, nullable=False) filename = Column(String, nullable=False) description = Column(Text, nullable=True) author = Column(String, nullable=True) license = Column(String, nullable=True) category = Column(String, nullable=True) file_size = Column(Integer, nullable=True) file_hash = Column(String, nullable=True, index=True) width = Column(Float, nullable=True) height = Column(Float, nullable=True) depth = Column(Float, nullable=True) faces = Column(Integer, nullable=True) created_at = Column(DateTime, default=datetime.now(timezone.utc)) thumbnail_path = Column(String, nullable=True) download_count = Column(Integer, default=0) tags = relationship("Tag", secondary=model_tags, back_populates="models") files = relationship("ModelFile", back_populates="model", cascade="all, delete-orphan") ratings = relationship("Rating", back_populates="model", cascade="all, delete-orphan") comments = relationship("Comment", back_populates="model", cascade="all, delete-orphan") collections = relationship("Collection", secondary=collection_models, back_populates="models") class ModelFile(Base): __tablename__ = "model_files" id = Column(Integer, primary_key=True, index=True) model_id = Column(Integer, ForeignKey("models.id"), nullable=False) filename = Column(String, nullable=False) file_path = Column(String, nullable=False) file_type = Column(String, nullable=False, default='stl') part_name = Column(String, nullable=True) is_primary = Column(Boolean, default=False) file_size = Column(Integer, nullable=True) file_hash = Column(String, nullable=True) model = relationship("Model3D", back_populates="files") class Rating(Base): __tablename__ = "ratings" id = Column(Integer, primary_key=True, index=True) model_id = Column(Integer, ForeignKey("models.id"), nullable=False) stars = Column(Integer, nullable=False) # 1-5 created_at = Column(DateTime, default=datetime.now(timezone.utc)) model = relationship("Model3D", back_populates="ratings") class Collection(Base): __tablename__ = "collections" id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False) description = Column(Text, nullable=True) created_at = Column(DateTime, default=datetime.now(timezone.utc)) models = relationship("Model3D", secondary=collection_models, back_populates="collections") class Comment(Base): __tablename__ = "comments" id = Column(Integer, primary_key=True, index=True) model_id = Column(Integer, ForeignKey("models.id"), nullable=False) author_name = Column(String, nullable=True) text = Column(Text, nullable=False) created_at = Column(DateTime, default=datetime.now(timezone.utc)) model = relationship("Model3D", back_populates="comments")