From 4e678a7a0ff0059a1e71dbb2feb06270e429e798 Mon Sep 17 00:00:00 2001 From: ccicnce113424 Date: Sun, 23 Nov 2025 21:29:04 +0800 Subject: [PATCH] feat: leaderboard and data export --- app/main/routes.py | 103 ++++++++++++++++++++++++++++++++++++++- app/templates/stats.html | 89 ++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 3 deletions(-) diff --git a/app/main/routes.py b/app/main/routes.py index 9735053..62bbb29 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -30,7 +30,108 @@ def rollcall(): @main_bp.route("/stats") @login_required def stats(): - return render_template("stats.html") + # 排行榜:按总积分、出勤次数、学号排序 + students = ( + Student.query.filter_by(user_id=current_user.id) + .order_by( + Student.total_score.desc(), + Student.attendance_count.desc(), + Student.student_no.asc(), + ) + .all() + ) + + # 最近点名记录 + records = ( + db.session.query(RollCall, Student) + .join(Student, RollCall.student_id == Student.id) + .filter(RollCall.user_id == current_user.id) + .order_by(RollCall.call_time.desc()) + .limit(50) + .all() + ) + + return render_template("stats.html", students=students, records=records) + + +@main_bp.route("/stats/export/students") +@login_required +def export_students(): + # 导出当前账号学生积分详单 + students = ( + Student.query.filter_by(user_id=current_user.id) + .order_by(Student.student_no.asc()) + .all() + ) + + import csv + from io import StringIO + from flask import Response + + output = StringIO() + writer = csv.writer(output) + writer.writerow(["学号", "姓名", "专业", "出勤次数", "随机点名次数", "总积分"]) + for s in students: + writer.writerow( + [ + s.student_no, + s.name, + s.major or "", + s.attendance_count or 0, + s.random_called_count or 0, + f"{(s.total_score or 0):.1f}", + ] + ) + + output.seek(0) + return Response( + output.getvalue().encode("utf-8-sig"), + mimetype="text/csv; charset=utf-8", + headers={ + "Content-Disposition": "attachment; filename=students_scores.csv", + }, + ) + + +@main_bp.route("/stats/export/rollcalls") +@login_required +def export_rollcalls(): + # 导出当前账号点名记录 + records = ( + db.session.query(RollCall, Student) + .join(Student, RollCall.student_id == Student.id) + .filter(RollCall.user_id == current_user.id) + .order_by(RollCall.call_time.asc()) + .all() + ) + + import csv + from io import StringIO + from flask import Response + + output = StringIO() + writer = csv.writer(output) + writer.writerow(["时间", "学号", "姓名", "模式", "状态", "积分变化"]) + for rc, s in records: + writer.writerow( + [ + rc.call_time.strftime("%Y-%m-%d %H:%M:%S"), + s.student_no, + s.name, + rc.mode, + rc.status, + f"{rc.score_change:.1f}", + ] + ) + + output.seek(0) + return Response( + output.getvalue().encode("utf-8-sig"), + mimetype="text/csv; charset=utf-8", + headers={ + "Content-Disposition": "attachment; filename=rollcalls.csv", + }, + ) @main_bp.route("/manage") diff --git a/app/templates/stats.html b/app/templates/stats.html index 321c35d..11eec07 100644 --- a/app/templates/stats.html +++ b/app/templates/stats.html @@ -1,6 +1,91 @@ {% extends 'base.html' %} {% block title %}统计 - 课堂点名系统{% endblock %} {% block content %} -

统计页面占位

-

上半部分:积分随时间走势图;下半部分:排行榜;右侧:点名记录。

+
+
+

积分排行榜

+
+ +
+ +
+
+ + + + + + + + + + + + + + {% for s in students %} + + + + + + + + + + {% else %} + + + + {% endfor %} + +
名次学号姓名专业总积分出勤次数随机点名次数
{{ loop.index }}{{ s.student_no }}{{ s.name }}{{ s.major }}{{ '%.1f'|format(s.total_score or 0) }}{{ s.attendance_count or 0 }}{{ s.random_called_count or 0 }}
暂无学生数据。
+
+ +
+
最近点名记录
+
+ + + + + + + + + + + + + {% for rc, s in records %} + + + + + + + + + {% else %} + + + + {% endfor %} + +
时间姓名学号模式状态积分
{{ rc.call_time.strftime('%m-%d %H:%M') }}{{ s.name }}{{ s.student_no }}{{ '随机' if rc.mode == 'random' else '顺序' }} + {% if rc.status == 'absent' %}缺勤{% elif rc.status == 'distracted' %}走神{% else %}到课{% endif %} + {{ '%.1f'|format(rc.score_change or 0) }}
暂无点名记录。
+
+
+
{% endblock %}