"""统计分析模块出入参(需求 2.6)。""" from __future__ import annotations from datetime import date from typing import Any from pydantic import BaseModel, Field class ClassOverviewItem(BaseModel): """2.6.1 多维度班级统计:每班总人数 + 男女分布。""" class_id: int class_no: str class_name: str status_text: str | None = None head_teacher_name: str | None = None total: int = Field(0, description="总人数") male: int = Field(0, description="男生数") female: int = Field(0, description="女生数") other: int = Field(0, description="未标注性别人数") class AllAboveItem(BaseModel): """2.6.2 每次考试都过线的学生。""" stu_id: int stu_no: str name: str class_name: str | None = None exam_count: int = Field(0, description="参加考核次数") min_score: float = Field(0, description="最低分(就是它决定了过线)") avg_score: float = Field(0, description="平均分") scores: list[dict[str, Any]] = Field(default_factory=list, description="各次成绩明细") class FailDetailItem(BaseModel): """2.6.2 不及格明细。""" stu_id: int stu_no: str name: str class_name: str | None = None fail_times: int = Field(0, description="不及格次数") fail_details: list[dict[str, Any]] = Field(default_factory=list, description="不及格场次明细") class ClassAvgScoreItem(BaseModel): """2.6.2 每场考试每个班级的平均分。""" exam_seq: int class_id: int | None = None class_name: str | None = None student_count: int = Field(0, description="参考人数") avg_score: float = Field(0, description="平均分") max_score: float = 0 min_score: float = 0 pass_rate: float = Field(0, description="及格率 %") class SalaryTopItem(BaseModel): """2.6.3 就业薪资 Top N。""" rank: int stu_id: int stu_no: str name: str class_name: str | None = None company: str | None = None salary: float = 0 salary_wan: float = 0 offer_date: date | None = None duration_days: int | None = None class StudentDurationItem(BaseModel): """2.6.3 每个学生的就业时长。""" stu_id: int stu_no: str name: str class_name: str | None = None company: str | None = None open_date: date | None = None offer_date: date | None = None duration_days: int | None = Field(None, description="offer 时间 - 开放时间(天)") state: str = Field("", description="就业中 / 已拿到 offer") class ClassAvgDurationItem(BaseModel): """2.6.3 每个班级的平均就业时长。""" class_id: int | None = None class_name: str | None = None student_count: int = Field(0, description="班级总人数") open_count: int = Field(0, description="已开放就业的人数(统计基数)") offer_count: int = Field(0, description="已下发 offer 的人数") avg_duration_days: float | None = Field(None, description="平均就业时长(天)") min_duration_days: int | None = None max_duration_days: int | None = None class VolatilityItem(BaseModel): """2.7.2 成绩波动分析。""" rank: int stu_id: int stu_no: str name: str class_name: str | None = None exam_count: int = 0 avg_score: float = 0 stddev: float = Field(0, description="成绩标准差") score_range: float = Field(0, description="最大分差 max-min") trend: str = Field("", description="上升 / 下降 / 基本持平(按最小二乘斜率,阈值 ±1 分/场)") trend_slope: float = Field(0, description="成绩趋势斜率,单位:分/场(对考核序次做最小二乘拟合)") scores: list[float] = Field(default_factory=list, description="按序次排列的成绩") class FunnelItem(BaseModel): """2.7.2 班级就业漏斗。""" class_id: int class_no: str class_name: str total: int = Field(0, description="总人数") employed: int = Field(0, description="已就业人数(拿到 offer)") high_salary: int = Field(0, description="高薪人数(>= 高薪线)") employment_rate: float = Field(0, description="就业率 %") high_salary_rate: float = Field(0, description="高薪占已就业比例 %") avg_salary: float | None = Field(None, description="已就业学生平均薪资") avg_duration_days: float | None = Field(None, description="平均就业时长(天)") class OverviewOut(BaseModel): """仪表盘概览。""" student_total: int = 0 student_by_status: dict[str, int] = Field(default_factory=dict, description="各状态人数") class_total: int = 0 teacher_total: int = 0 advisor_total: int = 0 employment_total: int = 0 employment_rate: float = Field(0, description="整体就业率 %") avg_salary: float | None = None high_salary_count: int = 0 score_record_total: int = 0 score_avg: float | None = None warning_student_count: int = Field(0, description="存在红线预警的学生数") top_classes: list[dict[str, Any]] = Field(default_factory=list, description="就业率前 5 班级")