@xcpcio/core 0.3.1 → 0.4.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 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 "0" + a;
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) {
@@ -281,20 +385,29 @@ class Team {
281
385
  this.group = [];
282
386
  this.tag = [];
283
387
  this.rank = 0;
388
+ this.organizationRank = -1;
284
389
  this.solvedProblemNum = 0;
390
+ this.attemptedProblemNum = 0;
285
391
  this.penalty = 0;
286
392
  this.problemStatistics = [];
287
393
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
288
394
  }
289
- penaltyToMinute() {
395
+ get penaltyToMinute() {
290
396
  return Math.floor(this.penalty / 60);
291
397
  }
398
+ get dict() {
399
+ const attemptedNum = this.attemptedProblemNum;
400
+ const solvedNum = this.solvedProblemNum;
401
+ return calcDict(attemptedNum, solvedNum);
402
+ }
292
403
  calcSolvedData() {
293
404
  this.solvedProblemNum = 0;
294
405
  this.penalty = 0;
406
+ this.attemptedProblemNum = 0;
295
407
  for (const p of this.problemStatistics) {
296
408
  if (p.isAccepted) {
297
409
  this.solvedProblemNum++;
410
+ this.attemptedProblemNum += p.failedCount + 1;
298
411
  this.penalty += p.penalty;
299
412
  }
300
413
  }
@@ -319,17 +432,18 @@ function createTeam(teamJSON) {
319
432
  t.id = teamJSON.id ?? teamJSON.team_id ?? "";
320
433
  t.name = teamJSON.name ?? teamJSON.team_name ?? "";
321
434
  t.organization = teamJSON.organization ?? "";
435
+ t.badge = teamJSON.badge;
322
436
  t.group = teamJSON.group ?? [];
323
437
  t.tag = teamJSON.group ?? [];
324
438
  t.coach = teamJSON.coach;
325
439
  t.members = teamJSON.members;
326
- if (teamJSON.official === true) {
440
+ if (Boolean(teamJSON.official) === true) {
327
441
  t.group.push("official");
328
442
  }
329
- if (teamJSON.unofficial === true) {
443
+ if (Boolean(teamJSON.unofficial) === true) {
330
444
  t.group.push("unofficial");
331
445
  }
332
- if (teamJSON.girl === true) {
446
+ if (Boolean(teamJSON.girl) === true) {
333
447
  t.group.push("girl");
334
448
  }
335
449
  t.group = [...new Set(t.group)];
@@ -528,7 +642,6 @@ class Rank {
528
642
  this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
529
643
  this.submissions = ___default.cloneDeep(submissions).sort(Submission.compare);
530
644
  this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
531
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
532
645
  }
533
646
  buildRank(options) {
534
647
  (() => {
@@ -541,7 +654,9 @@ class Rank {
541
654
  });
542
655
  t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
543
656
  }
544
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
657
+ this.contest.problems.forEach((p) => {
658
+ p.statistics.reset();
659
+ });
545
660
  for (const s of this.submissions) {
546
661
  const teamId = s.teamId;
547
662
  const problemId = s.problemId;
@@ -557,13 +672,13 @@ class Rank {
557
672
  }
558
673
  const problemStatistics = team.problemStatisticsMap.get(problemId);
559
674
  const submissions = problemStatistics.submissions;
560
- const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId);
561
675
  submissions.push(s);
562
676
  problem.statistics.submittedNum++;
563
677
  if (problemStatistics.isSolved) {
564
678
  continue;
565
679
  }
566
680
  if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
681
+ problem.statistics.ignoreNum++;
567
682
  problemStatistics.ignoreCount++;
568
683
  continue;
569
684
  }
@@ -574,10 +689,15 @@ class Rank {
574
689
  problemStatistics.isSolved = true;
575
690
  problemStatistics.solvedTimestamp = s.timestamp;
576
691
  problem.statistics.acceptedNum++;
577
- if (firstSolvedSubmissions.length === 0 || firstSolvedSubmissions[firstSolvedSubmissions.length - 1].timestamp === s.timestamp) {
692
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
693
+ if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
578
694
  problemStatistics.isFirstSolved = true;
579
- firstSolvedSubmissions.push(s);
695
+ problem.statistics.firstSolveSubmissions.push(s);
580
696
  }
697
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
698
+ problem.statistics.lastSolveSubmissions.pop();
699
+ }
700
+ problem.statistics.lastSolveSubmissions.push(s);
581
701
  }
582
702
  if (s.isRejected()) {
583
703
  problemStatistics.failedCount++;
@@ -596,6 +716,17 @@ class Rank {
596
716
  t.rank = rank++;
597
717
  }
598
718
  }
719
+ if (this.contest.organization) {
720
+ let rank = 1;
721
+ const se = /* @__PURE__ */ new Set();
722
+ for (const t of this.teams) {
723
+ if (!se.has(t.organization)) {
724
+ se.add(t.organization);
725
+ t.organizationRank = rank;
726
+ rank++;
727
+ }
728
+ }
729
+ }
599
730
  })();
600
731
  return this;
601
732
  }
@@ -691,13 +822,19 @@ class Resolver extends Rank {
691
822
 
692
823
  exports.dayjs = dayjs__default;
693
824
  exports.Contest = Contest;
825
+ exports.ContestIndex = ContestIndex;
826
+ exports.ContestIndexConfig = ContestIndexConfig;
694
827
  exports.Problem = Problem;
828
+ exports.ProblemStatistics = ProblemStatistics;
695
829
  exports.Rank = Rank;
696
830
  exports.Resolver = Resolver;
697
831
  exports.Submission = Submission;
698
832
  exports.Team = Team;
699
833
  exports.TeamProblemStatistics = TeamProblemStatistics;
834
+ exports.calcDict = calcDict;
700
835
  exports.createContest = createContest;
836
+ exports.createContestIndex = createContestIndex;
837
+ exports.createContestIndexList = createContestIndexList;
701
838
  exports.createDayJS = createDayJS;
702
839
  exports.createProblem = createProblem;
703
840
  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, Image, ContestState, Contest as Contest$1, Team as Team$1, Teams as Teams$1 } from '@xcpcio/types';
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
- interface ProblemStatistics {
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;
@@ -104,17 +133,21 @@ declare class Team {
104
133
  id: string;
105
134
  name: string;
106
135
  organization: string;
136
+ badge?: Image;
107
137
  group: Array<string>;
108
138
  tag: Array<string>;
109
139
  coach?: string | Array<string>;
110
140
  members?: string | Array<string>;
111
141
  rank: number;
142
+ organizationRank: number;
112
143
  solvedProblemNum: number;
144
+ attemptedProblemNum: number;
113
145
  penalty: number;
114
146
  problemStatistics: Array<TeamProblemStatistics>;
115
147
  problemStatisticsMap: Map<string, TeamProblemStatistics>;
116
148
  constructor();
117
- penaltyToMinute(): number;
149
+ get penaltyToMinute(): number;
150
+ get dict(): number;
118
151
  calcSolvedData(): void;
119
152
  static compare(lhs: Team, rhs: Team): number;
120
153
  }
@@ -128,7 +161,6 @@ declare class Rank {
128
161
  teamsMap: Map<string, Team>;
129
162
  submissions: Submissions;
130
163
  submissionsMap: Map<string, Submission>;
131
- firstSolvedSubmissions: Map<string, Submissions>;
132
164
  constructor(contest: Contest, teams: Teams, submissions: Submissions);
133
165
  buildRank(options?: {
134
166
  timestamp?: number;
@@ -159,4 +191,4 @@ declare function isRejected(status: SubmissionStatus): boolean;
159
191
  declare function isPending(status: SubmissionStatus): boolean;
160
192
  declare function isNotCalculatedPenaltyStatus(status: SubmissionStatus): boolean;
161
193
 
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 };
194
+ export { Contest, ContestIndex, ContestIndexConfig, ContestIndexList, Problem, ProblemStatistics, Problems, Rank, 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 "0" + a;
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) {
@@ -265,20 +369,29 @@ class Team {
265
369
  this.group = [];
266
370
  this.tag = [];
267
371
  this.rank = 0;
372
+ this.organizationRank = -1;
268
373
  this.solvedProblemNum = 0;
374
+ this.attemptedProblemNum = 0;
269
375
  this.penalty = 0;
270
376
  this.problemStatistics = [];
271
377
  this.problemStatisticsMap = /* @__PURE__ */ new Map();
272
378
  }
273
- penaltyToMinute() {
379
+ get penaltyToMinute() {
274
380
  return Math.floor(this.penalty / 60);
275
381
  }
382
+ get dict() {
383
+ const attemptedNum = this.attemptedProblemNum;
384
+ const solvedNum = this.solvedProblemNum;
385
+ return calcDict(attemptedNum, solvedNum);
386
+ }
276
387
  calcSolvedData() {
277
388
  this.solvedProblemNum = 0;
278
389
  this.penalty = 0;
390
+ this.attemptedProblemNum = 0;
279
391
  for (const p of this.problemStatistics) {
280
392
  if (p.isAccepted) {
281
393
  this.solvedProblemNum++;
394
+ this.attemptedProblemNum += p.failedCount + 1;
282
395
  this.penalty += p.penalty;
283
396
  }
284
397
  }
@@ -303,17 +416,18 @@ function createTeam(teamJSON) {
303
416
  t.id = teamJSON.id ?? teamJSON.team_id ?? "";
304
417
  t.name = teamJSON.name ?? teamJSON.team_name ?? "";
305
418
  t.organization = teamJSON.organization ?? "";
419
+ t.badge = teamJSON.badge;
306
420
  t.group = teamJSON.group ?? [];
307
421
  t.tag = teamJSON.group ?? [];
308
422
  t.coach = teamJSON.coach;
309
423
  t.members = teamJSON.members;
310
- if (teamJSON.official === true) {
424
+ if (Boolean(teamJSON.official) === true) {
311
425
  t.group.push("official");
312
426
  }
313
- if (teamJSON.unofficial === true) {
427
+ if (Boolean(teamJSON.unofficial) === true) {
314
428
  t.group.push("unofficial");
315
429
  }
316
- if (teamJSON.girl === true) {
430
+ if (Boolean(teamJSON.girl) === true) {
317
431
  t.group.push("girl");
318
432
  }
319
433
  t.group = [...new Set(t.group)];
@@ -512,7 +626,6 @@ class Rank {
512
626
  this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
513
627
  this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
514
628
  this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
515
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
516
629
  }
517
630
  buildRank(options) {
518
631
  (() => {
@@ -525,7 +638,9 @@ class Rank {
525
638
  });
526
639
  t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
527
640
  }
528
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
641
+ this.contest.problems.forEach((p) => {
642
+ p.statistics.reset();
643
+ });
529
644
  for (const s of this.submissions) {
530
645
  const teamId = s.teamId;
531
646
  const problemId = s.problemId;
@@ -541,13 +656,13 @@ class Rank {
541
656
  }
542
657
  const problemStatistics = team.problemStatisticsMap.get(problemId);
543
658
  const submissions = problemStatistics.submissions;
544
- const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId);
545
659
  submissions.push(s);
546
660
  problem.statistics.submittedNum++;
547
661
  if (problemStatistics.isSolved) {
548
662
  continue;
549
663
  }
550
664
  if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
665
+ problem.statistics.ignoreNum++;
551
666
  problemStatistics.ignoreCount++;
552
667
  continue;
553
668
  }
@@ -558,10 +673,15 @@ class Rank {
558
673
  problemStatistics.isSolved = true;
559
674
  problemStatistics.solvedTimestamp = s.timestamp;
560
675
  problem.statistics.acceptedNum++;
561
- if (firstSolvedSubmissions.length === 0 || firstSolvedSubmissions[firstSolvedSubmissions.length - 1].timestamp === s.timestamp) {
676
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
677
+ if (problem.statistics.firstSolveSubmissions.length === 0 || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp) {
562
678
  problemStatistics.isFirstSolved = true;
563
- firstSolvedSubmissions.push(s);
679
+ problem.statistics.firstSolveSubmissions.push(s);
564
680
  }
681
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
682
+ problem.statistics.lastSolveSubmissions.pop();
683
+ }
684
+ problem.statistics.lastSolveSubmissions.push(s);
565
685
  }
566
686
  if (s.isRejected()) {
567
687
  problemStatistics.failedCount++;
@@ -580,6 +700,17 @@ class Rank {
580
700
  t.rank = rank++;
581
701
  }
582
702
  }
703
+ if (this.contest.organization) {
704
+ let rank = 1;
705
+ const se = /* @__PURE__ */ new Set();
706
+ for (const t of this.teams) {
707
+ if (!se.has(t.organization)) {
708
+ se.add(t.organization);
709
+ t.organizationRank = rank;
710
+ rank++;
711
+ }
712
+ }
713
+ }
583
714
  })();
584
715
  return this;
585
716
  }
@@ -673,4 +804,4 @@ class Resolver extends Rank {
673
804
  }
674
805
  }
675
806
 
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 };
807
+ export { Contest, ContestIndex, ContestIndexConfig, Problem, ProblemStatistics, Rank, Resolver, Submission, Team, TeamProblemStatistics, calcDict, createContest, createContestIndex, createContestIndexList, createDayJS, createProblem, createProblems, createProblemsByProblemIds, createSubmission, createSubmissions, createTeam, createTeams, getImageSource, getTimeDiff, getTimestamp, isAccepted, isNotCalculatedPenaltyStatus, isPending, isRejected, stringToSubmissionStatus };
package/package.json CHANGED
@@ -1,31 +1,32 @@
1
1
  {
2
2
  "name": "@xcpcio/core",
3
+ "version": "0.4.0",
3
4
  "description": "XCPCIO Core",
4
- "version": "0.3.1",
5
- "license": "MIT",
6
5
  "author": "Dup4 <lyuzhi.pan@gmail.com>",
7
- "keywords": [
8
- "ICPC",
9
- "CCPC"
10
- ],
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/xcpcio/xcpcio",
11
8
  "repository": {
12
9
  "type": "git",
13
10
  "url": "git+https://github.com/xcpcio/xcpcio.git"
14
11
  },
15
- "homepage": "https://github.com/xcpcio/xcpcio",
16
12
  "bugs": {
17
13
  "url": "https://github.com/xcpcio/xcpcio/issues"
18
14
  },
19
- "main": "./dist/index.mjs",
20
- "module": "./dist/index.mjs",
21
- "types": "./dist/index.d.ts",
15
+ "keywords": [
16
+ "ICPC",
17
+ "CCPC"
18
+ ],
19
+ "sideEffects": false,
22
20
  "exports": {
23
21
  ".": {
22
+ "types": "./dist/index.d.ts",
24
23
  "require": "./dist/index.cjs",
25
- "import": "./dist/index.mjs",
26
- "types": "./dist/index.d.ts"
24
+ "import": "./dist/index.mjs"
27
25
  }
28
26
  },
27
+ "main": "./dist/index.mjs",
28
+ "module": "./dist/index.mjs",
29
+ "types": "./dist/index.d.ts",
29
30
  "typesVersions": {
30
31
  "*": {
31
32
  "*": [
@@ -38,7 +39,11 @@
38
39
  "src",
39
40
  "dist"
40
41
  ],
41
- "sideEffects": false,
42
+ "dependencies": {
43
+ "dayjs": "^1.11.8",
44
+ "lodash": "^4.17.21",
45
+ "@xcpcio/types": "0.4.0"
46
+ },
42
47
  "devDependencies": {
43
48
  "@babel/types": "^7.22.4",
44
49
  "@types/lodash": "^4.14.195",
@@ -50,18 +55,12 @@
50
55
  "esmo": "^0.14.1",
51
56
  "npm-run-all": "^4.1.5",
52
57
  "pnpm": "^7.33.0",
53
- "prettier": "^2.8.8",
54
58
  "taze": "^0.10.2",
55
59
  "typescript": "^4.9.5",
56
60
  "unbuild": "^0.7.6",
57
61
  "vite": "^4.3.9",
58
62
  "vitest": "^0.32.0"
59
63
  },
60
- "dependencies": {
61
- "dayjs": "^1.11.8",
62
- "lodash": "^4.17.21",
63
- "@xcpcio/types": "0.3.1"
64
- },
65
64
  "scripts": {
66
65
  "build": "unbuild",
67
66
  "dev": "unbuild --stub",
@@ -0,0 +1,123 @@
1
+ import type dayjs from "dayjs";
2
+
3
+ import type { ContestIndex as IContestIndex, Image } from "@xcpcio/types";
4
+
5
+ import { createDayJS } from "./utils";
6
+
7
+ export class ContestIndexConfig {
8
+ contestName: string;
9
+
10
+ startTime: dayjs.Dayjs;
11
+ endTime: dayjs.Dayjs;
12
+ freezeTime: dayjs.Dayjs;
13
+
14
+ totalDurationTimestamp: number;
15
+ freezeDurationTimestamp: number;
16
+ unFreezeDurationTimestamp: number;
17
+
18
+ logo?: Image;
19
+
20
+ constructor() {
21
+ this.contestName = "";
22
+
23
+ this.startTime = createDayJS();
24
+ this.endTime = createDayJS();
25
+ this.freezeTime = createDayJS();
26
+
27
+ this.totalDurationTimestamp = 0;
28
+ this.freezeDurationTimestamp = 0;
29
+ this.unFreezeDurationTimestamp = 0;
30
+ }
31
+ }
32
+
33
+ export class ContestIndex {
34
+ config: ContestIndexConfig;
35
+ boardLink: string;
36
+
37
+ constructor() {
38
+ this.config = new ContestIndexConfig();
39
+ this.boardLink = "";
40
+ }
41
+ }
42
+
43
+ export type ContestIndexList = Array<ContestIndex>;
44
+
45
+ export function createContestIndex(contestIndexJSON: IContestIndex): ContestIndex {
46
+ const c = new ContestIndex();
47
+ const cc = c.config;
48
+ const cjc = contestIndexJSON.config;
49
+
50
+ cc.contestName = cjc.contest_name;
51
+
52
+ cc.startTime = createDayJS(cjc.start_time);
53
+ cc.endTime = createDayJS(cjc.end_time);
54
+
55
+ cc.totalDurationTimestamp = cc.endTime.unix() - cc.startTime.unix();
56
+
57
+ {
58
+ // default value
59
+ cc.freezeTime = cc.endTime;
60
+ cc.freezeDurationTimestamp = 0;
61
+
62
+ if (cjc.frozen_time !== undefined && cjc.frozen_time != null) {
63
+ const frozenTime = Number(cjc.frozen_time);
64
+
65
+ cc.freezeTime = createDayJS(cc.endTime.unix() - frozenTime);
66
+ cc.freezeDurationTimestamp = frozenTime;
67
+ }
68
+
69
+ cc.unFreezeDurationTimestamp = cc.totalDurationTimestamp - cc.freezeDurationTimestamp;
70
+ }
71
+
72
+ cc.logo = cjc.logo;
73
+
74
+ c.boardLink = contestIndexJSON.board_link;
75
+
76
+ return c;
77
+ }
78
+
79
+ export function createContestIndexList(contestListJSON: any): ContestIndexList {
80
+ const contestIndexList = [] as ContestIndexList;
81
+
82
+ const dfs = (contestList: any) => {
83
+ if (Object.prototype.hasOwnProperty.call(contestList, "config")) {
84
+ contestIndexList.push(createContestIndex(contestList));
85
+ } else {
86
+ for (const k in contestList) {
87
+ dfs(contestList[k]);
88
+ }
89
+ }
90
+ };
91
+
92
+ dfs(contestListJSON);
93
+
94
+ contestIndexList.sort((a: ContestIndex, b: ContestIndex) => {
95
+ if (a.config.startTime.isBefore(b.config.startTime)) {
96
+ return 1;
97
+ }
98
+
99
+ if (a.config.startTime.isAfter(b.config.startTime)) {
100
+ return -1;
101
+ }
102
+
103
+ if (a.config.endTime.isBefore(b.config.endTime)) {
104
+ return 1;
105
+ }
106
+
107
+ if (a.config.endTime.isAfter(b.config.endTime)) {
108
+ return -1;
109
+ }
110
+
111
+ if (a.config.contestName < b.config.contestName) {
112
+ return 1;
113
+ }
114
+
115
+ if (a.config.contestName > b.config.contestName) {
116
+ return -1;
117
+ }
118
+
119
+ return 0;
120
+ });
121
+
122
+ return contestIndexList;
123
+ }
package/src/contest.ts CHANGED
@@ -1,7 +1,9 @@
1
- import { Contest as IContest, ContestState, Image, VERSION, StatusTimeDisplay } from "@xcpcio/types";
1
+ import type { Contest as IContest, Image, StatusTimeDisplay } from "@xcpcio/types";
2
+ import { ContestState, VERSION } from "@xcpcio/types";
2
3
 
3
- import { Problem, Problems, createProblems, createProblemsByProblemIds } from "./problem";
4
- import { dayjs, createDayJS, getTimeDiff } from "./utils";
4
+ import type { Problem, Problems } from "./problem";
5
+ import { createProblems, createProblemsByProblemIds } from "./problem";
6
+ import { createDayJS, dayjs, getTimeDiff } from "./utils";
5
7
 
6
8
  export class Contest {
7
9
  name = "";
@@ -164,7 +166,7 @@ export function createContest(contestJSON: IContest): Contest {
164
166
  c.problems = createProblems(contestJSON.problems);
165
167
  }
166
168
 
167
- c.problemsMap = new Map(c.problems.map((p) => [p.id, p]));
169
+ c.problemsMap = new Map(c.problems.map(p => [p.id, p]));
168
170
  }
169
171
 
170
172
  if (contestJSON.status_time_display !== undefined && contestJSON.status_time_display !== null) {
package/src/image.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Image } from "@xcpcio/types";
1
+ import type { Image } from "@xcpcio/types";
2
2
 
3
3
  export function getImageSource(image: Image): string {
4
4
  if (image?.url) {
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./contest-index";
1
2
  export * from "./utils";
2
3
  export * from "./contest";
3
4
  export * from "./image";
package/src/problem.ts CHANGED
@@ -1,13 +1,51 @@
1
- import { Problem as IProblem, Problems as IProblems, BalloonColor } from "@xcpcio/types";
1
+ import type { BalloonColor, Problem as IProblem, Problems as IProblems } from "@xcpcio/types";
2
2
 
3
- import { Submissions } from "./submission";
3
+ import type { Submissions } from "./submission";
4
+ import { calcDict } from "./utils";
4
5
 
5
- export interface ProblemStatistics {
6
+ export class ProblemStatistics {
6
7
  acceptedNum: number;
7
8
  rejectedNum: number;
8
9
  pendingNum: number;
9
10
 
10
11
  submittedNum: number;
12
+ attemptedNum: number;
13
+ ignoreNum: number;
14
+
15
+ firstSolveSubmissions: Submissions;
16
+ lastSolveSubmissions: Submissions;
17
+
18
+ constructor() {
19
+ this.acceptedNum = 0;
20
+ this.rejectedNum = 0;
21
+ this.pendingNum = 0;
22
+
23
+ this.submittedNum = 0;
24
+ this.attemptedNum = 0;
25
+ this.ignoreNum = 0;
26
+
27
+ this.firstSolveSubmissions = [];
28
+ this.lastSolveSubmissions = [];
29
+ }
30
+
31
+ reset() {
32
+ this.acceptedNum = 0;
33
+ this.rejectedNum = 0;
34
+ this.pendingNum = 0;
35
+
36
+ this.submittedNum = 0;
37
+
38
+ this.firstSolveSubmissions = [];
39
+ this.lastSolveSubmissions = [];
40
+ }
41
+
42
+ get dict() {
43
+ if (this.acceptedNum === 0) {
44
+ return 0;
45
+ }
46
+
47
+ return calcDict(this.attemptedNum, this.acceptedNum);
48
+ }
11
49
  }
12
50
 
13
51
  export class Problem {
@@ -29,12 +67,7 @@ export class Problem {
29
67
 
30
68
  this.name = "";
31
69
 
32
- this.statistics = {
33
- acceptedNum: 0,
34
- rejectedNum: 0,
35
- pendingNum: 0,
36
- submittedNum: 0,
37
- };
70
+ this.statistics = new ProblemStatistics();
38
71
  }
39
72
  }
40
73
 
package/src/rank.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import _ from "lodash";
2
2
 
3
- import { Contest } from "./contest";
4
- import { Team, Teams } from "./team";
5
- import { Submission, Submissions } from "./submission";
3
+ import type { Contest } from "./contest";
4
+ import type { Teams } from "./team";
5
+ import { Team } from "./team";
6
+ import type { Submissions } from "./submission";
7
+ import { Submission } from "./submission";
6
8
  import { TeamProblemStatistics } from "./problem";
7
9
 
8
10
  export class Rank {
@@ -14,18 +16,14 @@ export class Rank {
14
16
  submissions: Submissions;
15
17
  submissionsMap: Map<string, Submission>;
16
18
 
17
- firstSolvedSubmissions: Map<string, Submissions>;
18
-
19
19
  constructor(contest: Contest, teams: Teams, submissions: Submissions) {
20
20
  this.contest = contest;
21
21
 
22
22
  this.teams = _.cloneDeep(teams);
23
- this.teamsMap = new Map(this.teams.map((t) => [t.id, t]));
23
+ this.teamsMap = new Map(this.teams.map(t => [t.id, t]));
24
24
 
25
25
  this.submissions = _.cloneDeep(submissions).sort(Submission.compare);
26
- this.submissionsMap = new Map(this.submissions.map((s) => [s.id, s]));
27
-
28
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
26
+ this.submissionsMap = new Map(this.submissions.map(s => [s.id, s]));
29
27
  }
30
28
 
31
29
  buildRank(options?: { timestamp?: number }) {
@@ -39,10 +37,12 @@ export class Rank {
39
37
  return ps;
40
38
  });
41
39
 
42
- t.problemStatisticsMap = new Map(t.problemStatistics.map((ps) => [ps.problem.id, ps]));
40
+ t.problemStatisticsMap = new Map(t.problemStatistics.map(ps => [ps.problem.id, ps]));
43
41
  }
44
42
 
45
- this.firstSolvedSubmissions = new Map(this.contest.problems.map((p) => [p.id, []]));
43
+ this.contest.problems.forEach((p) => {
44
+ p.statistics.reset();
45
+ });
46
46
 
47
47
  for (const s of this.submissions) {
48
48
  const teamId = s.teamId;
@@ -62,7 +62,6 @@ export class Rank {
62
62
 
63
63
  const problemStatistics = team.problemStatisticsMap.get(problemId) as TeamProblemStatistics;
64
64
  const submissions = problemStatistics.submissions;
65
- const firstSolvedSubmissions = this.firstSolvedSubmissions.get(problemId) as Array<Submission>;
66
65
 
67
66
  submissions.push(s);
68
67
  problem.statistics.submittedNum++;
@@ -72,6 +71,7 @@ export class Rank {
72
71
  }
73
72
 
74
73
  if (s.isIgnore || s.isNotCalculatedPenaltyStatus()) {
74
+ problem.statistics.ignoreNum++;
75
75
  problemStatistics.ignoreCount++;
76
76
  continue;
77
77
  }
@@ -85,14 +85,21 @@ export class Rank {
85
85
  problemStatistics.solvedTimestamp = s.timestamp;
86
86
 
87
87
  problem.statistics.acceptedNum++;
88
+ problem.statistics.attemptedNum += problemStatistics.failedCount + 1;
88
89
 
89
90
  if (
90
- firstSolvedSubmissions.length === 0 ||
91
- firstSolvedSubmissions[firstSolvedSubmissions.length - 1].timestamp === s.timestamp
91
+ problem.statistics.firstSolveSubmissions.length === 0
92
+ || problem.statistics.firstSolveSubmissions[problem.statistics.firstSolveSubmissions.length - 1].timestamp === s.timestamp
92
93
  ) {
93
94
  problemStatistics.isFirstSolved = true;
94
- firstSolvedSubmissions.push(s);
95
+ problem.statistics.firstSolveSubmissions.push(s);
96
+ }
97
+
98
+ while (problem.statistics.lastSolveSubmissions.length > 0) {
99
+ problem.statistics.lastSolveSubmissions.pop();
95
100
  }
101
+
102
+ problem.statistics.lastSolveSubmissions.push(s);
96
103
  }
97
104
 
98
105
  if (s.isRejected()) {
@@ -106,7 +113,7 @@ export class Rank {
106
113
  }
107
114
  }
108
115
 
109
- this.teams.forEach((t) => t.calcSolvedData());
116
+ this.teams.forEach(t => t.calcSolvedData());
110
117
  this.teams.sort(Team.compare);
111
118
 
112
119
  {
@@ -115,6 +122,19 @@ export class Rank {
115
122
  t.rank = rank++;
116
123
  }
117
124
  }
125
+
126
+ if (this.contest.organization) {
127
+ let rank = 1;
128
+ const se = new Set<string>();
129
+
130
+ for (const t of this.teams) {
131
+ if (!se.has(t.organization)) {
132
+ se.add(t.organization);
133
+ t.organizationRank = rank;
134
+ rank++;
135
+ }
136
+ }
137
+ }
118
138
  })();
119
139
 
120
140
  return this;
package/src/resolver.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import _ from "lodash";
2
2
 
3
3
  import { Rank } from "./rank";
4
- import { Contest } from "./contest";
5
- import { Team, Teams } from "./team";
6
- import { Submission, Submissions } from "./submission";
7
- import { TeamProblemStatistics } from "./problem";
4
+ import type { Contest } from "./contest";
5
+ import type { Teams } from "./team";
6
+ import { Team } from "./team";
7
+ import type { Submissions } from "./submission";
8
+ import { Submission } from "./submission";
9
+ import type { TeamProblemStatistics } from "./problem";
8
10
  import { ResolverOperation } from "./resolver-operation";
9
11
 
10
12
  export class Resolver extends Rank {
@@ -22,7 +24,7 @@ export class Resolver extends Rank {
22
24
 
23
25
  {
24
26
  const ix = _.sortedIndex(
25
- submissions.map((s) => s.timestamp),
27
+ submissions.map(s => s.timestamp),
26
28
  contest.unFreezeDurationTimestamp,
27
29
  );
28
30
 
@@ -65,7 +67,7 @@ export class Resolver extends Rank {
65
67
  {
66
68
  const teams_ = _.cloneDeep(this.teams);
67
69
 
68
- for (let i = this.teams.length - 1; i >= 0; ) {
70
+ for (let i = this.teams.length - 1; i >= 0;) {
69
71
  const team = teams_[i];
70
72
  const teamId = team.id;
71
73
 
package/src/submission.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { Submission as ISubmission, Submissions as ISubmissions, SubmissionStatus } from "@xcpcio/types";
1
+ import type { Submission as ISubmission, Submissions as ISubmissions } from "@xcpcio/types";
2
+ import { SubmissionStatus } from "@xcpcio/types";
2
3
 
3
4
  import {
4
5
  isAccepted,
5
- isRejected,
6
- isPending,
7
6
  isNotCalculatedPenaltyStatus,
7
+ isPending,
8
+ isRejected,
8
9
  stringToSubmissionStatus,
9
10
  } from "./submission-status";
10
11
 
package/src/team.ts CHANGED
@@ -1,11 +1,14 @@
1
- import { Team as ITeam, Teams as ITeams } from "@xcpcio/types";
1
+ import type { Team as ITeam, Teams as ITeams, Image } from "@xcpcio/types";
2
2
 
3
- import { TeamProblemStatistics } from "./problem";
3
+ import type { TeamProblemStatistics } from "./problem";
4
+ import { calcDict } from "./utils";
4
5
 
5
6
  export class Team {
6
7
  id: string;
7
8
  name: string;
9
+
8
10
  organization: string;
11
+ badge?: Image;
9
12
 
10
13
  group: Array<string>;
11
14
  tag: Array<string>;
@@ -14,7 +17,11 @@ export class Team {
14
17
  members?: string | Array<string>;
15
18
 
16
19
  rank: number;
20
+ organizationRank: number;
21
+
17
22
  solvedProblemNum: number;
23
+ attemptedProblemNum: number;
24
+
18
25
  penalty: number;
19
26
 
20
27
  problemStatistics: Array<TeamProblemStatistics>;
@@ -23,30 +30,45 @@ export class Team {
23
30
  constructor() {
24
31
  this.id = "";
25
32
  this.name = "";
33
+
26
34
  this.organization = "";
27
35
 
28
36
  this.group = [];
29
37
  this.tag = [];
30
38
 
31
39
  this.rank = 0;
40
+ this.organizationRank = -1;
41
+
32
42
  this.solvedProblemNum = 0;
43
+ this.attemptedProblemNum = 0;
44
+
33
45
  this.penalty = 0;
34
46
 
35
47
  this.problemStatistics = [];
36
48
  this.problemStatisticsMap = new Map<string, TeamProblemStatistics>();
37
49
  }
38
50
 
39
- penaltyToMinute() {
51
+ get penaltyToMinute() {
40
52
  return Math.floor(this.penalty / 60);
41
53
  }
42
54
 
55
+ get dict() {
56
+ const attemptedNum = this.attemptedProblemNum;
57
+ const solvedNum = this.solvedProblemNum;
58
+
59
+ return calcDict(attemptedNum, solvedNum);
60
+ }
61
+
43
62
  calcSolvedData() {
44
63
  this.solvedProblemNum = 0;
45
64
  this.penalty = 0;
65
+ this.attemptedProblemNum = 0;
46
66
 
47
67
  for (const p of this.problemStatistics) {
48
68
  if (p.isAccepted) {
49
69
  this.solvedProblemNum++;
70
+ this.attemptedProblemNum += p.failedCount + 1;
71
+
50
72
  this.penalty += p.penalty;
51
73
  }
52
74
  }
@@ -80,21 +102,23 @@ export function createTeam(teamJSON: ITeam): Team {
80
102
  t.name = teamJSON.name ?? teamJSON.team_name ?? "";
81
103
 
82
104
  t.organization = teamJSON.organization ?? "";
105
+ t.badge = teamJSON.badge;
106
+
83
107
  t.group = teamJSON.group ?? [];
84
108
  t.tag = teamJSON.group ?? [];
85
109
 
86
110
  t.coach = teamJSON.coach;
87
111
  t.members = teamJSON.members;
88
112
 
89
- if (teamJSON.official === true) {
113
+ if (Boolean(teamJSON.official) === true) {
90
114
  t.group.push("official");
91
115
  }
92
116
 
93
- if (teamJSON.unofficial === true) {
117
+ if (Boolean(teamJSON.unofficial) === true) {
94
118
  t.group.push("unofficial");
95
119
  }
96
120
 
97
- if (teamJSON.girl === true) {
121
+ if (Boolean(teamJSON.girl) === true) {
98
122
  t.group.push("girl");
99
123
  }
100
124
 
@@ -106,7 +130,7 @@ export function createTeam(teamJSON: ITeam): Team {
106
130
 
107
131
  export function createTeams(teamsJSON: ITeams): Teams {
108
132
  if (Array.isArray(teamsJSON)) {
109
- return teamsJSON.map((t) => createTeam(t));
133
+ return teamsJSON.map(t => createTeam(t));
110
134
  } else {
111
135
  const teams = Object.entries(teamsJSON).map(([teamId, team]) =>
112
136
  createTeam({ ...team, team_id: team.team_id ?? teamId }),
@@ -0,0 +1,7 @@
1
+ export function calcDict(attemptedNum: number, solvedNum: number) {
2
+ if (solvedNum === 0) {
3
+ return 0;
4
+ }
5
+
6
+ return Math.floor((attemptedNum - solvedNum) * 100 / attemptedNum);
7
+ }
@@ -1,27 +1,22 @@
1
1
  import dayjs from "dayjs";
2
2
 
3
3
  import duration from "dayjs/plugin/duration";
4
- dayjs.extend(duration);
5
-
6
4
  import utc from "dayjs/plugin/utc";
7
- dayjs.extend(utc);
8
-
9
5
  import timezone from "dayjs/plugin/timezone";
10
- dayjs.extend(timezone);
11
-
12
6
  import advancedFormat from "dayjs/plugin/advancedFormat";
13
- dayjs.extend(advancedFormat);
14
-
15
7
  import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
16
- dayjs.extend(isSameOrBefore);
17
-
18
8
  import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
19
- dayjs.extend(isSameOrAfter);
20
-
21
9
  import minMax from "dayjs/plugin/minMax";
10
+ import relativeTime from "dayjs/plugin/relativeTime";
11
+
12
+ dayjs.extend(duration);
13
+ dayjs.extend(utc);
14
+ dayjs.extend(timezone);
15
+ dayjs.extend(advancedFormat);
16
+ dayjs.extend(isSameOrBefore);
17
+ dayjs.extend(isSameOrAfter);
22
18
  dayjs.extend(minMax);
23
19
 
24
- import relativeTime from "dayjs/plugin/relativeTime";
25
20
  dayjs.extend(relativeTime);
26
21
 
27
22
  export function createDayJS(time: Date | string | number | undefined = undefined): dayjs.Dayjs {
@@ -46,7 +41,10 @@ export function getTimestamp(time: number | dayjs.Dayjs): number {
46
41
 
47
42
  export function getTimeDiff(seconds: number): string {
48
43
  const two = (a: number) => {
49
- if (a < 10) return "0" + a;
44
+ if (a < 10) {
45
+ return `0${a}`;
46
+ }
47
+
50
48
  return String(a);
51
49
  };
52
50
 
@@ -1 +1,2 @@
1
+ export * from "./calc";
1
2
  export * from "./dayjs";