Files
secondary-school-grade-archive/backend/app/services/score_trend.py
T
dekun e329d3398a Initial commit: secondary school grade archive system.
Add FastAPI/React app with Docker deployment, Ubuntu one-click install, and docs for junior/senior high score tracking and mistake bank.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 11:18:58 +08:00

67 lines
2.0 KiB
Python

from sqlalchemy.orm import Session, joinedload
from app.core.config import settings
from app.models.user import ExamRecord, Subject, SubjectScore
from app.schemas import ExamTypeEnum, TrendPoint, TrendResponse
def build_trend(db: Session, student_id, subject_id: int) -> TrendResponse:
subject = db.get(Subject, subject_id)
if subject is None:
raise ValueError("科目不存在")
scores = (
db.query(SubjectScore)
.join(ExamRecord)
.options(joinedload(SubjectScore.exam_record))
.filter(
ExamRecord.student_id == student_id,
SubjectScore.subject_id == subject_id,
)
.order_by(ExamRecord.exam_date.asc(), ExamRecord.created_at.asc())
.all()
)
threshold = settings.FLUCTUATION_THRESHOLD
points: list[TrendPoint] = []
prev_ratio: float | None = None
for score in scores:
exam = score.exam_record
ratio = float(score.ratio)
delta = None if prev_ratio is None else ratio - prev_ratio
direction = None
is_volatile = False
if delta is not None:
if delta > 0:
direction = "up"
elif delta < 0:
direction = "down"
else:
direction = "flat"
is_volatile = abs(delta) >= threshold
points.append(
TrendPoint(
exam_id=exam.id,
exam_type=ExamTypeEnum(exam.exam_type.value),
exam_date=exam.exam_date,
title=exam.title,
ratio=ratio,
ratio_percent=round(ratio * 100, 2),
delta=delta,
delta_percent=round(delta * 100, 2) if delta is not None else None,
is_volatile=is_volatile,
direction=direction,
)
)
prev_ratio = ratio
return TrendResponse(
subject_id=subject_id,
subject_name=subject.name,
threshold=threshold,
points=points,
)