76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
|
|
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"<FileAttachment {self.original_name} ({self.file_type})>"
|