Skip to content

Commit a48c2ee

Browse files
authored
Merge pull request #7203 from topcoder-platform/PM-4608-2
PM-4608: show MM final scores during review
2 parents 02eb3f1 + e8c25c9 commit a48c2ee

7 files changed

Lines changed: 146 additions & 25 deletions

File tree

__tests__/shared/components/challenge-detail/MySubmissions/SubmissionsList/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('getDisplayedScores', () => {
2323
});
2424
});
2525

26-
test('shows final scores once the review phase is complete', () => {
26+
test('shows final scores after the review phase is complete', () => {
2727
expect(getDisplayedScores(
2828
{
2929
finalScore: 100,
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* eslint-env jest */
2+
import { config } from 'topcoder-react-utils';
3+
import getReviewSummations from '../../../src/shared/services/reviewSummations';
4+
5+
const baseUrl = `${config.API.V6}${config.URL.REVIEW_SUMMATIONS_API_URL}`;
6+
7+
describe('reviewSummations service', () => {
8+
const originalFetch = global.fetch;
9+
const originalHeaders = global.Headers;
10+
11+
beforeAll(() => {
12+
if (!global.Headers) {
13+
global.Headers = class HeadersMock {
14+
constructor() {
15+
this.values = {};
16+
}
17+
18+
set(key, value) {
19+
this.values[key] = value;
20+
}
21+
22+
get(key) {
23+
return this.values[key];
24+
}
25+
};
26+
}
27+
});
28+
29+
afterAll(() => {
30+
global.fetch = originalFetch;
31+
global.Headers = originalHeaders;
32+
});
33+
34+
beforeEach(() => {
35+
global.fetch = jest.fn();
36+
});
37+
38+
it('loads every page when review summations metadata reports more pages', async () => {
39+
global.fetch
40+
.mockResolvedValueOnce({
41+
ok: true,
42+
json: () => Promise.resolve({
43+
data: [{ id: 'summation-page-1' }],
44+
meta: {
45+
page: 1,
46+
perPage: 100,
47+
totalPages: 2,
48+
totalItems: 101,
49+
},
50+
}),
51+
})
52+
.mockResolvedValueOnce({
53+
ok: true,
54+
json: () => Promise.resolve({
55+
data: [{ id: 'summation-page-2' }],
56+
meta: {
57+
page: 2,
58+
perPage: 100,
59+
totalPages: 2,
60+
totalItems: 101,
61+
},
62+
}),
63+
});
64+
65+
const result = await getReviewSummations('token-v3', 'challenge-id');
66+
67+
expect(global.fetch).toHaveBeenCalledTimes(2);
68+
expect(global.fetch).toHaveBeenNthCalledWith(
69+
1,
70+
`${baseUrl}?challengeId=challenge-id&perPage=500&page=1`,
71+
expect.objectContaining({ method: 'GET' }),
72+
);
73+
expect(global.fetch).toHaveBeenNthCalledWith(
74+
2,
75+
`${baseUrl}?challengeId=challenge-id&perPage=500&page=2`,
76+
expect.objectContaining({ method: 'GET' }),
77+
);
78+
expect(result.data).toEqual([
79+
{ id: 'summation-page-1' },
80+
{ id: 'summation-page-2' },
81+
]);
82+
expect(result.meta).toEqual(expect.objectContaining({
83+
page: 2,
84+
perPage: 500,
85+
totalItems: 2,
86+
totalPages: 2,
87+
}));
88+
});
89+
90+
it('uses the short-page heuristic only when metadata does not include total pages', async () => {
91+
global.fetch.mockResolvedValueOnce({
92+
ok: true,
93+
json: () => Promise.resolve({
94+
data: [{ id: 'summation-only-page' }],
95+
meta: {
96+
page: 1,
97+
perPage: 100,
98+
},
99+
}),
100+
});
101+
102+
const result = await getReviewSummations(null, 'challenge-id');
103+
104+
expect(global.fetch).toHaveBeenCalledTimes(1);
105+
expect(result.data).toEqual([{ id: 'summation-only-page' }]);
106+
});
107+
});

src/shared/components/challenge-detail/MySubmissions/SubmissionsList/index.jsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import moment from 'moment';
99
import { PrimaryButton, Modal } from 'topcoder-react-ui-kit';
1010
import PT from 'prop-types';
1111
import { services } from 'topcoder-react-lib';
12-
import { isReviewPhaseComplete } from 'utils/challenge-detail/mm-final-results';
1312
import sortList from 'utils/challenge-detail/sort';
1413
import { getSubmissionStatus } from 'utils/challenge-detail/submission-status';
1514

@@ -77,15 +76,13 @@ const getSubmissionCreatedTime = (submission) => {
7776

7877
/**
7978
* Returns the scores that should be displayed for a Marathon Match submission row.
80-
* Initial score is the authoritative provisional score for MM submissions, and
81-
* final scores become visible once review is complete or the payload already
82-
* includes a final result during review.
79+
* Initial score is the authoritative provisional score for MM submissions, while
80+
* final scores should surface as soon as Review API provides them.
8381
*
8482
* @param {Object} submission submission attempt shown in My Submissions.
85-
* @param {Object} challenge challenge that owns the submission.
8683
* @returns {{ finalScore: number|null, provisionalScore: number|null }} display-ready scores.
8784
*/
88-
export function getDisplayedScores(submission = {}, challenge = {}) {
85+
export function getDisplayedScores(submission = {}) {
8986
const toNumericScore = (value) => {
9087
if (_.isNil(value) || value === '' || value === '-') {
9188
return null;
@@ -98,10 +95,9 @@ export function getDisplayedScores(submission = {}, challenge = {}) {
9895
const initialScore = toNumericScore(_.get(submission, 'initialScore'));
9996
const provisionalScore = toNumericScore(_.get(submission, 'provisionalScore'));
10097
const finalScore = toNumericScore(_.get(submission, 'finalScore'));
101-
const showFinalScore = isReviewPhaseComplete(challenge) || !_.isNil(finalScore);
10298

10399
return {
104-
finalScore: showFinalScore ? finalScore : null,
100+
finalScore,
105101
provisionalScore: !_.isNil(initialScore) ? initialScore : provisionalScore,
106102
};
107103
}
@@ -467,7 +463,7 @@ class SubmissionsListView extends React.Component {
467463
</div>
468464
{
469465
sortedSubmissions.map((mySubmission) => {
470-
let { finalScore, provisionalScore } = getDisplayedScores(mySubmission, challenge);
466+
let { finalScore, provisionalScore } = getDisplayedScores(mySubmission);
471467
if (_.isNumber(finalScore)) {
472468
if (finalScore > 0) {
473469
finalScore = finalScore.toFixed(2);

src/shared/components/challenge-detail/Submissions/SubmissionInformationModal/index.jsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,30 @@ class SubmissionInformationModal extends React.Component {
6161
render() {
6262
const {
6363
toggleTestcase, onClose, isLoadingSubmissionInformation,
64-
submissionInformation, showFinalResults,
64+
submissionInformation,
6565
} = this.props;
6666
const submissionBasicInfo = isLoadingSubmissionInformation
6767
? null : this.getSubmissionBasicInfo();
6868
const testcases = isLoadingSubmissionInformation ? [] : this.getTestcases();
69+
const toNumericScore = (value) => {
70+
if (_.isNil(value) || value === '' || value === '-') {
71+
return null;
72+
}
73+
74+
const numeric = Number(value);
75+
return Number.isFinite(numeric) ? numeric : null;
76+
};
77+
const displayedScores = submissionBasicInfo
78+
? {
79+
finalScore: toNumericScore(_.get(submissionBasicInfo, 'finalScore')),
80+
provisionalScore: (() => {
81+
const initialScore = toNumericScore(_.get(submissionBasicInfo, 'initialScore'));
82+
return !_.isNil(initialScore)
83+
? initialScore
84+
: toNumericScore(_.get(submissionBasicInfo, 'provisionalScore'));
85+
})(),
86+
}
87+
: { finalScore: null, provisionalScore: null };
6988

7089
return (
7190
<Modal theme={{ container: modal.container }} onCancel={() => onClose(false)}>
@@ -90,12 +109,14 @@ class SubmissionInformationModal extends React.Component {
90109
<div
91110
styleName="modal.details-item"
92111
>
93-
{(!submissionBasicInfo.finalScore && submissionBasicInfo.finalScore !== 0) || !showFinalResults ? '-' : submissionBasicInfo.finalScore}
112+
{displayedScores.finalScore === null ? '-' : displayedScores.finalScore}
94113
</div>
95114
<div
96115
styleName="modal.details-item"
97116
>
98-
{(!submissionBasicInfo.provisionalScore && submissionBasicInfo.provisionalScore !== 0) ? '-' : submissionBasicInfo.provisionalScore}
117+
{displayedScores.provisionalScore === null
118+
? '-'
119+
: displayedScores.provisionalScore}
99120
</div>
100121
<div styleName="modal.details-item">
101122
{moment(submissionBasicInfo.submissionTime)
@@ -180,7 +201,6 @@ SubmissionInformationModal.propTypes = {
180201
openTestcase: PT.shape({}).isRequired,
181202
clearTestcaseOpen: PT.func.isRequired,
182203
submission: PT.shape().isRequired,
183-
showFinalResults: PT.bool.isRequired,
184204
};
185205

186206
export default SubmissionInformationModal;

src/shared/components/challenge-detail/Submissions/SubmissionRow/SubmissionHistoryRow/index.jsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export default function SubmissionHistoryRow({
2727
provisionalScore,
2828
submissionTime,
2929
createdAt,
30-
showFinalResults,
3130
status,
3231
challengeStatus,
3332
auth,
@@ -38,6 +37,10 @@ export default function SubmissionHistoryRow({
3837
// todo: hide download button until update submissions API
3938
const hideDownloadForMMRDM = true;
4039
const parseScore = (value) => {
40+
if (value === null || value === undefined || value === '' || value === '-') {
41+
return null;
42+
}
43+
4144
const numeric = Number(value);
4245
return Number.isFinite(numeric) ? numeric : null;
4346
};
@@ -63,9 +66,6 @@ export default function SubmissionHistoryRow({
6366
}
6467
};
6568
const getFinalScore = () => {
66-
if (!showFinalResults) {
67-
return 'N/A';
68-
}
6969
if (finalScoreValue === null) {
7070
return 'N/A';
7171
}
@@ -139,7 +139,6 @@ export default function SubmissionHistoryRow({
139139
SubmissionHistoryRow.defaultProps = {
140140
finalScore: null,
141141
provisionalScore: null,
142-
showFinalResults: false,
143142
isLoggedIn: false,
144143
createdAt: null,
145144
submissionTime: null,
@@ -169,7 +168,6 @@ SubmissionHistoryRow.propTypes = {
169168
PT.oneOf([null]),
170169
]),
171170
challengeStatus: PT.string.isRequired,
172-
showFinalResults: PT.bool,
173171
auth: PT.shape().isRequired,
174172
numWinners: PT.number.isRequired,
175173
submissionId: PT.string.isRequired,

src/shared/components/challenge-detail/Submissions/SubmissionRow/index.jsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export default function SubmissionRow({
3535
} = latestSubmission;
3636

3737
const parseScore = (value) => {
38+
if (_.isNil(value) || value === '' || value === '-') {
39+
return null;
40+
}
41+
3842
const numeric = Number(value);
3943
return Number.isFinite(numeric) ? numeric : null;
4044
};
@@ -66,9 +70,6 @@ export default function SubmissionRow({
6670
};
6771

6872
const getFinalReviewResult = () => {
69-
if (!showFinalResults) {
70-
return 'N/A';
71-
}
7273
if (_.isNil(finalScore)) {
7374
return 'N/A';
7475
}

src/shared/services/reviewSummations.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ async function fetchReviewSummationsPage({
3030
const totalPages = _.get(payload, 'meta.totalPages')
3131
|| _.get(payload, 'meta.total_pages');
3232
const reachedEnd = !data.length
33-
|| (totalPages && page >= totalPages)
34-
|| data.length < DEFAULT_PER_PAGE;
33+
|| (totalPages ? page >= totalPages : data.length < DEFAULT_PER_PAGE);
3534

3635
if (reachedEnd) {
3736
return {

0 commit comments

Comments
 (0)