from __future__ import annotations import enum from datetime import datetime from sqlalchemy import Boolean, DateTime, String from sqlalchemy.orm import Mapped, mapped_column, validates from app.db.base import Base, TimestampMixin, UUIDMixin class UserRole(str, enum.Enum): ADMIN = "admin" PRUEFER = "pruefer" MITARBEITER = "mitarbeiter" LESER = "leser" class User(Base, UUIDMixin, TimestampMixin): __tablename__ = "users" email: Mapped[str] = mapped_column(String(255), unique=True, index=True) full_name: Mapped[str] = mapped_column(String(160), nullable=False) first_name: Mapped[str] = mapped_column(String(80)) last_name: Mapped[str] = mapped_column(String(80)) role: Mapped[str] = mapped_column(String(50), nullable=False, default=UserRole.MITARBEITER.value) password_hash: Mapped[str] = mapped_column(String(255)) is_active: Mapped[bool] = mapped_column(Boolean, default=True) must_change_password: Mapped[bool] = mapped_column(Boolean, default=True) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) last_login_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) password_changed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) @validates("first_name", "last_name") def _sync_full_name(self, key: str, value: str) -> str: first_name = value if key == "first_name" else getattr(self, "first_name", None) last_name = value if key == "last_name" else getattr(self, "last_name", None) if first_name is not None and last_name is not None: self.full_name = f"{first_name} {last_name}".strip() return value