Skip to content

Commit 9f26fa4

Browse files
committed
:docs: Improve PAST labels (#1526)
1 parent 8e33c51 commit 9f26fa4

File tree

4 files changed

+147
-48
lines changed

4 files changed

+147
-48
lines changed

src/lib/utils/contest.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,10 @@ export const getContestNameLabel = (contestId: string) => {
302302
return 'TDPC';
303303
}
304304

305+
if (contestId.startsWith('past')) {
306+
return getPastContestLabel(PAST_TRANSLATIONS, contestId);
307+
}
308+
305309
if (contestId === 'practice2') {
306310
return 'ACL Practice';
307311
}
@@ -338,6 +342,71 @@ export const getContestNameLabel = (contestId: string) => {
338342
return contestId.toUpperCase();
339343
};
340344

345+
/**
346+
* A mapping of contest dates to their respective Japanese translations.
347+
* Each key represents a date in the format 'YYYYMM', and the corresponding value
348+
* is the Japanese translation indicating the contest number.
349+
*
350+
* Note:
351+
* After the 15th contest, the URL includes the number of times the contest has been held
352+
*
353+
* See:
354+
* https://atcoder.jp/contests/archive?ratedType=0&category=50
355+
*
356+
* Example:
357+
* - '201912': ' 第 1 回' (The 1st contest in December 2019)
358+
* - '202303': ' 第 14 回' (The 14th contest in March 2023)
359+
*/
360+
export const PAST_TRANSLATIONS = {
361+
'201912': ' 第 1 回',
362+
'202004': ' 第 2 回',
363+
'202005': ' 第 3 回',
364+
'202010': ' 第 4 回',
365+
'202012': ' 第 5 回',
366+
'202104': ' 第 6 回',
367+
'202107': ' 第 7 回',
368+
'202109': ' 第 8 回',
369+
'202112': ' 第 9 回',
370+
'202203': ' 第 10 回',
371+
'202206': ' 第 11 回',
372+
'202209': ' 第 12 回',
373+
'202212': ' 第 13 回',
374+
'202303': ' 第 14 回',
375+
};
376+
377+
/**
378+
* A regular expression to match strings that representing the 15th or later PAST contests.
379+
* The string should start with "past" followed by exactly two digits and end with "-open".
380+
* The matching is case-insensitive.
381+
*
382+
* Examples:
383+
* - "past15-open" (matches)
384+
* - "past16-open" (matches)
385+
* - "past99-open" (matches)
386+
* - "past1-open" (does not match)
387+
*/
388+
const regexForPast = /^past(\d{2})-open$/i;
389+
390+
export function getPastContestLabel(
391+
translations: Readonly<ContestLabelTranslations>,
392+
contestId: string,
393+
): string {
394+
let label = contestId;
395+
396+
Object.entries(translations).forEach(([abbrEnglish, japanese]) => {
397+
label = label.replace(abbrEnglish, japanese);
398+
});
399+
400+
if (label == contestId) {
401+
label = label.replace(regexForPast, (_, round) => {
402+
return `PAST 第 ${round} 回-open`;
403+
});
404+
}
405+
406+
// Remove suffix
407+
return label.replace('-open', '').toUpperCase();
408+
}
409+
341410
/**
342411
* Generates a formatted contest label for AtCoder University contests.
343412
*

src/test/lib/utils/contest.test.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -366,18 +366,6 @@ describe('Contest', () => {
366366
});
367367
});
368368

369-
// TODO(#issue): Skipped until notational inconsistencies are resolved.
370-
// Current issues:
371-
// 1. Contest names use inconsistent formats (e.g., "past201912-open" vs "past17-open")
372-
// 2. Need to standardize naming conventions across all contests
373-
describe.skip('when contest_id contains past', () => {
374-
TestCasesForContestNameLabel.past.forEach(({ name, value }) => {
375-
runTests(`${name}`, [value], ({ contestId, expected }: TestCaseForContestNameLabel) => {
376-
expect(getContestNameLabel(contestId)).toEqual(expected);
377-
});
378-
});
379-
});
380-
381369
describe('when contest_id is practice2 (ACL practice)', () => {
382370
TestCasesForContestNameLabel.aclPractice.forEach(({ name, value }) => {
383371
runTests(`${name}`, [value], ({ contestId, expected }: TestCaseForContestNameLabel) => {
@@ -446,6 +434,18 @@ describe('Contest', () => {
446434
});
447435
});
448436

437+
describe('when contest_id contains past', () => {
438+
TestCasesForContestNameAndTaskIndex.past.forEach(({ name, value }) => {
439+
runTests(
440+
`${name}`,
441+
[value],
442+
({ contestId, taskTableIndex, expected }: TestCaseForContestNameAndTaskIndex) => {
443+
expect(addContestNameToTaskIndex(contestId, taskTableIndex)).toEqual(expected);
444+
},
445+
);
446+
});
447+
});
448+
449449
describe('when contest_id is tessoku-book', () => {
450450
TestCasesForContestNameAndTaskIndex.tessokuBook.forEach(({ name, value }) => {
451451
runTests(

src/test/lib/utils/test_cases/contest_name_and_task_index.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { createTestCase, zip } from '../../common/test_helpers';
22
import {
3+
getPastContestLabel,
34
getAtCoderUniversityContestLabel,
45
getAojContestLabel,
6+
PAST_TRANSLATIONS,
57
AOJ_COURSES,
68
} from '$lib/utils/contest';
79

@@ -81,6 +83,70 @@ export const typical90 = [
8183
}),
8284
];
8385

86+
const generatePastTestCases = (
87+
contestIds: string[],
88+
taskIndices: string[],
89+
): { name: string; value: TestCaseForContestNameAndTaskIndex }[] => {
90+
return zip(contestIds, taskIndices).map(([contestId, taskIndex]) => {
91+
const testCase = createTestCaseForContestNameAndTaskIndex(`PAST, ${contestId} ${taskIndex}`)({
92+
contestId: `${contestId}`,
93+
taskTableIndex: `${taskIndex}`,
94+
expected: `${getPastContestLabel(PAST_TRANSLATIONS, contestId)} - ${taskIndex}`,
95+
});
96+
97+
return testCase;
98+
});
99+
};
100+
101+
const PAST_TEST_DATA = {
102+
// 1st
103+
'past201912-open': {
104+
contestId: 'past201904',
105+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
106+
},
107+
// 2nd
108+
'past202004-open': {
109+
contestId: 'past202004-open',
110+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
111+
},
112+
// 3rd
113+
'past202005-open': {
114+
contestId: 'past202005-open',
115+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
116+
},
117+
// 9th
118+
'past202112-open': {
119+
contestId: 'past202203-open',
120+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
121+
},
122+
// 10th
123+
'past202203-open': {
124+
contestId: 'past202203-open',
125+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
126+
},
127+
// 14th
128+
'past202303-open': {
129+
contestId: 'past202303-open',
130+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
131+
},
132+
'past15-open': {
133+
contestId: 'past15-open',
134+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
135+
},
136+
'past16-open': {
137+
contestId: 'past16-open',
138+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
139+
},
140+
'past17-open': {
141+
contestId: 'past17-open',
142+
tasks: ['A', 'B', 'C', 'M', 'N', 'O'],
143+
},
144+
};
145+
146+
export const past = Object.entries(PAST_TEST_DATA).flatMap(([contestId, tasks]) =>
147+
generatePastTestCases(Array(tasks.tasks.length).fill(contestId), tasks.tasks),
148+
);
149+
84150
export const tessokuBook = [
85151
createTestCaseForContestNameAndTaskIndex('Tessoku Book, Task A01')({
86152
contestId: 'tessoku-book',

src/test/lib/utils/test_cases/contest_name_labels.ts

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,42 +21,6 @@ export const tdpc = [
2121
}),
2222
];
2323

24-
// Note: Not yet implemented, because notational distortion needs to be corrected for each contest.
25-
export const past = [
26-
createTestCaseForContestNameLabel('PAST 17th')({
27-
contestId: 'past17-open',
28-
expected: '',
29-
}),
30-
createTestCaseForContestNameLabel('PAST 16th')({
31-
contestId: 'past16-open',
32-
expected: '',
33-
}),
34-
createTestCaseForContestNameLabel('PAST 15th')({
35-
contestId: 'past15-open',
36-
expected: '',
37-
}),
38-
createTestCaseForContestNameLabel('PAST 14th')({
39-
contestId: 'past202303-open',
40-
expected: '',
41-
}),
42-
createTestCaseForContestNameLabel('PAST 13th')({
43-
contestId: 'past202212-open',
44-
expected: '',
45-
}),
46-
createTestCaseForContestNameLabel('PAST 3rd')({
47-
contestId: 'past202005-open',
48-
expected: '',
49-
}),
50-
createTestCaseForContestNameLabel('PAST 2nd')({
51-
contestId: 'past202004-open',
52-
expected: '',
53-
}),
54-
createTestCaseForContestNameLabel('PAST 1st')({
55-
contestId: 'past201912-open',
56-
expected: '',
57-
}),
58-
];
59-
6024
export const aclPractice = [
6125
createTestCaseForContestNameLabel('ACL Practice')({
6226
contestId: 'practice2',

0 commit comments

Comments
 (0)