@xcpcio/core 0.3.3 → 0.4.1
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/dist/index.cjs +172 -17
- package/dist/index.d.ts +44 -5
- package/dist/index.mjs +166 -18
- package/package.json +18 -19
- package/src/contest-index.ts +123 -0
- package/src/contest.ts +6 -4
- package/src/image.ts +1 -1
- package/src/index.ts +2 -0
- package/src/problem.ts +42 -9
- package/src/rank-statistics.ts +11 -0
- package/src/rank.ts +49 -14
- package/src/resolver.ts +8 -6
- package/src/submission.ts +4 -3
- package/src/team.ts +31 -7
- package/src/utils/calc.ts +7 -0
- package/src/utils/dayjs.ts +12 -14
- package/src/utils/index.ts +1 -0
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,13 @@ const minMax__default = /*#__PURE__*/_interopDefaultLegacy(minMax);
|
|
|
27
27
|
const relativeTime__default = /*#__PURE__*/_interopDefaultLegacy(relativeTime);
|
|
28
28
|
const ___default = /*#__PURE__*/_interopDefaultLegacy(_);
|
|
29
29
|
|
|
30
|
+
function calcDict(attemptedNum, solvedNum) {
|
|
31
|
+
if (solvedNum === 0) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
return Math.floor((attemptedNum - solvedNum) * 100 / attemptedNum);
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
dayjs__default.extend(duration__default);
|
|
31
38
|
dayjs__default.extend(utc__default);
|
|
32
39
|
dayjs__default.extend(timezone__default);
|
|
@@ -52,8 +59,9 @@ function getTimestamp(time) {
|
|
|
52
59
|
}
|
|
53
60
|
function getTimeDiff(seconds) {
|
|
54
61
|
const two = (a) => {
|
|
55
|
-
if (a < 10)
|
|
56
|
-
return
|
|
62
|
+
if (a < 10) {
|
|
63
|
+
return `0${a}`;
|
|
64
|
+
}
|
|
57
65
|
return String(a);
|
|
58
66
|
};
|
|
59
67
|
const h = Math.floor(seconds / 3600);
|
|
@@ -62,17 +70,113 @@ function getTimeDiff(seconds) {
|
|
|
62
70
|
return [two(h), two(m), two(s)].join(":");
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
class ContestIndexConfig {
|
|
74
|
+
constructor() {
|
|
75
|
+
this.contestName = "";
|
|
76
|
+
this.startTime = createDayJS();
|
|
77
|
+
this.endTime = createDayJS();
|
|
78
|
+
this.freezeTime = createDayJS();
|
|
79
|
+
this.totalDurationTimestamp = 0;
|
|
80
|
+
this.freezeDurationTimestamp = 0;
|
|
81
|
+
this.unFreezeDurationTimestamp = 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
class ContestIndex {
|
|
85
|
+
constructor() {
|
|
86
|
+
this.config = new ContestIndexConfig();
|
|
87
|
+
this.boardLink = "";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function createContestIndex(contestIndexJSON) {
|
|
91
|
+
const c = new ContestIndex();
|
|
92
|
+
const cc = c.config;
|
|
93
|
+
const cjc = contestIndexJSON.config;
|
|
94
|
+
cc.contestName = cjc.contest_name;
|
|
95
|
+
cc.startTime = createDayJS(cjc.start_time);
|
|
96
|
+
cc.endTime = createDayJS(cjc.end_time);
|
|
97
|
+
cc.totalDurationTimestamp = cc.endTime.unix() - cc.startTime.unix();
|
|
98
|
+
{
|
|
99
|
+
cc.freezeTime = cc.endTime;
|
|
100
|
+
cc.freezeDurationTimestamp = 0;
|
|
101
|
+
if (cjc.frozen_time !== void 0 && cjc.frozen_time != null) {
|
|
102
|
+
const frozenTime = Number(cjc.frozen_time);
|
|
103
|
+
cc.freezeTime = createDayJS(cc.endTime.unix() - frozenTime);
|
|
104
|
+
cc.freezeDurationTimestamp = frozenTime;
|
|
105
|
+
}
|
|
106
|
+
cc.unFreezeDurationTimestamp = cc.totalDurationTimestamp - cc.freezeDurationTimestamp;
|
|
107
|
+
}
|
|
108
|
+
cc.logo = cjc.logo;
|
|
109
|
+
c.boardLink = contestIndexJSON.board_link;
|
|
110
|
+
return c;
|
|
111
|
+
}
|
|
112
|
+
function createContestIndexList(contestListJSON) {
|
|
113
|
+
const contestIndexList = [];
|
|
114
|
+
const dfs = (contestList) => {
|
|
115
|
+
if (Object.prototype.hasOwnProperty.call(contestList, "config")) {
|
|
116
|
+
contestIndexList.push(createContestIndex(contestList));
|
|
117
|
+
} else {
|
|
118
|
+
for (const k in contestList) {
|
|
119
|
+
dfs(contestList[k]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
dfs(contestListJSON);
|
|
124
|
+
contestIndexList.sort((a, b) => {
|
|
125
|
+
if (a.config.startTime.isBefore(b.config.startTime)) {
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
128
|
+
if (a.config.startTime.isAfter(b.config.startTime)) {
|
|
129
|
+
return -1;
|
|
130
|
+
}
|
|
131
|
+
if (a.config.endTime.isBefore(b.config.endTime)) {
|
|
132
|
+
return 1;
|
|
133
|
+
}
|
|
134
|
+
if (a.config.endTime.isAfter(b.config.endTime)) {
|
|
135
|
+
return -1;
|
|
136
|
+
}
|
|
137
|
+
if (a.config.contestName < b.config.contestName) {
|
|
138
|
+
return 1;
|
|
139
|
+
}
|
|
140
|
+
if (a.config.contestName > b.config.contestName) {
|
|
141
|
+
return -1;
|
|
142
|
+
}
|
|
143
|
+
return 0;
|
|
144
|
+
});
|
|
145
|
+
return contestIndexList;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class ProblemStatistics {
|
|
149
|
+
constructor() {
|
|
150
|
+
this.acceptedNum = 0;
|
|
151
|
+
this.rejectedNum = 0;
|
|
152
|
+
this.pendingNum = 0;
|
|
153
|
+
this.submittedNum = 0;
|
|
154
|
+
this.attemptedNum = 0;
|
|
155
|
+
this.ignoreNum = 0;
|
|
156
|
+
this.firstSolveSubmissions = [];
|
|
157
|
+
this.lastSolveSubmissions = [];
|
|
158
|
+
}
|
|
159
|
+
reset() {
|
|
160
|
+
this.acceptedNum = 0;
|
|
161
|
+
this.rejectedNum = 0;
|
|
162
|
+
this.pendingNum = 0;
|
|
163
|
+
this.submittedNum = 0;
|
|
164
|
+
this.firstSolveSubmissions = [];
|
|
165
|
+
this.lastSolveSubmissions = [];
|
|
166
|
+
}
|
|
167
|
+
get dict() {
|
|
168
|
+
if (this.acceptedNum === 0) {
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
return calcDict(this.attemptedNum, this.acceptedNum);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
65
174
|
class Problem {
|
|
66
175
|
constructor() {
|
|
67
176
|
this.id = "";
|
|
68
177
|
this.label = "";
|
|
69
178
|
this.name = "";
|
|
70
|
-
this.statistics =
|
|
71
|
-
acceptedNum: 0,
|
|
72
|
-
rejectedNum: 0,
|
|
73
|
-
pendingNum: 0,
|
|
74
|
-
submittedNum: 0
|
|
75
|
-
};
|
|
179
|
+
this.statistics = new ProblemStatistics();
|
|
76
180
|
}
|
|
77
181
|
}
|
|
78
182
|
function createProblem(problemJSON) {
|
|
@@ -273,6 +377,15 @@ function getImageSource(image) {
|
|
|
273
377
|
return "";
|
|
274
378
|
}
|
|
275
379
|
|
|
380
|
+
class RankStatistics {
|
|
381
|
+
constructor() {
|
|
382
|
+
this.teamSolvedNum = [];
|
|
383
|
+
}
|
|
384
|
+
reset() {
|
|
385
|
+
this.teamSolvedNum = [];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
276
389
|
class Team {
|
|
277
390
|
constructor() {
|
|
278
391
|
this.id = "";
|
|
@@ -281,20 +394,29 @@ class Team {
|
|
|
281
394
|
this.group = [];
|
|
282
395
|
this.tag = [];
|
|
283
396
|
this.rank = 0;
|
|
397
|
+
this.organizationRank = -1;
|
|
284
398
|
this.solvedProblemNum = 0;
|
|
399
|
+
this.attemptedProblemNum = 0;
|
|
285
400
|
this.penalty = 0;
|
|
286
401
|
this.problemStatistics = [];
|
|
287
402
|
this.problemStatisticsMap = /* @__PURE__ */ new Map();
|
|
288
403
|
}
|
|
289
|
-
penaltyToMinute() {
|
|
404
|
+
get penaltyToMinute() {
|
|
290
405
|
return Math.floor(this.penalty / 60);
|
|
291
406
|
}
|
|
407
|
+
get dict() {
|
|
408
|
+
const attemptedNum = this.attemptedProblemNum;
|
|
409
|
+
const solvedNum = this.solvedProblemNum;
|
|
410
|
+
return calcDict(attemptedNum, solvedNum);
|
|
411
|
+
}
|
|
292
412
|
calcSolvedData() {
|
|
293
413
|
this.solvedProblemNum = 0;
|
|
294
414
|
this.penalty = 0;
|
|
415
|
+
this.attemptedProblemNum = 0;
|
|
295
416
|
for (const p of this.problemStatistics) {
|
|
296
417
|
if (p.isAccepted) {
|
|
297
418
|
this.solvedProblemNum++;
|
|
419
|
+
this.attemptedProblemNum += p.failedCount + 1;
|
|
298
420
|
this.penalty += p.penalty;
|
|
299
421
|
}
|
|
300
422
|
}
|
|
@@ -319,17 +441,18 @@ function createTeam(teamJSON) {
|
|
|
319
441
|
t.id = teamJSON.id ?? teamJSON.team_id ?? "";
|
|
320
442
|
t.name = teamJSON.name ?? teamJSON.team_name ?? "";
|
|
321
443
|
t.organization = teamJSON.organization ?? "";
|
|
444
|
+
t.badge = teamJSON.badge;
|
|
322
445
|
t.group = teamJSON.group ?? [];
|
|
323
446
|
t.tag = teamJSON.group ?? [];
|
|
324
447
|
t.coach = teamJSON.coach;
|
|
325
448
|
t.members = teamJSON.members;
|
|
326
|
-
if (teamJSON.official === true) {
|
|
449
|
+
if (Boolean(teamJSON.official) === true) {
|
|
327
450
|
t.group.push("official");
|
|
328
451
|
}
|
|
329
|
-
if (teamJSON.unofficial === true) {
|
|
452
|
+
if (Boolean(teamJSON.unofficial) === true) {
|
|
330
453
|
t.group.push("unofficial");
|
|
331
454
|
}
|
|
332
|
-
if (teamJSON.girl === true) {
|
|
455
|
+
if (Boolean(teamJSON.girl) === true) {
|
|
333
456
|
t.group.push("girl");
|
|
334
457
|
}
|
|
335
458
|
t.group = [...new Set(t.group)];
|
|
@@ -528,7 +651,7 @@ class Rank {
|
|
|
528
651
|
this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
|
|
529
652
|
this.submissions = ___default.cloneDeep(submissions).sort(Submission.compare);
|
|
530
653
|
this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
|
|
531
|
-
this.
|
|
654
|
+
this.rankStatistics = new RankStatistics();
|
|
532
655
|
}
|
|
533
656
|
buildRank(options) {
|
|
534
657
|
(() => {
|
|
@@ -541,7 +664,9 @@ class Rank {
|
|
|
541
664
|
});
|
|
542
665
|
t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
|
|
543
666
|
}
|
|
544
|
-
this.
|
|
667
|
+
this.contest.problems.forEach((p) => {
|
|
668
|
+
p.statistics.reset();
|
|
669
|
+
});
|
|
545
670
|
for (const s of this.submissions) {
|
|
546
671
|
const teamId = s.teamId;
|
|
547
672
|
const problemId = s.problemId;
|
|
@@ -557,13 +682,13 @@ class Rank {
|
|
|
557
682
|
}
|
|
558
683
|
const problemStatistics = team.problemStatisticsMap.get(problemId);
|
|
559
684
|
const submissions = problemStatistics.submissions;
|
|
560
|
-
const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId);
|
|
561
685
|
submissions.push(s);
|
|
562
686
|
problem.statistics.submittedNum++;
|
|
563
687
|
if (problemStatistics.isSolved) {
|
|
564
688
|
continue;
|
|
565
689
|
}
|
|
566
690
|
if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
|
|
691
|
+
problem.statistics.ignoreNum++;
|
|
567
692
|
problemStatistics.ignoreCount++;
|
|
568
693
|
continue;
|
|
569
694
|
}
|
|
@@ -574,10 +699,15 @@ class Rank {
|
|
|
574
699
|
problemStatistics.isSolved = true;
|
|
575
700
|
problemStatistics.solvedTimestamp = s.timestamp;
|
|
576
701
|
problem.statistics.acceptedNum++;
|
|
577
|
-
|
|
702
|
+
problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
|
|
703
|
+
if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
|
|
578
704
|
problemStatistics.isFirstSolved = true;
|
|
579
|
-
|
|
705
|
+
problem.statistics.firstSolveSubmissions.push(s);
|
|
706
|
+
}
|
|
707
|
+
while (problem.statistics.lastSolveSubmissions.length > 0) {
|
|
708
|
+
problem.statistics.lastSolveSubmissions.pop();
|
|
580
709
|
}
|
|
710
|
+
problem.statistics.lastSolveSubmissions.push(s);
|
|
581
711
|
}
|
|
582
712
|
if (s.isRejected()) {
|
|
583
713
|
problemStatistics.failedCount++;
|
|
@@ -596,6 +726,24 @@ class Rank {
|
|
|
596
726
|
t.rank = rank++;
|
|
597
727
|
}
|
|
598
728
|
}
|
|
729
|
+
if (this.contest.organization) {
|
|
730
|
+
let rank = 1;
|
|
731
|
+
const se = /* @__PURE__ */ new Set();
|
|
732
|
+
for (const t of this.teams) {
|
|
733
|
+
if (!se.has(t.organization)) {
|
|
734
|
+
se.add(t.organization);
|
|
735
|
+
t.organizationRank = rank;
|
|
736
|
+
rank++;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
})();
|
|
741
|
+
(() => {
|
|
742
|
+
this.rankStatistics.reset();
|
|
743
|
+
this.rankStatistics.teamSolvedNum = Array(this.contest.problems.length + 1).fill(0);
|
|
744
|
+
for (const t of this.teams) {
|
|
745
|
+
this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
|
|
746
|
+
}
|
|
599
747
|
})();
|
|
600
748
|
return this;
|
|
601
749
|
}
|
|
@@ -691,13 +839,20 @@ class Resolver extends Rank {
|
|
|
691
839
|
|
|
692
840
|
exports.dayjs = dayjs__default;
|
|
693
841
|
exports.Contest = Contest;
|
|
842
|
+
exports.ContestIndex = ContestIndex;
|
|
843
|
+
exports.ContestIndexConfig = ContestIndexConfig;
|
|
694
844
|
exports.Problem = Problem;
|
|
845
|
+
exports.ProblemStatistics = ProblemStatistics;
|
|
695
846
|
exports.Rank = Rank;
|
|
847
|
+
exports.RankStatistics = RankStatistics;
|
|
696
848
|
exports.Resolver = Resolver;
|
|
697
849
|
exports.Submission = Submission;
|
|
698
850
|
exports.Team = Team;
|
|
699
851
|
exports.TeamProblemStatistics = TeamProblemStatistics;
|
|
852
|
+
exports.calcDict = calcDict;
|
|
700
853
|
exports.createContest = createContest;
|
|
854
|
+
exports.createContestIndex = createContestIndex;
|
|
855
|
+
exports.createContestIndexList = createContestIndexList;
|
|
701
856
|
exports.createDayJS = createDayJS;
|
|
702
857
|
exports.createProblem = createProblem;
|
|
703
858
|
exports.createProblems = createProblems;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
export { default as dayjs } from 'dayjs';
|
|
3
|
-
import { SubmissionStatus, Submission as Submission$1, Submissions as Submissions$1, BalloonColor, Problem as Problem$1, Problems as Problems$1, StatusTimeDisplay,
|
|
3
|
+
import { Image, ContestIndex as ContestIndex$1, SubmissionStatus, Submission as Submission$1, Submissions as Submissions$1, BalloonColor, Problem as Problem$1, Problems as Problems$1, StatusTimeDisplay, ContestState, Contest as Contest$1, Team as Team$1, Teams as Teams$1 } from '@xcpcio/types';
|
|
4
|
+
|
|
5
|
+
declare class ContestIndexConfig {
|
|
6
|
+
contestName: string;
|
|
7
|
+
startTime: dayjs.Dayjs;
|
|
8
|
+
endTime: dayjs.Dayjs;
|
|
9
|
+
freezeTime: dayjs.Dayjs;
|
|
10
|
+
totalDurationTimestamp: number;
|
|
11
|
+
freezeDurationTimestamp: number;
|
|
12
|
+
unFreezeDurationTimestamp: number;
|
|
13
|
+
logo?: Image;
|
|
14
|
+
constructor();
|
|
15
|
+
}
|
|
16
|
+
declare class ContestIndex {
|
|
17
|
+
config: ContestIndexConfig;
|
|
18
|
+
boardLink: string;
|
|
19
|
+
constructor();
|
|
20
|
+
}
|
|
21
|
+
type ContestIndexList = Array<ContestIndex>;
|
|
22
|
+
declare function createContestIndex(contestIndexJSON: ContestIndex$1): ContestIndex;
|
|
23
|
+
declare function createContestIndexList(contestListJSON: any): ContestIndexList;
|
|
24
|
+
|
|
25
|
+
declare function calcDict(attemptedNum: number, solvedNum: number): number;
|
|
4
26
|
|
|
5
27
|
declare function createDayJS(time?: Date | string | number | undefined): dayjs.Dayjs;
|
|
6
28
|
declare function getTimestamp(time: number | dayjs.Dayjs): number;
|
|
@@ -24,11 +46,18 @@ type Submissions = Array<Submission>;
|
|
|
24
46
|
declare function createSubmission(submissionJSON: Submission$1): Submission;
|
|
25
47
|
declare function createSubmissions(submissionsJSON: Submissions$1): Submissions;
|
|
26
48
|
|
|
27
|
-
|
|
49
|
+
declare class ProblemStatistics {
|
|
28
50
|
acceptedNum: number;
|
|
29
51
|
rejectedNum: number;
|
|
30
52
|
pendingNum: number;
|
|
31
53
|
submittedNum: number;
|
|
54
|
+
attemptedNum: number;
|
|
55
|
+
ignoreNum: number;
|
|
56
|
+
firstSolveSubmissions: Submissions;
|
|
57
|
+
lastSolveSubmissions: Submissions;
|
|
58
|
+
constructor();
|
|
59
|
+
reset(): void;
|
|
60
|
+
get dict(): number;
|
|
32
61
|
}
|
|
33
62
|
declare class Problem {
|
|
34
63
|
id: string;
|
|
@@ -100,21 +129,31 @@ declare function createContest(contestJSON: Contest$1): Contest;
|
|
|
100
129
|
|
|
101
130
|
declare function getImageSource(image: Image): string;
|
|
102
131
|
|
|
132
|
+
declare class RankStatistics {
|
|
133
|
+
teamSolvedNum: Array<number>;
|
|
134
|
+
constructor();
|
|
135
|
+
reset(): void;
|
|
136
|
+
}
|
|
137
|
+
|
|
103
138
|
declare class Team {
|
|
104
139
|
id: string;
|
|
105
140
|
name: string;
|
|
106
141
|
organization: string;
|
|
142
|
+
badge?: Image;
|
|
107
143
|
group: Array<string>;
|
|
108
144
|
tag: Array<string>;
|
|
109
145
|
coach?: string | Array<string>;
|
|
110
146
|
members?: string | Array<string>;
|
|
111
147
|
rank: number;
|
|
148
|
+
organizationRank: number;
|
|
112
149
|
solvedProblemNum: number;
|
|
150
|
+
attemptedProblemNum: number;
|
|
113
151
|
penalty: number;
|
|
114
152
|
problemStatistics: Array<TeamProblemStatistics>;
|
|
115
153
|
problemStatisticsMap: Map<string, TeamProblemStatistics>;
|
|
116
154
|
constructor();
|
|
117
|
-
penaltyToMinute(): number;
|
|
155
|
+
get penaltyToMinute(): number;
|
|
156
|
+
get dict(): number;
|
|
118
157
|
calcSolvedData(): void;
|
|
119
158
|
static compare(lhs: Team, rhs: Team): number;
|
|
120
159
|
}
|
|
@@ -128,7 +167,7 @@ declare class Rank {
|
|
|
128
167
|
teamsMap: Map<string, Team>;
|
|
129
168
|
submissions: Submissions;
|
|
130
169
|
submissionsMap: Map<string, Submission>;
|
|
131
|
-
|
|
170
|
+
rankStatistics: RankStatistics;
|
|
132
171
|
constructor(contest: Contest, teams: Teams, submissions: Submissions);
|
|
133
172
|
buildRank(options?: {
|
|
134
173
|
timestamp?: number;
|
|
@@ -159,4 +198,4 @@ declare function isRejected(status: SubmissionStatus): boolean;
|
|
|
159
198
|
declare function isPending(status: SubmissionStatus): boolean;
|
|
160
199
|
declare function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean;
|
|
161
200
|
|
|
162
|
-
export { Contest, Problem, ProblemStatistics, Problems, Rank, Resolver, Submission, Submissions, Team, TeamProblemStatistics, Teams, createContest, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
|
|
201
|
+
export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, Problem, ProblemStatistics, Problems, Rank, RankStatistics, Resolver, Submission, Submissions, Team, TeamProblemStatistics, Teams, calcDict, createContest, createContestIndex, createContestIndexList, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,13 @@ import relativeTime from 'dayjs/plugin/relativeTime';
|
|
|
11
11
|
import { VERSION, ContestState, SubmissionStatus } from '@xcpcio/types';
|
|
12
12
|
import _ from 'lodash';
|
|
13
13
|
|
|
14
|
+
function calcDict(attemptedNum, solvedNum) {
|
|
15
|
+
if (solvedNum === 0) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
return Math.floor((attemptedNum - solvedNum) * 100 / attemptedNum);
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
dayjs.extend(duration);
|
|
15
22
|
dayjs.extend(utc);
|
|
16
23
|
dayjs.extend(timezone);
|
|
@@ -36,8 +43,9 @@ function getTimestamp(time) {
|
|
|
36
43
|
}
|
|
37
44
|
function getTimeDiff(seconds) {
|
|
38
45
|
const two = (a) => {
|
|
39
|
-
if (a < 10)
|
|
40
|
-
return
|
|
46
|
+
if (a < 10) {
|
|
47
|
+
return `0${a}`;
|
|
48
|
+
}
|
|
41
49
|
return String(a);
|
|
42
50
|
};
|
|
43
51
|
const h = Math.floor(seconds / 3600);
|
|
@@ -46,17 +54,113 @@ function getTimeDiff(seconds) {
|
|
|
46
54
|
return [two(h), two(m), two(s)].join(":");
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
class ContestIndexConfig {
|
|
58
|
+
constructor() {
|
|
59
|
+
this.contestName = "";
|
|
60
|
+
this.startTime = createDayJS();
|
|
61
|
+
this.endTime = createDayJS();
|
|
62
|
+
this.freezeTime = createDayJS();
|
|
63
|
+
this.totalDurationTimestamp = 0;
|
|
64
|
+
this.freezeDurationTimestamp = 0;
|
|
65
|
+
this.unFreezeDurationTimestamp = 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
class ContestIndex {
|
|
69
|
+
constructor() {
|
|
70
|
+
this.config = new ContestIndexConfig();
|
|
71
|
+
this.boardLink = "";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function createContestIndex(contestIndexJSON) {
|
|
75
|
+
const c = new ContestIndex();
|
|
76
|
+
const cc = c.config;
|
|
77
|
+
const cjc = contestIndexJSON.config;
|
|
78
|
+
cc.contestName = cjc.contest_name;
|
|
79
|
+
cc.startTime = createDayJS(cjc.start_time);
|
|
80
|
+
cc.endTime = createDayJS(cjc.end_time);
|
|
81
|
+
cc.totalDurationTimestamp = cc.endTime.unix() - cc.startTime.unix();
|
|
82
|
+
{
|
|
83
|
+
cc.freezeTime = cc.endTime;
|
|
84
|
+
cc.freezeDurationTimestamp = 0;
|
|
85
|
+
if (cjc.frozen_time !== void 0 && cjc.frozen_time != null) {
|
|
86
|
+
const frozenTime = Number(cjc.frozen_time);
|
|
87
|
+
cc.freezeTime = createDayJS(cc.endTime.unix() - frozenTime);
|
|
88
|
+
cc.freezeDurationTimestamp = frozenTime;
|
|
89
|
+
}
|
|
90
|
+
cc.unFreezeDurationTimestamp = cc.totalDurationTimestamp - cc.freezeDurationTimestamp;
|
|
91
|
+
}
|
|
92
|
+
cc.logo = cjc.logo;
|
|
93
|
+
c.boardLink = contestIndexJSON.board_link;
|
|
94
|
+
return c;
|
|
95
|
+
}
|
|
96
|
+
function createContestIndexList(contestListJSON) {
|
|
97
|
+
const contestIndexList = [];
|
|
98
|
+
const dfs = (contestList) => {
|
|
99
|
+
if (Object.prototype.hasOwnProperty.call(contestList, "config")) {
|
|
100
|
+
contestIndexList.push(createContestIndex(contestList));
|
|
101
|
+
} else {
|
|
102
|
+
for (const k in contestList) {
|
|
103
|
+
dfs(contestList[k]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
dfs(contestListJSON);
|
|
108
|
+
contestIndexList.sort((a, b) => {
|
|
109
|
+
if (a.config.startTime.isBefore(b.config.startTime)) {
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
if (a.config.startTime.isAfter(b.config.startTime)) {
|
|
113
|
+
return -1;
|
|
114
|
+
}
|
|
115
|
+
if (a.config.endTime.isBefore(b.config.endTime)) {
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
if (a.config.endTime.isAfter(b.config.endTime)) {
|
|
119
|
+
return -1;
|
|
120
|
+
}
|
|
121
|
+
if (a.config.contestName < b.config.contestName) {
|
|
122
|
+
return 1;
|
|
123
|
+
}
|
|
124
|
+
if (a.config.contestName > b.config.contestName) {
|
|
125
|
+
return -1;
|
|
126
|
+
}
|
|
127
|
+
return 0;
|
|
128
|
+
});
|
|
129
|
+
return contestIndexList;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
class ProblemStatistics {
|
|
133
|
+
constructor() {
|
|
134
|
+
this.acceptedNum = 0;
|
|
135
|
+
this.rejectedNum = 0;
|
|
136
|
+
this.pendingNum = 0;
|
|
137
|
+
this.submittedNum = 0;
|
|
138
|
+
this.attemptedNum = 0;
|
|
139
|
+
this.ignoreNum = 0;
|
|
140
|
+
this.firstSolveSubmissions = [];
|
|
141
|
+
this.lastSolveSubmissions = [];
|
|
142
|
+
}
|
|
143
|
+
reset() {
|
|
144
|
+
this.acceptedNum = 0;
|
|
145
|
+
this.rejectedNum = 0;
|
|
146
|
+
this.pendingNum = 0;
|
|
147
|
+
this.submittedNum = 0;
|
|
148
|
+
this.firstSolveSubmissions = [];
|
|
149
|
+
this.lastSolveSubmissions = [];
|
|
150
|
+
}
|
|
151
|
+
get dict() {
|
|
152
|
+
if (this.acceptedNum === 0) {
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
return calcDict(this.attemptedNum, this.acceptedNum);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
49
158
|
class Problem {
|
|
50
159
|
constructor() {
|
|
51
160
|
this.id = "";
|
|
52
161
|
this.label = "";
|
|
53
162
|
this.name = "";
|
|
54
|
-
this.statistics =
|
|
55
|
-
acceptedNum: 0,
|
|
56
|
-
rejectedNum: 0,
|
|
57
|
-
pendingNum: 0,
|
|
58
|
-
submittedNum: 0
|
|
59
|
-
};
|
|
163
|
+
this.statistics = new ProblemStatistics();
|
|
60
164
|
}
|
|
61
165
|
}
|
|
62
166
|
function createProblem(problemJSON) {
|
|
@@ -257,6 +361,15 @@ function getImageSource(image) {
|
|
|
257
361
|
return "";
|
|
258
362
|
}
|
|
259
363
|
|
|
364
|
+
class RankStatistics {
|
|
365
|
+
constructor() {
|
|
366
|
+
this.teamSolvedNum = [];
|
|
367
|
+
}
|
|
368
|
+
reset() {
|
|
369
|
+
this.teamSolvedNum = [];
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
260
373
|
class Team {
|
|
261
374
|
constructor() {
|
|
262
375
|
this.id = "";
|
|
@@ -265,20 +378,29 @@ class Team {
|
|
|
265
378
|
this.group = [];
|
|
266
379
|
this.tag = [];
|
|
267
380
|
this.rank = 0;
|
|
381
|
+
this.organizationRank = -1;
|
|
268
382
|
this.solvedProblemNum = 0;
|
|
383
|
+
this.attemptedProblemNum = 0;
|
|
269
384
|
this.penalty = 0;
|
|
270
385
|
this.problemStatistics = [];
|
|
271
386
|
this.problemStatisticsMap = /* @__PURE__ */ new Map();
|
|
272
387
|
}
|
|
273
|
-
penaltyToMinute() {
|
|
388
|
+
get penaltyToMinute() {
|
|
274
389
|
return Math.floor(this.penalty / 60);
|
|
275
390
|
}
|
|
391
|
+
get dict() {
|
|
392
|
+
const attemptedNum = this.attemptedProblemNum;
|
|
393
|
+
const solvedNum = this.solvedProblemNum;
|
|
394
|
+
return calcDict(attemptedNum, solvedNum);
|
|
395
|
+
}
|
|
276
396
|
calcSolvedData() {
|
|
277
397
|
this.solvedProblemNum = 0;
|
|
278
398
|
this.penalty = 0;
|
|
399
|
+
this.attemptedProblemNum = 0;
|
|
279
400
|
for (const p of this.problemStatistics) {
|
|
280
401
|
if (p.isAccepted) {
|
|
281
402
|
this.solvedProblemNum++;
|
|
403
|
+
this.attemptedProblemNum += p.failedCount + 1;
|
|
282
404
|
this.penalty += p.penalty;
|
|
283
405
|
}
|
|
284
406
|
}
|
|
@@ -303,17 +425,18 @@ function createTeam(teamJSON) {
|
|
|
303
425
|
t.id = teamJSON.id ?? teamJSON.team_id ?? "";
|
|
304
426
|
t.name = teamJSON.name ?? teamJSON.team_name ?? "";
|
|
305
427
|
t.organization = teamJSON.organization ?? "";
|
|
428
|
+
t.badge = teamJSON.badge;
|
|
306
429
|
t.group = teamJSON.group ?? [];
|
|
307
430
|
t.tag = teamJSON.group ?? [];
|
|
308
431
|
t.coach = teamJSON.coach;
|
|
309
432
|
t.members = teamJSON.members;
|
|
310
|
-
if (teamJSON.official === true) {
|
|
433
|
+
if (Boolean(teamJSON.official) === true) {
|
|
311
434
|
t.group.push("official");
|
|
312
435
|
}
|
|
313
|
-
if (teamJSON.unofficial === true) {
|
|
436
|
+
if (Boolean(teamJSON.unofficial) === true) {
|
|
314
437
|
t.group.push("unofficial");
|
|
315
438
|
}
|
|
316
|
-
if (teamJSON.girl === true) {
|
|
439
|
+
if (Boolean(teamJSON.girl) === true) {
|
|
317
440
|
t.group.push("girl");
|
|
318
441
|
}
|
|
319
442
|
t.group = [...new Set(t.group)];
|
|
@@ -512,7 +635,7 @@ class Rank {
|
|
|
512
635
|
this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
|
|
513
636
|
this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
|
|
514
637
|
this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
|
|
515
|
-
this.
|
|
638
|
+
this.rankStatistics = new RankStatistics();
|
|
516
639
|
}
|
|
517
640
|
buildRank(options) {
|
|
518
641
|
(() => {
|
|
@@ -525,7 +648,9 @@ class Rank {
|
|
|
525
648
|
});
|
|
526
649
|
t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
|
|
527
650
|
}
|
|
528
|
-
this.
|
|
651
|
+
this.contest.problems.forEach((p) => {
|
|
652
|
+
p.statistics.reset();
|
|
653
|
+
});
|
|
529
654
|
for (const s of this.submissions) {
|
|
530
655
|
const teamId = s.teamId;
|
|
531
656
|
const problemId = s.problemId;
|
|
@@ -541,13 +666,13 @@ class Rank {
|
|
|
541
666
|
}
|
|
542
667
|
const problemStatistics = team.problemStatisticsMap.get(problemId);
|
|
543
668
|
const submissions = problemStatistics.submissions;
|
|
544
|
-
const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId);
|
|
545
669
|
submissions.push(s);
|
|
546
670
|
problem.statistics.submittedNum++;
|
|
547
671
|
if (problemStatistics.isSolved) {
|
|
548
672
|
continue;
|
|
549
673
|
}
|
|
550
674
|
if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
|
|
675
|
+
problem.statistics.ignoreNum++;
|
|
551
676
|
problemStatistics.ignoreCount++;
|
|
552
677
|
continue;
|
|
553
678
|
}
|
|
@@ -558,10 +683,15 @@ class Rank {
|
|
|
558
683
|
problemStatistics.isSolved = true;
|
|
559
684
|
problemStatistics.solvedTimestamp = s.timestamp;
|
|
560
685
|
problem.statistics.acceptedNum++;
|
|
561
|
-
|
|
686
|
+
problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
|
|
687
|
+
if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
|
|
562
688
|
problemStatistics.isFirstSolved = true;
|
|
563
|
-
|
|
689
|
+
problem.statistics.firstSolveSubmissions.push(s);
|
|
690
|
+
}
|
|
691
|
+
while (problem.statistics.lastSolveSubmissions.length > 0) {
|
|
692
|
+
problem.statistics.lastSolveSubmissions.pop();
|
|
564
693
|
}
|
|
694
|
+
problem.statistics.lastSolveSubmissions.push(s);
|
|
565
695
|
}
|
|
566
696
|
if (s.isRejected()) {
|
|
567
697
|
problemStatistics.failedCount++;
|
|
@@ -580,6 +710,24 @@ class Rank {
|
|
|
580
710
|
t.rank = rank++;
|
|
581
711
|
}
|
|
582
712
|
}
|
|
713
|
+
if (this.contest.organization) {
|
|
714
|
+
let rank = 1;
|
|
715
|
+
const se = /* @__PURE__ */ new Set();
|
|
716
|
+
for (const t of this.teams) {
|
|
717
|
+
if (!se.has(t.organization)) {
|
|
718
|
+
se.add(t.organization);
|
|
719
|
+
t.organizationRank = rank;
|
|
720
|
+
rank++;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
})();
|
|
725
|
+
(() => {
|
|
726
|
+
this.rankStatistics.reset();
|
|
727
|
+
this.rankStatistics.teamSolvedNum = Array(this.contest.problems.length + 1).fill(0);
|
|
728
|
+
for (const t of this.teams) {
|
|
729
|
+
this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
|
|
730
|
+
}
|
|
583
731
|
})();
|
|
584
732
|
return this;
|
|
585
733
|
}
|
|
@@ -673,4 +821,4 @@ class Resolver extends Rank {
|
|
|
673
821
|
}
|
|
674
822
|
}
|
|
675
823
|
|
|
676
|
-
export { Contest, Problem, Rank, Resolver, Submission, Team, TeamProblemStatistics, createContest, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
|
|
824
|
+
export { Contest, ContestIndex, ContestIndexConfig, Problem, ProblemStatistics, Rank, RankStatistics, Resolver, Submission, Team, TeamProblemStatistics, calcDict, createContest, createContestIndex, createContestIndexList, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
|