from datetime import datetime import logging from werkzeug.security import generate_password_hash, check_password_hash from flask_login import UserMixin from app import db, login_manager import uuid import os import mimetypes # Add debug logging logger = logging.getLogger(__name__) logger.info("Loading models.py module") @login_manager.user_loader def load_user(id): return User.query.get(int(id)) class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) email = db.Column(db.String(120), unique=True, index=True) password_hash = db.Column(db.String(128)) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) is_admin = db.Column(db.Boolean, default=False) # Define relationships without circular backrefs folders = db.relationship('Folder', foreign_keys='Folder.user_id', backref=db.backref('owner', lazy='joined'), lazy='dynamic', cascade="all, delete-orphan") files = db.relationship('File', foreign_keys='File.user_id', backref=db.backref('owner', lazy='joined'), lazy='dynamic', cascade="all, delete-orphan") downloads = db.relationship('Download', foreign_keys='Download.user_id', backref=db.backref('downloader', lazy='joined'), lazy='dynamic', cascade="all, delete-orphan") # Add shares relationship for consistency with existing database shares_created = db.relationship('Share', foreign_keys='Share.user_id', backref=db.backref('creator', lazy='joined'), lazy='dynamic', cascade="all, delete-orphan") @property def password(self): raise AttributeError('password is not a readable attribute') @password.setter def password(self, password): self.password_hash = generate_password_hash(password) logger.info(f"Password set for user {self.username}") # Add set_password method for compatibility with existing code def set_password(self, password): """Set user password - compatibility method""" logger.info(f"set_password called for {self.username}") self.password_hash = generate_password_hash(password) def verify_password(self, password): return check_password_hash(self.password_hash, password) def __repr__(self): return f'' # Log that the User class is defined logger.info("User class defined with set_password method") class Folder(db.Model): __tablename__ = 'folders' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) parent_id = db.Column(db.Integer, db.ForeignKey('folders.id')) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Define relationship to parent folder with proper backref parent = db.relationship('Folder', remote_side=[id], backref=db.backref('children', lazy='dynamic')) # Define relationship to files in this folder files = db.relationship('File', foreign_keys='File.folder_id', backref=db.backref('folder', lazy='joined'), lazy='dynamic', cascade="all, delete-orphan") def get_path(self): """Get the full path of the folder""" if self.parent is None: return "/" + self.name return self.parent.get_path() + "/" + self.name def __repr__(self): return f'' class File(db.Model): __tablename__ = 'files' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(255), nullable=False) original_name = db.Column(db.String(255), nullable=False) path = db.Column(db.String(255), nullable=False) size = db.Column(db.Integer, nullable=False) type = db.Column(db.String(128)) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) folder_id = db.Column(db.Integer, db.ForeignKey('folders.id')) created_at = db.Column(db.DateTime, default=datetime.utcnow) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Define relationships shares = db.relationship('Share', backref='file', lazy='dynamic') @property def icon_class(self): """Get the appropriate Font Awesome icon class based on file extension""" extension = os.path.splitext(self.name)[1].lower() # Map extensions to icon classes icon_map = { '.pdf': 'fa-file-pdf', '.doc': 'fa-file-word', '.docx': 'fa-file-word', '.xls': 'fa-file-excel', '.xlsx': 'fa-file-excel', '.ppt': 'fa-file-powerpoint', '.pptx': 'fa-file-powerpoint', '.jpg': 'fa-file-image', '.jpeg': 'fa-file-image', '.png': 'fa-file-image', '.gif': 'fa-file-image', '.svg': 'fa-file-image', '.mp3': 'fa-file-audio', '.wav': 'fa-file-audio', '.ogg': 'fa-file-audio', '.mp4': 'fa-file-video', '.avi': 'fa-file-video', '.mov': 'fa-file-video', '.zip': 'fa-file-archive', '.rar': 'fa-file-archive', '.tar': 'fa-file-archive', '.gz': 'fa-file-archive', '.txt': 'fa-file-alt', '.md': 'fa-file-alt', '.html': 'fa-file-code', '.css': 'fa-file-code', '.js': 'fa-file-code', '.py': 'fa-file-code', '.java': 'fa-file-code', '.c': 'fa-file-code' } return icon_map.get(extension, 'fa-file') def get_mime_type(self): """Get the MIME type based on the file extension""" mime_type, _ = mimetypes.guess_type(self.name) return mime_type or 'application/octet-stream' def __repr__(self): return f'' class Share(db.Model): __tablename__ = 'shares' id = db.Column(db.Integer, primary_key=True) file_id = db.Column(db.Integer, db.ForeignKey('files.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) access_key = db.Column(db.String(64), unique=True, index=True, nullable=False) expires_at = db.Column(db.DateTime, nullable=True) created_at = db.Column(db.DateTime, default=datetime.utcnow) download_count = db.Column(db.Integer, default=0) def is_expired(self): """Check if the share has expired""" if self.expires_at is None: return False return datetime.utcnow() > self.expires_at def __repr__(self): return f'' class Download(db.Model): __tablename__ = 'downloads' id = db.Column(db.Integer, primary_key=True) file_id = db.Column(db.Integer, db.ForeignKey('files.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) download_date = db.Column(db.DateTime, default=datetime.utcnow) # Define relationship to file file = db.relationship('File', foreign_keys=[file_id], backref=db.backref('file_downloads', lazy='dynamic')) def __repr__(self): return f''