立项配置页初步优化

This commit is contained in:
Cheng Zhou
2026-02-24 16:53:22 +08:00
parent 0693f09b1d
commit 8f3f717e48
124 changed files with 12519 additions and 2954 deletions
+8
View File
@@ -24,6 +24,8 @@ class AECreate(BaseModel):
causality: Optional[str] = None
description: Optional[str] = None
outcome: Optional[str] = None
is_sae: bool = False
is_susar: bool = False
class AEUpdate(BaseModel):
@@ -36,6 +38,8 @@ class AEUpdate(BaseModel):
action_taken: Optional[str] = None
outcome: Optional[str] = None
reported_to_sponsor: Optional[bool] = None
is_sae: Optional[bool] = None
is_susar: Optional[bool] = None
status: Optional[str] = None
description: Optional[str] = None
@@ -54,6 +58,8 @@ class AERead(BaseModel):
action_taken: Optional[str]
outcome: Optional[str]
reported_to_sponsor: bool
is_sae: bool
is_susar: bool
report_due_date: Optional[date]
status: str
description: Optional[str]
@@ -70,6 +76,8 @@ class AERead(BaseModel):
if value is None:
return AESeriousness.I
normalized = str(value).strip().upper()
if normalized.startswith("AESERIOUSNESS."):
normalized = normalized.split(".", 1)[1]
digit_map = {
"1": "I",
"2": "II",
+10 -3
View File
@@ -1,5 +1,5 @@
import uuid
from datetime import datetime
from datetime import date, datetime
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -11,7 +11,9 @@ class SiteCreate(BaseModel):
pi_name: Optional[str] = None
contact: Optional[str] = None
is_active: bool = True
enrollment_target: Optional[int] = None
enrollment_plan_start_date: Optional[date] = None
enrollment_plan_end_date: Optional[date] = None
enrollment_plan_note: Optional[str] = None
class SiteUpdate(BaseModel):
@@ -20,7 +22,9 @@ class SiteUpdate(BaseModel):
pi_name: Optional[str] = None
contact: Optional[str] = None
is_active: Optional[bool] = None
enrollment_target: Optional[int] = None
enrollment_plan_start_date: Optional[date] = None
enrollment_plan_end_date: Optional[date] = None
enrollment_plan_note: Optional[str] = None
class SiteRead(BaseModel):
@@ -32,6 +36,9 @@ class SiteRead(BaseModel):
contact: Optional[str]
is_active: bool
enrollment_target: Optional[int]
enrollment_plan_start_date: Optional[date]
enrollment_plan_end_date: Optional[date]
enrollment_plan_note: Optional[str]
created_at: datetime
model_config = ConfigDict(from_attributes=True)
+59 -2
View File
@@ -1,5 +1,5 @@
import uuid
from datetime import datetime
from datetime import date, datetime
from typing import Literal, Optional
from pydantic import BaseModel, ConfigDict, Field
@@ -9,8 +9,27 @@ StudyStatus = Literal["DRAFT", "ACTIVE", "CLOSED"]
class StudyCreate(BaseModel):
name: str = Field(min_length=1)
code: Optional[str] = None
code: str = Field(min_length=1)
project_full_name: Optional[str] = None
sponsor: Optional[str] = None
protocol_no: Optional[str] = None
lead_unit: Optional[str] = None
principal_investigator: Optional[str] = None
main_pm: Optional[str] = None
research_analysis: Optional[str] = None
research_product: Optional[str] = None
control_product: Optional[str] = None
indication: Optional[str] = None
research_population: Optional[str] = None
research_design: Optional[str] = None
plan_start_date: Optional[date] = None
plan_end_date: Optional[date] = None
planned_site_count: Optional[int] = None
planned_enrollment_count: Optional[int] = None
enrollment_monthly_goal_note: Optional[str] = None
enrollment_stage_breakdown: Optional[str] = None
summary_note: Optional[str] = None
objective_note: Optional[str] = None
phase: Optional[str] = None
status: StudyStatus = "DRAFT"
visit_interval_days: Optional[int] = None
@@ -22,7 +41,26 @@ class StudyCreate(BaseModel):
class StudyUpdate(BaseModel):
code: Optional[str] = None
name: Optional[str] = None
project_full_name: Optional[str] = None
sponsor: Optional[str] = None
protocol_no: Optional[str] = None
lead_unit: Optional[str] = None
principal_investigator: Optional[str] = None
main_pm: Optional[str] = None
research_analysis: Optional[str] = None
research_product: Optional[str] = None
control_product: Optional[str] = None
indication: Optional[str] = None
research_population: Optional[str] = None
research_design: Optional[str] = None
plan_start_date: Optional[date] = None
plan_end_date: Optional[date] = None
planned_site_count: Optional[int] = None
planned_enrollment_count: Optional[int] = None
enrollment_monthly_goal_note: Optional[str] = None
enrollment_stage_breakdown: Optional[str] = None
summary_note: Optional[str] = None
objective_note: Optional[str] = None
phase: Optional[str] = None
status: Optional[StudyStatus] = None
is_locked: Optional[bool] = None
@@ -36,7 +74,26 @@ class StudyRead(BaseModel):
id: uuid.UUID
code: str
name: str
project_full_name: Optional[str]
sponsor: Optional[str]
protocol_no: Optional[str]
lead_unit: Optional[str]
principal_investigator: Optional[str]
main_pm: Optional[str]
research_analysis: Optional[str]
research_product: Optional[str]
control_product: Optional[str]
indication: Optional[str]
research_population: Optional[str]
research_design: Optional[str]
plan_start_date: Optional[date]
plan_end_date: Optional[date]
planned_site_count: Optional[int]
planned_enrollment_count: Optional[int]
enrollment_monthly_goal_note: Optional[str]
enrollment_stage_breakdown: Optional[str]
summary_note: Optional[str]
objective_note: Optional[str]
phase: Optional[str]
status: StudyStatus
is_locked: bool
+137
View File
@@ -0,0 +1,137 @@
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 = ""
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)
+2
View File
@@ -34,5 +34,7 @@ class SubjectRead(BaseModel):
created_at: datetime
updated_at: datetime
has_ae: Optional[bool] = None
has_sae: Optional[bool] = None
has_susar: Optional[bool] = None
model_config = ConfigDict(from_attributes=True)