默认看批改页当前的班级;顶部四个数字(已交、平均分、改过再交、待讲评), 未交和交了没人评的名单按班级分组并排显示;人气前五换成高分作品(每人最高的一版), 点开直接进批改页。接口去掉旧的四色标记统计和 top_viewed,新增 present_count、top_scored。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e4fb723544
commit
06267fb0ff
5 files changed
+540
-457
No files matched your search
@@ -448,25 +448,18 @@ router.get("/stats/:task_id", (c) => {
|
||||
else dist.count_4_plus++
|
||||
}
|
||||
|
||||
// 标记统计:该任务所有提交(不按学生过滤)
|
||||
const flagStats = { red: 0, blue: 0, green: 0, yellow: 0 }
|
||||
for (const s of subs) {
|
||||
if (s.flag && s.flag in flagStats) flagStats[s.flag as keyof typeof flagStats]++
|
||||
// 高分作品:每人分数最高的一份(同分取较早的),取前五
|
||||
const best = new Map<number, (typeof subs)[number]>()
|
||||
for (const s of studentSubs) {
|
||||
const cur = best.get(s.user_id)
|
||||
if (s.score > 0 && (!cur || s.score > cur.score || (s.score === cur.score && s.created < cur.created)))
|
||||
best.set(s.user_id, s)
|
||||
}
|
||||
|
||||
const topViewed = classname
|
||||
? all(
|
||||
`SELECT s.id, s.view_count, u.username, u.classname ${SUBMISSION_FROM}
|
||||
WHERE s.task_id = $id AND u.role = 'normal' AND u.classname = $classname
|
||||
ORDER BY s.view_count DESC LIMIT 5`,
|
||||
{ id: taskId, classname },
|
||||
)
|
||||
: all(
|
||||
`SELECT s.id, s.view_count, u.username, u.classname ${SUBMISSION_FROM}
|
||||
WHERE s.task_id = $id AND u.role = 'normal'
|
||||
ORDER BY s.view_count DESC LIMIT 5`,
|
||||
{ id: taskId },
|
||||
)
|
||||
const studentById = new Map(students.map((s) => [s.id, s]))
|
||||
const topScored = [...best.values()]
|
||||
.sort((a, b) => b.score - a.score || (a.created < b.created ? -1 : 1))
|
||||
.slice(0, 5)
|
||||
.map((s) => ({ ...tag(studentById.get(s.user_id)!), score: s.score, submission_id: uuidOut(s.id) }))
|
||||
|
||||
const unrated = students.filter((s) => submittedIds.has(s.id) && !ratedIds.has(s.id))
|
||||
return c.json({
|
||||
@@ -477,14 +470,9 @@ router.get("/stats/:task_id", (c) => {
|
||||
unsubmitted_users: students.filter((s) => !submittedIds.has(s.id)).map(tag),
|
||||
unrated_users: unrated.map(tag),
|
||||
submission_count_distribution: dist,
|
||||
flag_stats: flagStats,
|
||||
present_count: studentSubs.filter((s) => s.flag === "present").length,
|
||||
classes,
|
||||
top_viewed: topViewed.map((s) => ({
|
||||
username: s.username,
|
||||
classname: s.classname,
|
||||
view_count: s.view_count,
|
||||
submission_id: uuidOut(s.id),
|
||||
})),
|
||||
top_scored: topScored,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, test } from "bun:test"
|
||||
import { get, all } from "../src/db"
|
||||
import { get, all, run } from "../src/db"
|
||||
import { app } from "../src/app"
|
||||
import { config } from "../src/config"
|
||||
import { mkdtemp, readFile, readdir, rm } from "node:fs/promises"
|
||||
@@ -609,3 +609,40 @@ describe("同学作品与批改", () => {
|
||||
expect((await teacher("PUT", `/api/submission/${uuid(latest)}/flag`, { flag: "present" })).json.flag).toBe("present")
|
||||
})
|
||||
})
|
||||
|
||||
describe("课堂总结", () => {
|
||||
test("按班级统计,高分作品每人只取最高的一份", async () => {
|
||||
makeUser("adm", "admin")
|
||||
const task = makeTask("挑战", "challenge", 1)
|
||||
const a = makeUser("weba1小明", "normal", "a1")
|
||||
const b = makeUser("weba1小红", "normal", "a1")
|
||||
makeUser("weba1小刚", "normal", "a1")
|
||||
const other = makeUser("weba2小李", "normal", "a2")
|
||||
makeSubmission(a, task, 3, "2026-01-01 00:00:00.000000")
|
||||
const aBest = makeSubmission(a, task, 4.5, "2026-01-02 00:00:00.000000")
|
||||
makeSubmission(a, task, 4, "2026-01-03 00:00:00.000000")
|
||||
const bSub = makeSubmission(b, task, 0)
|
||||
run(`UPDATE submission_submission SET flag = 'present' WHERE id = $id`, { id: bSub })
|
||||
makeSubmission(other, task, 5)
|
||||
|
||||
const adm = await client("adm")
|
||||
const res = await adm("GET", `/api/submission/stats/${task}?classname=a1`)
|
||||
expect(res.status).toBe(200)
|
||||
expect(res.json).toMatchObject({
|
||||
submitted_count: 2,
|
||||
unsubmitted_count: 1,
|
||||
average_score: 4,
|
||||
unrated_count: 2,
|
||||
present_count: 1,
|
||||
classes: ["a1", "a2"],
|
||||
submission_count_distribution: { count_1: 1, count_2: 0, count_3: 1, count_4_plus: 0 },
|
||||
})
|
||||
expect(res.json.unsubmitted_users).toEqual([{ username: "weba1小刚", classname: "a1" }])
|
||||
expect(res.json.top_scored).toEqual([
|
||||
{ username: "weba1小明", classname: "a1", score: 4.5, submission_id: uuid(aBest) },
|
||||
])
|
||||
|
||||
const all = await adm("GET", `/api/submission/stats/${task}`)
|
||||
expect(all.json.top_scored.map((t: any) => t.username)).toEqual(["weba2小李", "weba1小明"])
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user