From f88acb3f254d47ddd6111abe8f71d4f623fa9039 Mon Sep 17 00:00:00 2001 From: Huan Li Date: Thu, 4 Jul 2019 12:41:27 +0800 Subject: [PATCH] Update MM challenge leaderboard --- README.md | 5 + __tests__/__snapshots__/index.js.snap | 16 ++ .../reducers/__snapshots__/challenge.js.snap | 72 +++++++ config/default.json | 5 + config/development.json | 3 + config/jest/default.js | 8 +- config/production.json | 3 + config/webpack/default.js | 11 ++ docs/actions.challenge.md | 45 ++++- docs/index.md | 11 ++ docs/reducers.challenge.md | 72 +++++-- docs/services.submissions.md | 57 ++++++ docs/submission.md | 38 ++++ package.json | 1 + src/actions/challenge-listing.js | 6 +- src/actions/challenge.js | 83 +++++++- src/config/index.js | 4 - src/index.js | 2 +- src/reducers/challenge.js | 42 +++++ src/services/api.js | 1 + src/services/index.js | 2 + src/services/members.js | 12 ++ src/services/submissions.js | 57 ++++++ src/utils/index.js | 2 + src/utils/submission.js | 177 ++++++++++++++++++ 25 files changed, 696 insertions(+), 39 deletions(-) create mode 100644 config/default.json create mode 100644 config/development.json create mode 100644 config/production.json create mode 100644 docs/services.submissions.md create mode 100644 docs/submission.md delete mode 100644 src/config/index.js create mode 100644 src/services/submissions.js create mode 100644 src/utils/submission.js diff --git a/README.md b/README.md index f2158298..a3a77830 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,11 @@ The [Topcoder](https://www.topcoder.com) lib for internal ReactJS projects. + +### Configuration for AV-Scan scorer review type ID + +Change the property in `AV_SCAN_SCORER_REVIEW_TYPE_ID` in config. + ### Development ```shell # Install dependencies diff --git a/__tests__/__snapshots__/index.js.snap b/__tests__/__snapshots__/index.js.snap index e7ee51bc..fd8bd27a 100644 --- a/__tests__/__snapshots__/index.js.snap +++ b/__tests__/__snapshots__/index.js.snap @@ -17,6 +17,8 @@ Object { "getActiveChallengesCountInit": [Function], "getDetailsDone": [Function], "getDetailsInit": [Function], + "getMmSubmissionsDone": [Function], + "getMmSubmissionsInit": [Function], "getSubmissionsDone": [Function], "getSubmissionsInit": [Function], "loadResultsDone": [Function], @@ -426,14 +428,17 @@ Object { "countReset": [Function], "debug": [Function], "dir": [Function], + "dirxml": [Function], "error": [Function], "group": [Function], "groupCollapsed": [Function], "groupEnd": [Function], "info": [Function], "log": [Function], + "table": [Function], "time": [Function], "timeEnd": [Function], + "timeLog": [Function], "trace": [Function], "warn": [Function], }, @@ -467,6 +472,7 @@ Object { "getApiV2": [Function], "getApiV3": [Function], "getApiV4": [Function], + "getApiV5": [Function], "getTcM2mToken": [Function], }, "billing": Object { @@ -510,6 +516,10 @@ Object { "default": undefined, "getReviewOpportunitiesService": [Function], }, + "submissions": Object { + "default": undefined, + "getService": [Function], + }, "terms": Object { "default": undefined, "getService": [Function], @@ -527,6 +537,12 @@ Object { "getService": [Function], }, }, + "submission": Object { + "default": undefined, + "getFinalScore": [Function], + "getProvisionalScore": [Function], + "processMMSubmissions": [Function], + }, "tc": Object { "COMPETITION_TRACKS": Object { "DATA_SCIENCE": "data_science", diff --git a/__tests__/reducers/__snapshots__/challenge.js.snap b/__tests__/reducers/__snapshots__/challenge.js.snap index 9db7c531..7ddb822b 100644 --- a/__tests__/reducers/__snapshots__/challenge.js.snap +++ b/__tests__/reducers/__snapshots__/challenge.js.snap @@ -6,7 +6,9 @@ Object { "details": null, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -27,7 +29,9 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -48,7 +52,9 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -66,7 +72,9 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "12345", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -87,8 +95,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [], @@ -114,8 +124,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -143,8 +155,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -170,8 +184,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "12345", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -196,8 +212,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -223,8 +241,10 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -252,8 +272,10 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -281,8 +303,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -310,8 +334,10 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "12345", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -339,8 +365,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [], @@ -366,8 +394,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -395,8 +425,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -422,8 +454,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "12345", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -448,8 +482,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -471,7 +507,9 @@ Object { "details": null, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -492,7 +530,9 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -513,7 +553,9 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -531,7 +573,9 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "12345", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -552,8 +596,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [], @@ -579,8 +625,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -608,8 +656,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -635,8 +685,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "12345", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -661,8 +713,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -684,7 +738,9 @@ Object { "details": null, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -705,7 +761,9 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -726,7 +784,9 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -744,7 +804,9 @@ Object { "fetchChallengeFailure": false, "loadingCheckpoints": false, "loadingDetailsForChallengeId": "12345", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object {}, "mySubmissionsManagement": Object {}, "registering": false, @@ -765,8 +827,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [], @@ -792,8 +856,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "12345", "v2": Array [ @@ -821,8 +887,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -848,8 +916,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "12345", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, @@ -874,8 +944,10 @@ Object { "fetchChallengeFailure": "Unknown error", "loadingCheckpoints": false, "loadingDetailsForChallengeId": "", + "loadingMMSubmissionsForChallengeId": "", "loadingResultsForChallengeId": "", "loadingSubmissionsForChallengeId": "", + "mmSubmissions": Array [], "mySubmissions": Object { "challengeId": "", "v2": null, diff --git a/config/default.json b/config/default.json new file mode 100644 index 00000000..23136c0f --- /dev/null +++ b/config/default.json @@ -0,0 +1,5 @@ +{ + "AV_SCAN_SCORER_REVIEW_TYPE_ID": "", + "PAGE_SIZE": 50, + "REVIEW_OPPORTUNITY_PAGE_SIZE": 1000 +} diff --git a/config/development.json b/config/development.json new file mode 100644 index 00000000..d7c751d4 --- /dev/null +++ b/config/development.json @@ -0,0 +1,3 @@ +{ + "AV_SCAN_SCORER_REVIEW_TYPE_ID": "" +} diff --git a/config/jest/default.js b/config/jest/default.js index 9479649f..aa792822 100644 --- a/config/jest/default.js +++ b/config/jest/default.js @@ -1,3 +1,9 @@ const config = require('topcoder-react-utils/config/jest/default'); +const nodeConfig = require('config'); -module.exports = config; +module.exports = { + ...config, + globals: { + CONFIG: nodeConfig, + }, +}; diff --git a/config/production.json b/config/production.json new file mode 100644 index 00000000..d7c751d4 --- /dev/null +++ b/config/production.json @@ -0,0 +1,3 @@ +{ + "AV_SCAN_SCORER_REVIEW_TYPE_ID": "" +} diff --git a/config/webpack/default.js b/config/webpack/default.js index 932840d6..a1a3c63b 100644 --- a/config/webpack/default.js +++ b/config/webpack/default.js @@ -1,7 +1,15 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +const webpack = require('webpack'); + module.exports = { + plugins: [ + // eslint-disable-next-line global-require + new webpack.DefinePlugin({ CONFIG: JSON.stringify(require('config')) }), + ], // Don't include the dependencies to keep built bundle small, // they will be provided by the app using this lib externals: [ + 'config', 'qs', 'lodash', 'le_node', @@ -17,4 +25,7 @@ module.exports = { 'to-capital-case', 'topcoder-react-utils', ], + node: { + fs: 'empty', + }, }; diff --git a/docs/actions.challenge.md b/docs/actions.challenge.md index 23d16fb5..015a2abd 100644 --- a/docs/actions.challenge.md +++ b/docs/actions.challenge.md @@ -24,6 +24,8 @@ Actions related to Topcoder challenges APIs. * [.updateChallengeDone(uuid, challenge, tokenV3)](#module_actions.challenge.updateChallengeDone) ⇒ Action * [.getActiveChallengesCountInit()](#module_actions.challenge.getActiveChallengesCountInit) ⇒ Action * [.getActiveChallengesCountDone(handle, tokenV3)](#module_actions.challenge.getActiveChallengesCountDone) ⇒ Action + * [.getMMSubmissionsInit(challengeId)](#module_actions.challenge.getMMSubmissionsInit) ⇒ Action + * [.getMMSubmissionsDone(challengeId, submitterIds, registrants, tokenV3)](#module_actions.challenge.getMMSubmissionsDone) ⇒ Action @@ -31,14 +33,14 @@ Actions related to Topcoder challenges APIs. Creates an action that drops from Redux store all checkpoints loaded before. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) ### actions.challenge.dropResults() ⇒ Action Creates an action that drops from Redux store all challenge results loaded before. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) ### actions.challenge.getDetailsInit(challengeId) ⇒ Action @@ -93,7 +95,7 @@ challenge. Creates an action that signals beginning of registration for a challenge. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) ### actions.challenge.registerDone(auth, challengeId) ⇒ Action @@ -114,7 +116,7 @@ Creates an action that registers user for a challenge. Creates an action that signals beginning of user unregistration from a challenge. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) ### actions.challenge.unregisterDone(auth, challengeId) ⇒ Action @@ -161,7 +163,7 @@ Creates an action that loads challenge results. Creates an action that signals beginning of challenge checkpoints data loading. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) ### actions.challenge.fetchCheckpointsDone(tokenV2, challengeId) @@ -180,7 +182,7 @@ Creates an action that loads challenge checkpoints data. Creates an action that Toggles checkpoint details panel in the Topcoder Submission Management Page. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) **Todo** - [ ] This is UI action relevant to a specific page in specific app. Must be @@ -197,7 +199,7 @@ Creates an action that Toggles checkpoint details panel in the Topcoder ### actions.challenge.updateChallengeInit(uuid) ⇒ Action Creates an action that signals beginning of challenge details update. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) **Todo** - [ ] No idea, why we have this action. This functionality should be covered @@ -214,7 +216,7 @@ Creates an action that signals beginning of challenge details update. ### actions.challenge.updateChallengeDone(uuid, challenge, tokenV3) ⇒ Action Creates an action that updates challenge details. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) **Todo** - [ ] No idea, why we have this action. This functionality should be covered @@ -233,7 +235,7 @@ Creates an action that updates challenge details. ### actions.challenge.getActiveChallengesCountInit() ⇒ Action Creates an action that signals beginning of getting count of user's active challenges. -**Kind**: static method of [actions.challenge](#module_actions.challenge) +**Kind**: static method of [actions.challenge](#module_actions.challenge) ### actions.challenge.getActiveChallengesCountDone(handle, tokenV3) ⇒ Action @@ -246,3 +248,28 @@ Creates an action that gets count of user's active challenges from the backend. | handle | String | Topcoder user handle. | | tokenV3 | String | Optional. Topcoder auth token v3. Without token only public challenges will be counted. With the token provided, the action will also count private challenges related to this user. | + + +### actions.challenge.getMMSubmissionsInit(challengeId) ⇒ Action +Creates an action that signals beginning of Marathon Match submissions loading. + +**Kind**: static method of [actions.challenge](#module_actions.challenge) + + +| Param | Type | Description | +| --- | --- | --- | +| challengeId | String | The challenge id. | + +### actions.challenge.getMMSubmissionsDone(challengeId, submitterIds, registrants, tokenV3) ⇒ Action +Creates an action that loads Marathon Match submissions to the specified +challenge. + +**Kind**: static method of [actions.challenge](#module_actions.challenge) + + +| Param | Type | Description | +| --- | --- | --- | +| challengeId | String | The challenge id. | +| submitterIds | Array | The ids of submitters | +| registrants | Array | The registrants of challenge | +| tokenV3 | String | Topcoder auth token v3. | diff --git a/docs/index.md b/docs/index.md index 185c46d5..9139f14a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -143,6 +143,12 @@ action to really cancel group loading.

Reducer for state.terms.

+services.submissions
+

his module provides a service for convenient manipulation with + Topcoder submissions via TC API.

+
+
+
services.api

This module provides a service for conventient access to Topcoder APIs.

@@ -301,3 +307,8 @@ the proxy will forward them to the service only if LOG_ENTRIES_TOKEN is set).

Collection of url function.

+
+submission
+

Collection of submission function.

+
+
diff --git a/docs/reducers.challenge.md b/docs/reducers.challenge.md index d2f2bd0e..eb3514f6 100644 --- a/docs/reducers.challenge.md +++ b/docs/reducers.challenge.md @@ -15,6 +15,8 @@ State segment managed by this reducer has the following strcuture: * [.default](#module_reducers.challenge.default) * [.factory(options)](#module_reducers.challenge.factory) ⇒ Promise * _inner_ + * [~onGetMMSubmissionsInit(state, action)](#module_reducers.challenge..onGetMMSubmissionsInit) ⇒ Object + * [~onGetMMSubmissionsDone(state, action)](#module_reducers.challenge..onGetDetailsDone) ⇒ Object * [~onGetDetailsInit(state, action)](#module_reducers.challenge..onGetDetailsInit) ⇒ Object * [~onGetDetailsDone(state, action)](#module_reducers.challenge..onGetDetailsDone) ⇒ Object * [~onGetSubmissionsInit(state, action)](#module_reducers.challenge..onGetSubmissionsInit) ⇒ Object @@ -34,7 +36,7 @@ State segment managed by this reducer has the following strcuture: ### reducers.challenge.default Reducer with default intial state. -**Kind**: static property of [reducers.challenge](#module_reducers.challenge) +**Kind**: static property of [reducers.challenge](#module_reducers.challenge) ### reducers.challenge.factory(options) ⇒ Promise @@ -42,7 +44,7 @@ Factory which creates a new reducer with its initial state tailored to the given options object, if specified (for server-side rendering). If options object is not specified, it creates just the default reducer. Accepted options are: -**Kind**: static method of [reducers.challenge](#module_reducers.challenge) +**Kind**: static method of [reducers.challenge](#module_reducers.challenge) **Resolves**: Function(state, action): state New reducer. | Param | Type | Default | Description | @@ -53,18 +55,48 @@ object is not specified, it creates just the default reducer. Accepted options a | [options.challenge.challengeDetails.id] | String | '' | Optional. ID of the challenge to load details for. | | [options.challenge.challengeDetails.mySubmission] | Boolean | false | Optional. The flag indicates whether load my submission. | + + +### reducers.challenge~onGetMMSubmissionsInit(state, action) ⇒ Object +Handles CHALLENGE/GET_MM_SUBMISSION_INIT action. + +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Returns**: Object - New state + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + + +### reducers.challenge~onGetMMSubmissionsDone(state, action) ⇒ Object +Handles CHALLENGE/GET_MM_SUBMISSION_DONE action. +Note, that it silently discards received details if the ID of received +challenge mismatches the one stored in loadingMMSubmissionsForChallengeId field +of the state. + +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Returns**: Object - New state. + +| Param | Type | +| --- | --- | +| state | Object | +| action | Object | + + ### reducers.challenge~onGetDetailsInit(state, action) ⇒ Object Handles CHALLENGE/GET_DETAILS_INIT action. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: Object - New state | Param | Type | | --- | --- | -| state | Object | -| action | Object | +| state | Object | +| action | Object | @@ -74,26 +106,26 @@ Note, that it silently discards received details if the ID of received challenge mismatches the one stored in loadingDetailsForChallengeId field of the state. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: Object - New state. | Param | Type | | --- | --- | -| state | Object | -| action | Object | +| state | Object | +| action | Object | ### reducers.challenge~onGetSubmissionsInit(state, action) ⇒ Object Handles CHALLENGE/GET_SUBMISSION_INIT action. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: Object - New state. | Param | Type | | --- | --- | -| state | Object | -| action | Object | +| state | Object | +| action | Object| @@ -128,8 +160,8 @@ Handles CHALLENGE/LOAD_RESULTS_INIT action. | Param | Type | | --- | --- | -| state | Object | -| action | Object | +| state | Object | +| action | Object | @@ -141,7 +173,7 @@ Handles CHALLENGE/LOAD_RESULTS_DONE action. | Param | Type | | --- | --- | | state | Object | -| action | Object | +| action | Object | @@ -164,15 +196,15 @@ Handles CHALLENGE/UNREGISTER_DONE action. | Param | Type | | --- | --- | -| state | Object | -| action | Object | +| state | Object | +| action | Object | ### reducers.challenge~onUpdateChallengeInit(state, actions) ⇒ Object Handles CHALLENGE/UPDATE_CHALLENGE_INIT. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: Object - New state. | Param | Type | Description | @@ -185,7 +217,7 @@ Handles CHALLENGE/UPDATE_CHALLENGE_INIT. ### reducers.challenge~onUpdateChallengeDone(state, actions) ⇒ Object Handles CHALLENGE/UPDATE_CHALLENGE_DONE. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: Object - New state. | Param | Type | Description | @@ -198,7 +230,7 @@ Handles CHALLENGE/UPDATE_CHALLENGE_DONE. ### reducers.challenge~onGetActiveChallengesCountDone(state, action) ⇒ Object Handles CHALLENGE/GET_ACTIVE_CHALLENGES_COUNT_DONE action. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: Object - New state | Param | Type | Description | @@ -211,7 +243,7 @@ Handles CHALLENGE/GET_ACTIVE_CHALLENGES_COUNT_DONE action. ### reducers.challenge~create(initialState) ⇒ function Creates a new Challenge reducer with the specified initial state. -**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) +**Kind**: inner method of [reducers.challenge](#module_reducers.challenge) **Returns**: function - Challenge reducer. | Param | Type | Description | diff --git a/docs/services.submissions.md b/docs/services.submissions.md new file mode 100644 index 00000000..201297e5 --- /dev/null +++ b/docs/services.submissions.md @@ -0,0 +1,57 @@ + + +## services.submissions +This module provides a service for searching for Topcoder +submissions via API V5. + +* [services.submissions](#module_services.submissions) + * _static_ + * [.getService(tokenV3)](#module_services.submissions.getService) ⇒ SubmissionService + * _inner_ + * [~SubmissionsService](#module_services.submissions..SubmissionsService) + * [new SubmissionsService(tokenV3)](#new_module_services.submissions..SubmissionsService_new) + * [.getSubmissions(filters, params)](#module_services.submissions..SubmissionsService+getSubmissions) ⇒ Promise + + + +### services.submissions.getService(tokenV3) ⇒ SubmissionsService +Returns a new or existing submissions service. + +**Kind**: static method of [services.submissions](#module_services.submissions) +**Returns**: SubmissionsService - Submissions service object + +| Param | Type | Description | +| --- | --- | --- | +| tokenV3 | String | Auth token for Topcoder API v5. | + + + +### services.submissions~SubmissionsService +Service class. + +**Kind**: inner class of [services.submissions](#module_services.submissions) + +* [~SubmissionsService](#module_services.submissions..SubmissionsService) + * [new SubmissionsService(tokenV3)](#new_module_services.submissions..SubmissionsService_new) + * [.getSubmissions(filters, params)](#module_services.submissions..SubmissionsService+getSubmissions) ⇒ Promise + + + +#### new SubmissionsService(tokenV3) + +| Param | Type | Description | +| --- | --- | --- | +| tokenV3 | String | Auth token for Topcoder API v5. | + + + +#### submissionsService.getSubmissions(filters, params) ⇒ Promise +Get submissions of challenge + +**Kind**: instance method of [SubmissionsService](#module_services.submissions..SubmissionsService) +**Returns**: Promise - Resolves to the submissions array. + +| Param | Type | Description | +| --- | --- | --- | +| filters | Object | The filters object | +| params | Object | The params object | diff --git a/docs/submission.md b/docs/submission.md new file mode 100644 index 00000000..ff48298b --- /dev/null +++ b/docs/submission.md @@ -0,0 +1,38 @@ + + +## submission +Collection of submission functions. + +* [submission](#module_submission) + * [.getProvisionalScore(submission)](#module_submission.getProvisionalScore) + * [.getFinalScore(submission)](#module_submission.getFinalScore) + * [.processMMSubmissions(submissions, resources, registrants)](#module_submission.processMMSubmissions) + + +### submission.getProvisionalScore(submission) +Get provisional score of submission +**Kind**: static method of [submission](#module_submission) + +| Param | Type | +| --- | --- | +| submission | Object | + + +### submission.getFinalScore(submission) +Get final score of submission +**Kind**: static method of [submission](#module_submission) + +| Param | Type | +| --- | --- | +| submission | Object | + + +### submission.processMMSubmissions(submissions, resources, registrants) +Process submissions of MM challenge +**Kind**: static method of [submission](#module_submission) + +| Param | Type | +| --- | --- | +| submission | Object | +| resources | Array | +| registrants | Array | diff --git a/package.json b/package.json index a8163f0b..30247526 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "version": "0.7.15", "dependencies": { "auth0-js": "^6.8.4", + "config": "^3.1.0", "isomorphic-fetch": "^2.2.1", "le_node": "^1.7.0", "lodash": "^4.17.10", diff --git a/src/actions/challenge-listing.js b/src/actions/challenge-listing.js index e60f10f0..1ab0d8f5 100644 --- a/src/actions/challenge-listing.js +++ b/src/actions/challenge-listing.js @@ -1,7 +1,7 @@ /** * Challenge listing actions. */ - +/* global CONFIG */ import _ from 'lodash'; import { createActions } from 'redux-actions'; import { decodeToken } from 'tc-accounts'; @@ -10,12 +10,12 @@ import { processSRM, COMPETITION_TRACKS } from '../utils/tc'; import { services } from '../services'; import { errors } from '../utils'; import * as filterUtil from '../utils/challenge/filter'; -import * as config from '../config'; + const { fireErrorMessage } = errors; const { getService } = services.challenge; const { getReviewOpportunitiesService } = services.reviewOpportunities; -const { PAGE_SIZE, REVIEW_OPPORTUNITY_PAGE_SIZE } = config; +const { PAGE_SIZE, REVIEW_OPPORTUNITY_PAGE_SIZE } = CONFIG; /** * Process filter diff --git a/src/actions/challenge.js b/src/actions/challenge.js index f5dfb09b..9c00f33f 100644 --- a/src/actions/challenge.js +++ b/src/actions/challenge.js @@ -2,12 +2,48 @@ * @module "actions.challenge" * @desc Actions related to Topcoder challenges APIs. */ - +/* global CONFIG */ import _ from 'lodash'; import { config } from 'topcoder-react-utils'; import { createActions } from 'redux-actions'; import { getService as getChallengesService } from '../services/challenges'; +import { getService as getSubmissionService } from '../services/submissions'; +import { getService as getMemberService } from '../services/members'; import { getApi } from '../services/api'; +import * as submissionUtil from '../utils/submission'; + +const { PAGE_SIZE } = CONFIG; + +/** + * Private. Loads from the backend all data matching some conditions. + * @param {Function} getter Given params object of shape { limit, offset } + * loads from the backend at most "limit" data, skipping the first + * "offset" ones. Returns loaded data as an array. + * @param {Number} page Optional. Next page of data to load. + * @param {Array} prev Optional. data loaded so far. + */ +function getAll(getter, page = 1, prev) { + /* Amount of submissions to fetch in one API call. 50 is the current maximum + * amount of submissions the backend returns, event when the larger limit is + * explicitely required. */ + return getter({ + page, + perPage: PAGE_SIZE, + }).then((res) => { + if (res.length === 0) { + return prev || res; + } + // parse submissions + let current = []; + if (prev) { + current = prev.concat(res); + } else { + current = res; + } + return getAll(getter, 1 + page, current); + }); +} + /** * @static @@ -83,6 +119,49 @@ function getSubmissionsDone(challengeId, tokenV2) { }); } +/** + * @static + * @desc Creates an action that signals beginning of Marathon Match submissions loading. + * @param {String} challengeId Challenge ID. + * @return {Action} + */ +function getMMSubmissionsInit(challengeId) { + /* As a safeguard, we enforce challengeId to be string (in case somebody + * passes in a number, by mistake). */ + return _.toString(challengeId); +} + + +/** + * @static + * @desc Creates an action that loads Marathon Match submissions to the specified + * challenge. + * @param {String} challengeId Challenge ID. + * @param {Array} submitterIds The array of submitter ids. + * @param {Array} registrants The array of register. + * @param {String} tokenV3 Topcoder auth token v3. + * @return {Action} + */ +function getMMSubmissionsDone(challengeId, submitterIds, registrants, tokenV3) { + const filter = { challengeId }; + const memberService = getMemberService(tokenV3); + const submissionsService = getSubmissionService(tokenV3); + const calls = [ + memberService.getMembersInformation(submitterIds), + getAll(params => submissionsService.getSubmissions(filter, params)), + ]; + return Promise.all(calls).then(([resources, submissions]) => { + const finalSubmissions = submissionUtil + .processMMSubmissions(submissions, resources, registrants); + return { + challengeId, + submissions: finalSubmissions, + tokenV3, + }; + }); +} + + /** * @static * @desc Creates an action that signals beginning of registration for a @@ -305,5 +384,7 @@ export default createActions({ UPDATE_CHALLENGE_DONE: updateChallengeDone, GET_ACTIVE_CHALLENGES_COUNT_INIT: getActiveChallengesCountInit, GET_ACTIVE_CHALLENGES_COUNT_DONE: getActiveChallengesCountDone, + GET_MM_SUBMISSIONS_INIT: getMMSubmissionsInit, + GET_MM_SUBMISSIONS_DONE: getMMSubmissionsDone, }, }); diff --git a/src/config/index.js b/src/config/index.js deleted file mode 100644 index 2a5ab790..00000000 --- a/src/config/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - PAGE_SIZE: 50, - REVIEW_OPPORTUNITY_PAGE_SIZE: 1000, -}; diff --git a/src/index.js b/src/index.js index 15b0e7d7..7dcdfde6 100644 --- a/src/index.js +++ b/src/index.js @@ -12,5 +12,5 @@ export { actions } from './actions'; export { services } from './services'; export { - challenge, logger, errors, tc, time, mock, url, + challenge, logger, errors, tc, time, mock, url, submission, } from './utils'; diff --git a/src/reducers/challenge.js b/src/reducers/challenge.js index 889623e1..2d5b47c9 100644 --- a/src/reducers/challenge.js +++ b/src/reducers/challenge.js @@ -113,6 +113,44 @@ function onGetSubmissionsDone(state, action) { }; } +/** + * Handles CHALLENGE/GET_MM_SUBMISSION_INIT action. + * @param {Object} state + * @param {Object} action + * @return {Object} New state. + */ +function onGetMMSubmissionsInit(state, action) { + return { + ...state, + loadingMMSubmissionsForChallengeId: action.payload, + mmSubmissions: [], + }; +} + +/** + * Handles CHALLENGE/GET_MM_SUBMISSION_DONE action. + * @param {Object} state Previous state. + * @param {Object} action Action. + */ +function onGetMMSubmissionsDone(state, action) { + if (action.error) { + logger.error('Failed to get Marathon Match submissions for the challenge', action.payload); + return { + ...state, + loadingMMSubmissionsForChallengeId: '', + mmSubmissions: [], + }; + } + + const { challengeId, submissions } = action.payload; + if (challengeId.toString() !== state.loadingMMSubmissionsForChallengeId) return state; + return { + ...state, + loadingMMSubmissionsForChallengeId: '', + mmSubmissions: submissions, + }; +} + /** * Handles challengeActions.fetchCheckpointsDone action. * @param {Object} state Previous state. @@ -294,6 +332,8 @@ function create(initialState) { [a.getDetailsDone]: onGetDetailsDone, [a.getSubmissionsInit]: onGetSubmissionsInit, [a.getSubmissionsDone]: onGetSubmissionsDone, + [a.getMmSubmissionsInit]: onGetMMSubmissionsInit, + [a.getMmSubmissionsDone]: onGetMMSubmissionsDone, [smpActions.smp.deleteSubmissionDone]: (state, { payload }) => ({ ...state, mySubmissions: { @@ -324,6 +364,7 @@ function create(initialState) { loadingCheckpoints: false, loadingDetailsForChallengeId: '', loadingResultsForChallengeId: '', + loadingMMSubmissionsForChallengeId: '', mySubmissions: {}, checkpoints: null, registering: false, @@ -331,6 +372,7 @@ function create(initialState) { resultsLoadedForChallengeId: '', unregistering: false, updatingChallengeUuid: '', + mmSubmissions: [], })); } diff --git a/src/services/api.js b/src/services/api.js index 7c1e3355..97a045a1 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -264,6 +264,7 @@ export function getApi(version, token) { export const getApiV2 = token => getApi('V2', token); export const getApiV3 = token => getApi('V3', token); export const getApiV4 = token => getApi('V4', token); +export const getApiV5 = token => getApi('V5', token); /** * Gets a valid TC M2M token, either requesting one from TC Auth0 API, or diff --git a/src/services/index.js b/src/services/index.js index b9707791..4d776832 100644 --- a/src/services/index.js +++ b/src/services/index.js @@ -14,6 +14,7 @@ import * as userSetting from './user-settings'; import * as user from './user'; import * as lookup from './lookup'; import * as userTraits from './user-traits'; +import * as submissions from './submissions'; export const services = { api, @@ -29,6 +30,7 @@ export const services = { reviewOpportunities, lookup, userTraits, + submissions, }; export default undefined; diff --git a/src/services/members.js b/src/services/members.js index 62afbf36..9afa0311 100644 --- a/src/services/members.js +++ b/src/services/members.js @@ -289,6 +289,18 @@ class MembersService { const res = await this.private.api.get(`/members/${handle}/verify?token=${emailVerifyToken}`); return getApiResponsePayload(res); } + + /** + * Get members information + * @param {Array} userIds the member ids + */ + async getMembersInformation(userIds) { + const query = `query=${encodeURI(_.map(userIds, id => `userId:${id}`).join(' OR '))}`; + const limit = `limit=${userIds.length}`; + const url = `/members/_search?fields=userId%2Chandle%2CphotoURL%2CfirstName%2ClastName&${query}&${limit}`; + const res = await this.private.api.get(url); + return getApiResponsePayload(res); + } } let lastInstance = null; diff --git a/src/services/submissions.js b/src/services/submissions.js new file mode 100644 index 00000000..6de0e6c2 --- /dev/null +++ b/src/services/submissions.js @@ -0,0 +1,57 @@ +/** + * @module "services.submission" + * @desc This module provides a service for convenient manipulation with + * Topcoder submissions via TC API. Currently only used for MM challenges + */ + +import qs from 'qs'; +import { getApi } from './api'; + +/** + * Submission service. + */ +class SubmissionsService { + /** + * Creates a new SubmissionService instance. + * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. + */ + constructor(tokenV3) { + this.private = { + apiV5: getApi('V5', tokenV3), + tokenV3, + }; + } + + /** + * Get submissions of challenge + * @param {Object} filters + * @param {Object} params + * @return {Promise} Resolves to the api response. + */ + async getSubmissions(filters, params) { + const query = { + ...filters, + ...params, + }; + const url = `/submissions?${qs.stringify(query, { encode: false })}`; + return this.private.apiV5.get(url) + .then(res => (res.ok ? res.json() : new Error(res.statusText))) + .then(res => res); + } +} + +let lastInstance = null; +/** + * Returns a new or existing submissions service. + * @param {String} tokenV3 Optional. Auth token for Topcoder API v3. + * @return {SubmissionsService} Submissions service object + */ +export function getService(tokenV3) { + if (!lastInstance || lastInstance.private.tokenV3 !== tokenV3) { + lastInstance = new SubmissionsService(tokenV3); + } + return lastInstance; +} + +/* Using default export would be confusing in this case. */ +export default undefined; diff --git a/src/utils/index.js b/src/utils/index.js index e7e4ff52..e3fc752f 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -10,6 +10,7 @@ import * as filter from './challenge/filter'; import * as buckets from './challenge/buckets'; import * as sort from './challenge/sort'; import * as url from './url'; +import * as submission from './submission'; const challenge = { filter, @@ -25,4 +26,5 @@ export { mock, errors, url, + submission, }; diff --git a/src/utils/submission.js b/src/utils/submission.js new file mode 100644 index 00000000..d3bec7e4 --- /dev/null +++ b/src/utils/submission.js @@ -0,0 +1,177 @@ +/** + * Various submissions functions. + */ +/* global CONFIG */ +/* eslint-disable no-param-reassign */ +import _ from 'lodash'; + +const { AV_SCAN_SCORER_REVIEW_TYPE_ID } = CONFIG; + +function round(num, decimal) { + if (_.isNaN(num)) { + return 0; + } + const p1 = 10 ** (decimal + 1); + const p2 = 10 ** decimal; + return Math.round(num * p1 / 10) / p2; +} + +function removeDecimal(num, decimal) { + return ((num % decimal) + decimal) % decimal; +} + +function toFixed(num, decimal) { + const result = _.toFinite(round(num, decimal).toFixed(decimal)); + const integerResult = _.toFinite(removeDecimal(result, decimal)); + if (_.isInteger(integerResult)) { + return integerResult; + } + return result; +} + +function getMMChallengeHandleStyle(handle, registrants) { + const style = _.get(_.find(registrants, m => m.handle === handle), 'colorStyle', null); + if (style) return JSON.parse(style.replace(/(\w+):\s*([^;]*)/g, '{"$1": "$2"}')); + return {}; +} + +/** + * Process each submission rank of MM challenge + * @param submissions the array of submissions + */ +function processRanks(submissions) { + let maxFinalScore = 0; + submissions.sort((a, b) => { + let pA = _.get(a, 'submissions[0]', { provisionalScore: 0 }).provisionalScore; + let pB = _.get(b, 'submissions[0]', { provisionalScore: 0 }).provisionalScore; + if (pA === '-') pA = 0; + if (pB === '-') pB = 0; + return pB - pA; + }); + _.each(submissions, (submission, i) => { + submissions[i].provisionalRank = i + 1; + }); + + submissions.sort((a, b) => { + let pA = _.get(a, 'submissions[0]', { provisionalScore: 0 }).finalScore; + let pB = _.get(b, 'submissions[0]', { provisionalScore: 0 }).finalScore; + if (pA === '-') pA = 0; + if (pB === '-') pB = 0; + if (pA > 0) maxFinalScore = pA; + if (pB > 0) maxFinalScore = pB; + return pB - pA; + }); + if (maxFinalScore > 0) { + _.each(submissions, (submission, i) => { + submissions[i].finalRank = i + 1; + }); + } + return { submissions, maxFinalScore }; +} + +/** + * Get provisional score of submission + * @param submission + */ +export function getProvisionalScore(submission) { + const { submissions: subs } = submission; + if (!subs || subs.length === 0) { + return 0; + } + const { provisionalScore } = subs[0]; + if (!provisionalScore || provisionalScore < 0) { + return 0; + } + return provisionalScore; +} + +/** + * Get final score of submission + * @param submission + */ +export function getFinalScore(submission) { + const { submissions: subs } = submission; + if (!subs || subs.length === 0) { + return 0; + } + const { finalScore } = subs[0]; + if (!finalScore || finalScore < 0) { + return 0; + } + return finalScore; +} + +/** + * Process submissions of MM challenge + * @param submissions the array of submissions + * @param resources the challenge resources + * @param registrants the challenge registrants + */ +export function processMMSubmissions(submissions, resources, registrants) { + const data = {}; + const result = []; + + _.each(submissions, (submission) => { + const { memberId } = submission; + let memberHandle; + const resource = _.find(resources, r => _.get(r, 'userId').toString() === memberId.toString()); + if (_.isEmpty(resource)) { + memberHandle = memberId; + } else { + memberHandle = _.has(resource, 'handle') ? _.get(resource, 'handle') : memberId.toString(); + } + if (!data[memberHandle]) { + data[memberHandle] = []; + } + const validReviews = _.filter(submission.review, + r => !_.isEmpty(r) && (r.typeId !== AV_SCAN_SCORER_REVIEW_TYPE_ID)); + validReviews.sort((a, b) => { + const dateA = new Date(a.created); + const dateB = new Date(b.created); + return dateB - dateA; + }); + let provisionalScore; + if (validReviews.length > 0) { + provisionalScore = _.get(validReviews, '[0].score', 0); + if (_.isString(provisionalScore)) provisionalScore = _.toFinite(provisionalScore); + provisionalScore = toFixed(provisionalScore, 5); + } else { + provisionalScore = -1; + } + + let finalScore = _.get(submission, 'reviewSummation[0].aggregateScore', 0); + if (_.isString(finalScore)) finalScore = _.toFinite(finalScore); + if (finalScore > 0) { + finalScore = toFixed(finalScore, 5); + } else { + finalScore = 0; + } + data[memberHandle].push({ + submissionId: submission.id, + submissionTime: submission.created, + provisionalScore, + finalScore, + }); + }); + + _.each(data, (value, key) => { + result.push({ + submissions: [...value.sort((a, b) => new Date(b.submissionTime) + .getTime() - new Date(a.submissionTime).getTime())], + member: key, + colorStyle: getMMChallengeHandleStyle(key, registrants), + }); + }); + + const { submissions: finalSubmissions, maxFinalScore } = processRanks(result); + finalSubmissions.sort((a, b) => { + if (maxFinalScore === 0) { + return a.provisionalRank - b.provisionalRank; + } + return a.finalRank - b.finalRank; + }); + + return finalSubmissions; +} + +export default undefined; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy