@xcpcio/core 0.2.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/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/index.cjs +716 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.mjs +676 -0
- package/package.json +73 -0
- package/src/contest.ts +191 -0
- package/src/image.ts +13 -0
- package/src/index.ts +9 -0
- package/src/problem.ts +157 -0
- package/src/rank.ts +122 -0
- package/src/resolver-operation.ts +22 -0
- package/src/resolver.ts +120 -0
- package/src/submission-status.ts +151 -0
- package/src/submission.ts +87 -0
- package/src/team.ts +116 -0
- package/src/utils/dayjs.ts +61 -0
- package/src/utils/index.ts +1 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { SubmissionStatus } from "@xcpcio/types";
|
|
2
|
+
|
|
3
|
+
export function stringToSubmissionStatus(status: string): SubmissionStatus {
|
|
4
|
+
status = status.toUpperCase().replace(" ", "_");
|
|
5
|
+
|
|
6
|
+
if (["OK", "AC", SubmissionStatus.ACCEPTED.toString()].includes(status)) {
|
|
7
|
+
return SubmissionStatus.ACCEPTED;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if ([SubmissionStatus.CORRECT.toString()].includes(status)) {
|
|
11
|
+
return SubmissionStatus.CORRECT;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if ([SubmissionStatus.PARTIALLY_CORRECT.toString()].includes(status)) {
|
|
15
|
+
return SubmissionStatus.PARTIALLY_CORRECT;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (["WA", SubmissionStatus.WRONG_ANSWER.toString()].includes(status)) {
|
|
19
|
+
return SubmissionStatus.WRONG_ANSWER;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (["RJ", "INCORRECT", SubmissionStatus.REJECTED.toString()].includes(status)) {
|
|
23
|
+
return SubmissionStatus.REJECTED;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (["PD", SubmissionStatus.PENDING.toString()].includes(status)) {
|
|
27
|
+
return SubmissionStatus.PENDING;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ([SubmissionStatus.WAITING.toString()].includes(status)) {
|
|
31
|
+
return SubmissionStatus.WAITING;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ([SubmissionStatus.JUDGING.toString()].includes(status)) {
|
|
35
|
+
return SubmissionStatus.JUDGING;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if ([SubmissionStatus.FROZEN.toString()].includes(status)) {
|
|
39
|
+
return SubmissionStatus.FROZEN;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (["CE", SubmissionStatus.COMPILE_ERROR.toString()].includes(status)) {
|
|
43
|
+
return SubmissionStatus.COMPILE_ERROR;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (["PE", SubmissionStatus.PRESENTATION_ERROR.toString()].includes(status)) {
|
|
47
|
+
return SubmissionStatus.PRESENTATION_ERROR;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (["TL", "TLE", SubmissionStatus.TIME_LIMIT_EXCEEDED.toString()].includes(status)) {
|
|
51
|
+
return SubmissionStatus.TIME_LIMIT_EXCEEDED;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (["ML", "MLE", SubmissionStatus.MEMORY_LIMIT_EXCEEDED.toString()].includes(status)) {
|
|
55
|
+
return SubmissionStatus.MEMORY_LIMIT_EXCEEDED;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (["OL", "OLE", SubmissionStatus.OUTPUT_LIMIT_EXCEEDED.toString()].includes(status)) {
|
|
59
|
+
return SubmissionStatus.OUTPUT_LIMIT_EXCEEDED;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (["IL", "ILE", SubmissionStatus.IDLENESS_LIMIT_EXCEEDED.toString()].includes(status)) {
|
|
63
|
+
return SubmissionStatus.IDLENESS_LIMIT_EXCEEDED;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (["RT", "RE", "RTE", SubmissionStatus.RUNTIME_ERROR.toString()].includes(status)) {
|
|
67
|
+
return SubmissionStatus.RUNTIME_ERROR;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (["JE", SubmissionStatus.JUDGEMENT_FAILED.toString()].includes(status)) {
|
|
71
|
+
return SubmissionStatus.JUDGEMENT_FAILED;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (["SE", SubmissionStatus.SYSTEM_ERROR.toString()].includes(status)) {
|
|
75
|
+
return SubmissionStatus.SYSTEM_ERROR;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if ([SubmissionStatus.HACKED.toString()].includes(status)) {
|
|
79
|
+
return SubmissionStatus.HACKED;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if ([SubmissionStatus.CONFIGURATION_ERROR.toString()].includes(status)) {
|
|
83
|
+
return SubmissionStatus.CONFIGURATION_ERROR;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if ([SubmissionStatus.CANCELED.toString()].includes(status)) {
|
|
87
|
+
return SubmissionStatus.CANCELED;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ([SubmissionStatus.SKIPPED.toString()].includes(status)) {
|
|
91
|
+
return SubmissionStatus.SKIPPED;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if ([SubmissionStatus.SECURITY_VIOLATED.toString()].includes(status)) {
|
|
95
|
+
return SubmissionStatus.SECURITY_VIOLATED;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if ([SubmissionStatus.DENIAL_OF_JUDGEMENT.toString()].includes(status)) {
|
|
99
|
+
return SubmissionStatus.DENIAL_OF_JUDGEMENT;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return SubmissionStatus.UNKNOWN;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function isAccepted(status: SubmissionStatus): boolean {
|
|
106
|
+
const acceptedArray = [SubmissionStatus.ACCEPTED, SubmissionStatus.CORRECT];
|
|
107
|
+
|
|
108
|
+
return acceptedArray.includes(status);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function isRejected(status: SubmissionStatus): boolean {
|
|
112
|
+
const rejectArray = [
|
|
113
|
+
SubmissionStatus.RUNTIME_ERROR,
|
|
114
|
+
SubmissionStatus.TIME_LIMIT_EXCEEDED,
|
|
115
|
+
SubmissionStatus.MEMORY_LIMIT_EXCEEDED,
|
|
116
|
+
SubmissionStatus.OUTPUT_LIMIT_EXCEEDED,
|
|
117
|
+
SubmissionStatus.IDLENESS_LIMIT_EXCEEDED,
|
|
118
|
+
SubmissionStatus.WRONG_ANSWER,
|
|
119
|
+
SubmissionStatus.REJECTED,
|
|
120
|
+
SubmissionStatus.JUDGEMENT_FAILED,
|
|
121
|
+
SubmissionStatus.HACKED,
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
return rejectArray.includes(status);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function isPending(status: SubmissionStatus): boolean {
|
|
128
|
+
const pendingArray = [
|
|
129
|
+
SubmissionStatus.PENDING,
|
|
130
|
+
SubmissionStatus.WAITING,
|
|
131
|
+
SubmissionStatus.JUDGING,
|
|
132
|
+
SubmissionStatus.FROZEN,
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
return pendingArray.includes(status);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean {
|
|
139
|
+
const isNotCalculatedPenaltyArray = [
|
|
140
|
+
SubmissionStatus.COMPILE_ERROR,
|
|
141
|
+
SubmissionStatus.PRESENTATION_ERROR,
|
|
142
|
+
SubmissionStatus.CONFIGURATION_ERROR,
|
|
143
|
+
SubmissionStatus.SYSTEM_ERROR,
|
|
144
|
+
SubmissionStatus.CANCELED,
|
|
145
|
+
SubmissionStatus.SKIPPED,
|
|
146
|
+
SubmissionStatus.UNKNOWN,
|
|
147
|
+
SubmissionStatus.UNDEFINED,
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
return isNotCalculatedPenaltyArray.includes(status);
|
|
151
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Submission as ISubmission, Submissions as ISubmissions, SubmissionStatus } from "@xcpcio/types";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
isAccepted,
|
|
5
|
+
isRejected,
|
|
6
|
+
isPending,
|
|
7
|
+
isNotCalculatedPenaltyStatus,
|
|
8
|
+
stringToSubmissionStatus,
|
|
9
|
+
} from "./submission-status";
|
|
10
|
+
|
|
11
|
+
export class Submission {
|
|
12
|
+
id: string;
|
|
13
|
+
teamId: string;
|
|
14
|
+
problemId: string;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
|
|
17
|
+
status = SubmissionStatus.UNKNOWN;
|
|
18
|
+
isIgnore = false;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
this.id = "";
|
|
22
|
+
this.teamId = "";
|
|
23
|
+
this.problemId = "";
|
|
24
|
+
this.timestamp = 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
isAccepted() {
|
|
28
|
+
return isAccepted(this.status);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
isRejected() {
|
|
32
|
+
return isRejected(this.status);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isPending() {
|
|
36
|
+
return isPending(this.status);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
isNotCalculatedPenaltyStatus() {
|
|
40
|
+
return isNotCalculatedPenaltyStatus(this.status);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static compare(lhs: Submission, rhs: Submission): number {
|
|
44
|
+
if (lhs.timestamp !== rhs.timestamp) {
|
|
45
|
+
return lhs.timestamp - rhs.timestamp;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (lhs.teamId === rhs.teamId) {
|
|
49
|
+
if (lhs.isAccepted() && !rhs.isAccepted()) {
|
|
50
|
+
return -1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!lhs.isAccepted() && rhs.isAccepted()) {
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type Submissions = Array<Submission>;
|
|
63
|
+
|
|
64
|
+
export function createSubmission(submissionJSON: ISubmission): Submission {
|
|
65
|
+
const s = new Submission();
|
|
66
|
+
|
|
67
|
+
s.id = String(submissionJSON.id ?? submissionJSON.submission_id ?? "");
|
|
68
|
+
s.teamId = String(submissionJSON.team_id);
|
|
69
|
+
s.problemId = String(submissionJSON.problem_id);
|
|
70
|
+
s.timestamp = submissionJSON.timestamp;
|
|
71
|
+
s.status = stringToSubmissionStatus(submissionJSON.status);
|
|
72
|
+
s.isIgnore = submissionJSON.is_ignore ?? false;
|
|
73
|
+
|
|
74
|
+
return s;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createSubmissions(submissionsJSON: ISubmissions): Submissions {
|
|
78
|
+
if (Array.isArray(submissionsJSON)) {
|
|
79
|
+
return submissionsJSON.map((s, index) => createSubmission({ ...s, id: s.submission_id ?? String(index) }));
|
|
80
|
+
} else {
|
|
81
|
+
const submissions = Object.entries(submissionsJSON).map(([submissionId, s]) =>
|
|
82
|
+
createSubmission({ ...s, id: s.submission_id ?? submissionId }),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return submissions;
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/team.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { Team as ITeam, Teams as ITeams } from "@xcpcio/types";
|
|
2
|
+
|
|
3
|
+
import { TeamProblemStatistics } from "./problem";
|
|
4
|
+
|
|
5
|
+
export class Team {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
organization: string;
|
|
9
|
+
|
|
10
|
+
group: Array<string>;
|
|
11
|
+
tag: Array<string>;
|
|
12
|
+
|
|
13
|
+
coach?: string | Array<string>;
|
|
14
|
+
members?: string | Array<string>;
|
|
15
|
+
|
|
16
|
+
rank: number;
|
|
17
|
+
solvedProblemNum: number;
|
|
18
|
+
penalty: number;
|
|
19
|
+
|
|
20
|
+
problemStatistics: Array<TeamProblemStatistics>;
|
|
21
|
+
problemStatisticsMap: Map<string, TeamProblemStatistics>;
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.id = "";
|
|
25
|
+
this.name = "";
|
|
26
|
+
this.organization = "";
|
|
27
|
+
|
|
28
|
+
this.group = [];
|
|
29
|
+
this.tag = [];
|
|
30
|
+
|
|
31
|
+
this.rank = 0;
|
|
32
|
+
this.solvedProblemNum = 0;
|
|
33
|
+
this.penalty = 0;
|
|
34
|
+
|
|
35
|
+
this.problemStatistics = [];
|
|
36
|
+
this.problemStatisticsMap = new Map<string, TeamProblemStatistics>();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
penaltyToMinute() {
|
|
40
|
+
return Math.floor(this.penalty / 60);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
calcSolvedData() {
|
|
44
|
+
this.solvedProblemNum = 0;
|
|
45
|
+
this.penalty = 0;
|
|
46
|
+
|
|
47
|
+
for (const p of this.problemStatistics) {
|
|
48
|
+
if (p.isAccepted) {
|
|
49
|
+
this.solvedProblemNum++;
|
|
50
|
+
this.penalty += p.penalty;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static compare(lhs: Team, rhs: Team): number {
|
|
56
|
+
if (lhs.solvedProblemNum !== rhs.solvedProblemNum) {
|
|
57
|
+
return rhs.solvedProblemNum - lhs.solvedProblemNum;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (lhs.penalty !== rhs.penalty) {
|
|
61
|
+
return lhs.penalty - rhs.penalty;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (lhs.name < rhs.name) {
|
|
65
|
+
return -1;
|
|
66
|
+
} else if (lhs.name > rhs.name) {
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type Teams = Array<Team>;
|
|
75
|
+
|
|
76
|
+
export function createTeam(teamJSON: ITeam): Team {
|
|
77
|
+
const t = new Team();
|
|
78
|
+
|
|
79
|
+
t.id = teamJSON.id ?? teamJSON.team_id ?? "";
|
|
80
|
+
t.name = teamJSON.name ?? teamJSON.team_name ?? "";
|
|
81
|
+
|
|
82
|
+
t.organization = teamJSON.organization ?? "";
|
|
83
|
+
t.group = teamJSON.group ?? [];
|
|
84
|
+
t.tag = teamJSON.group ?? [];
|
|
85
|
+
|
|
86
|
+
t.coach = teamJSON.coach;
|
|
87
|
+
t.members = teamJSON.members;
|
|
88
|
+
|
|
89
|
+
if (teamJSON.official === true) {
|
|
90
|
+
t.group.push("official");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (teamJSON.unofficial === true) {
|
|
94
|
+
t.group.push("unofficial");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (teamJSON.girl === true) {
|
|
98
|
+
t.group.push("girl");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
t.group = [...new Set(t.group)];
|
|
102
|
+
t.group.sort();
|
|
103
|
+
|
|
104
|
+
return t;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function createTeams(teamsJSON: ITeams): Teams {
|
|
108
|
+
if (Array.isArray(teamsJSON)) {
|
|
109
|
+
return teamsJSON.map((t) => createTeam(t));
|
|
110
|
+
} else {
|
|
111
|
+
const teams = Object.entries(teamsJSON).map(([teamId, team]) =>
|
|
112
|
+
createTeam({ ...team, team_id: team.team_id ?? teamId }),
|
|
113
|
+
);
|
|
114
|
+
return teams;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
2
|
+
|
|
3
|
+
import duration from "dayjs/plugin/duration";
|
|
4
|
+
dayjs.extend(duration);
|
|
5
|
+
|
|
6
|
+
import utc from "dayjs/plugin/utc";
|
|
7
|
+
dayjs.extend(utc);
|
|
8
|
+
|
|
9
|
+
import timezone from "dayjs/plugin/timezone";
|
|
10
|
+
dayjs.extend(timezone);
|
|
11
|
+
|
|
12
|
+
import advancedFormat from "dayjs/plugin/advancedFormat";
|
|
13
|
+
dayjs.extend(advancedFormat);
|
|
14
|
+
|
|
15
|
+
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
|
16
|
+
dayjs.extend(isSameOrBefore);
|
|
17
|
+
|
|
18
|
+
import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
|
|
19
|
+
dayjs.extend(isSameOrAfter);
|
|
20
|
+
|
|
21
|
+
import minMax from "dayjs/plugin/minMax";
|
|
22
|
+
dayjs.extend(minMax);
|
|
23
|
+
|
|
24
|
+
import relativeTime from "dayjs/plugin/relativeTime";
|
|
25
|
+
dayjs.extend(relativeTime);
|
|
26
|
+
|
|
27
|
+
export function createDayJS(time: Date | string | number | undefined = undefined): dayjs.Dayjs {
|
|
28
|
+
if (time === undefined) {
|
|
29
|
+
return dayjs();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof time == "number" && String(time).length === 10) {
|
|
33
|
+
return dayjs.unix(time);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return dayjs(time);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getTimestamp(time: number | dayjs.Dayjs): number {
|
|
40
|
+
if (typeof time === "number") {
|
|
41
|
+
return time;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return time.unix();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getTimeDiff(seconds: number): string {
|
|
48
|
+
const two = (a: number) => {
|
|
49
|
+
if (a < 10) return "0" + a;
|
|
50
|
+
return String(a);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const h = Math.floor(seconds / 3600);
|
|
54
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
55
|
+
const s = seconds % 60;
|
|
56
|
+
|
|
57
|
+
return [two(h), two(m), two(s)].join(":");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { dayjs };
|
|
61
|
+
export default dayjs;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dayjs";
|