Files
ctms/backend/app/schemas/study_setup_config.py
Cheng Zhou 41968384b5 完善后台管理与测试支撑
1、为后台用户列表增加关键词和状态筛选,后端同步支持过滤与总数统计。

2、创建用户时要求填写初始密码,并补充前端校验和自动填充隔离。

3、优化项目管理列表角色展示、项目详情抽屉脏数据保护和审计详情抽屉关闭体验。

4、清理旧费用附件关联、统一测试模型注册,并更新迁移端点和注册筛选测试。
2026-06-04 11:12:40 +08:00

193 lines
5.3 KiB
Python

import uuid
from datetime import datetime
from typing import Optional
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: Optional[ProjectPublishSnapshot] = 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)