"""Pydantic schemas for the 安全问卷 · 供应商安全评估 router.""" from __future__ import annotations from datetime import datetime from typing import Any from uuid import UUID from pydantic import BaseModel, Field # ── Templates ───────────────────────────────────────────────────────── class TemplateCreateRequest(BaseModel): key: str = Field(min_length=2, max_length=64) name_zh: str = Field(min_length=1, max_length=255) name_en: str = Field(min_length=1, max_length=255) description_zh: str | None = Field(default=None, max_length=4000) description_en: str | None = Field(default=None, max_length=4000) kind: str = Field(default="vendor", max_length=16) tier: str | None = Field(default=None, max_length=16) enabled: bool = True sort_order: int = 0 class TemplateUpdateRequest(BaseModel): name_zh: str | None = Field(default=None, max_length=255) name_en: str | None = Field(default=None, max_length=255) description_zh: str | None = Field(default=None, max_length=4000) description_en: str | None = Field(default=None, max_length=4000) tier: str | None = Field(default=None, max_length=16) enabled: bool | None = None sort_order: int | None = None class TemplateCloneRequest(BaseModel): new_key: str = Field(min_length=2, max_length=64) name_zh: str = Field(default="", max_length=255) name_en: str = Field(default="", max_length=255) class TemplateQuestionCreateRequest(BaseModel): code: str = Field(min_length=1, max_length=32) section_key: str = Field(default="general", max_length=64) section_zh: str = Field(min_length=1, max_length=255) section_en: str = Field(min_length=1, max_length=255) text_zh: str = Field(min_length=1, max_length=8000) text_en: str = Field(min_length=1, max_length=8000) guidance_zh: str | None = Field(default=None, max_length=8000) guidance_en: str | None = Field(default=None, max_length=8000) answer_type: str = Field(max_length=24) options: list[dict[str, Any]] | None = None weight: int = Field(default=1, ge=0, le=100) expected_answer: str | None = Field(default=None, max_length=64) requires_evidence: bool = False sort_order: int = 0 class TemplateQuestionUpdateRequest(BaseModel): section_key: str | None = Field(default=None, max_length=64) section_zh: str | None = Field(default=None, max_length=255) section_en: str | None = Field(default=None, max_length=255) text_zh: str | None = Field(default=None, max_length=8000) text_en: str | None = Field(default=None, max_length=8000) guidance_zh: str | None = Field(default=None, max_length=8000) guidance_en: str | None = Field(default=None, max_length=8000) answer_type: str | None = Field(default=None, max_length=24) options: list[dict[str, Any]] | None = None weight: int | None = Field(default=None, ge=0, le=100) expected_answer: str | None = Field(default=None, max_length=64) requires_evidence: bool | None = None sort_order: int | None = None # ── Assessments ─────────────────────────────────────────────────────── class AssessmentCreateRequest(BaseModel): template_id: UUID vendor_name: str = Field(min_length=1, max_length=255) supplier_id: UUID | None = None vendor_contact_name: str | None = Field(default=None, max_length=128) vendor_contact_email: str | None = Field(default=None, max_length=255) tier: str = Field(default="medium", max_length=16) owner_person_id: UUID | None = None reviewer_person_id: UUID | None = None due_at: datetime | None = None review_period_months: int | None = Field(default=None, ge=1, le=120) class AssessmentMetaUpdateRequest(BaseModel): vendor_name: str | None = Field(default=None, max_length=255) supplier_id: UUID | None = None vendor_contact_name: str | None = Field(default=None, max_length=128) vendor_contact_email: str | None = Field(default=None, max_length=255) tier: str | None = Field(default=None, max_length=16) owner_person_id: UUID | None = None reviewer_person_id: UUID | None = None due_at: datetime | None = None review_period_months: int | None = Field(default=None, ge=1, le=120) class AnswerSetRequest(BaseModel): # A scalar answer or a list (multi_choice); comment optional. answer_value: Any | None = None answer_comment: str | None = Field(default=None, max_length=8000) class AnswerReviewRequest(BaseModel): review_flag: str | None = Field(default=None, max_length=16) reviewer_note: str | None = Field(default=None, max_length=8000) class AssessmentCompleteRequest(BaseModel): summary: str | None = Field(default=None, max_length=16000) reviewer_person_id: UUID | None = None class RatingOverrideRequest(BaseModel): # null clears the override and reverts to the computed rating. rating: str | None = Field(default=None, max_length=16) class StatusChangeRequest(BaseModel): status: str = Field(max_length=16) # ── Evidence attachments ────────────────────────────────────────────── class AttachmentPresignRequest(BaseModel): filename: str = Field(min_length=1, max_length=255) content_type: str = Field(min_length=3, max_length=128) size_bytes: int = Field(gt=0) checksum_sha256: str = Field(min_length=44, max_length=44) class AttachmentRegisterRequest(BaseModel): object_key: str = Field(min_length=1, max_length=512) filename: str = Field(min_length=1, max_length=255) content_type: str = Field(min_length=3, max_length=128) size_bytes: int | None = Field(default=None, ge=0) answer_id: UUID | None = None kind: str = Field(default="evidence", max_length=24)