import uuid from datetime import datetime from typing import Literal from pydantic import BaseModel, ConfigDict, Field ConfirmStatus = Literal["待确认", "已确认", "退回"] MilestoneStatus = Literal["未开始", "进行中", "已完成", "延期"] class ProjectMilestoneItem(BaseModel): id: str name: str = "" planDate: str = "" startDate: str = "" endDate: str = "" durationDays: int = 1 owner: str = "" remark: str = "" status: MilestoneStatus = "未开始" class EnrollmentPlanItem(BaseModel): totalTarget: int = 0 startDate: str = "" endDate: str = "" monthlyGoalNote: str = "" stageBreakdown: str = "" class SiteMilestoneItem(BaseModel): id: str milestone: str = "" planDate: str = "" owner: str = "" remark: str = "" status: MilestoneStatus = "未开始" class SiteEnrollmentPlanItem(BaseModel): id: str siteId: str = "" siteName: str = "" target: int = 0 startDate: str = "" endDate: str = "" note: str = "" stageBreakdown: str = "" class MonitoringStrategyItem(BaseModel): id: str strategyType: str = "" detail: str = "" frequency: str = "" updatedAt: str = "" enabled: bool = True class CenterConfirmItem(BaseModel): id: str siteId: str = "" siteName: str = "" confirmer: str = "" confirmStatus: ConfirmStatus = "待确认" confirmDate: str = "" note: str = "" class StudySetupConfigData(BaseModel): projectMilestones: list[ProjectMilestoneItem] = Field(default_factory=list) enrollmentPlan: EnrollmentPlanItem = Field(default_factory=EnrollmentPlanItem) siteMilestones: list[SiteMilestoneItem] = Field(default_factory=list) siteEnrollmentPlans: list[SiteEnrollmentPlanItem] = Field(default_factory=list) monitoringStrategies: list[MonitoringStrategyItem] = Field(default_factory=list) centerConfirm: list[CenterConfirmItem] = Field(default_factory=list) class StudySetupConfigUpsert(BaseModel): expected_version: int | None = None data: StudySetupConfigData class StudySetupConfigPublish(BaseModel): expected_version: int | None = None class StudySetupConfigRollback(BaseModel): expected_version: int | None = None target_version: int class StudySetupConfigVersionRead(BaseModel): id: uuid.UUID study_id: uuid.UUID study_setup_config_id: uuid.UUID version: int display_version: int source_version: int | None = None config: StudySetupConfigData published_by: uuid.UUID | None = None published_by_name: str | None = None published_at: datetime created_at: datetime model_config = ConfigDict(from_attributes=True) class SetupProjectionSkippedItem(BaseModel): site_id: str reason: str class SetupProjectionSummary(BaseModel): study_updated: bool = False site_updated_count: int = 0 site_skipped_count: int = 0 warnings: list[str] = Field(default_factory=list) skipped_items: list[SetupProjectionSkippedItem] = Field(default_factory=list) class StudySetupConfigRead(BaseModel): id: uuid.UUID study_id: uuid.UUID version: int data: StudySetupConfigData publish_status: str published_data: StudySetupConfigData | None = None published_by: uuid.UUID | None = None published_by_name: str | None = None published_at: datetime | None = None saved_by: uuid.UUID | None saved_by_name: str | None = None projection_status: str | None = None projection_summary: SetupProjectionSummary | None = None created_at: datetime updated_at: datetime model_config = ConfigDict(from_attributes=True)