import uuid from datetime import UTC, datetime from sqlalchemy import Index from app import db class FileAttachment(db.Model): """Polymorphic file attachment model for Cards, Comments, and other entities""" __tablename__ = "file_attachments" id = db.Column(db.Integer, primary_key=True) uuid = db.Column( db.String(36), nullable=False, unique=True, default=lambda: str(uuid.uuid4()) ) filename = db.Column(db.String(255), nullable=False) original_name = db.Column(db.String(255), nullable=False) file_type = db.Column(db.String(50), nullable=False) # 'image', 'pdf', 'document' mime_type = db.Column(db.String(100), nullable=False) file_size = db.Column(db.Integer, nullable=False) # MinIO storage information minio_bucket = db.Column(db.String(100), nullable=False) minio_object_name = db.Column(db.String(255), nullable=False, unique=True) # Thumbnail information (optional, for images) thumbnail_minio_object_name = db.Column(db.String(255)) thumbnail_minio_bucket = db.Column(db.String(100)) # Polymorphic relationship - can attach to different entity types attachable_type = db.Column( db.String(50), nullable=False ) # 'Card', 'Comment', 'Epic' attachable_id = db.Column(db.Integer, nullable=False) # ID of the attached entity # Upload metadata uploaded_by = db.Column( db.Integer, db.ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) created_at = db.Column(db.DateTime, default=lambda: datetime.now(UTC)) # Relationships uploader = db.relationship("User", backref="uploaded_files") # Indexes for efficient queries __table_args__ = ( Index("ix_file_attachments_attachable", "attachable_type", "attachable_id"), Index("ix_file_attachments_user", "uploaded_by"), Index("ix_file_attachments_uuid", "uuid"), ) def to_dict(self): """Convert file attachment to dictionary""" return { "id": self.id, "uuid": self.uuid, "filename": self.filename, "original_name": self.original_name, "file_type": self.file_type, "mime_type": self.mime_type, "file_size": self.file_size, "minio_bucket": self.minio_bucket, "minio_object_name": self.minio_object_name, "thumbnail_minio_object_name": self.thumbnail_minio_object_name, "thumbnail_minio_bucket": self.thumbnail_minio_bucket, "attachable_type": self.attachable_type, "attachable_id": self.attachable_id, "uploaded_by": self.uploaded_by, "created_at": self.created_at.isoformat() if self.created_at else None, } def __repr__(self): return f""