Files
stu_teacher/scheme/statistics.py
T

131 lines
4.4 KiB
Python
Raw Normal View History

2026-09-21 23:17:51 +08:00
# 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位)")