From 8bf3bda682da447d50543bc4e18ccf0a7a47300b Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Wed, 10 Jan 2018 01:06:54 +0100 Subject: [PATCH 1/6] Add resubmit (debug mode) button to reference solution. --- .../ReferenceSolution/ReferenceSolution.js | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/pages/ReferenceSolution/ReferenceSolution.js b/src/pages/ReferenceSolution/ReferenceSolution.js index 296bdb8fa..55bb64952 100644 --- a/src/pages/ReferenceSolution/ReferenceSolution.js +++ b/src/pages/ReferenceSolution/ReferenceSolution.js @@ -59,6 +59,7 @@ class ReferenceSolution extends Component { params: { exerciseId, referenceSolutionId }, refreshSolutionEvaluations, evaluateReferenceSolution, + evaluateReferenceSolutionInDebugMode, intl: { formatMessage }, links: { EXERCISES_URI, EXERCISE_URI_FACTORY } } = this.props; @@ -166,6 +167,22 @@ class ReferenceSolution extends Component { defaultMessage="Resubmit" /> } + {permissionHints && + permissionHints.evaluate !== false && + }

@@ -202,6 +219,7 @@ ReferenceSolution.propTypes = { loadAsync: PropTypes.func.isRequired, refreshSolutionEvaluations: PropTypes.func.isRequired, evaluateReferenceSolution: PropTypes.func.isRequired, + evaluateReferenceSolutionInDebugMode: PropTypes.func.isRequired, referenceSolutions: ImmutablePropTypes.map, intl: intlShape.isRequired, links: PropTypes.object.isRequired @@ -219,7 +237,9 @@ export default withLinks( dispatch(fetchReferenceSolutions(params.exerciseId)); }, evaluateReferenceSolution: () => - dispatch(evaluateReferenceSolution(params.referenceSolutionId)) + dispatch(evaluateReferenceSolution(params.referenceSolutionId)), + evaluateReferenceSolutionInDebugMode: () => + dispatch(evaluateReferenceSolution(params.referenceSolutionId, true)) }) )(ReferenceSolution) ) From 9e8151c9f638c1f7bcc46b897c36da5e7fcd2410 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Wed, 10 Jan 2018 01:32:08 +0100 Subject: [PATCH 2/6] Improving simple user container appearance. --- .../UsersNameContainer/UsersNameContainer.css | 18 ++++++++++++++++-- .../UsersNameContainer/UsersNameContainer.js | 5 +++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/containers/UsersNameContainer/UsersNameContainer.css b/src/containers/UsersNameContainer/UsersNameContainer.css index c28d7074b..001cc26ca 100644 --- a/src/containers/UsersNameContainer/UsersNameContainer.css +++ b/src/containers/UsersNameContainer/UsersNameContainer.css @@ -1,5 +1,19 @@ .simpleName { - margin-left: 4px; - margin-right: 4px; white-space: nowrap; } + +.simpleName:after { + content: ', '; +} + +.simpleName:first-of-type { + margin-left: 2px; +} + +.simpleName:last-of-type { + margin-right: 3px; +} + +.simpleName:last-of-type:after { + content: ''; +} diff --git a/src/containers/UsersNameContainer/UsersNameContainer.js b/src/containers/UsersNameContainer/UsersNameContainer.js index 13a62e0ad..ab27d5ed1 100644 --- a/src/containers/UsersNameContainer/UsersNameContainer.js +++ b/src/containers/UsersNameContainer/UsersNameContainer.js @@ -11,6 +11,7 @@ import UsersName, { LoadingUsersName, FailedUsersName } from '../../components/Users/UsersName'; +import { LoadingIcon, FailedIcon } from '../../components/icons'; import './UsersNameContainer.css'; @@ -35,8 +36,8 @@ class UsersNameContainer extends Component { return ( } - failed={} + loading={isSimple ? : } + failed={isSimple ? : } > {user => isSimple From 76072522e6ed938b04db5a772d5da77525336cf3 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Wed, 10 Jan 2018 01:56:19 +0100 Subject: [PATCH 3/6] Fixing minor bug on Exercises page. --- src/pages/Exercises/Exercises.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pages/Exercises/Exercises.js b/src/pages/Exercises/Exercises.js index 05d206a76..988331d46 100644 --- a/src/pages/Exercises/Exercises.js +++ b/src/pages/Exercises/Exercises.js @@ -129,22 +129,22 @@ class Exercises extends Component { /> } footer={ - isSuperAdmin &&

- + {isSuperAdmin && + }

} unlimitedHeight From 28c844946ee7225b7d55515bee6df91e4c8651b1 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Wed, 10 Jan 2018 15:32:36 +0100 Subject: [PATCH 4/6] Make last name in group hierarchy panel also a link --- src/components/Groups/HierarchyLine/HierarchyLine.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Groups/HierarchyLine/HierarchyLine.js b/src/components/Groups/HierarchyLine/HierarchyLine.js index 53f70b2ef..9e5cb6eef 100644 --- a/src/components/Groups/HierarchyLine/HierarchyLine.js +++ b/src/components/Groups/HierarchyLine/HierarchyLine.js @@ -1,12 +1,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Well } from 'react-bootstrap'; +import Icon from 'react-fontawesome'; import GroupsNameContainer from '../../../containers/GroupsNameContainer'; import './HierarchyLine.css'; const HierarchyLine = ({ groupId, parentGroupsIds }) => + {parentGroupsIds.map( (groupId, i) => i !== 0 && @@ -15,7 +17,7 @@ const HierarchyLine = ({ groupId, parentGroupsIds }) => / )} - + ; HierarchyLine.propTypes = { From 039816e1ce9ecaadb6c9b642092c58302279aace Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Fri, 12 Jan 2018 01:33:32 +0100 Subject: [PATCH 5/6] Fixing bud in simple config edit form. Change of environments now correctly resubmits config form as well. --- .../EditExerciseSimpleConfigForm.js | 96 ++++++++++--------- .../EditSimpleLimitsForm.js | 40 ++++---- src/helpers/exerciseSimpleForm.js | 72 +++++++------- .../EditExerciseSimpleConfig.js | 70 ++++++++------ 4 files changed, 154 insertions(+), 124 deletions(-) diff --git a/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js b/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js index 9d19c27cc..2bfa740ad 100644 --- a/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js +++ b/src/components/forms/EditExerciseSimpleConfigForm/EditExerciseSimpleConfigForm.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { reduxForm, getFormValues } from 'redux-form'; import { connect } from 'react-redux'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import { Alert } from 'react-bootstrap'; import FormBox from '../../widgets/FormBox'; @@ -30,7 +30,8 @@ const EditExerciseSimpleConfigForm = ({ formErrors, supplementaryFiles, exerciseTests, - smartFill + smartFill, + intl: { locale } }) => {(...files) =>
- {exerciseTests.map((test, idx) => - - )} + {exerciseTests + .sort((a, b) => a.name.localeCompare(b.name, locale)) + .map((test, idx) => + + )}
}
; @@ -142,7 +145,8 @@ EditExerciseSimpleConfigForm.propTypes = { formErrors: PropTypes.object, supplementaryFiles: ImmutablePropTypes.map, exerciseTests: PropTypes.array, - smartFill: PropTypes.func.isRequired + smartFill: PropTypes.func.isRequired, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; const FORM_NAME = 'editExerciseSimpleConfig'; @@ -193,32 +197,34 @@ const validate = formData => { : undefined; }; -export default connect( - (state, { exercise }) => { - const getSupplementaryFilesForExercise = createGetSupplementaryFiles( - exercise.supplementaryFilesIds - ); - return { - supplementaryFiles: getSupplementaryFilesForExercise(state), - formValues: getFormValues(FORM_NAME)(state), - formErrors: exerciseConfigFormErrors(state, FORM_NAME) - }; - }, - dispatch => ({ - smartFill: (testId, tests, files) => () => - dispatch(smartFillExerciseConfigForm(FORM_NAME, testId, tests, files)) - }) -)( - reduxForm({ - form: FORM_NAME, - enableReinitialize: true, - keepDirtyOnReinitialize: false, - immutableProps: [ - 'formValues', - 'supplementaryFiles', - 'exerciseTests', - 'handleSubmit' - ], - validate - })(EditExerciseSimpleConfigForm) +export default injectIntl( + connect( + (state, { exercise }) => { + const getSupplementaryFilesForExercise = createGetSupplementaryFiles( + exercise.supplementaryFilesIds + ); + return { + supplementaryFiles: getSupplementaryFilesForExercise(state), + formValues: getFormValues(FORM_NAME)(state), + formErrors: exerciseConfigFormErrors(state, FORM_NAME) + }; + }, + dispatch => ({ + smartFill: (testId, tests, files) => () => + dispatch(smartFillExerciseConfigForm(FORM_NAME, testId, tests, files)) + }) + )( + reduxForm({ + form: FORM_NAME, + enableReinitialize: true, + keepDirtyOnReinitialize: false, + immutableProps: [ + 'formValues', + 'supplementaryFiles', + 'exerciseTests', + 'handleSubmit' + ], + validate + })(EditExerciseSimpleConfigForm) + ) ); diff --git a/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js b/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js index c560f8fe9..4cb229df7 100644 --- a/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js +++ b/src/components/forms/EditSimpleLimitsForm/EditSimpleLimitsForm.js @@ -1,7 +1,7 @@ import React from 'react'; import { Alert, Table } from 'react-bootstrap'; import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, injectIntl } from 'react-intl'; import { reduxForm } from 'redux-form'; import { EditSimpleLimitsField } from '../Fields'; @@ -31,7 +31,8 @@ const EditSimpleLimitsForm = ({ submitting, submitFailed, submitSucceeded, - invalid + invalid, + intl: { locale } }) => - {tests.map(test => + {tests.sort((a, b) => a.name.localeCompare(b.name, locale)).map(test => {test.name} @@ -181,7 +182,8 @@ EditSimpleLimitsForm.propTypes = { submitting: PropTypes.bool, submitFailed: PropTypes.bool, submitSucceeded: PropTypes.bool, - invalid: PropTypes.bool + invalid: PropTypes.bool, + intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; const validate = ({ limits }) => { @@ -232,17 +234,19 @@ const validate = ({ limits }) => { return errors; }; -export default reduxForm({ - form: 'editSimpleLimits', - enableReinitialize: true, - keepDirtyOnReinitialize: false, - immutableProps: [ - 'environments', - 'tests', - 'cloneHorizontally', - 'cloneVertically', - 'cloneAll', - 'handleSubmit' - ], - validate -})(EditSimpleLimitsForm); +export default injectIntl( + reduxForm({ + form: 'editSimpleLimits', + enableReinitialize: true, + keepDirtyOnReinitialize: false, + immutableProps: [ + 'environments', + 'tests', + 'cloneHorizontally', + 'cloneVertically', + 'cloneAll', + 'handleSubmit' + ], + validate + })(EditSimpleLimitsForm) +); diff --git a/src/helpers/exerciseSimpleForm.js b/src/helpers/exerciseSimpleForm.js index 23783d8cf..1a4ef97c4 100644 --- a/src/helpers/exerciseSimpleForm.js +++ b/src/helpers/exerciseSimpleForm.js @@ -5,35 +5,9 @@ import { encodeEnvironmentId } from '../redux/modules/simpleLimits'; -export const getEnvInitValues = environmentConfigs => { - let res = {}; - for (const env of environmentConfigs) { - res[env.runtimeEnvironmentId] = true; - } - return res; -}; - -export const transformAndSendEnvValues = ( - formData, - environments, - editEnvironmentConfigs, - reloadConfigAndLimits -) => { - let res = []; - for (const env in formData) { - if (formData[env] !== true && formData[env] !== 'true') { - continue; - } - let envObj = { runtimeEnvironmentId: env }; - const currentFullEnv = environments.find(e => e.id === env); - envObj.variablesTable = currentFullEnv.defaultVariables; - res.push(envObj); - } - return editEnvironmentConfigs({ environmentConfigs: res }).then( - reloadConfigAndLimits - ); -}; - +/* + * Tests and Score + */ export const getTestsInitValues = (exerciseTests, scoreConfig, locale) => { const jsonScoreConfig = yaml.safeLoad(scoreConfig); const testWeights = jsonScoreConfig.testWeights || {}; @@ -81,7 +55,39 @@ export const transformAndSendTestsValues = ( ]); }; -export const getSimpleConfigInitValues = (config, tests, locale) => { +/* + * Environments + */ +export const getEnvInitValues = environmentConfigs => { + let res = {}; + for (const env of environmentConfigs) { + res[env.runtimeEnvironmentId] = true; + } + return res; +}; + +export const transformEnvValues = ( + formData, + environments, + editEnvironmentConfigs +) => { + let res = []; + for (const env in formData) { + if (formData[env] !== true && formData[env] !== 'true') { + continue; + } + let envObj = { runtimeEnvironmentId: env }; + const currentFullEnv = environments.find(e => e.id === env); + envObj.variablesTable = currentFullEnv.defaultVariables; + res.push(envObj); + } + return res; +}; + +/* + * Configuration variables + */ +export const getSimpleConfigInitValues = (config, tests) => { const confTests = tests && config[0] && config[0].tests ? config[0].tests : []; @@ -191,6 +197,7 @@ export const transformAndSendConfigValues = ( pipelines, environments, tests, + originalConfig, setConfig ) => { let testVars = []; @@ -305,9 +312,8 @@ export const transformAndSendConfigValues = ( return setConfig({ config: envs }); }; -/** - * Assemble data from all simpleLimits environments into one table. - * Also ensures proper encoding for environment IDs and test names, which are used as keys. +/* + * Memory and Time limits */ export const getLimitsInitValues = ( limits, diff --git a/src/pages/EditExerciseSimpleConfig/EditExerciseSimpleConfig.js b/src/pages/EditExerciseSimpleConfig/EditExerciseSimpleConfig.js index 0c537c2fb..b92da1500 100644 --- a/src/pages/EditExerciseSimpleConfig/EditExerciseSimpleConfig.js +++ b/src/pages/EditExerciseSimpleConfig/EditExerciseSimpleConfig.js @@ -62,7 +62,7 @@ import { pipelinesSelector } from '../../redux/selectors/pipelines'; import { getEnvInitValues, - transformAndSendEnvValues, + transformEnvValues, getTestsInitValues, transformAndSendTestsValues, getSimpleConfigInitValues, @@ -213,22 +213,42 @@ class EditExerciseSimpleConfig extends Component { > - {(environmentConfigs, ...environments) => - - transformAndSendEnvValues( - data, - environments, - editEnvironmentConfigs, - reloadConfigAndLimits(exercise.id) - )} - />} + {(config, tests, environmentConfigs, ...pipelines) => + + {(...environments) => + { + const newEnvironments = transformEnvValues( + data, + environments + ); + return editEnvironmentConfigs({ + environmentConfigs: newEnvironments + }) + .then(() => + transformAndSendConfigValues( + getSimpleConfigInitValues(config, tests), + pipelines, + newEnvironments, + tests, + config, + setConfig + ) + ) + .then(reloadConfigAndLimits(exercise.id)); + }} + />} + } @@ -248,25 +268,22 @@ class EditExerciseSimpleConfig extends Component { ...pipelines.toArray() ]} > - {(config, tests, environments, ...pipelines) => { - const sortedTests = tests.sort((a, b) => - a.name.localeCompare(b.name, locale) - ); - return tests.length > 0 + {(config, tests, environments, ...pipelines) => + tests.length > 0 ? transformAndSendConfigValues( data, pipelines, environments, - sortedTests, + tests, + config, setConfig )} /> @@ -282,8 +299,7 @@ class EditExerciseSimpleConfig extends Component { id="app.editExerciseSimpleConfig.noTests" defaultMessage="There are no tests yet. The form cannot be displayed until at least one test is created." /> - ; - }} + } @@ -309,9 +325,7 @@ class EditExerciseSimpleConfig extends Component { editEnvironmentSimpleLimits )} environments={exercise.runtimeEnvironments} - tests={tests.sort((a, b) => - a.name.localeCompare(b.name, locale) - )} + tests={tests} initialValues={getLimitsInitValues( limits, tests, From f00e9f86c59a4203a63d0ba09f1a91ffef71c081 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Sat, 13 Jan 2018 01:46:18 +0100 Subject: [PATCH 6/6] Refactoring of exerciseSimpleForm functions. Making sure that the configuration which is not handled by simple form is not lost when form is saved. --- src/helpers/exerciseSimpleForm.js | 333 ++++++++++++++++-------------- 1 file changed, 173 insertions(+), 160 deletions(-) diff --git a/src/helpers/exerciseSimpleForm.js b/src/helpers/exerciseSimpleForm.js index 1a4ef97c4..ac993f830 100644 --- a/src/helpers/exerciseSimpleForm.js +++ b/src/helpers/exerciseSimpleForm.js @@ -87,6 +87,77 @@ export const transformEnvValues = ( /* * Configuration variables */ + +// Configure simple variables using mapping between name and testObj property. +// Object has the same properites as testObj and values are variable names. +const simpleConfigVariablesMapping = { + expectedOutput: 'expected-output', + runArgs: 'run-args', + outputFile: 'actual-output', + stdinFile: 'stdin-file', + judgeBinary: 'judge-type', + customJudgeBinary: 'custom-judge', + judgeArgs: 'judge-args' +}; + +// Variable types as they should be sent back to API +const simpleConfigVariablesTypes = { + 'expected-output': 'remote-file', + 'run-args': 'string[]', + 'actual-output': 'file', + 'stdin-file': 'remote-file', + 'judge-type': 'string', + 'custom-judge': 'remote-file', + 'judge-args': 'string[]', + 'input-files': 'remote-file[]', + 'actual-inputs': 'file[]' +}; + +// Fetch one simple variable (string or array) and fill it into the testObj under selected property. +const getSimpleConfigSimpleVariable = ( + variables, + testObj, + variableName, + propertyName +) => { + const variable = variables.find(variable => variable.name === variableName); + const isArray = variable && variable.type && variable.type.endsWith('[]'); + if (isArray) { + testObj[propertyName] = []; // array needs default + } + if (variable) { + let value = variable.value; + if (isArray && !value) { + value = []; + } else if (!isArray) { + value = value.trim(); + } + testObj[propertyName] = isArray && !Array.isArray(value) ? [value] : value; + } +}; + +// Get input files and their corresponding actual names (and fill them to testObj). +const getSimpleConfigInputFiles = (variables, testObj) => { + const inputFiles = variables.find( + variable => variable.name === 'input-files' + ); + const actualInputs = variables.find( + variable => variable.name === 'actual-inputs' + ); + if (inputFiles) { + testObj.inputFiles = inputFiles.value + ? inputFiles.value.map((value, i) => ({ + file: value, + name: + actualInputs && actualInputs.value && actualInputs.value[i] + ? actualInputs.value[i].trim() + : '' + })) + : []; + } +}; + +// Prepare the initial form data for configuration form ... export const getSimpleConfigInitValues = (config, tests) => { const confTests = tests && config[0] && config[0].tests ? config[0].tests : []; @@ -104,92 +175,82 @@ export const getSimpleConfigInitValues = (config, tests) => { ) : []; - const inputFiles = variables.find( - variable => variable.name === 'input-files' - ); - const actualInputs = variables.find( - variable => variable.name === 'actual-inputs' - ); - if (inputFiles) { - testObj.inputFiles = inputFiles.value - ? inputFiles.value.map((value, i) => ({ - file: value, - name: - actualInputs && actualInputs.value && actualInputs.value[i] - ? actualInputs.value[i].trim() - : '' - })) - : []; - } + // Fetch values from variables and fill them to testObj. + getSimpleConfigInputFiles(variables, testObj); - const expectedOutput = variables.find( - variable => variable.name === 'expected-output' - ); - if (expectedOutput) { - testObj.expectedOutput = expectedOutput.value; + for (const property in simpleConfigVariablesMapping) { + getSimpleConfigSimpleVariable( + variables, + testObj, + simpleConfigVariablesMapping[property], + property + ); } - const runArgs = variables.find(variable => variable.name === 'run-args'); - testObj.runArgs = []; - if (runArgs && runArgs.value) { - testObj.runArgs = Array.isArray(runArgs.value) - ? runArgs.value - : [runArgs.value]; + // Additional updates after simple variables were set + testObj.useOutFile = testObj.outputFile !== ''; + testObj.useCustomJudge = testObj.customJudgeBinary !== ''; + if (testObj.useCustomJudge) { + testObj.judgeBinary = ''; + } else if (!testObj.judgeBinary) { + testObj.judgeBinary = 'recodex-judge-normal'; } - const actualOutput = variables.find( - variable => variable.name === 'actual-output' - ); - if (actualOutput && actualOutput.value && actualOutput.value.trim()) { - testObj.useOutFile = true; - testObj.outputFile = actualOutput.value.trim(); - } else { - testObj.useOutFile = false; - testObj.outputFile = ''; - } + res[encodeTestId(test.id)] = testObj; + } - const stdinFile = variables.find( - variable => variable.name === 'stdin-file' - ); - if (stdinFile) { - testObj.inputStdin = stdinFile.value; - } + return { config: res }; +}; - const standardJudge = variables.find( - variable => variable.name === 'judge-type' - ); - const customJudge = variables.find( - variable => variable.name === 'custom-judge' - ); +// Prepare one variable to be sent in to the API +const transformConfigSimpleVariable = (variables, name, value) => { + variables.push({ name, type: simpleConfigVariablesTypes[name], value }); +}; - testObj.useCustomJudge = false; - testObj.customJudgeBinary = ''; - testObj.judgeBinary = ''; - if (customJudge && customJudge.value) { - testObj.customJudgeBinary = customJudge.value; - testObj.useCustomJudge = true; - } - if (!testObj.useCustomJudge) { - testObj.judgeBinary = - standardJudge && standardJudge.value - ? standardJudge.value - : 'recodex-judge-normal'; - } +const transformConfigInputFiles = (variables, test) => { + let inputFiles = []; + let renamed = []; + const inFilesArr = + test.inputFiles && Array.isArray(test.inputFiles) ? test.inputFiles : []; - const judgeArgs = variables.find( - variable => variable.name === 'judge-args' - ); - testObj.judgeArgs = []; - if (judgeArgs && judgeArgs.value) { - testObj.judgeArgs = Array.isArray(judgeArgs.value) - ? judgeArgs.value - : [judgeArgs.value]; - } + for (const item of inFilesArr) { + inputFiles.push(item.file); + renamed.push(item.name.trim()); + } - res[encodeTestId(test.id)] = testObj; + transformConfigSimpleVariable(variables, 'input-files', inputFiles); + transformConfigSimpleVariable(variables, 'actual-inputs', renamed); +}; + +// Prepare variables for execution pipeline of one test in one environment +const transformConfigTestExecutionVariables = test => { + // Final updates ... + if (!test.useCustomJudge) { + test.customJudgeBinary = ''; + } + test.outputFile = test.useOutFile ? test.outputFile.trim() : ''; + + // Prepare variables for the config + let variables = []; + for (const property in simpleConfigVariablesMapping) { + transformConfigSimpleVariable( + variables, + simpleConfigVariablesMapping[property], + test[property] + ); } - return { config: res }; + transformConfigInputFiles(variables, test); + + return variables; +}; + +const mergeOriginalVariables = (newVars, origVars) => { + origVars.forEach(ov => { + if (!newVars.find(nv => nv.name === ov.name)) { + newVars.push(ov); // add missing variable + } + }); }; export const transformAndSendConfigValues = ( @@ -200,76 +261,6 @@ export const transformAndSendConfigValues = ( originalConfig, setConfig ) => { - let testVars = []; - for (let t of tests) { - const testName = t.id; - const test = formData.config[encodeTestId(testName)]; - let variables = []; - - variables.push({ - name: 'custom-judge', - type: 'remote-file', - value: test.useCustomJudge ? test.customJudgeBinary : '' - }); - variables.push({ - name: 'expected-output', - type: 'remote-file', - value: test.expectedOutput - }); - variables.push({ - name: 'judge-type', - type: 'string', - value: test.judgeBinary - }); - variables.push({ - name: 'stdin-file', - type: 'remote-file', - value: test.inputStdin - }); - variables.push({ - name: 'judge-args', - type: 'string[]', - value: test.judgeArgs - }); - variables.push({ - name: 'run-args', - type: 'string[]', - value: test.runArgs - }); - if (test.useOutFile) { - variables.push({ - name: 'actual-output', - type: 'file', - value: test.useOutFile ? test.outputFile.trim() : '' - }); - } - - let inputFiles = []; - let renamedNames = []; - const inFilesArr = - test.inputFiles && Array.isArray(test.inputFiles) ? test.inputFiles : []; - for (const item of inFilesArr) { - inputFiles.push(item.file); - renamedNames.push(item.name.trim()); - } - variables.push({ - name: 'input-files', - type: 'remote-file[]', - value: inputFiles - }); - variables.push({ - name: 'actual-inputs', - type: 'file[]', - value: renamedNames - }); - - testVars.push({ - name: testName, - variables: variables, - producesFiles: test.useOutFile - }); - } - let envs = []; for (const environment of environments) { const envId = environment.runtimeEnvironmentId; @@ -277,28 +268,50 @@ export const transformAndSendConfigValues = ( pipeline => pipeline.runtimeEnvironmentIds.indexOf(envId) >= 0 ); + // Find the right pipelines ... + const compilationPipeline = envPipelines.find( + pipeline => pipeline.parameters.isCompilationPipeline + ); + const executionPipelineStdout = envPipelines.find( + pipeline => + pipeline.parameters.isExecutionPipeline && + pipeline.parameters.producesStdout + ); + const executionPipelineFiles = envPipelines.find( + pipeline => + pipeline.parameters.isExecutionPipeline && + pipeline.parameters.producesFiles + ); + + // Create configuration for all tests ... let testsCfg = []; - for (const testVar of testVars) { - const compilationPipelineId = envPipelines.filter( - pipeline => pipeline.parameters.isCompilationPipeline - )[0].id; - const executionPipelineId = envPipelines.filter( - pipeline => - pipeline.parameters.isExecutionPipeline && - (testVar.producesFiles - ? pipeline.parameters.producesFiles - : pipeline.parameters.producesStdout) - )[0].id; + for (const t of tests) { + const testName = t.id; + const test = formData.config[encodeTestId(testName)]; + const executionPipeline = test.useOutFile + ? executionPipelineFiles + : executionPipelineStdout; + + const testVars = transformConfigTestExecutionVariables(test); + + // Get original values so they wont get lost ... + const originalPipelines = originalConfig + .find(config => config.name === envId) // config for the right environment + .tests.find(test => test.name === testName).pipelines; // and the right test + const origCompilationVars = originalPipelines[0].variables; // the first pipeline is for compilation + const origExecutionVars = originalPipelines[1].variables; // the second pipeline is for execution + mergeOriginalVariables(testVars, origExecutionVars); + testsCfg.push({ - name: testVar.name, + name: testName, pipelines: [ { - name: compilationPipelineId, - variables: [] + name: compilationPipeline.id, + variables: origCompilationVars // copied from original config }, { - name: executionPipelineId, - variables: testVar.variables + name: executionPipeline.id, + variables: testVars // TODO merge with original config } ] });