- 补档:做对 20 道、第一道中等题、交满 30 次、5 天交过题、一次就对 5 道、错 3 次后做对、一天做对 5 道; 新线「挑战成功」1 次 / 5 次 - 比赛里的提交也计入成就(比赛其实是单班测验,2612 班整班在比赛里做题,原来一个成就都没有); 参加过几场比赛改成判题时就更新,以前只在后台重扫时刷新 - 奖杯收藏家从 15 个提到 20 个 - 0044 迁移:按新口径从提交记录重数指标,给达标的人补发(已经拿到的不收回) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
/**
|
||
* 成就指标注册表。对齐旧后端 `achievement/metrics.py` 的 METRIC_REGISTRY ——
|
||
* 那边是装饰器注册,这里是一张表,作用一样:**后台下拉框里有什么,取决于代码里注册了什么**。
|
||
*
|
||
* 加一个新维度必须同时改这里和 `achievements.ts` 的计算逻辑并部署。
|
||
* 只加这里而不算,会造出一个谁也拿不到的成就;只算而不加这里,后台就选不到它。
|
||
*/
|
||
export interface AchievementMetric {
|
||
key: string
|
||
name: string
|
||
helpText: string
|
||
/** 元指标:统计的是「已解锁成就数」自身,判定要在其它成就结算完之后再跑一轮 */
|
||
meta?: boolean
|
||
}
|
||
|
||
export const ACHIEVEMENT_METRICS: AchievementMetric[] = [
|
||
{
|
||
key: 'accepted_count',
|
||
name: '做对的题数',
|
||
helpText: '去重后通过的题目数量(含比赛题)',
|
||
},
|
||
{
|
||
key: 'mid_ac_count',
|
||
name: '做对的中等题',
|
||
helpText: '去重后通过的中等难度题目数(含比赛题)',
|
||
},
|
||
{
|
||
key: 'hard_ac_count',
|
||
name: '做对的困难题',
|
||
helpText: '去重后通过的困难题目数(含比赛题)',
|
||
},
|
||
{
|
||
key: 'submission_count',
|
||
name: '提交次数',
|
||
helpText: '提交次数(含比赛)',
|
||
},
|
||
{ key: 'active_days', name: '有提交的天数', helpText: '有过提交的累计天数' },
|
||
{
|
||
key: 'max_ac_streak_days',
|
||
name: '连续做对的天数',
|
||
helpText: '连续每天至少 AC 一题的最长天数',
|
||
},
|
||
// 按周不按天:学生跟着课表来,2025 秋最长连续交题天数的中位数是 1 天、连续 7 天的
|
||
// 只有 3 人,按天的「坚持一周」基本拿不到;按周的连续 2/4/8 周分别约 73%/49%/8%
|
||
{
|
||
key: 'max_ac_week_streak',
|
||
name: '连续有新做对的周数',
|
||
helpText: '连续几个自然周(周一起)每周都至少新通过一道题(含比赛题)',
|
||
},
|
||
{ key: 'languages_used', name: '用过几种语言', helpText: '用过多少种编程语言' },
|
||
{
|
||
key: 'contest_joined',
|
||
name: '参加比赛',
|
||
helpText: '交过题的比赛数量',
|
||
},
|
||
{ key: 'badge_count', name: '专题奖章', helpText: '获得的专题奖章数量' },
|
||
{
|
||
key: 'problemset_completed',
|
||
name: '做完的专题',
|
||
helpText: '完成的专题数量',
|
||
},
|
||
{
|
||
key: 'challenge_count',
|
||
name: '挑战成功',
|
||
helpText: '做对之后的挑战,做到了几条(每道题的每条挑战算一次)',
|
||
},
|
||
{
|
||
key: 'first_try_ac_count',
|
||
name: '一次就对的题',
|
||
helpText: '首次提交即通过的次数',
|
||
},
|
||
{
|
||
key: 'midnight_submissions',
|
||
name: '凌晨提交',
|
||
helpText: '0:00–5:00 之间的提交次数',
|
||
},
|
||
{
|
||
key: 'early_bird_submissions',
|
||
name: '早起提交',
|
||
helpText: '5:00–7:00 之间的提交次数',
|
||
},
|
||
{
|
||
key: 'compile_error_count',
|
||
name: '编译错误',
|
||
helpText: '累计编译错误的次数',
|
||
},
|
||
{
|
||
key: 'max_wa_before_ac',
|
||
name: '错了几次才对',
|
||
helpText: '单题失败最多多少次后终于通过',
|
||
},
|
||
{
|
||
key: 'max_ac_in_one_day',
|
||
name: '一天做对最多',
|
||
helpText: '一天之内最多通过多少题',
|
||
},
|
||
{
|
||
key: 'max_code_lines',
|
||
name: '最长的代码行数',
|
||
helpText: '做对的提交里最长的代码有多少行(空行不算,含比赛)',
|
||
},
|
||
{
|
||
key: 'achievement_unlocked_count',
|
||
name: '拿到的成就数',
|
||
helpText: '已解锁的成就数量(不含白金档和已下架的)',
|
||
meta: true,
|
||
},
|
||
]
|
||
|
||
const BY_KEY = new Map(ACHIEVEMENT_METRICS.map((item) => [item.key, item]))
|
||
|
||
export function findMetric(key: string) {
|
||
return BY_KEY.get(key) ?? null
|
||
}
|
||
|
||
export function metricName(key: string) {
|
||
return BY_KEY.get(key)?.name ?? key
|
||
}
|