Skip to content

Commit

Permalink
Ux improvements (#152)
Browse files Browse the repository at this point in the history
* Minor modification of submission table.

* Improving evaluation results table and byte pretty print function.

* Fixing bugs in EditGroup form.

* Fixing and improving assignment-exercise sync dialog.
  • Loading branch information
Martin Kruliš committed Dec 19, 2017
1 parent 250283f commit 1864790
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 106 deletions.
199 changes: 124 additions & 75 deletions src/components/Assignments/Assignment/AssignmentSync/AssignmentSync.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,137 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FormattedMessage, FormattedDate, FormattedTime } from 'react-intl';
import { Row, Col, Alert } from 'react-bootstrap';

import Button from '../../../widgets/FlatButton';

const AssignmentSync = ({ syncInfo, exerciseSync }) =>
!syncInfo.exerciseConfig.upToDate ||
!syncInfo.configurationType.upToDate ||
!syncInfo.exerciseEnvironmentConfigs.upToDate ||
!syncInfo.hardwareGroups.upToDate ||
!syncInfo.localizedTexts.upToDate ||
!syncInfo.limits.upToDate ||
!syncInfo.scoreConfig.upToDate ||
!syncInfo.scoreCalculator.upToDate ||
!syncInfo.exerciseTests.upToDate
const syncMessages = {
supplementaryFiles: (
<FormattedMessage
id="app.assignment.syncSupplementaryFiles"
defaultMessage="Supplementary files"
/>
),
attachmentFiles: (
<FormattedMessage
id="app.assignment.syncAttachmentFiles"
defaultMessage="Text attachment files"
/>
),
exerciseTests: (
<FormattedMessage
id="app.assignment.syncExerciseTests"
defaultMessage="Exercise tests"
/>
),
localizedTexts: (
<FormattedMessage
id="app.assignment.syncLocalizedTexts"
defaultMessage="Localized texts"
/>
),
configurationType: (
<FormattedMessage
id="app.assignment.syncConfigurationType"
defaultMessage="Configuration was switched to advanced mode"
/>
),
scoreConfig: (
<FormattedMessage
id="app.assignment.syncScoreConfig"
defaultMessage="Test score configuration"
/>
),
scoreCalculator: (
<FormattedMessage
id="app.assignment.syncScoreCalculator"
defaultMessage="Test score calculator"
/>
),
exerciseConfig: (
<FormattedMessage
id="app.assignment.syncExerciseConfig"
defaultMessage="Exercise configuration"
/>
),
runtimeEnvironments: (
<FormattedMessage
id="app.assignment.syncRuntimeEnvironments"
defaultMessage="Selection of runtime environments"
/>
),
exerciseEnvironmentConfigs: (
<FormattedMessage
id="app.assignment.syncExerciseEnvironmentConfigs"
defaultMessage="Environment configuration"
/>
),
hardwareGroups: (
<FormattedMessage
id="app.assignment.syncHardwareGroups"
defaultMessage="Hardware groups"
/>
),
limits: (
<FormattedMessage id="app.assignment.syncLimits" defaultMessage="Limits" />
)
};

const getSyncMessages = syncInfo => {
const res = [];
for (const field in syncMessages) {
if (!syncInfo[field]) {
console.log('Field ' + field + ' not present.');
continue;
}

if (!syncInfo[field].upToDate) {
res.push(
<li key={field}>
{syncMessages[field]}
</li>
);
}
}
return res;
};

const AssignmentSync = ({ syncInfo, exerciseSync }) => {
const messages = getSyncMessages(syncInfo);
return messages.length > 0
? <Row>
<Col sm={12}>
<Alert bsStyle="warning">
<h4>
<FormattedMessage
id="app.assignment.syncRequired"
defaultMessage="The exercise was updated!"
defaultMessage="The exercise data are newer than assignment data. Exercise was updated {exerciseUpdated}, but the assignment was last updated {assignmentUpdated}!"
values={{
exerciseUpdated: syncInfo.updatedAt.exercise
? <span>
<FormattedDate
value={new Date(syncInfo.updatedAt.exercise * 1000)}
/>
{' ('}
<FormattedTime
value={new Date(syncInfo.updatedAt.exercise * 1000)}
/>
{')'}
</span>
: '??',
assignmentUpdated: syncInfo.updatedAt.assignment
? <span>
<FormattedDate
value={new Date(syncInfo.updatedAt.assignment * 1000)}
/>
{' ('}
<FormattedTime
value={new Date(syncInfo.updatedAt.assignment * 1000)}
/>
{')'}
</span>
: '??'
}}
/>
</h4>
<div>
Expand All @@ -30,69 +140,7 @@ const AssignmentSync = ({ syncInfo, exerciseSync }) =>
defaultMessage="The exercise for this assignment was updated in following categories:"
/>
<ul>
{!syncInfo.exerciseConfig.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncExerciseConfig"
defaultMessage="Exercise configuration"
/>
</li>}
{!syncInfo.configurationType.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncConfigType"
defaultMessage="Type of exercise configuration"
/>
</li>}
{!syncInfo.exerciseEnvironmentConfigs.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncExerciseEnvironmentConfigs"
defaultMessage="Environment configuration"
/>
</li>}
{!syncInfo.hardwareGroups.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncHardwareGroups"
defaultMessage="Hardware groups"
/>
</li>}
{!syncInfo.localizedTexts.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncLocalizedTexts"
defaultMessage="Localized texts"
/>
</li>}
{!syncInfo.limits.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncLimits"
defaultMessage="Limits"
/>
</li>}
{!syncInfo.scoreConfig.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncScoreConfig"
defaultMessage="Score configuration"
/>
</li>}
{!syncInfo.scoreCalculator.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncScoreCalculator"
defaultMessage="Score calculator"
/>
</li>}
{!syncInfo.exerciseTests.upToDate &&
<li>
<FormattedMessage
id="app.assignment.syncExerciseTests"
defaultMessage="Exercise tests"
/>
</li>}
{messages}
</ul>
</div>
<p>
Expand All @@ -107,6 +155,7 @@ const AssignmentSync = ({ syncInfo, exerciseSync }) =>
</Col>
</Row>
: <div />;
};

AssignmentSync.propTypes = {
syncInfo: PropTypes.object.isRequired,
Expand Down
41 changes: 22 additions & 19 deletions src/components/Submissions/TestResultsTable/TestResultsTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import exitCodeMapping from '../../helpers/exitCodeMapping';

const hasValue = value => value !== null;

const tickOrCrossAndRatioOrValue = (isOK, ratio, value, pretty) =>
const tickOrCrossAndRatioOrValue = (isOK, ratio, value, pretty, multiplier) =>
<td
className={classNames({
'text-center': true,
Expand All @@ -30,14 +30,15 @@ const tickOrCrossAndRatioOrValue = (isOK, ratio, value, pretty) =>
maximumFactionDigits={3}
/>}
{hasValue(value) && ') '}
{hasValue(value) && pretty(value * 1000)}
{hasValue(value) && pretty(value * multiplier)}
</small>
</td>;

const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
<Table responsive>
<thead>
<tr>
<th />
<th className="text-center">
<OverlayTrigger
placement="top"
Expand All @@ -55,12 +56,6 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
</span>
</OverlayTrigger>
</th>
<th>
<FormattedMessage
id="app.submissions.testResultsTable.testName"
defaultMessage="Test name"
/>
</th>
<th className="text-center">
<OverlayTrigger
placement="top"
Expand All @@ -73,7 +68,7 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
</Tooltip>
}
>
<Icon name="question" />
<Icon name="balance-scale" />
</OverlayTrigger>
</th>
<th className="text-center">
Expand All @@ -83,12 +78,15 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
<Tooltip id="memoryExceeded">
<FormattedMessage
id="app.submissions.testResultsTable.memoryExceeded"
defaultMessage="Memory limit"
defaultMessage="Measured memory utilization"
/>
</Tooltip>
}
>
<Icon name="microchip" />
<span>
<Icon name="thermometer-half" />&nbsp;&nbsp;
<Icon name="microchip" />
</span>
</OverlayTrigger>
</th>
<th className="text-center">
Expand All @@ -98,12 +96,15 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
<Tooltip id="timeExceeded">
<FormattedMessage
id="app.submissions.testResultsTable.timeExceeded"
defaultMessage="Time limit"
defaultMessage="Measured execution time"
/>
</Tooltip>
}
>
<Icon name="hourglass-end" />
<span>
<Icon name="thermometer-half" />&nbsp;&nbsp;
<Icon name="bolt" />
</span>
</OverlayTrigger>
</th>
<th className="text-center">
Expand All @@ -118,7 +119,7 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
</Tooltip>
}
>
<Icon name="exclamation-circle" />
<Icon name="power-off" />
</OverlayTrigger>
</th>
</tr>
Expand All @@ -140,6 +141,9 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
exitCode
}) =>
<tr key={testName}>
<td>
{testName}
</td>
<td
className={classNames({
'text-center': true,
Expand All @@ -151,9 +155,6 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
<FormattedNumber value={score} style="percent" />
</b>
</td>
<td>
{testName}
</td>
<td className="text-center">
<b>
{status === 'OK' &&
Expand Down Expand Up @@ -184,13 +185,15 @@ const TestResultsTable = ({ results, runtimeEnvironmentId }) =>
memoryExceeded === false,
memoryRatio,
memory,
prettyPrintBytes
prettyPrintBytes,
1024
)}
{tickOrCrossAndRatioOrValue(
timeExceeded === false,
timeRatio,
time,
prettyMs
prettyMs,
1000
)}

<td className="text-center">
Expand Down
3 changes: 3 additions & 0 deletions src/components/forms/EditGroupForm/EditGroupForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ const validate = ({ localizedTexts = [], threshold }) => {
const errors = {};

if (threshold) {
threshold = String(threshold);
const numericThreshold = Number(threshold);
if (threshold !== Math.round(numericThreshold).toString()) {
errors['threshold'] = (
Expand Down Expand Up @@ -254,5 +255,7 @@ const validate = ({ localizedTexts = [], threshold }) => {

export default reduxForm({
form: 'editGroup',
enableReinitialize: true,
keepDirtyOnReinitialize: false,
validate
})(EditGroupForm);
4 changes: 3 additions & 1 deletion src/components/helpers/stringFormatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export function parseBytes(value) {
absValue /= base;
++unit;
}
const rounded = Math.round(absValue * 1000) / 1000;
const intDigits = Math.round(absValue).toString().length;
const fractionBase = intDigits > 3 ? 1 : Math.pow(10, 4 - intDigits);
const rounded = Math.round(absValue * fractionBase) / fractionBase;
return { value: rounded.toString(), unit: units[unit] };
}

Expand Down
Loading

0 comments on commit 1864790

Please sign in to comment.