Allow uploading of files and folders.
This commit is contained in:
92
models/note_attachment.py
Normal file
92
models/note_attachment.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
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'
|
||||
Reference in New Issue
Block a user