feat: add student number limit and time range for chart

main
ccicnce113424 5 months ago
parent 695bce53b5
commit e10d0aab58

@ -361,7 +361,19 @@ def score_student():
@main_bp.route("/api/stats/top_students_timeline")
@login_required
def top_students_timeline():
# 选取当前账号积分前5名学生
# 可选参数top_n, days
try:
top_n = int(request.args.get("top_n", 5))
except ValueError:
top_n = 5
if top_n <= 0:
top_n = 5
if top_n > 10:
top_n = 10
days = request.args.get("days") # None / "7" / "30" 等
# 选取当前账号积分前 top_n 名学生
top_students = (
Student.query.filter_by(user_id=current_user.id)
.order_by(
@ -369,7 +381,7 @@ def top_students_timeline():
Student.attendance_count.desc(),
Student.student_no.asc(),
)
.limit(5)
.limit(top_n)
.all()
)
@ -378,16 +390,25 @@ def top_students_timeline():
student_ids = [s.id for s in top_students]
# 取这些学生的所有点名记录,按时间排序
records = (
RollCall.query.filter(
RollCall.user_id == current_user.id,
RollCall.student_id.in_(student_ids),
)
.order_by(RollCall.call_time.asc())
.all()
# 取这些学生的所有点名记录,按时间排序,可选时间范围
query = RollCall.query.filter(
RollCall.user_id == current_user.id,
RollCall.student_id.in_(student_ids),
)
if days:
from datetime import datetime, timedelta
try:
days_int = int(days)
if days_int > 0:
cutoff_utc = datetime.utcnow() - timedelta(days=days_int)
query = query.filter(RollCall.call_time >= cutoff_utc)
except ValueError:
pass
records = query.order_by(RollCall.call_time.asc()).all()
if not records:
return jsonify({"labels": [], "series": []})

@ -4,6 +4,36 @@
<div class="row mb-3">
<div class="col-md-8">
<h4>积分随时间走势图(前几名)</h4>
<form class="row g-2 mb-2" id="chart-filter-form">
<div class="col-auto">
<label class="col-form-label">时间范围</label>
</div>
<div class="col-auto">
<select id="filter-days" class="form-select form-select-sm">
<option value="">全部</option>
<option value="7">最近7天</option>
<option value="30">最近30天</option>
</select>
</div>
<div class="col-auto">
<label class="col-form-label">前 N 名</label>
</div>
<div class="col-auto">
<input
type="number"
id="filter-top-n"
class="form-control form-control-sm"
value="5"
min="1"
max="10"
/>
</div>
<div class="col-auto">
<button type="button" id="btn-refresh-chart" class="btn btn-sm btn-outline-primary">
更新
</button>
</div>
</form>
<canvas id="scoreChart" height="150"></canvas>
</div>
<div class="col-md-4 text-end align-self-end">
@ -91,8 +121,21 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script>
let chartInstance = null;
async function loadScoreChart() {
const resp = await fetch("/api/stats/top_students_timeline");
const days = document.getElementById("filter-days").value;
const topNInput = document.getElementById("filter-top-n");
let topN = parseInt(topNInput.value || "5", 10);
if (Number.isNaN(topN) || topN <= 0) topN = 5;
if (topN > 10) topN = 10;
topNInput.value = topN;
const params = new URLSearchParams();
params.set("top_n", topN.toString());
if (days) params.set("days", days);
const resp = await fetch(`/api/stats/top_students_timeline?${params.toString()}`);
const data = await resp.json();
if (!data.labels || data.labels.length === 0) {
return;
@ -107,7 +150,11 @@
"#6610f2",
];
new Chart(ctx, {
if (chartInstance) {
chartInstance.destroy();
}
chartInstance = new Chart(ctx, {
type: "line",
data: {
labels: data.labels,
@ -134,6 +181,11 @@
});
}
document.addEventListener("DOMContentLoaded", loadScoreChart);
document.addEventListener("DOMContentLoaded", () => {
document
.getElementById("btn-refresh-chart")
.addEventListener("click", loadScoreChart);
loadScoreChart();
});
</script>
{% endblock %}

Loading…
Cancel
Save