from datetime import datetime, timezone from app import db import bcrypt def utc_now(): """Return current UTC time as timezone-aware datetime""" return datetime.now(timezone.utc) def format_datetime(dt: datetime) -> str: """Format datetime to ISO format with UTC timezone indicator""" if dt is None: return None # If datetime is naive (no timezone), assume it's UTC if dt.tzinfo is None: dt = dt.replace(tzinfo=timezone.utc) return dt.isoformat() class User(db.Model): """User model for authentication and authorization""" __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(50), unique=True, nullable=False, index=True) email = db.Column(db.String(100), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(255), nullable=False) role = db.Column(db.Enum('admin', 'power_user', 'user', name='user_role'), default='user') created_at = db.Column(db.DateTime, default=utc_now) is_active = db.Column(db.Boolean, default=True) # Relationships credentials = db.relationship('UserCredential', back_populates='user', cascade='all, delete-orphan') tasks = db.relationship('Task', back_populates='created_by_user', cascade='all, delete-orphan') def set_password(self, password: str) -> None: """Hash and set the user's password""" self.password_hash = bcrypt.hashpw( password.encode('utf-8'), bcrypt.gensalt() ).decode('utf-8') def check_password(self, password: str) -> bool: """Verify the user's password""" return bcrypt.checkpw( password.encode('utf-8'), self.password_hash.encode('utf-8') ) def to_dict(self) -> dict: """Convert user to dictionary (excluding sensitive data)""" return { 'id': self.id, 'username': self.username, 'email': self.email, 'role': self.role, 'created_at': format_datetime(self.created_at), 'is_active': self.is_active } def __repr__(self): return f''