from datetime import datetime from enum import StrEnum from typing import Annotated from bson import ObjectId from pydantic import BaseModel, BeforeValidator, EmailStr, Field def validate_object_id(v) -> str: """Convert ObjectId to string""" if isinstance(v, ObjectId): return str(v) if isinstance(v, str): return v raise ValueError('Invalid ObjectId') PyObjectId = Annotated[str, BeforeValidator(validate_object_id)] class UserRole(StrEnum): CLIENT = "client" REVIEWER = "reviewer" LINGUIST = "linguist" PRODUCTION = "production" PROJECT_MANAGER = "project_manager" ADMIN = "admin" class AuthProvider(StrEnum): LOCAL = "local" MICROSOFT = "microsoft" class User(BaseModel): id: PyObjectId | None = Field(None, alias="_id") email: EmailStr hashed_password: str | None = None # Optional for Microsoft users full_name: str role: UserRole = UserRole.CLIENT auth_provider: AuthProvider = AuthProvider.LOCAL is_active: bool = True pm_client_ids: list[str] = [] # Client IDs where this user is Project Manager (admin-assigned) languages: list[str] = [] # BCP-47 language codes the user is competent in (R-8) created_at: datetime | None = None updated_at: datetime | None = None class Config: populate_by_name = True use_enum_values = True class UserInDB(User): pass class UserCreate(BaseModel): email: EmailStr password: str full_name: str role: UserRole = UserRole.CLIENT class UserUpdate(BaseModel): email: EmailStr | None = None full_name: str | None = None role: UserRole | None = None is_active: bool | None = None pm_client_ids: list[str] | None = None languages: list[str] | None = None