Skip to content

Commit d45f1a9

Browse files
committed
docs: Add plan (#3153)
1 parent 0950290 commit d45f1a9

File tree

1 file changed

+202
-0
lines changed
  • docs/dev-notes/2026-02-14/add-awc-to-contest-table

1 file changed

+202
-0
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Plan: AtCoder Weekday Contest (AWC) テーブル追加
2+
3+
## Context
4+
5+
Issue [#3153](https://github.com/AtCoder-NoviSteps/AtCoderNoviSteps/issues/3153) の対応。新しいコンテストシリーズ「AtCoder Weekday Contest」のテーブルを追加する。パターン1(範囲フィルタ型)で、ARC104Onwards / AGC001Onwards と同じ構造。
6+
7+
**仕様**:
8+
9+
- ContestType: `AWC`(新規)
10+
- contest_id: `awc0001`, `awc0002`, ...(awc + 4桁数字)
11+
- 範囲: awc0001〜(上限なし)
12+
- セクション: A〜E(5問/回)
13+
- タイトル: "AtCoder Weekday Contest"
14+
15+
## 実装手順
16+
17+
### Step 1: Prisma スキーマに `AWC` 追加
18+
19+
**File**: [prisma/schema.prisma](prisma/schema.prisma)
20+
21+
`ContestType` enum に `AWC` を追加(`AGC_LIKE` の次)。
22+
23+
```
24+
pnpm exec prisma migrate dev --name add_awc_to_contest_type
25+
```
26+
27+
### Step 2: TypeScript 型更新
28+
29+
**File**: [src/lib/types/contest.ts](src/lib/types/contest.ts)
30+
31+
- `ContestType` オブジェクトに `AWC: 'AWC'` 追加(`AGC_LIKE` の次)
32+
- `contestTypePriorities``[ContestType.AWC, 16]` 追加し、以降(UNIVERSITY 〜 AOJ_JAG)の優先度を +1 シフト:
33+
- UNIVERSITY: 16 → 17
34+
- FPS_24: 17 → 18
35+
- OTHERS: 18 → 19
36+
- AOJ_COURSES: 19 → 20
37+
- AOJ_PCK: 20 → 21
38+
- AOJ_JAG: 21 → 22
39+
40+
### Step 3: `classifyContest()` / `getContestNameLabel()` 更新
41+
42+
**File**: [src/lib/utils/contest.ts](src/lib/utils/contest.ts)
43+
44+
- `classifyContest()` に AWC パターン追加(L17付近、AGC の後):
45+
```typescript
46+
if (/^awc\d{4}$/.exec(contest_id)) {
47+
return ContestType.AWC;
48+
}
49+
```
50+
- `regexForAxc` の下に `regexForAwc = /^(awc)(\d{4})/i` 追加
51+
- `getContestNameLabel()` に AWC 分岐追加(`regexForAxc` の後):
52+
`"awc0001"``"AWC 0001"`
53+
54+
#### 単体テスト
55+
56+
既存テストの構造に合わせて、3つのテストケースファイルに AWC を追加し、テスト本体にも describe ブロックを追加する。
57+
58+
**1. `classifyContest()` テスト**
59+
60+
- **File**: [src/test/lib/utils/test_cases/contest_type.ts](src/test/lib/utils/test_cases/contest_type.ts)
61+
- `awc` エクスポートを追加。テストケース:
62+
- `awc0001``ContestType.AWC`
63+
- `awc0002``ContestType.AWC`
64+
- `awc9999``ContestType.AWC`
65+
66+
- **File**: [src/test/lib/utils/contest.test.ts](src/test/lib/utils/contest.test.ts)
67+
- `classify contest > AtCoder``when contest_id contains awc` describe ブロック追加(`agc-like` の後)
68+
- `get contest priority > AtCoder` にも同様の describe ブロック追加
69+
70+
**2. `getContestNameLabel()` テスト**
71+
72+
- **File**: [src/test/lib/utils/test_cases/contest_name_labels.ts](src/test/lib/utils/test_cases/contest_name_labels.ts)
73+
- `awc` エクスポートを追加。テストケース:
74+
- `awc0001``"AWC 0001"`
75+
- `awc0002``"AWC 0002"`
76+
- `awc9999``"AWC 9999"`
77+
78+
- **File**: [src/test/lib/utils/contest.test.ts](src/test/lib/utils/contest.test.ts)
79+
- `get contest name label > AtCoder``when contest_id contains awc` describe ブロック追加
80+
81+
**3. `addContestNameToTaskIndex()` テスト**
82+
83+
- **File**: [src/test/lib/utils/test_cases/contest_name_and_task_index.ts](src/test/lib/utils/test_cases/contest_name_and_task_index.ts)
84+
- `awc` エクスポートを追加。テストケース:
85+
- `awc0001`, task `A``"AWC 0001 - A"`
86+
- `awc0001`, task `E``"AWC 0001 - E"`
87+
88+
- **File**: [src/test/lib/utils/contest.test.ts](src/test/lib/utils/contest.test.ts)
89+
- `add contest name to task index > AtCoder``when contest_id contains awc` describe ブロック追加
90+
91+
### Step 4: Provider クラス実装
92+
93+
**File**: [src/lib/utils/contest_table_provider.ts](src/lib/utils/contest_table_provider.ts)
94+
95+
`ABCLikeProvider`(L468)の後に `AWC0001OnwardsProvider` を追加。`ARC104OnwardsProvider`(L336-359)をテンプレートとして使用:
96+
97+
```typescript
98+
export class AWC0001OnwardsProvider extends ContestTableProviderBase {
99+
protected setFilterCondition(): (taskResult: TaskResult) => boolean {
100+
return (taskResult: TaskResult) => {
101+
if (classifyContest(taskResult.contest_id) !== this.contestType) {
102+
return false;
103+
}
104+
const contestRound = parseContestRound(taskResult.contest_id, 'awc');
105+
return contestRound >= 1 && contestRound <= 9999;
106+
};
107+
}
108+
109+
getMetadata(): ContestTableMetaData {
110+
return {
111+
title: 'AtCoder Weekday Contest 0001 〜 ',
112+
abbreviationName: 'awc0001Onwards',
113+
};
114+
}
115+
116+
getContestRoundLabel(contestId: string): string {
117+
const contestNameLabel = getContestNameLabel(contestId);
118+
return contestNameLabel.replace('AWC ', '');
119+
}
120+
}
121+
```
122+
123+
- `getDisplayConfig()` はベースクラスのデフォルト(`isShownHeader: true`, `isShownRoundLabel: true`, `isShownTaskIndex: false`)をそのまま使用
124+
125+
### Step 5: プリセット登録
126+
127+
**File**: [src/lib/utils/contest_table_provider.ts](src/lib/utils/contest_table_provider.ts)
128+
129+
- `prepareContestProviderPresets()`(L1128)に追加:
130+
```typescript
131+
AWC0001Onwards: () =>
132+
new ContestTableProviderGroup(`AWC 0001 Onwards`, {
133+
buttonLabel: 'AWC 0001 〜 ',
134+
ariaLabel: 'Filter contests from AWC 0001 onwards',
135+
}).addProvider(new AWC0001OnwardsProvider(ContestType.AWC)),
136+
```
137+
- `contestTableProviderGroups`(L1307)に追加:
138+
```typescript
139+
awc0001Onwards: prepareContestProviderPresets().AWC0001Onwards(),
140+
```
141+
142+
### Step 6: シードデータ追加
143+
144+
**File**: [prisma/tasks.ts](prisma/tasks.ts)
145+
146+
AWC のタスクデータを追加(実際の問題名は AtCoder Problems API から確認が必要)。
147+
148+
### Step 7: テスト実装
149+
150+
**Files**:
151+
152+
- [src/test/lib/utils/test_cases/contest_table_provider.ts](src/test/lib/utils/test_cases/contest_table_provider.ts) — モックデータ追加
153+
- [src/test/lib/utils/contest_table_provider.test.ts](src/test/lib/utils/contest_table_provider.test.ts) — テストスイート追加
154+
155+
テスト内容:
156+
157+
1. フィルタリング検証(AWC のみ通過、他を除外)
158+
2. メタデータ(title, abbreviationName)
159+
3. ディスプレイ設定(isShownHeader, isShownRoundLabel, isShownTaskIndex)
160+
4. ラウンドラベル(`"awc0001"``"0001"`
161+
5. テーブル構造生成(5問 A〜E)
162+
6. 空入力ハンドリング
163+
7. プリセットグループの検証
164+
165+
`classifyContest` モックに AWC 分岐を追加:
166+
167+
```typescript
168+
} else if (/^awc\d{4}$/.test(contestId)) {
169+
return ContestType.AWC;
170+
```
171+
172+
## 検証
173+
174+
```bash
175+
pnpm exec prisma migrate dev --name add_awc_contest_type
176+
pnpm test:unit src/test/lib/utils/contest_table_provider.test.ts
177+
pnpm check
178+
pnpm lint
179+
pnpm format
180+
```
181+
182+
## 実装完了後の教訓
183+
184+
### Step 3 までのフロー(Utility関数)
185+
186+
- `classifyContest()` → コンテスト文字列を認識(パターン照合)
187+
- `getContestNameLabel()` → 表示用フォーマット("awc0001" → "AWC 0001")
188+
- コンテスト型を優先度マップに登録 → 画面表示順を制御
189+
190+
**重要**: regex パターンの桁数に注意。AWC は `\d{4}` (4桁)、ARC/AGC は `\d{3}` (3桁) だが、分類ロジック (`classifyContest`) の順序が重要。前後の誤判定を避けるため、より特異度の高いパターン(4桁)を先に配置。
191+
192+
### Step 4-5 のフロー(Provider クラス + 登録)
193+
194+
- `ContestTableProviderBase` を拡張し `setFilterCondition()``getMetadata()``getContestRoundLabel()` 実装
195+
- `prepareContestProviderPresets()` → Provider ファクトリ関数
196+
- `contestTableProviderGroups` → 名前ごとにインスタンス化
197+
198+
**重要**: Provider クラスは `ABCLikeProvider` の直後に配置するのではなく、コンテスト系列ごとにグループ化するのが推奨。AWC はコンテスト系列なので、AGC や ARC の近くに配置。
199+
200+
### テスト戦略
201+
202+
Unit テスト(`contest.test.ts`)で基本分類を検証済みなので、Provider テストは省略可能。ただし、複雑な範囲フィルタを使う場合は Provider 単体テストを追加推奨。

0 commit comments

Comments
 (0)