@xcpcio/core 0.58.3 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -22
- package/src/award.ts +0 -32
- package/src/balloon.ts +0 -25
- package/src/basic-types.ts +0 -4
- package/src/battle-of-giants.ts +0 -154
- package/src/contest-index.ts +0 -99
- package/src/contest-options.ts +0 -49
- package/src/contest.ts +0 -351
- package/src/export/cf.ts +0 -125
- package/src/export/general-excel.ts +0 -257
- package/src/export/icpc-standings-csv.ts +0 -89
- package/src/export/index.ts +0 -3
- package/src/group.ts +0 -13
- package/src/image.ts +0 -21
- package/src/index.ts +0 -19
- package/src/person.ts +0 -42
- package/src/problem.ts +0 -222
- package/src/rank-statistics.ts +0 -33
- package/src/rank.ts +0 -613
- package/src/rating/index.ts +0 -5
- package/src/rating/rating-calculator.ts +0 -100
- package/src/rating/rating-history.ts +0 -81
- package/src/rating/rating-user.ts +0 -99
- package/src/rating/rating-utility.ts +0 -80
- package/src/rating/rating.ts +0 -136
- package/src/resolver-operation.ts +0 -22
- package/src/resolver-vue.ts +0 -183
- package/src/resolver.ts +0 -138
- package/src/submission-status.ts +0 -157
- package/src/submission.ts +0 -177
- package/src/team.ts +0 -339
- package/src/utils/calc.ts +0 -7
- package/src/utils/color.ts +0 -28
- package/src/utils/dayjs.ts +0 -59
- package/src/utils/index.ts +0 -3
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import type { Rank } from "../rank";
|
|
2
|
-
|
|
3
|
-
import _ from "lodash";
|
|
4
|
-
import stringWidth from "string-width";
|
|
5
|
-
import * as XLSX from "xlsx-js-style";
|
|
6
|
-
|
|
7
|
-
import { isValidMedalType } from "../award";
|
|
8
|
-
|
|
9
|
-
enum SpecialCellType {
|
|
10
|
-
FIRST_SOLVED = "firstSolved",
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface SpecialCell {
|
|
14
|
-
row: number;
|
|
15
|
-
col: number;
|
|
16
|
-
type: SpecialCellType;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface AoaConvertResult {
|
|
20
|
-
aoa: string[][];
|
|
21
|
-
specialCells: SpecialCell[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class GeneralExcelConverter {
|
|
25
|
-
constructor() { }
|
|
26
|
-
|
|
27
|
-
public convert(oriRank: Rank): XLSX.WorkBook {
|
|
28
|
-
const rank = _.cloneDeep(oriRank);
|
|
29
|
-
|
|
30
|
-
rank.options.disableFilterTeamsByGroup();
|
|
31
|
-
rank.options.disableFilterSubmissionByTimestamp();
|
|
32
|
-
|
|
33
|
-
const workbook = XLSX.utils.book_new();
|
|
34
|
-
|
|
35
|
-
for (const [k, v] of rank.contest.group) {
|
|
36
|
-
rank.options.setGroup(k);
|
|
37
|
-
rank.buildRank();
|
|
38
|
-
|
|
39
|
-
const sheet = this.convertToSheet(rank);
|
|
40
|
-
XLSX.utils.book_append_sheet(workbook, sheet, v.names.get(v.defaultLang) as string);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return workbook;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public convertAndWrite(rank: Rank, filename: string): any {
|
|
47
|
-
return XLSX.writeFile(
|
|
48
|
-
this.convert(rank),
|
|
49
|
-
filename,
|
|
50
|
-
{
|
|
51
|
-
compression: true,
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private convertToSheet(rank: Rank): XLSX.WorkSheet {
|
|
57
|
-
const aoa = this.convertToAoa(rank);
|
|
58
|
-
const sheet = XLSX.utils.aoa_to_sheet(aoa.aoa);
|
|
59
|
-
|
|
60
|
-
const cols = [];
|
|
61
|
-
const head = aoa.aoa[1];
|
|
62
|
-
for (let j = 0; j < head.length; j++) {
|
|
63
|
-
let wch = 10;
|
|
64
|
-
for (let i = 1; i < aoa.aoa.length; i++) {
|
|
65
|
-
wch = Math.max(wch, stringWidth(aoa.aoa[i][j]) + 2);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
cols.push({
|
|
69
|
-
wch,
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
sheet["!cols"] = cols;
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
const mergeRange = { s: { r: 0, c: 0 }, e: { r: 0, c: head.length - 1 } };
|
|
77
|
-
const merges = [{ s: mergeRange.s, e: mergeRange.e }];
|
|
78
|
-
sheet["!merges"] = merges;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const font = {
|
|
82
|
-
name: "Arial Unicode MS",
|
|
83
|
-
bold: false,
|
|
84
|
-
italic: false,
|
|
85
|
-
sz: 12,
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const borderStyle = {
|
|
89
|
-
style: "thin",
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const cellStyle = {
|
|
93
|
-
alignment: {
|
|
94
|
-
vertical: "center",
|
|
95
|
-
horizontal: "center",
|
|
96
|
-
},
|
|
97
|
-
border: {
|
|
98
|
-
top: borderStyle,
|
|
99
|
-
bottom: borderStyle,
|
|
100
|
-
left: borderStyle,
|
|
101
|
-
right: borderStyle,
|
|
102
|
-
},
|
|
103
|
-
font,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const firstSolvedCellStyle = {
|
|
107
|
-
...cellStyle,
|
|
108
|
-
fill: {
|
|
109
|
-
fgColor: { rgb: "009900" },
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
for (let i = 1; i < aoa.aoa.length; i++) {
|
|
114
|
-
for (let j = 0; j < aoa.aoa[i].length; j++) {
|
|
115
|
-
const cellAddress = XLSX.utils.encode_cell({ r: i, c: j });
|
|
116
|
-
const cell = sheet[cellAddress];
|
|
117
|
-
const specialCell = aoa.specialCells.find(sc => sc.row === i && sc.col === j);
|
|
118
|
-
if (specialCell?.type === SpecialCellType.FIRST_SOLVED) {
|
|
119
|
-
cell.s = firstSolvedCellStyle;
|
|
120
|
-
} else {
|
|
121
|
-
cell.s = cellStyle;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
{
|
|
127
|
-
const cellAddress = XLSX.utils.encode_cell({ r: 0, c: 0 });
|
|
128
|
-
const cell = sheet[cellAddress];
|
|
129
|
-
const titleStyle = _.cloneDeep(cellStyle);
|
|
130
|
-
titleStyle.font.sz = 28;
|
|
131
|
-
titleStyle.font.bold = true;
|
|
132
|
-
cell.s = titleStyle;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return sheet;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private convertToAoa(rank: Rank): AoaConvertResult {
|
|
139
|
-
const aoa: string[][] = [];
|
|
140
|
-
const specialCells: SpecialCell[] = [];
|
|
141
|
-
|
|
142
|
-
const enableAwards = rank.contest.isEnableAwards(rank.options.group);
|
|
143
|
-
const enableMembers = (Array.isArray(rank.teams) && rank.teams[0]?.members) ?? false;
|
|
144
|
-
const enableCoach = rank.teams[0]?.coach ?? false;
|
|
145
|
-
|
|
146
|
-
{
|
|
147
|
-
aoa.push([rank.contest.name]);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
{
|
|
151
|
-
const head: string[] = [];
|
|
152
|
-
head.push("Rank");
|
|
153
|
-
|
|
154
|
-
if (rank.contest.organization) {
|
|
155
|
-
head.push(`${rank.contest.organization} Rank`);
|
|
156
|
-
head.push(rank.contest.organization);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
head.push("Team", "Solved", "Penalty", ...rank.contest.problems.map(p => p.label), "Dirt");
|
|
160
|
-
|
|
161
|
-
if (enableAwards) {
|
|
162
|
-
head.push("Medal");
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (enableMembers) {
|
|
166
|
-
head.push("Member1", "Member2", "Member3");
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (enableCoach) {
|
|
170
|
-
head.push("Coach");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
head.push("Unofficial");
|
|
174
|
-
head.push("Girl");
|
|
175
|
-
|
|
176
|
-
aoa.push(head);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const team of rank.teams) {
|
|
180
|
-
const arr: string[] = [];
|
|
181
|
-
|
|
182
|
-
arr.push(team.rank.toString());
|
|
183
|
-
|
|
184
|
-
if (team.organization) {
|
|
185
|
-
if (team.organizationRank !== -1) {
|
|
186
|
-
arr.push(team.organizationRank.toString());
|
|
187
|
-
} else {
|
|
188
|
-
arr.push("");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
arr.push(team.organization);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
arr.push(team.name, team.solvedProblemNum.toString(), team.penaltyToMinute.toString());
|
|
195
|
-
|
|
196
|
-
for (const p of team.problemStatistics) {
|
|
197
|
-
if (p.isUnSubmitted) {
|
|
198
|
-
arr.push("-");
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (p.isSolved) {
|
|
202
|
-
arr.push(`+${p.totalCount}(${p.solvedTimestampToMinute})`);
|
|
203
|
-
if (p.isFirstSolved) {
|
|
204
|
-
specialCells.push({
|
|
205
|
-
row: aoa.length,
|
|
206
|
-
col: arr.length - 1,
|
|
207
|
-
type: SpecialCellType.FIRST_SOLVED,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (p.isWrongAnswer) {
|
|
213
|
-
arr.push(`-${p.failedCount}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (p.isPending) {
|
|
217
|
-
arr.push(`? ${p.failedCount} + ${p.pendingCount}`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
arr.push(`${team.dirt}%`);
|
|
222
|
-
|
|
223
|
-
if (enableAwards) {
|
|
224
|
-
const medals = team.awards
|
|
225
|
-
.filter(a => isValidMedalType(a))
|
|
226
|
-
.map(a => a.toString());
|
|
227
|
-
arr.push(medals.join(", "));
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (enableMembers) {
|
|
231
|
-
const members = team.members;
|
|
232
|
-
if (Array.isArray(members)) {
|
|
233
|
-
arr.push(members[0] ?? "");
|
|
234
|
-
arr.push(members[1] ?? "");
|
|
235
|
-
arr.push(members[2] ?? "");
|
|
236
|
-
} else {
|
|
237
|
-
arr.push("", "", "");
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (enableCoach) {
|
|
242
|
-
if (typeof team.coach === "string") {
|
|
243
|
-
arr.push(team.coach ?? "");
|
|
244
|
-
} else {
|
|
245
|
-
arr.push("");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
arr.push(team.isUnofficial ? "Y" : "N");
|
|
250
|
-
arr.push(team.isGirl ? "Y" : "N");
|
|
251
|
-
|
|
252
|
-
aoa.push(arr);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return { aoa, specialCells };
|
|
256
|
-
}
|
|
257
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import type { Rank } from "../rank";
|
|
2
|
-
import type { Team } from "../team";
|
|
3
|
-
|
|
4
|
-
import _ from "lodash";
|
|
5
|
-
import ordinal from "ordinal";
|
|
6
|
-
import Papa from "papaparse";
|
|
7
|
-
|
|
8
|
-
import { isValidMedalType } from "../award";
|
|
9
|
-
|
|
10
|
-
interface ICPCTeamResult {
|
|
11
|
-
teamId: string;
|
|
12
|
-
rank: number;
|
|
13
|
-
medalCitation: string;
|
|
14
|
-
problemsSolved: number;
|
|
15
|
-
totalTime: number;
|
|
16
|
-
lastProblemTime: number;
|
|
17
|
-
siteCitation: string;
|
|
18
|
-
citation: string;
|
|
19
|
-
teamName: string;
|
|
20
|
-
institution: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class ICPCStandingsCsvConverter {
|
|
24
|
-
constructor() {}
|
|
25
|
-
|
|
26
|
-
public convert(oriRank: Rank) {
|
|
27
|
-
const rank = _.cloneDeep(oriRank);
|
|
28
|
-
|
|
29
|
-
rank.options.disableFilterTeamsByGroup();
|
|
30
|
-
rank.options.disableFilterSubmissionByTimestamp();
|
|
31
|
-
|
|
32
|
-
rank.options.setGroup("official");
|
|
33
|
-
rank.buildRank();
|
|
34
|
-
|
|
35
|
-
const resList: Array<ICPCTeamResult> = [];
|
|
36
|
-
|
|
37
|
-
for (const team of rank.teams) {
|
|
38
|
-
const res: ICPCTeamResult = {
|
|
39
|
-
teamId: team.icpcID ?? "",
|
|
40
|
-
rank: team.rank,
|
|
41
|
-
medalCitation: this.getMedalCitation(team),
|
|
42
|
-
problemsSolved: team.solvedProblemNum,
|
|
43
|
-
totalTime: team.penaltyToMinute,
|
|
44
|
-
lastProblemTime: team.lastSolvedProblemStatistics?.solvedTimestampToMinute ?? 0,
|
|
45
|
-
siteCitation: rank.contest.name,
|
|
46
|
-
citation: ordinal(team.rank),
|
|
47
|
-
teamName: team.name,
|
|
48
|
-
institution: team.organization,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
resList.push(res);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const csv = Papa.unparse(resList);
|
|
55
|
-
|
|
56
|
-
return csv;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private getMedalCitation(team: Team): string {
|
|
60
|
-
if (team.solvedProblemNum === 0) {
|
|
61
|
-
return "";
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const medals = team.awards
|
|
65
|
-
.filter(a => isValidMedalType(a))
|
|
66
|
-
.map(a => a.toString());
|
|
67
|
-
|
|
68
|
-
if (medals.length === 1) {
|
|
69
|
-
const medal = medals[0];
|
|
70
|
-
if (medal === "Gold") {
|
|
71
|
-
return "Gold Medal";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (medal === "Silver") {
|
|
75
|
-
return "Silver Medal";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (medal === "Bronze") {
|
|
79
|
-
return "Bronze Medal";
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (medal === "Honorable") {
|
|
83
|
-
return "Honorable Mention";
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return "";
|
|
88
|
-
}
|
|
89
|
-
}
|
package/src/export/index.ts
DELETED
package/src/group.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Lang } from "@xcpcio/types";
|
|
2
|
-
|
|
3
|
-
export class Group {
|
|
4
|
-
names: Map<Lang, string>;
|
|
5
|
-
defaultLang: Lang;
|
|
6
|
-
isDefault: boolean;
|
|
7
|
-
|
|
8
|
-
constructor() {
|
|
9
|
-
this.names = new Map<Lang, string>();
|
|
10
|
-
this.defaultLang = "zh-CN";
|
|
11
|
-
this.isDefault = false;
|
|
12
|
-
}
|
|
13
|
-
}
|
package/src/image.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Image } from "@xcpcio/types";
|
|
2
|
-
|
|
3
|
-
export function getImageSource(image: Image, asset_host?: string): string {
|
|
4
|
-
if (image?.url) {
|
|
5
|
-
if (!asset_host) {
|
|
6
|
-
return image.url;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (image.url.startsWith("http")) {
|
|
10
|
-
return image.url;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return `${asset_host}/${image.url}`;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (image?.base64) {
|
|
17
|
-
return `data:image/${image.type ?? "png"};base64,${image.base64}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return "";
|
|
21
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export * from "./award";
|
|
2
|
-
export * from "./balloon";
|
|
3
|
-
export * from "./basic-types";
|
|
4
|
-
export * from "./battle-of-giants";
|
|
5
|
-
export * from "./contest";
|
|
6
|
-
export * from "./contest-index";
|
|
7
|
-
export * from "./export";
|
|
8
|
-
export * from "./image";
|
|
9
|
-
export * from "./person";
|
|
10
|
-
export * from "./problem";
|
|
11
|
-
export * from "./rank";
|
|
12
|
-
export * from "./rank-statistics";
|
|
13
|
-
export * from "./rating";
|
|
14
|
-
export * from "./resolver";
|
|
15
|
-
export * from "./resolver-vue";
|
|
16
|
-
export * from "./submission";
|
|
17
|
-
export * from "./submission-status";
|
|
18
|
-
export * from "./team";
|
|
19
|
-
export * from "./utils";
|
package/src/person.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { IPerson } from "@xcpcio/types";
|
|
2
|
-
|
|
3
|
-
export class Person {
|
|
4
|
-
name: string;
|
|
5
|
-
|
|
6
|
-
constructor(name = "") {
|
|
7
|
-
this.name = name;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
toJSON(): IPerson {
|
|
11
|
-
return {
|
|
12
|
-
name: this.name,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
static fromJSON(iPerson: IPerson | string): Person {
|
|
17
|
-
if (typeof iPerson === "string") {
|
|
18
|
-
iPerson = JSON.parse(iPerson) as IPerson;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const person = new Person();
|
|
22
|
-
person.name = iPerson.name;
|
|
23
|
-
|
|
24
|
-
return person;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type Persons = Array<Person>;
|
|
29
|
-
|
|
30
|
-
export function createPersons(iPersons: string | Array<string>): Persons {
|
|
31
|
-
if (typeof iPersons === "string") {
|
|
32
|
-
for (const c of " ,、|") {
|
|
33
|
-
if (iPersons.includes(c)) {
|
|
34
|
-
return iPersons.split(c).map(name => new Person(name));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return [new Person(iPersons)];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return iPersons.map(name => new Person(name));
|
|
42
|
-
}
|
package/src/problem.ts
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import type { BalloonColor, Problem as IProblem, Problems as IProblems } from "@xcpcio/types";
|
|
2
|
-
import type { Submissions } from "./submission";
|
|
3
|
-
|
|
4
|
-
import _ from "lodash";
|
|
5
|
-
|
|
6
|
-
import { calcDirt, getWhiteOrBlackColor } from "./utils";
|
|
7
|
-
|
|
8
|
-
export class ProblemStatistics {
|
|
9
|
-
acceptedNum: number;
|
|
10
|
-
rejectedNum: number;
|
|
11
|
-
pendingNum: number;
|
|
12
|
-
|
|
13
|
-
submittedNum: number;
|
|
14
|
-
attemptedNum: number;
|
|
15
|
-
ignoreNum: number;
|
|
16
|
-
|
|
17
|
-
firstSolveSubmissions: Submissions;
|
|
18
|
-
lastSolveSubmissions: Submissions;
|
|
19
|
-
|
|
20
|
-
se: number;
|
|
21
|
-
|
|
22
|
-
constructor() {
|
|
23
|
-
this.acceptedNum = 0;
|
|
24
|
-
this.rejectedNum = 0;
|
|
25
|
-
this.pendingNum = 0;
|
|
26
|
-
|
|
27
|
-
this.submittedNum = 0;
|
|
28
|
-
this.attemptedNum = 0;
|
|
29
|
-
this.ignoreNum = 0;
|
|
30
|
-
|
|
31
|
-
this.se = 0;
|
|
32
|
-
|
|
33
|
-
this.firstSolveSubmissions = [];
|
|
34
|
-
this.lastSolveSubmissions = [];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
reset() {
|
|
38
|
-
this.acceptedNum = 0;
|
|
39
|
-
this.rejectedNum = 0;
|
|
40
|
-
this.pendingNum = 0;
|
|
41
|
-
|
|
42
|
-
this.submittedNum = 0;
|
|
43
|
-
this.attemptedNum = 0;
|
|
44
|
-
this.ignoreNum = 0;
|
|
45
|
-
|
|
46
|
-
this.se = 0;
|
|
47
|
-
|
|
48
|
-
this.firstSolveSubmissions = [];
|
|
49
|
-
this.lastSolveSubmissions = [];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get dirt() {
|
|
53
|
-
if (this.acceptedNum === 0) {
|
|
54
|
-
return 0;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return calcDirt(this.attemptedNum, this.acceptedNum);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
calcSE(totalTeams: number) {
|
|
61
|
-
const res = (totalTeams - this.acceptedNum) / totalTeams;
|
|
62
|
-
this.se = Math.round(res * 100) / 100;
|
|
63
|
-
|
|
64
|
-
return this.se;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export class Problem {
|
|
69
|
-
id: string;
|
|
70
|
-
label: string;
|
|
71
|
-
|
|
72
|
-
name: string;
|
|
73
|
-
|
|
74
|
-
timeLimit?: string;
|
|
75
|
-
memoryLimit?: string;
|
|
76
|
-
|
|
77
|
-
balloonColor: BalloonColor;
|
|
78
|
-
|
|
79
|
-
statistics: ProblemStatistics;
|
|
80
|
-
|
|
81
|
-
constructor() {
|
|
82
|
-
this.id = "";
|
|
83
|
-
this.label = "";
|
|
84
|
-
|
|
85
|
-
this.name = "";
|
|
86
|
-
|
|
87
|
-
this.statistics = new ProblemStatistics();
|
|
88
|
-
|
|
89
|
-
this.balloonColor = {
|
|
90
|
-
background_color: "#a0f0a0",
|
|
91
|
-
color: "#000",
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export type Problems = Array<Problem>;
|
|
97
|
-
|
|
98
|
-
export function createProblem(problemJSON: IProblem): Problem {
|
|
99
|
-
const p = new Problem();
|
|
100
|
-
|
|
101
|
-
p.id = String(problemJSON.id);
|
|
102
|
-
p.label = problemJSON.label;
|
|
103
|
-
|
|
104
|
-
p.name = problemJSON.name ?? "";
|
|
105
|
-
|
|
106
|
-
p.timeLimit = problemJSON.time_limit;
|
|
107
|
-
p.memoryLimit = problemJSON.memory_limit;
|
|
108
|
-
|
|
109
|
-
if (problemJSON.balloon_color) {
|
|
110
|
-
p.balloonColor = _.cloneDeep(problemJSON.balloon_color);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
p.balloonColor.color = getWhiteOrBlackColor(p.balloonColor.background_color as string);
|
|
114
|
-
|
|
115
|
-
return p;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function createProblems(problemsJSON: IProblems): Problems {
|
|
119
|
-
return problemsJSON.map(pJSON => createProblem(pJSON));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function createProblemsByProblemIds(problemIds: string[], balloonColors?: BalloonColor[]): Problems {
|
|
123
|
-
const problems = problemIds.map((label: string, index: number) => {
|
|
124
|
-
const p = new Problem();
|
|
125
|
-
p.id = String(index);
|
|
126
|
-
p.label = label;
|
|
127
|
-
|
|
128
|
-
return p;
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
if (balloonColors !== undefined && balloonColors !== null) {
|
|
132
|
-
for (const index in balloonColors) {
|
|
133
|
-
problems[index].balloonColor = _.cloneDeep(balloonColors[index]);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
problems.forEach((p) => {
|
|
138
|
-
p.balloonColor.color = getWhiteOrBlackColor(p.balloonColor.background_color as string);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
return problems;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export class TeamProblemStatistics {
|
|
145
|
-
isFirstSolved: boolean;
|
|
146
|
-
|
|
147
|
-
isSolved: boolean;
|
|
148
|
-
solvedTimestamp: number;
|
|
149
|
-
|
|
150
|
-
isSubmitted: boolean;
|
|
151
|
-
lastSubmitTimestamp: number;
|
|
152
|
-
|
|
153
|
-
failedCount: number;
|
|
154
|
-
pendingCount: number;
|
|
155
|
-
ignoreCount: number;
|
|
156
|
-
totalCount: number;
|
|
157
|
-
|
|
158
|
-
submissions: Submissions;
|
|
159
|
-
problem: Problem;
|
|
160
|
-
|
|
161
|
-
contestPenalty: number;
|
|
162
|
-
|
|
163
|
-
constructor(options?: { teamProblemStatistics?: TeamProblemStatistics }) {
|
|
164
|
-
this.isFirstSolved = options?.teamProblemStatistics?.isFirstSolved ?? false;
|
|
165
|
-
|
|
166
|
-
this.isSolved = options?.teamProblemStatistics?.isSolved ?? false;
|
|
167
|
-
this.solvedTimestamp = options?.teamProblemStatistics?.solvedTimestamp ?? 0;
|
|
168
|
-
|
|
169
|
-
this.isSubmitted = options?.teamProblemStatistics?.isSubmitted ?? false;
|
|
170
|
-
this.lastSubmitTimestamp = options?.teamProblemStatistics?.lastSubmitTimestamp ?? 0;
|
|
171
|
-
|
|
172
|
-
this.failedCount = options?.teamProblemStatistics?.failedCount ?? 0;
|
|
173
|
-
this.pendingCount = options?.teamProblemStatistics?.pendingCount ?? 0;
|
|
174
|
-
this.ignoreCount = options?.teamProblemStatistics?.ignoreCount ?? 0;
|
|
175
|
-
this.totalCount = options?.teamProblemStatistics?.totalCount ?? 0;
|
|
176
|
-
|
|
177
|
-
this.submissions = options?.teamProblemStatistics?.submissions ?? [];
|
|
178
|
-
this.problem = options?.teamProblemStatistics?.problem ?? new Problem();
|
|
179
|
-
|
|
180
|
-
this.contestPenalty = options?.teamProblemStatistics?.contestPenalty ?? 20 * 60;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
get isAccepted() {
|
|
184
|
-
return this.isSolved;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
get isWrongAnswer() {
|
|
188
|
-
return !this.isSolved && this.pendingCount === 0 && this.failedCount > 0;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
get isPending() {
|
|
192
|
-
return !this.isSolved && this.pendingCount > 0;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
get isUnSubmitted() {
|
|
196
|
-
return this.totalCount === 0;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
get solvedTimestampToMinute() {
|
|
200
|
-
return Math.floor(this.solvedTimestamp / 60);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
get penalty() {
|
|
204
|
-
if (this.isSolved === false) {
|
|
205
|
-
return 0;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return this.solvedTimestampToMinute * 60 + this.failedCount * this.contestPenalty;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
get penaltyToMinute() {
|
|
212
|
-
return Math.floor(this.penalty / 60);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
get penaltyInSecond() {
|
|
216
|
-
if (this.isSolved === false) {
|
|
217
|
-
return 0;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return this.solvedTimestamp + this.failedCount * this.contestPenalty;
|
|
221
|
-
}
|
|
222
|
-
}
|
package/src/rank-statistics.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export class RankStatistics {
|
|
2
|
-
teamSolvedNum: Array<number>;
|
|
3
|
-
teamSolvedNumIndex: Array<number>;
|
|
4
|
-
maxSolvedProblems: number;
|
|
5
|
-
|
|
6
|
-
effectiveTeamNum: number;
|
|
7
|
-
|
|
8
|
-
totalTeamNum: number;
|
|
9
|
-
|
|
10
|
-
constructor() {
|
|
11
|
-
this.teamSolvedNum = [];
|
|
12
|
-
this.teamSolvedNumIndex = [];
|
|
13
|
-
this.maxSolvedProblems = 0;
|
|
14
|
-
|
|
15
|
-
this.effectiveTeamNum = 0;
|
|
16
|
-
|
|
17
|
-
this.totalTeamNum = 0;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
reset() {
|
|
21
|
-
this.teamSolvedNum = [];
|
|
22
|
-
this.teamSolvedNumIndex = [];
|
|
23
|
-
this.maxSolvedProblems = 0;
|
|
24
|
-
|
|
25
|
-
this.effectiveTeamNum = 0;
|
|
26
|
-
|
|
27
|
-
this.totalTeamNum = 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
getTeamSolvedNumIndex(solvedNum: number): number {
|
|
31
|
-
return this.teamSolvedNumIndex[solvedNum] ?? 0;
|
|
32
|
-
}
|
|
33
|
-
}
|