feat(题目页): 「统计」页签重做——学生看难不难、几次做对、常错在哪,老师按班看谁没做对
- 全部按人算:原来的「通过数」是 AC 条数、「通过率」按提交算;击败用户、饼图、历年 AC 率去掉 - 错得最多的那种给一句该怎么办(都指向「运行例子」),自己交了 3 次以上还没对再多一张卡 - 老师选一个班:做对 / 交了没对(次数、最后结果)/ 没交;班里错得和全站不一样时点出来 - 题库入口工具栏的「课堂统计」并进这里(题单没有页签,按钮留着) - 新接口 /problems/:id/stats(按内部题号,比赛题不再查到同号公开题),删掉 beat-count、yearly-ac Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
91d9c9c6d7
commit
99319fdcc5
10 files changed
+1017
-317
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod"
|
||||
|
||||
import { paginatedSchema, sampleUserSchema } from "./common"
|
||||
import { judgeStatusSchema } from "./judge-status"
|
||||
import { problemLanguageSchema } from "./language"
|
||||
|
||||
/**
|
||||
@@ -446,11 +447,64 @@ export const problemAuthorSchema = z.object({
|
||||
problemCount: z.number().int().nonnegative(),
|
||||
})
|
||||
|
||||
export const yearlyAcSchema = z.object({
|
||||
year: z.number().int(),
|
||||
total: z.number().int().nonnegative(),
|
||||
accepted: z.number().int().nonnegative(),
|
||||
acRate: z.number(),
|
||||
const failureCountSchema = z.object({ result: judgeStatusSchema, count: z.number().int() })
|
||||
|
||||
/** 老师选中一个班时的明细:谁做对了、谁交了没对(卡在哪)、谁还没交 */
|
||||
export const problemClassDetailSchema = z.object({
|
||||
className: z.string(),
|
||||
/** 班里的学生数(学生角色、没禁用) */
|
||||
roster: z.number().int(),
|
||||
/** 这个班一起做这道题的那天(东八区 YYYY-MM-DD,交的人最多的那天)—— 「2025年12月5日做的」 */
|
||||
day: z.string(),
|
||||
failures: z.array(failureCountSchema),
|
||||
/** 交了没对,次数多的在前。名字是剥掉 ks<班级号> 前缀的那一段 */
|
||||
unsolved: z.array(
|
||||
z.object({ realName: z.string(), attempts: z.number().int(), lastResult: judgeStatusSchema }),
|
||||
),
|
||||
solved: z.array(z.string()),
|
||||
/** 班里还没交过的 */
|
||||
untouched: z.array(z.string()),
|
||||
})
|
||||
|
||||
/**
|
||||
* 题目页「统计」页签。全部**按人**算,不按提交条数 —— 原来的「通过数」是 AC 条数
|
||||
* (3092 是 219 条、202 个人),「通过率」是按提交算的,学生看了不知道这题难不难。
|
||||
* 原来的「击败用户」(分母是近两年所有登录过的人)、饼图、历年 AC 率一起去掉了。
|
||||
*/
|
||||
export const problemStatsSchema = z.object({
|
||||
/** 比赛还没结束(又不是比赛管理员):别人的情况一律不给,下面的数字都是 0 / 空 */
|
||||
locked: z.boolean(),
|
||||
/** 交过的人 */
|
||||
tried: z.number().int(),
|
||||
solved: z.number().int(),
|
||||
/** 做对的人用了几次:1 次 / 2–3 次 / 4 次以上 */
|
||||
tries: z.object({ one: z.number().int(), few: z.number().int(), many: z.number().int() }),
|
||||
/** 没通过的提交按结果分,多的在前(不含判题中) */
|
||||
failures: z.array(failureCountSchema),
|
||||
/** 答案错误里第 1 个测试点就没过的条数 —— 多半是例子都没对上,提示先点「运行例子」 */
|
||||
wrongAnswerFirstCase: z.number().int(),
|
||||
/** 我自己:交了几次、做对没有。没登录为 null */
|
||||
me: z.object({ attempts: z.number().int(), solved: z.boolean() }).nullable(),
|
||||
/** 学生自己班这题 x / y 人做对(y 是班里交过的人)。没班级、比赛里为 null */
|
||||
myClass: z
|
||||
.object({ className: z.string(), tried: z.number().int(), solved: z.number().int() })
|
||||
.nullable(),
|
||||
/**
|
||||
* 老师:一起做过这题的班(同一天至少 5 个人交过 —— 零星几个人自己刷到、补做的不算),
|
||||
* 按那天倒序。原来工具栏上的「课堂统计」并进了这里。学生、比赛里为 null
|
||||
*/
|
||||
classes: z
|
||||
.array(
|
||||
z.object({
|
||||
className: z.string(),
|
||||
tried: z.number().int(),
|
||||
solved: z.number().int(),
|
||||
day: z.string(),
|
||||
}),
|
||||
)
|
||||
.nullable(),
|
||||
/** 老师选中的那个班(默认 classes 第一个)的明细 */
|
||||
classDetail: problemClassDetailSchema.nullable(),
|
||||
})
|
||||
|
||||
export type AstRuleEngine = z.infer<typeof astRuleEngineSchema>
|
||||
@@ -468,4 +522,5 @@ export type SqlConfig = z.infer<typeof sqlConfigSchema>
|
||||
export type SqlDisplay = z.infer<typeof sqlDisplaySchema>
|
||||
export type SqlDisplayTable = z.infer<typeof sqlDisplayTableSchema>
|
||||
export type SqlDisplayColumn = z.infer<typeof sqlDisplayColumnSchema>
|
||||
export type YearlyAc = z.infer<typeof yearlyAcSchema>
|
||||
export type ProblemStats = z.infer<typeof problemStatsSchema>
|
||||
export type ProblemClassDetail = z.infer<typeof problemClassDetailSchema>
|
||||
Reference in new issue
Block a user