Files

124 lines
4.2 KiB
Python

from datetime import datetime
from app import db
class ExtraFile(db.Model):
"""Model for extra files attached to sets (BrickLink XML, Stud.io, box art, etc)."""
__tablename__ = 'extra_files'
id = db.Column(db.Integer, primary_key=True)
set_id = db.Column(db.Integer, db.ForeignKey('sets.id', ondelete='CASCADE'), nullable=False)
# File information
file_name = db.Column(db.String(255), nullable=False)
original_filename = db.Column(db.String(255), nullable=False) # Original name before hashing
file_path = db.Column(db.String(500), nullable=False)
file_type = db.Column(db.String(50), nullable=False) # Extension
file_size = db.Column(db.Integer, nullable=False) # Size in bytes
# Metadata
description = db.Column(db.Text)
category = db.Column(db.String(50)) # 'bricklink', 'studio', 'box_art', 'document', 'photo', 'other'
# Tracking
uploaded_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
uploaded_by = db.Column(db.Integer, db.ForeignKey('users.id'))
# Relationships
lego_set = db.relationship('Set', back_populates='extra_files')
uploader = db.relationship('User', backref='uploaded_files')
def __repr__(self):
return f'<ExtraFile {self.file_name} for Set {self.set_id}>'
@property
def file_size_formatted(self):
"""Return human-readable file size."""
size = self.file_size
for unit in ['B', 'KB', 'MB', 'GB']:
if size < 1024.0:
return f"{size:.1f} {unit}"
size /= 1024.0
return f"{size:.1f} TB"
@property
def file_icon(self):
"""Return Bootstrap icon class based on file type."""
icon_map = {
# Images
'jpg': 'file-image',
'jpeg': 'file-image',
'png': 'file-image',
'gif': 'file-image',
'webp': 'file-image',
'bmp': 'file-image',
# Documents
'pdf': 'file-pdf',
'doc': 'file-word',
'docx': 'file-word',
'txt': 'file-text',
'rtf': 'file-text',
# Spreadsheets
'xls': 'file-excel',
'xlsx': 'file-excel',
'csv': 'file-spreadsheet',
# Data files
'xml': 'file-code',
'json': 'file-code',
# 3D/CAD files
'ldr': 'box-seam', # LDraw
'mpd': 'box-seam', # LDraw
'io': 'box-seam', # Stud.io
'lxf': 'box-seam', # LEGO Digital Designer
'lxfml': 'box-seam',
# Archives
'zip': 'file-zip',
'rar': 'file-zip',
'7z': 'file-zip',
'tar': 'file-zip',
'gz': 'file-zip',
# Other
'default': 'file-earmark'
}
return icon_map.get(self.file_type.lower(), icon_map['default'])
@property
def is_image(self):
"""Check if file is an image."""
image_types = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']
return self.file_type.lower() in image_types
@property
def can_preview(self):
"""Check if file can be previewed in browser."""
preview_types = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'pdf', 'txt']
return self.file_type.lower() in preview_types
def to_dict(self):
"""Convert to dictionary for JSON responses."""
return {
'id': self.id,
'set_id': self.set_id,
'file_name': self.file_name,
'original_filename': self.original_filename,
'file_path': self.file_path.replace('\\', '/'),
'file_type': self.file_type,
'file_size': self.file_size,
'file_size_formatted': self.file_size_formatted,
'description': self.description,
'category': self.category,
'uploaded_at': self.uploaded_at.isoformat(),
'is_image': self.is_image,
'can_preview': self.can_preview,
'file_icon': self.file_icon,
'download_url': f'/extra-files/download/{self.id}'
}