Files

131 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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位)")