Skip to content

Commit c80a8b5

Browse files
birdofpreyruDushyant Bhalgami
authored and
Dushyant Bhalgami
committed
CODE 30102897
1 parent e3b5d0c commit c80a8b5

File tree

7 files changed

+89
-273
lines changed

7 files changed

+89
-273
lines changed

__tests__/__snapshots__/index.js.snap

-2
Original file line numberDiff line numberDiff line change
@@ -318,10 +318,8 @@ Object {
318318
},
319319
},
320320
"submission": Object {
321-
"default": undefined,
322321
"getFinalScore": [Function],
323322
"getProvisionalScore": [Function],
324-
"processMMSubmissions": [Function],
325323
},
326324
"tc": Object {
327325
"COMPETITION_TRACKS": Object {

dist/dev/index.js

+57-57
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"lint:js": "./node_modules/.bin/eslint --ext .js,.jsx .",
3232
"test": "npm run lint && npm run jest"
3333
},
34-
"version": "0.7.11-16",
34+
"version": "0.8.1",
3535
"dependencies": {
3636
"auth0-js": "^6.8.4",
3737
"config": "^3.2.0",

src/actions/challenge.js

+3-55
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,12 @@
33
* @desc Actions related to Topcoder challenges APIs.
44
*/
55

6-
/* global CONFIG */
76
import _ from 'lodash';
87
import { config } from 'topcoder-react-utils';
98
import { createActions } from 'redux-actions';
109
import { getService as getChallengesService } from '../services/challenges';
1110
import { getService as getSubmissionService } from '../services/submissions';
12-
import { getService as getMemberService } from '../services/members';
1311
import { getApi } from '../services/api';
14-
import * as submissionUtil from '../utils/submission';
15-
16-
const { PAGE_SIZE } = CONFIG;
17-
18-
/**
19-
* Private. Loads from the backend all data matching some conditions.
20-
* @param {Function} getter Given params object of shape { limit, offset }
21-
* loads from the backend at most "limit" data, skipping the first
22-
* "offset" ones. Returns loaded data as an array.
23-
* @param {Number} page Optional. Next page of data to load.
24-
* @param {Number} perPage Optional. The size of the page content to load.
25-
* @param {Array} prev Optional. data loaded so far.
26-
*/
27-
function getAll(getter, page = 1, perPage = PAGE_SIZE, prev) {
28-
/* Amount of submissions to fetch in one API call. 50 is the current maximum
29-
* amount of submissions the backend returns, event when the larger limit is
30-
* explicitely required. */
31-
return getter({
32-
page,
33-
perPage,
34-
}).then((res) => {
35-
if (res.length === 0) {
36-
return prev || res;
37-
}
38-
// parse submissions
39-
let current = [];
40-
if (prev) {
41-
current = prev.concat(res);
42-
} else {
43-
current = res;
44-
}
45-
return getAll(getter, 1 + page, perPage, current);
46-
});
47-
}
4812

4913
/**
5014
* @static
@@ -142,26 +106,10 @@ function getMMSubmissionsInit(challengeId) {
142106
* @param {String} tokenV3 Topcoder auth token v3.
143107
* @return {Action}
144108
*/
145-
function getMMSubmissionsDone(challengeId, registrants, tokenV3) {
146-
const filter = { challengeId };
147-
const memberService = getMemberService(tokenV3);
109+
async function getMMSubmissionsDone(challengeId, registrants, tokenV3) {
148110
const submissionsService = getSubmissionService(tokenV3);
149-
150-
// TODO: Move those numbers to configs
151-
return getAll(params => submissionsService.getSubmissions(filter, params), 1, 500)
152-
.then((submissions) => {
153-
const userIds = _.uniq(_.map(submissions, sub => sub.memberId));
154-
return memberService.getMembersInformation(userIds)
155-
.then((resources) => {
156-
const finalSubmissions = submissionUtil
157-
.processMMSubmissions(submissions, resources, registrants);
158-
return {
159-
challengeId,
160-
submissions: finalSubmissions,
161-
tokenV3,
162-
};
163-
});
164-
});
111+
const submissions = await submissionsService.getSubmissions(challengeId);
112+
return { challengeId, submissions, tokenV3 };
165113
}
166114

167115
/**

src/services/challenges.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,6 @@ class ChallengesService {
465465
return this.private.getChallenges(endpoint, filters, params);
466466
}
467467

468-
469468
/**
470469
* Gets SRM matches related to the user.
471470
* @param {String} handle
@@ -575,6 +574,26 @@ class ChallengesService {
575574
if (res.status !== 200) throw new Error(res.content);
576575
return res.content;
577576
}
577+
578+
/**
579+
* Gets roles of a user in the specified challenge. The user tested is
580+
* the owner of authentication token used to instantiate the service.
581+
*
582+
* Notice, if you have already loaded the challenge as that user, these roles
583+
* are attached to the challenge object under `userDetails.roles` path during
584+
* challenge normalization. However, if you have not, this method is the most
585+
* efficient way to get them, as it by-passes any unnecessary normalizations
586+
* of the challenge object.
587+
*
588+
* @param {Number} challengeId Challenge ID.
589+
*/
590+
async getUserRolesInChallenge(challengeId) {
591+
const user = decodeToken(this.private.tokenV3);
592+
const username = user.handle || user.payload.handle;
593+
const url = `/members/${username.toLowerCase()}/challenges`;
594+
const data = await this.private.getChallenges(url, { id: challengeId });
595+
return data.challenges[0].userDetails.roles;
596+
}
578597
}
579598

580599
let lastInstance = null;

src/services/submissions.js

+8-16
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
* Topcoder submissions via TC API. Currently only used for MM challenges
55
*/
66

7-
import qs from 'qs';
87
import { getApi } from './api';
98

109
/**
@@ -17,26 +16,20 @@ class SubmissionsService {
1716
*/
1817
constructor(tokenV3) {
1918
this.private = {
20-
apiV5: getApi('V5', tokenV3),
19+
broker: getApi('MM_BROKER', tokenV3),
2120
tokenV3,
2221
};
2322
}
2423

2524
/**
2625
* Get submissions of challenge
27-
* @param {Object} filters
28-
* @param {Object} params
26+
* @param {Object} challengeId
2927
* @return {Promise} Resolves to the api response.
3028
*/
31-
async getSubmissions(filters, params) {
32-
const query = {
33-
...filters,
34-
...params,
35-
};
36-
const url = `/submissions?${qs.stringify(query, { encode: false })}`;
37-
return this.private.apiV5.get(url)
38-
.then(res => (res.ok ? res.json() : new Error(res.statusText)))
39-
.then(res => res);
29+
async getSubmissions(challengeId) {
30+
const url = `/submissions?challengeId=${challengeId}`;
31+
return this.private.broker.get(url)
32+
.then(res => (res.ok ? res.json() : new Error(res.statusText)));
4033
}
4134

4235
/**
@@ -46,9 +39,8 @@ class SubmissionsService {
4639
*/
4740
async getSubmissionInformation(submissionId) {
4841
const url = `/submissions/${submissionId}`;
49-
return this.private.apiV5.get(url)
50-
.then(res => (res.ok ? res.json() : new Error(res.statusText)))
51-
.then(res => res);
42+
return this.private.broker.get(url)
43+
.then(res => (res.ok ? res.json() : new Error(res.statusText)));
5244
}
5345
}
5446

src/utils/submission.js

-141
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,6 @@
11
/**
22
* Various submissions functions.
33
*/
4-
/* global CONFIG */
5-
/* eslint-disable no-param-reassign */
6-
import _ from 'lodash';
7-
8-
const { AV_SCAN_SCORER_REVIEW_TYPE_ID } = CONFIG;
9-
10-
function removeDecimal(num) {
11-
const re = new RegExp('^-?\\d+');
12-
return num.toString().match(re)[0];
13-
}
14-
15-
function toAcurateFixed(num, decimal) {
16-
const re = new RegExp(`^-?\\d+(?:.\\d{0,${(decimal)}})?`);
17-
return num.toString().match(re)[0];
18-
}
19-
20-
function toFixed(num, decimal) {
21-
if (_.isNaN(parseFloat(num))) return num;
22-
num = parseFloat(num);
23-
24-
const result = _.toFinite(toAcurateFixed(num, decimal));
25-
const integerResult = _.toFinite(removeDecimal(num));
26-
27-
if (_.isInteger(result)) {
28-
return integerResult;
29-
}
30-
return result;
31-
}
32-
33-
function getMMChallengeHandleStyle(handle, registrants) {
34-
const style = _.get(_.find(registrants, m => m.handle === handle), 'colorStyle', null);
35-
if (style) return JSON.parse(style.replace(/(\w+):\s*([^;]*)/g, '{"$1": "$2"}'));
36-
return {};
37-
}
38-
39-
/**
40-
* Process each submission rank of MM challenge
41-
* @param submissions the array of submissions
42-
*/
43-
function processRanks(submissions) {
44-
let maxFinalScore = 0;
45-
submissions.sort((a, b) => {
46-
let pA = _.get(a, 'submissions[0]', { provisionalScore: 0 }).provisionalScore;
47-
let pB = _.get(b, 'submissions[0]', { provisionalScore: 0 }).provisionalScore;
48-
if (pA === '-') pA = 0;
49-
if (pB === '-') pB = 0;
50-
if (pA === pB) {
51-
const timeA = new Date(_.get(a, 'submissions[0].submissionTime'));
52-
const timeB = new Date(_.get(b, 'submissions[0].submissionTime'));
53-
return timeA - timeB;
54-
}
55-
return pB - pA;
56-
});
57-
_.each(submissions, (submission, i) => {
58-
submissions[i].provisionalRank = i + 1;
59-
});
60-
61-
submissions.sort((a, b) => {
62-
let pA = _.get(a, 'submissions[0]', { finalScore: 0 }).finalScore;
63-
let pB = _.get(b, 'submissions[0]', { finalScore: 0 }).finalScore;
64-
if (pA === '-') pA = 0;
65-
if (pB === '-') pB = 0;
66-
if (pA > 0) maxFinalScore = pA;
67-
if (pB > 0) maxFinalScore = pB;
68-
if (pA === pB) {
69-
const timeA = new Date(_.get(a, 'submissions[0].submissionTime'));
70-
const timeB = new Date(_.get(b, 'submissions[0].submissionTime'));
71-
return timeA - timeB;
72-
}
73-
return pB - pA;
74-
});
75-
if (maxFinalScore > 0) {
76-
_.each(submissions, (submission, i) => {
77-
submissions[i].finalRank = i + 1;
78-
});
79-
}
80-
return { submissions, maxFinalScore };
81-
}
824

835
/**
846
* Get provisional score of submission
@@ -111,66 +33,3 @@ export function getFinalScore(submission) {
11133
}
11234
return finalScore;
11335
}
114-
115-
/**
116-
* Process submissions of MM challenge
117-
* @param submissions the array of submissions
118-
* @param resources the challenge resources
119-
* @param registrants the challenge registrants
120-
*/
121-
export function processMMSubmissions(submissions, resources, registrants) {
122-
const data = {};
123-
const result = [];
124-
125-
_.each(submissions, (submission) => {
126-
const { memberId } = submission;
127-
let memberHandle;
128-
const resource = _.find(resources, r => _.get(r, 'userId').toString() === memberId.toString());
129-
if (_.isEmpty(resource)) {
130-
memberHandle = memberId;
131-
} else {
132-
memberHandle = _.has(resource, 'handle') ? _.get(resource, 'handle') : memberId.toString();
133-
}
134-
if (!data[memberHandle]) {
135-
data[memberHandle] = [];
136-
}
137-
const validReviews = _.filter(submission.review,
138-
r => !_.isEmpty(r) && (r.typeId !== AV_SCAN_SCORER_REVIEW_TYPE_ID));
139-
validReviews.sort((a, b) => {
140-
const dateA = new Date(a.created);
141-
const dateB = new Date(b.created);
142-
return dateB - dateA;
143-
});
144-
145-
const provisionalScore = toFixed(_.get(validReviews, '[0].score', '-'), 5);
146-
const finalScore = toFixed(_.get(submission, 'reviewSummation[0].aggregateScore', '-'), 5);
147-
148-
data[memberHandle].push({
149-
submissionId: submission.id,
150-
submissionTime: submission.created,
151-
provisionalScore,
152-
finalScore,
153-
});
154-
});
155-
156-
_.each(data, (value, key) => {
157-
result.push({
158-
submissions: [...value.sort((a, b) => new Date(b.submissionTime)
159-
.getTime() - new Date(a.submissionTime).getTime())],
160-
member: key,
161-
colorStyle: getMMChallengeHandleStyle(key, registrants),
162-
});
163-
});
164-
165-
const { submissions: finalSubmissions, maxFinalScore } = processRanks(result);
166-
finalSubmissions.sort((a, b) => {
167-
if (maxFinalScore === 0) {
168-
return a.provisionalRank - b.provisionalRank;
169-
}
170-
return a.finalRank - b.finalRank;
171-
});
172-
173-
return finalSubmissions;
174-
}
175-
176-
export default undefined;

0 commit comments

Comments
 (0)