92 lines
3.3 KiB
Python
92 lines
3.3 KiB
Python
"""
|
|
Note attachment model for storing uploaded files associated with notes.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from . import db
|
|
|
|
|
|
class NoteAttachment(db.Model):
|
|
"""Model for files attached to notes (images, documents, etc.)"""
|
|
__tablename__ = 'note_attachment'
|
|
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
note_id = db.Column(db.Integer, db.ForeignKey('note.id'), nullable=False)
|
|
|
|
# File information
|
|
filename = db.Column(db.String(255), nullable=False) # Stored filename
|
|
original_filename = db.Column(db.String(255), nullable=False) # Original upload name
|
|
file_path = db.Column(db.String(500), nullable=False) # Relative path from uploads dir
|
|
file_size = db.Column(db.Integer) # Size in bytes
|
|
mime_type = db.Column(db.String(100))
|
|
|
|
# File type for easier filtering
|
|
file_type = db.Column(db.String(20)) # 'image', 'document', 'archive', etc.
|
|
|
|
# Metadata
|
|
uploaded_at = db.Column(db.DateTime, default=datetime.now)
|
|
uploaded_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
|
|
|
# For images: dimensions
|
|
image_width = db.Column(db.Integer)
|
|
image_height = db.Column(db.Integer)
|
|
|
|
# Soft delete
|
|
is_deleted = db.Column(db.Boolean, default=False)
|
|
deleted_at = db.Column(db.DateTime)
|
|
|
|
# Relationships
|
|
note = db.relationship('Note', backref='attachments')
|
|
uploaded_by = db.relationship('User', backref='uploaded_attachments')
|
|
|
|
def __repr__(self):
|
|
return f'<NoteAttachment {self.original_filename} for Note {self.note_id}>'
|
|
|
|
@property
|
|
def is_image(self):
|
|
"""Check if attachment is an image"""
|
|
return self.file_type == 'image'
|
|
|
|
@property
|
|
def url(self):
|
|
"""Get the URL to access this attachment"""
|
|
return f'/uploads/notes/{self.file_path}'
|
|
|
|
def get_file_extension(self):
|
|
"""Get file extension"""
|
|
return self.original_filename.rsplit('.', 1)[1].lower() if '.' in self.original_filename else ''
|
|
|
|
@staticmethod
|
|
def allowed_file(filename, file_type='any'):
|
|
"""Check if file extension is allowed"""
|
|
ALLOWED_EXTENSIONS = {
|
|
'image': {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'},
|
|
'document': {'pdf', 'doc', 'docx', 'txt', 'md', 'csv', 'xls', 'xlsx'},
|
|
'archive': {'zip', 'tar', 'gz', '7z'},
|
|
'any': {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'pdf', 'doc',
|
|
'docx', 'txt', 'md', 'csv', 'xls', 'xlsx', 'zip', 'tar', 'gz', '7z'}
|
|
}
|
|
|
|
if '.' not in filename:
|
|
return False
|
|
|
|
ext = filename.rsplit('.', 1)[1].lower()
|
|
allowed = ALLOWED_EXTENSIONS.get(file_type, ALLOWED_EXTENSIONS['any'])
|
|
return ext in allowed
|
|
|
|
@staticmethod
|
|
def get_file_type(filename):
|
|
"""Determine file type from extension"""
|
|
if '.' not in filename:
|
|
return 'unknown'
|
|
|
|
ext = filename.rsplit('.', 1)[1].lower()
|
|
|
|
if ext in {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg'}:
|
|
return 'image'
|
|
elif ext in {'pdf', 'doc', 'docx', 'txt', 'md', 'csv', 'xls', 'xlsx'}:
|
|
return 'document'
|
|
elif ext in {'zip', 'tar', 'gz', '7z'}:
|
|
return 'archive'
|
|
else:
|
|
return 'other' |