@xcpcio/core 0.5.2 → 0.6.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/dist/index.cjs +117 -4
- package/dist/index.d.ts +31 -4
- package/dist/index.mjs +117 -4
- package/package.json +2 -2
- package/src/contest.ts +36 -4
- package/src/group.ts +13 -0
- package/src/rank-statistics.ts +3 -0
- package/src/rank.ts +121 -2
- package/src/team.ts +3 -0
package/dist/index.cjs
CHANGED
|
@@ -177,6 +177,14 @@ class TeamProblemStatistics {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
class Group {
|
|
181
|
+
constructor() {
|
|
182
|
+
this.names = /* @__PURE__ */ new Map();
|
|
183
|
+
this.defaultLang = "zh-CN";
|
|
184
|
+
this.isDefault = false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
180
188
|
class Contest {
|
|
181
189
|
constructor() {
|
|
182
190
|
this.name = "";
|
|
@@ -195,6 +203,8 @@ class Contest {
|
|
|
195
203
|
incorrect: true,
|
|
196
204
|
pending: true
|
|
197
205
|
};
|
|
206
|
+
this.group = /* @__PURE__ */ new Map();
|
|
207
|
+
this.tag = /* @__PURE__ */ new Map();
|
|
198
208
|
}
|
|
199
209
|
getContestDuration(timeFormat = "HH:mm:ss") {
|
|
200
210
|
return dayjs__default.duration(this.endTime.diff(this.startTime)).format(timeFormat);
|
|
@@ -292,8 +302,29 @@ function createContest(contestJSON) {
|
|
|
292
302
|
c.badge = contestJSON.badge;
|
|
293
303
|
c.medal = contestJSON.medal;
|
|
294
304
|
c.organization = contestJSON.organization;
|
|
295
|
-
|
|
296
|
-
|
|
305
|
+
{
|
|
306
|
+
const g = new Group();
|
|
307
|
+
g.names.set("en", "All");
|
|
308
|
+
g.names.set("zh-CN", "\u6240\u6709\u961F\u4F0D");
|
|
309
|
+
g.isDefault = true;
|
|
310
|
+
c.group.set("all", g);
|
|
311
|
+
}
|
|
312
|
+
for (const [k, v] of Object.entries(contestJSON?.group ?? {})) {
|
|
313
|
+
let key = k;
|
|
314
|
+
const g = new Group();
|
|
315
|
+
g.names.set("zh-CN", v);
|
|
316
|
+
if (k === "official") {
|
|
317
|
+
g.names.set("en", "Official");
|
|
318
|
+
}
|
|
319
|
+
if (k === "unofficial") {
|
|
320
|
+
g.names.set("en", "Unofficial");
|
|
321
|
+
}
|
|
322
|
+
if (k === "girl" || k === "girls") {
|
|
323
|
+
g.names.set("en", "Girls");
|
|
324
|
+
key = "girl";
|
|
325
|
+
}
|
|
326
|
+
c.group.set(key, g);
|
|
327
|
+
}
|
|
297
328
|
c.banner = contestJSON.banner;
|
|
298
329
|
c.logo = contestJSON.logo;
|
|
299
330
|
c.boardLink = contestJSON.board_link;
|
|
@@ -373,9 +404,11 @@ function getImageSource(image) {
|
|
|
373
404
|
class RankStatistics {
|
|
374
405
|
constructor() {
|
|
375
406
|
this.teamSolvedNum = [];
|
|
407
|
+
this.maxSolvedProblems = 0;
|
|
376
408
|
}
|
|
377
409
|
reset() {
|
|
378
410
|
this.teamSolvedNum = [];
|
|
411
|
+
this.maxSolvedProblems = 0;
|
|
379
412
|
}
|
|
380
413
|
}
|
|
381
414
|
|
|
@@ -394,6 +427,7 @@ class Team {
|
|
|
394
427
|
this.group = [];
|
|
395
428
|
this.tag = [];
|
|
396
429
|
this.rank = 0;
|
|
430
|
+
this.originalRank = 0;
|
|
397
431
|
this.organizationRank = -1;
|
|
398
432
|
this.solvedProblemNum = 0;
|
|
399
433
|
this.attemptedProblemNum = 0;
|
|
@@ -407,6 +441,7 @@ class Team {
|
|
|
407
441
|
}
|
|
408
442
|
reset() {
|
|
409
443
|
this.rank = 0;
|
|
444
|
+
this.originalRank = 0;
|
|
410
445
|
this.organizationRank = -1;
|
|
411
446
|
this.solvedProblemNum = 0;
|
|
412
447
|
this.attemptedProblemNum = 0;
|
|
@@ -693,6 +728,12 @@ class RankOptions {
|
|
|
693
728
|
this.enableFilterSubmissionsByTimestamp = false;
|
|
694
729
|
this.width = 0;
|
|
695
730
|
this.timestamp = 0;
|
|
731
|
+
this.enableFilterTeamsByGroup = false;
|
|
732
|
+
this.group = "";
|
|
733
|
+
this.filterOrganizations = [];
|
|
734
|
+
this.filterOrganizationMap = /* @__PURE__ */ new Map();
|
|
735
|
+
this.filterTeams = [];
|
|
736
|
+
this.filterTeamMap = /* @__PURE__ */ new Map();
|
|
696
737
|
}
|
|
697
738
|
setWidth(width, contest) {
|
|
698
739
|
this.width = width;
|
|
@@ -702,6 +743,32 @@ class RankOptions {
|
|
|
702
743
|
disableFilterSubmissionByTimestamp() {
|
|
703
744
|
this.enableFilterSubmissionsByTimestamp = false;
|
|
704
745
|
}
|
|
746
|
+
setGroup(group) {
|
|
747
|
+
this.group = group;
|
|
748
|
+
this.enableFilterTeamsByGroup = true;
|
|
749
|
+
if (this.group === "all") {
|
|
750
|
+
this.disableFilterTeamsByGroup();
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
disableFilterTeamsByGroup() {
|
|
754
|
+
this.enableFilterTeamsByGroup = false;
|
|
755
|
+
}
|
|
756
|
+
setFilterOrganizations(filterOrganizations) {
|
|
757
|
+
const m = /* @__PURE__ */ new Map();
|
|
758
|
+
filterOrganizations.forEach((item) => {
|
|
759
|
+
m.set(item.value, item);
|
|
760
|
+
});
|
|
761
|
+
this.filterOrganizations = filterOrganizations;
|
|
762
|
+
this.filterOrganizationMap = m;
|
|
763
|
+
}
|
|
764
|
+
setFilterTeams(filterTeams) {
|
|
765
|
+
const m = /* @__PURE__ */ new Map();
|
|
766
|
+
filterTeams.forEach((item) => {
|
|
767
|
+
m.set(item.value, item);
|
|
768
|
+
});
|
|
769
|
+
this.filterTeams = filterTeams;
|
|
770
|
+
this.filterTeamMap = m;
|
|
771
|
+
}
|
|
705
772
|
}
|
|
706
773
|
class Rank {
|
|
707
774
|
constructor(contest, teams, submissions) {
|
|
@@ -710,11 +777,23 @@ class Rank {
|
|
|
710
777
|
this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
|
|
711
778
|
this.submissions = ___default.cloneDeep(submissions).sort(Submission.compare);
|
|
712
779
|
this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
|
|
780
|
+
this.organizations = this.buildOrganizations();
|
|
781
|
+
this.originTeams = this.teams.map((t) => t);
|
|
782
|
+
this.originTeams.sort(Team.compare);
|
|
713
783
|
this.rankStatistics = new RankStatistics();
|
|
714
784
|
this.options = new RankOptions();
|
|
715
785
|
}
|
|
716
786
|
buildRank() {
|
|
717
787
|
(() => {
|
|
788
|
+
(() => {
|
|
789
|
+
this.teams = [];
|
|
790
|
+
for (const [_k, v] of this.teamsMap) {
|
|
791
|
+
if (this.filterTeamByOrg(v)) {
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
this.teams.push(v);
|
|
795
|
+
}
|
|
796
|
+
})();
|
|
718
797
|
for (const t of this.teams) {
|
|
719
798
|
t.reset();
|
|
720
799
|
t.problemStatistics = this.contest.problems.map((p) => {
|
|
@@ -744,7 +823,7 @@ class Rank {
|
|
|
744
823
|
const team = this.teamsMap.get(teamId);
|
|
745
824
|
const problem = this.contest.problemsMap.get(problemId);
|
|
746
825
|
(() => {
|
|
747
|
-
if (team === void 0 || problem === void 0) {
|
|
826
|
+
if (team === void 0 || this.filterTeamByOrg(team) || problem === void 0) {
|
|
748
827
|
return;
|
|
749
828
|
}
|
|
750
829
|
const problemStatistics = team.problemStatisticsMap.get(problemId);
|
|
@@ -804,8 +883,11 @@ class Rank {
|
|
|
804
883
|
}
|
|
805
884
|
preSubmissionTimestampToMinute = s.timestampToMinute;
|
|
806
885
|
}
|
|
807
|
-
this.teams.forEach((t) => t.
|
|
886
|
+
this.teams.forEach((t) => t.calcSolvedData());
|
|
887
|
+
this.teams.sort(Team.compare);
|
|
888
|
+
this.buildTeamRank();
|
|
808
889
|
this.buildOrgRank();
|
|
890
|
+
this.teams.forEach((t) => t.postProcessPlaceChartPoints());
|
|
809
891
|
})();
|
|
810
892
|
(() => {
|
|
811
893
|
this.rankStatistics.reset();
|
|
@@ -813,14 +895,19 @@ class Rank {
|
|
|
813
895
|
for (const t of this.teams) {
|
|
814
896
|
this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
|
|
815
897
|
}
|
|
898
|
+
if (this.teams.length > 0) {
|
|
899
|
+
this.rankStatistics.maxSolvedProblems = this.teams[0].solvedProblemNum;
|
|
900
|
+
}
|
|
816
901
|
})();
|
|
817
902
|
return this;
|
|
818
903
|
}
|
|
819
904
|
buildTeamRank() {
|
|
820
905
|
let rank = 1;
|
|
906
|
+
let originalRank = 1;
|
|
821
907
|
let preTeam = null;
|
|
822
908
|
for (const t of this.teams) {
|
|
823
909
|
t.rank = rank++;
|
|
910
|
+
t.originalRank = originalRank++;
|
|
824
911
|
if (preTeam !== null) {
|
|
825
912
|
if (t.isEqualRank(preTeam)) {
|
|
826
913
|
t.rank = preTeam.rank;
|
|
@@ -851,6 +938,32 @@ class Rank {
|
|
|
851
938
|
preTeam = t;
|
|
852
939
|
}
|
|
853
940
|
}
|
|
941
|
+
buildOrganizations() {
|
|
942
|
+
if (!this.contest.organization) {
|
|
943
|
+
return [];
|
|
944
|
+
}
|
|
945
|
+
const res = new Array();
|
|
946
|
+
const se = /* @__PURE__ */ new Set();
|
|
947
|
+
this.teams.forEach((t) => {
|
|
948
|
+
const org = t.organization;
|
|
949
|
+
if (se.has(org)) {
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
res.push(org);
|
|
953
|
+
se.add(org);
|
|
954
|
+
});
|
|
955
|
+
res.sort();
|
|
956
|
+
return res;
|
|
957
|
+
}
|
|
958
|
+
filterTeamByOrg(team) {
|
|
959
|
+
const o = this.options;
|
|
960
|
+
if (o.enableFilterTeamsByGroup) {
|
|
961
|
+
if (!team.group?.includes(o.group)) {
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
854
967
|
getSubmissions() {
|
|
855
968
|
if (this.options.enableFilterSubmissionsByTimestamp === false) {
|
|
856
969
|
return this.submissions;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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, Image, ContestState, Contest as Contest$1, ContestIndex as ContestIndex$1, Team as Team$1, Teams as Teams$1 } from '@xcpcio/types';
|
|
3
|
+
import { SubmissionStatus, Submission as Submission$1, Submissions as Submissions$1, BalloonColor, Problem as Problem$1, Problems as Problems$1, Lang, StatusTimeDisplay, Image, ContestState, Contest as Contest$1, ContestIndex as ContestIndex$1, Team as Team$1, Teams as Teams$1 } from '@xcpcio/types';
|
|
4
4
|
|
|
5
5
|
declare class Submission {
|
|
6
6
|
id: string;
|
|
@@ -77,6 +77,13 @@ declare function createDayJS(time?: Date | string | number | undefined): dayjs.D
|
|
|
77
77
|
declare function getTimestamp(time: number | dayjs.Dayjs): number;
|
|
78
78
|
declare function getTimeDiff(seconds: number): string;
|
|
79
79
|
|
|
80
|
+
declare class Group {
|
|
81
|
+
names: Map<Lang, string>;
|
|
82
|
+
defaultLang: Lang;
|
|
83
|
+
isDefault: boolean;
|
|
84
|
+
constructor();
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
declare class Contest {
|
|
81
88
|
name: string;
|
|
82
89
|
startTime: dayjs.Dayjs;
|
|
@@ -92,8 +99,8 @@ declare class Contest {
|
|
|
92
99
|
badge?: string;
|
|
93
100
|
medal?: Record<string, Record<string, number>>;
|
|
94
101
|
organization?: string;
|
|
95
|
-
group
|
|
96
|
-
tag
|
|
102
|
+
group: Map<string, Group>;
|
|
103
|
+
tag: Map<string, string>;
|
|
97
104
|
logo?: Image;
|
|
98
105
|
banner?: Image;
|
|
99
106
|
boardLink?: string;
|
|
@@ -132,6 +139,7 @@ declare function getImageSource(image: Image): string;
|
|
|
132
139
|
|
|
133
140
|
declare class RankStatistics {
|
|
134
141
|
teamSolvedNum: Array<number>;
|
|
142
|
+
maxSolvedProblems: number;
|
|
135
143
|
constructor();
|
|
136
144
|
reset(): void;
|
|
137
145
|
}
|
|
@@ -152,6 +160,7 @@ declare class Team {
|
|
|
152
160
|
coach?: string | Array<string>;
|
|
153
161
|
members?: string | Array<string>;
|
|
154
162
|
rank: number;
|
|
163
|
+
originalRank: number;
|
|
155
164
|
organizationRank: number;
|
|
156
165
|
solvedProblemNum: number;
|
|
157
166
|
attemptedProblemNum: number;
|
|
@@ -175,13 +184,27 @@ type Teams = Array<Team>;
|
|
|
175
184
|
declare function createTeam(teamJSON: Team$1): Team;
|
|
176
185
|
declare function createTeams(teamsJSON: Teams$1): Teams;
|
|
177
186
|
|
|
187
|
+
interface SelectOptionItem {
|
|
188
|
+
value: string;
|
|
189
|
+
text: string;
|
|
190
|
+
}
|
|
178
191
|
declare class RankOptions {
|
|
179
192
|
enableFilterSubmissionsByTimestamp: boolean;
|
|
180
193
|
width: number;
|
|
181
194
|
timestamp: number;
|
|
195
|
+
enableFilterTeamsByGroup: boolean;
|
|
196
|
+
group: string;
|
|
197
|
+
filterOrganizations: Array<SelectOptionItem>;
|
|
198
|
+
filterOrganizationMap: Map<string, SelectOptionItem>;
|
|
199
|
+
filterTeams: Array<SelectOptionItem>;
|
|
200
|
+
filterTeamMap: Map<string, SelectOptionItem>;
|
|
182
201
|
constructor();
|
|
183
202
|
setWidth(width: number, contest: Contest): void;
|
|
184
203
|
disableFilterSubmissionByTimestamp(): void;
|
|
204
|
+
setGroup(group: string): void;
|
|
205
|
+
disableFilterTeamsByGroup(): void;
|
|
206
|
+
setFilterOrganizations(filterOrganizations: Array<SelectOptionItem>): void;
|
|
207
|
+
setFilterTeams(filterTeams: Array<SelectOptionItem>): void;
|
|
185
208
|
}
|
|
186
209
|
declare class Rank {
|
|
187
210
|
readonly contest: Contest;
|
|
@@ -189,12 +212,16 @@ declare class Rank {
|
|
|
189
212
|
teamsMap: Map<string, Team>;
|
|
190
213
|
submissions: Submissions;
|
|
191
214
|
submissionsMap: Map<string, Submission>;
|
|
215
|
+
organizations: Array<string>;
|
|
216
|
+
originTeams: Teams;
|
|
192
217
|
rankStatistics: RankStatistics;
|
|
193
218
|
options: RankOptions;
|
|
194
219
|
constructor(contest: Contest, teams: Teams, submissions: Submissions);
|
|
195
220
|
buildRank(): this;
|
|
196
221
|
buildTeamRank(): void;
|
|
197
222
|
buildOrgRank(): void;
|
|
223
|
+
buildOrganizations(): string[];
|
|
224
|
+
filterTeamByOrg(team: Team): boolean;
|
|
198
225
|
getSubmissions(): Submissions;
|
|
199
226
|
}
|
|
200
227
|
|
|
@@ -222,4 +249,4 @@ declare function isRejected(status: SubmissionStatus): boolean;
|
|
|
222
249
|
declare function isPending(status: SubmissionStatus): boolean;
|
|
223
250
|
declare function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean;
|
|
224
251
|
|
|
225
|
-
export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, PlaceChartPointData, Problem, ProblemStatistics, Problems, Rank, RankOptions, 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 };
|
|
252
|
+
export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, PlaceChartPointData, Problem, ProblemStatistics, Problems, Rank, RankOptions, RankStatistics, Resolver, SelectOptionItem, 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
|
@@ -161,6 +161,14 @@ class TeamProblemStatistics {
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
class Group {
|
|
165
|
+
constructor() {
|
|
166
|
+
this.names = /* @__PURE__ */ new Map();
|
|
167
|
+
this.defaultLang = "zh-CN";
|
|
168
|
+
this.isDefault = false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
164
172
|
class Contest {
|
|
165
173
|
constructor() {
|
|
166
174
|
this.name = "";
|
|
@@ -179,6 +187,8 @@ class Contest {
|
|
|
179
187
|
incorrect: true,
|
|
180
188
|
pending: true
|
|
181
189
|
};
|
|
190
|
+
this.group = /* @__PURE__ */ new Map();
|
|
191
|
+
this.tag = /* @__PURE__ */ new Map();
|
|
182
192
|
}
|
|
183
193
|
getContestDuration(timeFormat = "HH:mm:ss") {
|
|
184
194
|
return dayjs.duration(this.endTime.diff(this.startTime)).format(timeFormat);
|
|
@@ -276,8 +286,29 @@ function createContest(contestJSON) {
|
|
|
276
286
|
c.badge = contestJSON.badge;
|
|
277
287
|
c.medal = contestJSON.medal;
|
|
278
288
|
c.organization = contestJSON.organization;
|
|
279
|
-
|
|
280
|
-
|
|
289
|
+
{
|
|
290
|
+
const g = new Group();
|
|
291
|
+
g.names.set("en", "All");
|
|
292
|
+
g.names.set("zh-CN", "\u6240\u6709\u961F\u4F0D");
|
|
293
|
+
g.isDefault = true;
|
|
294
|
+
c.group.set("all", g);
|
|
295
|
+
}
|
|
296
|
+
for (const [k, v] of Object.entries(contestJSON?.group ?? {})) {
|
|
297
|
+
let key = k;
|
|
298
|
+
const g = new Group();
|
|
299
|
+
g.names.set("zh-CN", v);
|
|
300
|
+
if (k === "official") {
|
|
301
|
+
g.names.set("en", "Official");
|
|
302
|
+
}
|
|
303
|
+
if (k === "unofficial") {
|
|
304
|
+
g.names.set("en", "Unofficial");
|
|
305
|
+
}
|
|
306
|
+
if (k === "girl" || k === "girls") {
|
|
307
|
+
g.names.set("en", "Girls");
|
|
308
|
+
key = "girl";
|
|
309
|
+
}
|
|
310
|
+
c.group.set(key, g);
|
|
311
|
+
}
|
|
281
312
|
c.banner = contestJSON.banner;
|
|
282
313
|
c.logo = contestJSON.logo;
|
|
283
314
|
c.boardLink = contestJSON.board_link;
|
|
@@ -357,9 +388,11 @@ function getImageSource(image) {
|
|
|
357
388
|
class RankStatistics {
|
|
358
389
|
constructor() {
|
|
359
390
|
this.teamSolvedNum = [];
|
|
391
|
+
this.maxSolvedProblems = 0;
|
|
360
392
|
}
|
|
361
393
|
reset() {
|
|
362
394
|
this.teamSolvedNum = [];
|
|
395
|
+
this.maxSolvedProblems = 0;
|
|
363
396
|
}
|
|
364
397
|
}
|
|
365
398
|
|
|
@@ -378,6 +411,7 @@ class Team {
|
|
|
378
411
|
this.group = [];
|
|
379
412
|
this.tag = [];
|
|
380
413
|
this.rank = 0;
|
|
414
|
+
this.originalRank = 0;
|
|
381
415
|
this.organizationRank = -1;
|
|
382
416
|
this.solvedProblemNum = 0;
|
|
383
417
|
this.attemptedProblemNum = 0;
|
|
@@ -391,6 +425,7 @@ class Team {
|
|
|
391
425
|
}
|
|
392
426
|
reset() {
|
|
393
427
|
this.rank = 0;
|
|
428
|
+
this.originalRank = 0;
|
|
394
429
|
this.organizationRank = -1;
|
|
395
430
|
this.solvedProblemNum = 0;
|
|
396
431
|
this.attemptedProblemNum = 0;
|
|
@@ -677,6 +712,12 @@ class RankOptions {
|
|
|
677
712
|
this.enableFilterSubmissionsByTimestamp = false;
|
|
678
713
|
this.width = 0;
|
|
679
714
|
this.timestamp = 0;
|
|
715
|
+
this.enableFilterTeamsByGroup = false;
|
|
716
|
+
this.group = "";
|
|
717
|
+
this.filterOrganizations = [];
|
|
718
|
+
this.filterOrganizationMap = /* @__PURE__ */ new Map();
|
|
719
|
+
this.filterTeams = [];
|
|
720
|
+
this.filterTeamMap = /* @__PURE__ */ new Map();
|
|
680
721
|
}
|
|
681
722
|
setWidth(width, contest) {
|
|
682
723
|
this.width = width;
|
|
@@ -686,6 +727,32 @@ class RankOptions {
|
|
|
686
727
|
disableFilterSubmissionByTimestamp() {
|
|
687
728
|
this.enableFilterSubmissionsByTimestamp = false;
|
|
688
729
|
}
|
|
730
|
+
setGroup(group) {
|
|
731
|
+
this.group = group;
|
|
732
|
+
this.enableFilterTeamsByGroup = true;
|
|
733
|
+
if (this.group === "all") {
|
|
734
|
+
this.disableFilterTeamsByGroup();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
disableFilterTeamsByGroup() {
|
|
738
|
+
this.enableFilterTeamsByGroup = false;
|
|
739
|
+
}
|
|
740
|
+
setFilterOrganizations(filterOrganizations) {
|
|
741
|
+
const m = /* @__PURE__ */ new Map();
|
|
742
|
+
filterOrganizations.forEach((item) => {
|
|
743
|
+
m.set(item.value, item);
|
|
744
|
+
});
|
|
745
|
+
this.filterOrganizations = filterOrganizations;
|
|
746
|
+
this.filterOrganizationMap = m;
|
|
747
|
+
}
|
|
748
|
+
setFilterTeams(filterTeams) {
|
|
749
|
+
const m = /* @__PURE__ */ new Map();
|
|
750
|
+
filterTeams.forEach((item) => {
|
|
751
|
+
m.set(item.value, item);
|
|
752
|
+
});
|
|
753
|
+
this.filterTeams = filterTeams;
|
|
754
|
+
this.filterTeamMap = m;
|
|
755
|
+
}
|
|
689
756
|
}
|
|
690
757
|
class Rank {
|
|
691
758
|
constructor(contest, teams, submissions) {
|
|
@@ -694,11 +761,23 @@ class Rank {
|
|
|
694
761
|
this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
|
|
695
762
|
this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
|
|
696
763
|
this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
|
|
764
|
+
this.organizations = this.buildOrganizations();
|
|
765
|
+
this.originTeams = this.teams.map((t) => t);
|
|
766
|
+
this.originTeams.sort(Team.compare);
|
|
697
767
|
this.rankStatistics = new RankStatistics();
|
|
698
768
|
this.options = new RankOptions();
|
|
699
769
|
}
|
|
700
770
|
buildRank() {
|
|
701
771
|
(() => {
|
|
772
|
+
(() => {
|
|
773
|
+
this.teams = [];
|
|
774
|
+
for (const [_k, v] of this.teamsMap) {
|
|
775
|
+
if (this.filterTeamByOrg(v)) {
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
this.teams.push(v);
|
|
779
|
+
}
|
|
780
|
+
})();
|
|
702
781
|
for (const t of this.teams) {
|
|
703
782
|
t.reset();
|
|
704
783
|
t.problemStatistics = this.contest.problems.map((p) => {
|
|
@@ -728,7 +807,7 @@ class Rank {
|
|
|
728
807
|
const team = this.teamsMap.get(teamId);
|
|
729
808
|
const problem = this.contest.problemsMap.get(problemId);
|
|
730
809
|
(() => {
|
|
731
|
-
if (team === void 0 || problem === void 0) {
|
|
810
|
+
if (team === void 0 || this.filterTeamByOrg(team) || problem === void 0) {
|
|
732
811
|
return;
|
|
733
812
|
}
|
|
734
813
|
const problemStatistics = team.problemStatisticsMap.get(problemId);
|
|
@@ -788,8 +867,11 @@ class Rank {
|
|
|
788
867
|
}
|
|
789
868
|
preSubmissionTimestampToMinute = s.timestampToMinute;
|
|
790
869
|
}
|
|
791
|
-
this.teams.forEach((t) => t.
|
|
870
|
+
this.teams.forEach((t) => t.calcSolvedData());
|
|
871
|
+
this.teams.sort(Team.compare);
|
|
872
|
+
this.buildTeamRank();
|
|
792
873
|
this.buildOrgRank();
|
|
874
|
+
this.teams.forEach((t) => t.postProcessPlaceChartPoints());
|
|
793
875
|
})();
|
|
794
876
|
(() => {
|
|
795
877
|
this.rankStatistics.reset();
|
|
@@ -797,14 +879,19 @@ class Rank {
|
|
|
797
879
|
for (const t of this.teams) {
|
|
798
880
|
this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
|
|
799
881
|
}
|
|
882
|
+
if (this.teams.length > 0) {
|
|
883
|
+
this.rankStatistics.maxSolvedProblems = this.teams[0].solvedProblemNum;
|
|
884
|
+
}
|
|
800
885
|
})();
|
|
801
886
|
return this;
|
|
802
887
|
}
|
|
803
888
|
buildTeamRank() {
|
|
804
889
|
let rank = 1;
|
|
890
|
+
let originalRank = 1;
|
|
805
891
|
let preTeam = null;
|
|
806
892
|
for (const t of this.teams) {
|
|
807
893
|
t.rank = rank++;
|
|
894
|
+
t.originalRank = originalRank++;
|
|
808
895
|
if (preTeam !== null) {
|
|
809
896
|
if (t.isEqualRank(preTeam)) {
|
|
810
897
|
t.rank = preTeam.rank;
|
|
@@ -835,6 +922,32 @@ class Rank {
|
|
|
835
922
|
preTeam = t;
|
|
836
923
|
}
|
|
837
924
|
}
|
|
925
|
+
buildOrganizations() {
|
|
926
|
+
if (!this.contest.organization) {
|
|
927
|
+
return [];
|
|
928
|
+
}
|
|
929
|
+
const res = new Array();
|
|
930
|
+
const se = /* @__PURE__ */ new Set();
|
|
931
|
+
this.teams.forEach((t) => {
|
|
932
|
+
const org = t.organization;
|
|
933
|
+
if (se.has(org)) {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
res.push(org);
|
|
937
|
+
se.add(org);
|
|
938
|
+
});
|
|
939
|
+
res.sort();
|
|
940
|
+
return res;
|
|
941
|
+
}
|
|
942
|
+
filterTeamByOrg(team) {
|
|
943
|
+
const o = this.options;
|
|
944
|
+
if (o.enableFilterTeamsByGroup) {
|
|
945
|
+
if (!team.group?.includes(o.group)) {
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
838
951
|
getSubmissions() {
|
|
839
952
|
if (this.options.enableFilterSubmissionsByTimestamp === false) {
|
|
840
953
|
return this.submissions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcpcio/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "XCPCIO Core",
|
|
5
5
|
"author": "Dup4 <lyuzhi.pan@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"dayjs": "^1.11.8",
|
|
44
44
|
"lodash": "^4.17.21",
|
|
45
|
-
"@xcpcio/types": "0.
|
|
45
|
+
"@xcpcio/types": "0.6.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@babel/types": "^7.22.4",
|
package/src/contest.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { ContestState, VERSION } from "@xcpcio/types";
|
|
|
4
4
|
import type { Problem, Problems } from "./problem";
|
|
5
5
|
import { createProblems, createProblemsByProblemIds } from "./problem";
|
|
6
6
|
import { createDayJS, dayjs, getTimeDiff } from "./utils";
|
|
7
|
+
import { Group } from "./group";
|
|
7
8
|
|
|
8
9
|
export class Contest {
|
|
9
10
|
name = "";
|
|
@@ -27,8 +28,8 @@ export class Contest {
|
|
|
27
28
|
medal?: Record<string, Record<string, number>>;
|
|
28
29
|
organization?: string;
|
|
29
30
|
|
|
30
|
-
group
|
|
31
|
-
tag
|
|
31
|
+
group: Map<string, Group>;
|
|
32
|
+
tag: Map<string, string>;
|
|
32
33
|
|
|
33
34
|
logo?: Image;
|
|
34
35
|
banner?: Image;
|
|
@@ -56,6 +57,9 @@ export class Contest {
|
|
|
56
57
|
incorrect: true,
|
|
57
58
|
pending: true,
|
|
58
59
|
};
|
|
60
|
+
|
|
61
|
+
this.group = new Map<string, Group>();
|
|
62
|
+
this.tag = new Map<string, string>();
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
getContestDuration(timeFormat = "HH:mm:ss"): string {
|
|
@@ -189,8 +193,36 @@ export function createContest(contestJSON: IContest): Contest {
|
|
|
189
193
|
c.medal = contestJSON.medal;
|
|
190
194
|
c.organization = contestJSON.organization;
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
|
|
196
|
+
{
|
|
197
|
+
const g = new Group();
|
|
198
|
+
g.names.set("en", "All");
|
|
199
|
+
g.names.set("zh-CN", "所有队伍");
|
|
200
|
+
g.isDefault = true;
|
|
201
|
+
|
|
202
|
+
c.group.set("all", g);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
for (const [k, v] of Object.entries(contestJSON?.group ?? {})) {
|
|
206
|
+
let key = k;
|
|
207
|
+
|
|
208
|
+
const g = new Group();
|
|
209
|
+
g.names.set("zh-CN", v);
|
|
210
|
+
|
|
211
|
+
if (k === "official") {
|
|
212
|
+
g.names.set("en", "Official");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (k === "unofficial") {
|
|
216
|
+
g.names.set("en", "Unofficial");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (k === "girl" || k === "girls") {
|
|
220
|
+
g.names.set("en", "Girls");
|
|
221
|
+
key = "girl";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
c.group.set(key, g);
|
|
225
|
+
}
|
|
194
226
|
|
|
195
227
|
c.banner = contestJSON.banner;
|
|
196
228
|
|
package/src/group.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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/rank-statistics.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
export class RankStatistics {
|
|
2
2
|
teamSolvedNum: Array<number>;
|
|
3
|
+
maxSolvedProblems: number;
|
|
3
4
|
|
|
4
5
|
constructor() {
|
|
5
6
|
this.teamSolvedNum = [];
|
|
7
|
+
this.maxSolvedProblems = 0;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
reset() {
|
|
9
11
|
this.teamSolvedNum = [];
|
|
12
|
+
this.maxSolvedProblems = 0;
|
|
10
13
|
}
|
|
11
14
|
}
|
package/src/rank.ts
CHANGED
|
@@ -8,15 +8,37 @@ import { Submission } from "./submission";
|
|
|
8
8
|
import { TeamProblemStatistics } from "./problem";
|
|
9
9
|
import { RankStatistics } from "./rank-statistics";
|
|
10
10
|
|
|
11
|
+
export interface SelectOptionItem {
|
|
12
|
+
value: string;
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
export class RankOptions {
|
|
12
17
|
enableFilterSubmissionsByTimestamp: boolean;
|
|
13
18
|
width: number;
|
|
14
19
|
timestamp: number;
|
|
15
20
|
|
|
21
|
+
enableFilterTeamsByGroup: boolean;
|
|
22
|
+
group: string;
|
|
23
|
+
|
|
24
|
+
filterOrganizations: Array<SelectOptionItem>;
|
|
25
|
+
filterOrganizationMap: Map<string, SelectOptionItem>;
|
|
26
|
+
filterTeams: Array<SelectOptionItem>;
|
|
27
|
+
filterTeamMap: Map<string, SelectOptionItem>;
|
|
28
|
+
|
|
16
29
|
constructor() {
|
|
17
30
|
this.enableFilterSubmissionsByTimestamp = false;
|
|
18
31
|
this.width = 0;
|
|
19
32
|
this.timestamp = 0;
|
|
33
|
+
|
|
34
|
+
this.enableFilterTeamsByGroup = false;
|
|
35
|
+
this.group = "";
|
|
36
|
+
|
|
37
|
+
this.filterOrganizations = [];
|
|
38
|
+
this.filterOrganizationMap = new Map<string, SelectOptionItem>();
|
|
39
|
+
|
|
40
|
+
this.filterTeams = [];
|
|
41
|
+
this.filterTeamMap = new Map<string, SelectOptionItem>();
|
|
20
42
|
}
|
|
21
43
|
|
|
22
44
|
setWidth(width: number, contest: Contest) {
|
|
@@ -28,6 +50,39 @@ export class RankOptions {
|
|
|
28
50
|
disableFilterSubmissionByTimestamp() {
|
|
29
51
|
this.enableFilterSubmissionsByTimestamp = false;
|
|
30
52
|
}
|
|
53
|
+
|
|
54
|
+
setGroup(group: string) {
|
|
55
|
+
this.group = group;
|
|
56
|
+
this.enableFilterTeamsByGroup = true;
|
|
57
|
+
|
|
58
|
+
if (this.group === "all") {
|
|
59
|
+
this.disableFilterTeamsByGroup();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
disableFilterTeamsByGroup() {
|
|
64
|
+
this.enableFilterTeamsByGroup = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setFilterOrganizations(filterOrganizations: Array<SelectOptionItem>) {
|
|
68
|
+
const m = new Map<string, SelectOptionItem>();
|
|
69
|
+
filterOrganizations.forEach((item) => {
|
|
70
|
+
m.set(item.value, item);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
this.filterOrganizations = filterOrganizations;
|
|
74
|
+
this.filterOrganizationMap = m;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setFilterTeams(filterTeams: Array<SelectOptionItem>) {
|
|
78
|
+
const m = new Map<string, SelectOptionItem>();
|
|
79
|
+
filterTeams.forEach((item) => {
|
|
80
|
+
m.set(item.value, item);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.filterTeams = filterTeams;
|
|
84
|
+
this.filterTeamMap = m;
|
|
85
|
+
}
|
|
31
86
|
}
|
|
32
87
|
|
|
33
88
|
export class Rank {
|
|
@@ -39,6 +94,9 @@ export class Rank {
|
|
|
39
94
|
submissions: Submissions;
|
|
40
95
|
submissionsMap: Map<string, Submission>;
|
|
41
96
|
|
|
97
|
+
organizations: Array<string>;
|
|
98
|
+
originTeams: Teams;
|
|
99
|
+
|
|
42
100
|
rankStatistics: RankStatistics;
|
|
43
101
|
|
|
44
102
|
options: RankOptions;
|
|
@@ -52,6 +110,10 @@ export class Rank {
|
|
|
52
110
|
this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
|
|
53
111
|
this.submissionsMap = new Map(this.submissions.map(s => [s.id, s]));
|
|
54
112
|
|
|
113
|
+
this.organizations = this.buildOrganizations();
|
|
114
|
+
this.originTeams = this.teams.map(t => t);
|
|
115
|
+
this.originTeams.sort(Team.compare);
|
|
116
|
+
|
|
55
117
|
this.rankStatistics = new RankStatistics();
|
|
56
118
|
|
|
57
119
|
this.options = new RankOptions();
|
|
@@ -59,6 +121,18 @@ export class Rank {
|
|
|
59
121
|
|
|
60
122
|
buildRank() {
|
|
61
123
|
(() => {
|
|
124
|
+
(() => {
|
|
125
|
+
this.teams = [];
|
|
126
|
+
|
|
127
|
+
for (const [_k, v] of this.teamsMap) {
|
|
128
|
+
if (this.filterTeamByOrg(v)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.teams.push(v);
|
|
133
|
+
}
|
|
134
|
+
})();
|
|
135
|
+
|
|
62
136
|
for (const t of this.teams) {
|
|
63
137
|
t.reset();
|
|
64
138
|
|
|
@@ -97,7 +171,7 @@ export class Rank {
|
|
|
97
171
|
const problem = this.contest.problemsMap.get(problemId);
|
|
98
172
|
|
|
99
173
|
(() => {
|
|
100
|
-
if (team === undefined || problem === undefined) {
|
|
174
|
+
if (team === undefined || this.filterTeamByOrg(team) || problem === undefined) {
|
|
101
175
|
return;
|
|
102
176
|
}
|
|
103
177
|
|
|
@@ -177,8 +251,12 @@ export class Rank {
|
|
|
177
251
|
preSubmissionTimestampToMinute = s.timestampToMinute;
|
|
178
252
|
}
|
|
179
253
|
|
|
180
|
-
this.teams.forEach(t => t.
|
|
254
|
+
this.teams.forEach(t => t.calcSolvedData());
|
|
255
|
+
this.teams.sort(Team.compare);
|
|
256
|
+
this.buildTeamRank();
|
|
181
257
|
this.buildOrgRank();
|
|
258
|
+
|
|
259
|
+
this.teams.forEach(t => t.postProcessPlaceChartPoints());
|
|
182
260
|
})();
|
|
183
261
|
|
|
184
262
|
(() => {
|
|
@@ -188,6 +266,10 @@ export class Rank {
|
|
|
188
266
|
for (const t of this.teams) {
|
|
189
267
|
this.rankStatistics.teamSolvedNum[t.solvedProblemNum]++;
|
|
190
268
|
}
|
|
269
|
+
|
|
270
|
+
if (this.teams.length > 0) {
|
|
271
|
+
this.rankStatistics.maxSolvedProblems = this.teams[0].solvedProblemNum;
|
|
272
|
+
}
|
|
191
273
|
})();
|
|
192
274
|
|
|
193
275
|
return this;
|
|
@@ -195,9 +277,11 @@ export class Rank {
|
|
|
195
277
|
|
|
196
278
|
buildTeamRank() {
|
|
197
279
|
let rank = 1;
|
|
280
|
+
let originalRank = 1;
|
|
198
281
|
let preTeam = null;
|
|
199
282
|
for (const t of this.teams) {
|
|
200
283
|
t.rank = rank++;
|
|
284
|
+
t.originalRank = originalRank++;
|
|
201
285
|
|
|
202
286
|
if (preTeam !== null) {
|
|
203
287
|
if (t.isEqualRank(preTeam)) {
|
|
@@ -238,6 +322,41 @@ export class Rank {
|
|
|
238
322
|
}
|
|
239
323
|
}
|
|
240
324
|
|
|
325
|
+
buildOrganizations() {
|
|
326
|
+
if (!this.contest.organization) {
|
|
327
|
+
return [];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const res = new Array<string>();
|
|
331
|
+
const se = new Set<string>();
|
|
332
|
+
|
|
333
|
+
this.teams.forEach((t) => {
|
|
334
|
+
const org = t.organization;
|
|
335
|
+
if (se.has(org)) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
res.push(org);
|
|
340
|
+
se.add(org);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
res.sort();
|
|
344
|
+
|
|
345
|
+
return res;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
filterTeamByOrg(team: Team) {
|
|
349
|
+
const o = this.options;
|
|
350
|
+
|
|
351
|
+
if (o.enableFilterTeamsByGroup) {
|
|
352
|
+
if (!team.group?.includes(o.group)) {
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
|
|
241
360
|
getSubmissions() {
|
|
242
361
|
if (this.options.enableFilterSubmissionsByTimestamp === false) {
|
|
243
362
|
return this.submissions;
|
package/src/team.ts
CHANGED
|
@@ -30,6 +30,7 @@ export class Team {
|
|
|
30
30
|
members?: string | Array<string>;
|
|
31
31
|
|
|
32
32
|
rank: number;
|
|
33
|
+
originalRank: number;
|
|
33
34
|
organizationRank: number;
|
|
34
35
|
|
|
35
36
|
solvedProblemNum: number;
|
|
@@ -57,6 +58,7 @@ export class Team {
|
|
|
57
58
|
this.tag = [];
|
|
58
59
|
|
|
59
60
|
this.rank = 0;
|
|
61
|
+
this.originalRank = 0;
|
|
60
62
|
this.organizationRank = -1;
|
|
61
63
|
|
|
62
64
|
this.solvedProblemNum = 0;
|
|
@@ -77,6 +79,7 @@ export class Team {
|
|
|
77
79
|
|
|
78
80
|
reset() {
|
|
79
81
|
this.rank = 0;
|
|
82
|
+
this.originalRank = 0;
|
|
80
83
|
this.organizationRank = -1;
|
|
81
84
|
|
|
82
85
|
this.solvedProblemNum = 0;
|