File size: 6,733 Bytes
ad05511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f3c81c5
ad05511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
"""
用户输入数据模型定义
使用Pydantic进行数据验证
"""

from pydantic import BaseModel, Field
from typing import Optional, List
from enum import Enum

class RiskLevel(str, Enum):
    """风险等级枚举"""
    LOW = "low"
    MEDIUM = "medium" 
    HIGH = "high"

class ModelType(str, Enum):
    """模型类型枚举"""
    SARCO_I = "sarcoI"
    SARCO_II = "sarcoII"

class UserInput(BaseModel):
    """用户输入数据模型"""
    
    # 基本信息 (注意:本系统适用于18-55岁成年人群)
    age_years: float = Field(..., ge=16, le=120, description="年龄(岁) - 推荐18-55岁")
    race_ethnicity: int = Field(..., ge=0, le=4, description="种族/民族编码(0-4)")
    
    # 身体指标  
    body_mass_index: float = Field(..., ge=10, le=80, description="体质量指数BMI")
    WWI: float = Field(..., ge=3, le=50, description="腰围重量指数")
    
    # NHANES PAQ 原始问卷数据
    # 工作相关活动
    PAQ605: int = Field(..., ge=1, le=2, description="工作高强度活动(1=是,2=否)")
    PAQ610: Optional[int] = Field(0, ge=0, le=7, description="工作高强度活动天数/周")
    PAD615: Optional[float] = Field(0, ge=0, le=1440, description="工作高强度活动分钟数/天")
    PAQ620: int = Field(..., ge=1, le=2, description="工作中等强度活动(1=是,2=否)")
    PAQ625: Optional[int] = Field(0, ge=0, le=7, description="工作中等强度活动天数/周")
    PAD630: Optional[float] = Field(0, ge=0, le=1440, description="工作中等强度活动分钟数/天")
    
    # 交通相关活动
    PAQ635: int = Field(..., ge=1, le=2, description="步行/骑车通勤(1=是,2=否)")
    PAQ640: Optional[int] = Field(0, ge=0, le=7, description="步行/骑车通勤天数/周")
    PAD645: Optional[float] = Field(0, ge=0, le=1440, description="步行/骑车通勤分钟数/天")
    
    # 休闲运动活动
    PAQ650: int = Field(..., ge=1, le=2, description="休闲高强度活动(1=是,2=否)")
    PAQ655: Optional[int] = Field(0, ge=0, le=7, description="休闲高强度活动天数/周")
    PAD660: Optional[float] = Field(0, ge=0, le=1440, description="休闲高强度活动分钟数/天")
    PAQ665: int = Field(..., ge=1, le=2, description="休闲中等强度活动(1=是,2=否)")
    PAQ670: Optional[int] = Field(0, ge=0, le=7, description="休闲中等强度活动天数/周")
    PAD675: Optional[float] = Field(0, ge=0, le=1440, description="休闲中等强度活动分钟数/天")
    
    # 久坐行为
    PAD680: float = Field(..., ge=0, le=1440, description="每日久坐时间(分钟)")
    
    # 医疗史 (筛查模型必需)
    arthritis: Optional[int] = Field(0, ge=0, le=1, description="关节炎史(0=无,1=有)")
    diabetes: Optional[int] = Field(0, ge=0, le=1, description="糖尿病史(0=无,1=有)")
    
    # 计算得出的衍生特征 (前端计算后传入)
    Activity_Sedentary_Ratio: Optional[float] = Field(None, ge=0, le=100, description="久坐活动比例")
    Total_Moderate_Minutes_week: Optional[float] = Field(None, ge=0, le=10080, description="每周中等强度运动时间(分钟)")
    Total_Moderate_Equivalent_Minutes: Optional[float] = Field(None, ge=0, le=10080, description="每周中等强度等效运动时间(分钟)")
    Avg_Vigorous_Duration_Per_Bout: Optional[float] = Field(None, ge=0, le=1440, description="平均高强度运动时长每次(分钟)")
    Vigorous_MET_Ratio: Optional[float] = Field(None, ge=0, le=10, description="高强度运动比例")
    Activity_Diversity_Index: Optional[float] = Field(None, ge=0, le=5, description="活动多样性指数(0-5)")
    sedentary_minutes: Optional[float] = Field(None, ge=0, le=1440, description="每日久坐时间(分钟,同PAD680)")

    class Config:
        json_schema_extra = {
            "example": {
                "age_years": 45,
                "race_ethnicity": 2,
                "body_mass_index": 28.5,
                "WWI": 11.2,
                "PAQ605": 2,
                "PAQ620": 1,
                "PAQ625": 3,
                "PAD630": 60,
                "PAQ635": 1,
                "PAQ640": 5,
                "PAD645": 30,
                "PAQ650": 1,
                "PAQ655": 2,
                "PAD660": 45,
                "PAQ665": 1,
                "PAQ670": 3,
                "PAD675": 60,
                "PAD680": 480,
                "arthritis": 0,
                "diabetes": 0,
                "Activity_Sedentary_Ratio": 0.6,
                "Total_Moderate_Minutes_week": 150,
                "Total_Moderate_Equivalent_Minutes": 150,
                "Avg_Vigorous_Duration_Per_Bout": 30,
                "Vigorous_MET_Ratio": 0.3,
                "Activity_Diversity_Index": 3,
                "sedentary_minutes": 480
            }
        }

class ScreeningRequest(BaseModel):
    """筛查请求模型"""
    user_data: UserInput
    models: List[ModelType] = [ModelType.SARCO_I, ModelType.SARCO_II]

class AdvisoryRequest(BaseModel):
    """建议请求模型"""
    user_data: UserInput
    risk_types: List[ModelType] = []
    num_recommendations: int = Field(default=3, ge=1, le=5, description="建议数量")
    language: str = Field(default="zh", description="语言设置 (zh/en)")

class ScreeningResponse(BaseModel):
    """筛查响应模型"""
    # 筛查模型结果 (高召回率)
    sarcoI_risk: RiskLevel
    sarcoI_probability: float
    sarcoI_threshold: float
    sarcoII_risk: RiskLevel
    sarcoII_probability: float
    sarcoII_threshold: float

    # 建议模型结果 (高精确率)
    sarcoI_advisory_risk: Optional[RiskLevel] = None
    sarcoI_advisory_probability: Optional[float] = None
    sarcoI_advisory_threshold: Optional[float] = None
    sarcoII_advisory_risk: Optional[RiskLevel] = None
    sarcoII_advisory_probability: Optional[float] = None
    sarcoII_advisory_threshold: Optional[float] = None

    # 综合结果
    overall_risk: RiskLevel
    confidence: float
    processing_time: float

class RecommendationItem(BaseModel):
    """单个建议项"""
    title: str
    description: str
    priority: str
    target_change: Optional[str] = None
    expected_impact: Optional[str] = None

class AdvisoryResponse(BaseModel):
    """建议响应模型"""
    sarcoI_recommendations: List[RecommendationItem]
    sarcoII_recommendations: List[RecommendationItem]
    recommendations: Optional[List[RecommendationItem]] = None  # 合并的建议列表,用于前端显示
    priority_actions: List[str]
    target_metrics: dict
    processing_time: float
    success: bool
    fallback_used: bool = False

class ErrorResponse(BaseModel):
    """错误响应模型"""
    error: str
    detail: str
    timestamp: str