"""Schémas d'entrée/sortie de l'API CRUD et de la matrice.""" import datetime as dt import uuid from pydantic import BaseModel, ConfigDict, Field from app.models import ( AssignmentSource, AssignmentStatus, PositionCategory, Role, TshirtSize, ) from app.validation import Violation class ApiModel(BaseModel): """Base commune : autorise la construction depuis un objet ORM.""" model_config = ConfigDict(from_attributes=True) # --------------------------------------------------------------------------- # # Volunteer # # --------------------------------------------------------------------------- # class VolunteerCreate(ApiModel): first_name: str last_name: str email: str | None = None phone: str | None = None tshirt_size: TshirtSize | None = None diet: str | None = None comment: str | None = None skill_codes: list[str] = Field(default_factory=list) class VolunteerUpdate(ApiModel): first_name: str | None = None last_name: str | None = None email: str | None = None phone: str | None = None tshirt_size: TshirtSize | None = None diet: str | None = None comment: str | None = None # None = inchangé ; [] = vider les compétences. skill_codes: list[str] | None = None class VolunteerRead(ApiModel): id: uuid.UUID edition_id: uuid.UUID first_name: str last_name: str email: str | None phone: str | None tshirt_size: TshirtSize | None diet: str | None comment: str | None skill_codes: list[str] created_at: dt.datetime updated_at: dt.datetime # --------------------------------------------------------------------------- # # Position # # --------------------------------------------------------------------------- # class PositionCreate(ApiModel): name: str location: str | None = None category: PositionCategory lead_account_id: uuid.UUID | None = None class PositionUpdate(ApiModel): name: str | None = None location: str | None = None category: PositionCategory | None = None lead_account_id: uuid.UUID | None = None class PositionRead(ApiModel): id: uuid.UUID edition_id: uuid.UUID name: str location: str | None category: PositionCategory lead_account_id: uuid.UUID | None created_at: dt.datetime updated_at: dt.datetime # --------------------------------------------------------------------------- # # Shift # # --------------------------------------------------------------------------- # class ShiftCreate(ApiModel): position_id: uuid.UUID starts_at: dt.datetime ends_at: dt.datetime min_staff: int = Field(default=0, ge=0) ideal_staff: int = Field(default=0, ge=0) required_skill_codes: list[str] = Field(default_factory=list) class ShiftUpdate(ApiModel): position_id: uuid.UUID | None = None starts_at: dt.datetime | None = None ends_at: dt.datetime | None = None min_staff: int | None = Field(default=None, ge=0) ideal_staff: int | None = Field(default=None, ge=0) required_skill_codes: list[str] | None = None class ShiftRead(ApiModel): id: uuid.UUID edition_id: uuid.UUID position_id: uuid.UUID starts_at: dt.datetime ends_at: dt.datetime min_staff: int ideal_staff: int required_skill_codes: list[str] created_at: dt.datetime updated_at: dt.datetime # --------------------------------------------------------------------------- # # Assignment # # --------------------------------------------------------------------------- # class AssignmentCreate(ApiModel): volunteer_id: uuid.UUID shift_id: uuid.UUID status: AssignmentStatus = AssignmentStatus.PROPOSED source: AssignmentSource = AssignmentSource.MANUAL locked: bool = False class AssignmentUpdate(ApiModel): status: AssignmentStatus | None = None source: AssignmentSource | None = None locked: bool | None = None class AssignmentRead(ApiModel): id: uuid.UUID edition_id: uuid.UUID volunteer_id: uuid.UUID shift_id: uuid.UUID status: AssignmentStatus source: AssignmentSource locked: bool created_at: dt.datetime updated_at: dt.datetime class AssignmentWriteResult(ApiModel): assignment: AssignmentRead warnings: list[Violation] = Field(default_factory=list) class AssignmentValidateRequest(ApiModel): volunteer_id: uuid.UUID shift_id: uuid.UUID class AssignmentValidateResult(ApiModel): blocking: bool violations: list[Violation] = Field(default_factory=list) # --------------------------------------------------------------------------- # # Matrice poste × créneau # # --------------------------------------------------------------------------- # class MatrixShift(ApiModel): shift_id: uuid.UUID starts_at: dt.datetime ends_at: dt.datetime min_staff: int ideal_staff: int confirmed: int proposed: int class MatrixPosition(ApiModel): id: uuid.UUID name: str category: PositionCategory location: str | None shifts: list[MatrixShift] class MatrixResponse(ApiModel): edition_id: uuid.UUID positions: list[MatrixPosition] # --------------------------------------------------------------------------- # # Authentification et comptes # # --------------------------------------------------------------------------- # class MagicLinkRequest(ApiModel): email: str class VerifyRequest(ApiModel): token: str class LoginRequest(ApiModel): email: str password: str class ChangePasswordRequest(ApiModel): current_password: str new_password: str class AccountRead(ApiModel): id: uuid.UUID edition_id: uuid.UUID email: str role: Role volunteer_id: uuid.UUID | None # Mode de connexion : "password" (admins) ou "magic_link". auth_method: str created_at: dt.datetime updated_at: dt.datetime class TokenResponse(ApiModel): access_token: str token_type: str account: AccountRead class AccountCreate(ApiModel): email: str role: Role volunteer_id: uuid.UUID | None = None # Requis pour un compte COORDINATION, interdit sinon. password: str | None = None