feat(课堂看板): 流程图画到 A / S 在老师的看板上也算做完,只画了图的学生不再显示「没开始」
和学生首页的课堂条同一个口径:格子的状态、做对的时间、尝试次数都把流程图提交算进去; 「最后一次提交」也算上今天画的图。有的题这节课整个班都在画流程图,原来看板上是满屏「没开始」 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
972dac3776
commit
b5d2f1589e
1 file changed
+43
-4
@@ -516,8 +516,44 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
|
|||||||
.groupBy(schema.submission.userId)
|
.groupBy(schema.submission.userId)
|
||||||
: [],
|
: [],
|
||||||
])
|
])
|
||||||
|
/**
|
||||||
|
* 流程图作业:画到 A / S 也算这道题做完,和学生首页的课堂条同一个口径(/me/class-activity)。
|
||||||
|
* 有的题这节课整个班都在画流程图,只数代码提交的话,看板上满屏「没开始」
|
||||||
|
*/
|
||||||
|
const drawn =
|
||||||
|
ids.length && userIds.length
|
||||||
|
? await db
|
||||||
|
.select({
|
||||||
|
userId: schema.flowchartSubmission.userId,
|
||||||
|
problemId: schema.flowchartSubmission.problemId,
|
||||||
|
attempts: sql<number>`count(*) filter (where ${gte(schema.flowchartSubmission.createTime, start)})::int`,
|
||||||
|
firstPassedAt: sql<
|
||||||
|
string | null
|
||||||
|
>`min(${schema.flowchartSubmission.createTime}) filter (where ${and(eq(schema.flowchartSubmission.status, 2), inArray(schema.flowchartSubmission.aiGrade, FLOWCHART_PASS_GRADES))})`,
|
||||||
|
lastAt: sql<
|
||||||
|
string | null
|
||||||
|
>`max(${schema.flowchartSubmission.createTime}) filter (where ${gte(schema.flowchartSubmission.createTime, start)})`,
|
||||||
|
})
|
||||||
|
.from(schema.flowchartSubmission)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
inArray(schema.flowchartSubmission.userId, userIds),
|
||||||
|
inArray(schema.flowchartSubmission.problemId, ids),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.groupBy(schema.flowchartSubmission.userId, schema.flowchartSubmission.problemId)
|
||||||
|
: []
|
||||||
const cellByKey = new Map(cells.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
const cellByKey = new Map(cells.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
||||||
|
const drawnByKey = new Map(drawn.map((row) => [`${row.userId}:${row.problemId}`, row]))
|
||||||
const lastByUser = new Map(lastSubmits.map((row) => [row.userId, row.lastAt]))
|
const lastByUser = new Map(lastSubmits.map((row) => [row.userId, row.lastAt]))
|
||||||
|
// 今天只画了流程图的学生,「最后一次提交」也得算上画图,不然他在看板上是一直没动手
|
||||||
|
for (const row of drawn) {
|
||||||
|
if (!row.lastAt) continue
|
||||||
|
const known = lastByUser.get(row.userId)
|
||||||
|
if (!known || Date.parse(row.lastAt) > Date.parse(known)) lastByUser.set(row.userId, row.lastAt)
|
||||||
|
}
|
||||||
|
const earliest = (a: string | null | undefined, b: string | null | undefined) =>
|
||||||
|
!a ? (b ?? null) : !b ? a : Date.parse(a) <= Date.parse(b) ? a : b
|
||||||
const attendedByUser = new Map(attendance.map((row) => [row.userId, row.days]))
|
const attendedByUser = new Map(attendance.map((row) => [row.userId, row.days]))
|
||||||
|
|
||||||
return success(c, {
|
return success(c, {
|
||||||
@@ -536,11 +572,14 @@ classroomRoutes.get("/classroom/board", requireTeacher, async (c) => {
|
|||||||
username: student.username,
|
username: student.username,
|
||||||
realName: student.realName ?? null,
|
realName: student.realName ?? null,
|
||||||
cells: ids.map((id) => {
|
cells: ids.map((id) => {
|
||||||
const cell = cellByKey.get(`${student.userId}:${id}`)
|
const key = `${student.userId}:${id}`
|
||||||
|
const cell = cellByKey.get(key)
|
||||||
|
const flow = drawnByKey.get(key)
|
||||||
|
const acceptedAt = earliest(cell?.firstAcceptedAt, flow?.firstPassedAt)
|
||||||
return {
|
return {
|
||||||
status: !cell ? "none" : cell.firstAcceptedAt ? "accepted" : "tried",
|
status: !cell && !flow ? "none" : acceptedAt ? "accepted" : "tried",
|
||||||
attempts: cell?.attempts ?? 0,
|
attempts: (cell?.attempts ?? 0) + (flow?.attempts ?? 0),
|
||||||
acceptedAt: cell?.firstAcceptedAt ?? null,
|
acceptedAt,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
lastSubmitAt: lastByUser.get(student.userId) ?? null,
|
lastSubmitAt: lastByUser.get(student.userId) ?? null,
|
||||||
|
|||||||
Reference in new issue
Block a user