Files
student_manage_system/routers/stats.py
T

129 lines
4.5 KiB
Python

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List, Optional
from database import get_db
from models import Student, Class, Score, Employment
router = APIRouter(prefix="/api/stats", tags=["stats"])
@router.get("/classes")
def class_stats(db: Session = Depends(get_db)):
"""按班级统计学生人数和性别分布"""
results = (
db.query(
Student.class_id,
Class.name.label("class_name"),
func.count(Student.id).label("total"),
func.sum(func.if_(Student.sex == 1, 1, 0)).label("male"),
func.sum(func.if_(Student.sex == 2, 1, 0)).label("female"),
)
.join(Class, Student.class_id == Class.id)
.filter(Student.is_deleted == False, Class.is_deleted == False)
.group_by(Student.class_id, Class.name)
.all()
)
return [
{
"class_id": r.class_id,
"class_name": r.class_name,
"total_students": r.total,
"male_count": r.male,
"female_count": r.female,
}
for r in results
]
@router.get("/scores")
def score_stats(class_id: Optional[int] = None, score_num: Optional[str] = None, db: Session = Depends(get_db)):
"""成绩统计:平均分、最高分、最低分、人数"""
query = (
db.query(
Score.num.label("score_num"),
Student.class_id,
Class.num.label("class_num"),
Class.name.label("class_name"),
func.avg(Score.score).label("avg_score"),
func.max(Score.score).label("max_score"),
func.min(Score.score).label("min_score"),
func.count(Score.id).label("count"),
)
.join(Student, Score.sid == Student.id)
# 添加班级名称
.join(Class, Student.class_id == Class.id)
.filter(Student.is_deleted == False, Score.is_deleted == False)
.group_by(Score.num, Student.class_id)
)
if class_id:
query = query.filter(Student.class_id == class_id)
if score_num:
query = query.filter(Score.num == score_num)
results = query.all()
return [
{
"score_num": r.score_num,
"class_id": r.class_id,
"class_num": r.class_num,
"class_name": r.class_name,
"avg_score": float(r.avg_score),
"max_score": float(r.max_score),
"min_score": float(r.min_score),
"count": r.count,
}
for r in results
]
@router.get("/employment")
def employment_stats(class_id: Optional[int] = None, db: Session = Depends(get_db)):
"""就业统计:已就业人数、总人数、平均薪资、平均签约周期"""
from sqlalchemy import cast, Integer
query = (
db.query(
Employment.class_id,
Class.name.label("class_name"),
func.count(Employment.id).label("employed_count"),
func.count(Student.id).label("total_count"),
func.avg(Employment.employment_salary).label("avg_salary"),
)
.join(Class, Employment.class_id == Class.id)
.join(Student, Employment.sid == Student.id)
.filter(Employment.is_deleted == False, Student.is_deleted == False, Class.is_deleted == False)
.group_by(Employment.class_id, Class.name)
)
if class_id:
query = query.filter(Employment.class_id == class_id)
results = query.all()
output = []
for r in results:
# 计算平均签约周期(天数)
durations = (
db.query(
func.ceil(
func.cast(func.julianday(Employment.offer_recived_time) - func.julianday(Employment.employment_open_time), Integer)
)
)
.filter(
Employment.class_id == r.class_id,
Employment.is_deleted == False,
Employment.employment_open_time.isnot(None),
Employment.offer_recived_time.isnot(None),
)
.all()
)
avg_days = sum(d[0] for d in durations if d[0] is not None) / len(durations) if durations else None
output.append({
"class_id": r.class_id,
"class_name": r.class_name,
"employed_count": r.employed_count,
"total_count": r.total_count,
"avg_salary": float(r.avg_salary) if r.avg_salary else None,
"avg_duration_days": round(avg_days, 1) if avg_days is not None else None,
})
return output