917ab7ccf1
Add early termination visit workflow with ordering, non-applicable visit handling, visit window display, and medication adherence support. Extend monitoring visit issue template fields, site scoping, setup draft project info handling, login security UI, attachment behavior, and related tests/migrations.
202 lines
5.5 KiB
Python
202 lines
5.5 KiB
Python
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 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: 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)
|
|
monitoringStrategies: list[MonitoringStrategyItem] = 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)
|