131 lines
4.4 KiB
Python
131 lines
4.4 KiB
Python
# scheme/statistics.py
|
||
# 统计分析模块的请求/响应模型(含 2.7 通用高级筛选器)
|
||
from typing import Any, List, Literal, Union
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
# ============================================================
|
||
# 2.7.1 通用高级筛选器:支持 AND/OR 逻辑组合与嵌套 sub_rules
|
||
# ============================================================
|
||
class FilterRule(BaseModel):
|
||
"""单条筛选规则"""
|
||
field: str = Field(..., description="字段名,如 age/gender/salary/class_name")
|
||
operator: Literal[">", "<", "=", "!=", ">=", "<=", "like", "in"] = Field(
|
||
..., description="比较操作符"
|
||
)
|
||
value: Any = Field(..., description="比较值;operator=in 时必须是列表")
|
||
|
||
|
||
class FilterGroup(BaseModel):
|
||
"""逻辑组合规则:可嵌套 sub_rules 实现任意深度的 AND/OR 组合"""
|
||
logic: Literal["AND", "OR"] = Field("AND", description="组合方式")
|
||
sub_rules: List[Union["FilterGroup", "FilterRule"]] = Field(
|
||
..., min_length=1, description="子规则(可继续嵌套 FilterGroup)"
|
||
)
|
||
|
||
|
||
# Pydantic v2 递归模型需要显式 rebuild
|
||
FilterGroup.model_rebuild()
|
||
|
||
|
||
class FilterRequest(BaseModel):
|
||
"""高级筛选请求体"""
|
||
model: Literal["student"] = Field(..., description="目标模型(当前支持 student)")
|
||
rules: List[Union[FilterGroup, FilterRule]] = Field(
|
||
..., min_length=1, description="筛选规则列表(顶层默认 AND 连接)"
|
||
)
|
||
|
||
|
||
class FilterResponse(BaseModel):
|
||
total: int
|
||
items: List[dict] = Field(..., description="命中的学生记录")
|
||
|
||
|
||
# ============================================================
|
||
# 2.6 统计分析响应模型
|
||
# ============================================================
|
||
class ClassGenderStat(BaseModel):
|
||
"""2.6.1 多维度班级统计:总人数 + 性别分布"""
|
||
class_id: int
|
||
class_name: str
|
||
total: int
|
||
male: int
|
||
female: int
|
||
|
||
|
||
class AllAboveStudent(BaseModel):
|
||
"""2.6.2 每次考试都在分数线以上的学生"""
|
||
stu_id: int
|
||
stu_name: str
|
||
class_name: str
|
||
exam_count: int = Field(..., description="参加考核次数")
|
||
min_score: float = Field(..., description="最低分(>= 分数线)")
|
||
scores: List[dict] = Field(..., description="各次成绩明细 [{exam_id, score}]")
|
||
|
||
|
||
class FailStudent(BaseModel):
|
||
"""2.6.2 不及格次数 >= N 的学生"""
|
||
stu_id: int
|
||
stu_name: str
|
||
class_name: str
|
||
fail_count: int = Field(..., description="不及格次数")
|
||
fail_details: List[dict] = Field(..., description="不及格成绩明细 [{exam_id, score}]")
|
||
|
||
|
||
class ClassExamAvg(BaseModel):
|
||
"""2.6.2 每次考试每个班级的平均分(支持动态排序)"""
|
||
exam_id: int
|
||
class_id: int
|
||
class_name: str
|
||
avg_score: float
|
||
|
||
|
||
class TopSalaryStudent(BaseModel):
|
||
"""2.6.3 薪资 Top N"""
|
||
stu_id: int
|
||
stu_name: str
|
||
class_name: str
|
||
job_time: Any = Field(None, description="offer 下发时间")
|
||
company_name: str
|
||
salary: float
|
||
|
||
|
||
class EmploymentDuration(BaseModel):
|
||
"""2.6.3 单个学生就业时长(天)"""
|
||
stu_id: int
|
||
stu_name: str
|
||
class_name: str
|
||
employment_open_time: Any
|
||
job_time: Any = Field(None, description="未拿到 offer 为 null")
|
||
duration_days: int = Field(..., description="offer下发时间 - 就业开放时间;未拿到 offer 为 -1")
|
||
|
||
|
||
class ClassAvgDuration(BaseModel):
|
||
"""2.6.3 每个班级平均就业时长"""
|
||
class_id: int
|
||
class_name: str
|
||
opened_count: int = Field(..., description="进入就业阶段人数(有开放时间)")
|
||
offered_count: int = Field(..., description="已拿到 offer 人数")
|
||
avg_duration_days: float = Field(..., description="平均就业时长(仅统计已拿到 offer 的学生)")
|
||
|
||
|
||
class ScoreVolatility(BaseModel):
|
||
"""2.7.2 成绩波动分析:最大分差 Top N"""
|
||
stu_id: int
|
||
stu_name: str
|
||
class_name: str
|
||
max_score: float
|
||
min_score: float
|
||
diff: float = Field(..., description="最大分差 = 最高分 - 最低分")
|
||
|
||
|
||
class EmploymentFunnel(BaseModel):
|
||
"""2.7.2 班级就业漏斗:总人数 -> 已就业 -> 高薪(>10k) -> 就业率"""
|
||
class_id: int
|
||
class_name: str
|
||
total: int = Field(..., description="班级总人数")
|
||
employed: int = Field(..., description="已就业人数")
|
||
high_salary: int = Field(..., description="高薪人数(薪资 > 10000)")
|
||
employment_rate: float = Field(..., description="就业率(百分比,保留2位)")
|