import uuid from datetime import datetime from pydantic import BaseModel, ConfigDict, Field, model_validator class ProjectMilestoneItem(BaseModel): id: str name: str = "" planDate: str = "" startDate: str = "" endDate: str = "" durationDays: int = 1 owner: str = "" remark: str = "" status: str = "未开始" 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: str = "未开始" class SiteEnrollmentPlanItem(BaseModel): id: str siteId: str = "" siteName: str = "" target: int = 0 startDate: str = "" endDate: str = "" note: str = "" stageBreakdown: str = "" class CenterConfirmItem(BaseModel): id: str siteId: str = "" siteName: str = "" confirmer: str = "" confirmStatus: str = "待确认" confirmDate: str = "" note: str = "" class VisitScheduleItem(BaseModel): visit_code: str = "" baseline_offset_days: int = 0 window_before_days: int = 0 window_after_days: int = 0 class ProjectPublishSnapshot(BaseModel): code: str = "" name: str = "" project_full_name: str = "" sponsor: str = "" protocol_no: str = "" lead_unit: str = "" principal_investigator: str = "" main_pm: str = "" research_analysis: str = "" research_product: str = "" control_product: str = "" indication: str = "" research_population: str = "" research_design: str = "" plan_start_date: str = "" plan_end_date: str = "" planned_site_count: int | None = None planned_enrollment_count: int | None = None status: str = "" visit_schedule: list[VisitScheduleItem] = Field(default_factory=list) @model_validator(mode="after") def normalize_visit_schedule(self): for item in self.visit_schedule: item.visit_code = item.visit_code.strip() return self class StudySetupConfigData(BaseModel): projectInfo: ProjectPublishSnapshot = Field(default_factory=ProjectPublishSnapshot) 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) centerConfirm: list[CenterConfirmItem] = Field(default_factory=list) class StudySetupConfigUpsert(BaseModel): expected_version: int | None = None data: StudySetupConfigData force_draft: bool = False class StudySetupConfigPublish(BaseModel): expected_version: int | None = None class StudySetupConfigRollback(BaseModel): expected_version: int | None = None target_version: int class StudySetupConfigCheckoutBranch(BaseModel): expected_version: int | None = None target_version: int class StudySetupConfigDraftAction(BaseModel): expected_version: int | None = None class StudySetupConfigMergeToMain(BaseModel): expected_version: int | None = None source_version: int class StudySetupConfigVersionRead(BaseModel): id: uuid.UUID study_id: uuid.UUID study_setup_config_id: uuid.UUID version: int display_version: int branch_name: str = "main" branch_seq: int = 1 version_label: str = "" source_version: int | None = None parent_version_id: uuid.UUID | None = None merged_from_version_id: uuid.UUID | None = None config: StudySetupConfigData published_project_snapshot: "ProjectPublishSnapshot | None" = None is_current_published: bool = False is_active_draft_base: bool = False 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 current_branch_name: str = "main" current_published_version_id: uuid.UUID | None = None current_published_version_label: str | None = None active_branch_base_version_id: uuid.UUID | None = None active_branch_base_version_label: str | None = None data: StudySetupConfigData publish_status: str published_data: StudySetupConfigData | None = None published_project_snapshot: ProjectPublishSnapshot | 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)