Skip to content

Commit

Permalink
Implementing comparison of files from different solutions using diff-…
Browse files Browse the repository at this point in the history
…view component.
  • Loading branch information
krulis-martin committed Sep 12, 2022
1 parent 7964edd commit c72fc43
Show file tree
Hide file tree
Showing 14 changed files with 879 additions and 284 deletions.
213 changes: 112 additions & 101 deletions src/components/Assignments/SolutionsTable/SolutionsTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl';
import { Table } from 'react-bootstrap';
import { defaultMemoize } from 'reselect';

import NoSolutionYetTableRow from './NoSolutionYetTableRow';
import SolutionsTableRow from './SolutionsTableRow';
import { LoadingIcon } from '../../icons';
import { EMPTY_ARRAY } from '../../../helpers/common';

import styles from './SolutionsTable.less';

const createHighlightsIndex = defaultMemoize(highlights => new Set(highlights));

const SolutionsTable = ({
assignmentId,
groupId,
Expand All @@ -18,122 +22,128 @@ const SolutionsTable = ({
noteMaxlen = null,
compact = false,
selected = null,
highlights = EMPTY_ARRAY,
assignmentSolver = null,
assignmentSolversLoading = false,
showActionButtons = true,
onSelect = null,
}) => (
<Table responsive className={styles.solutionsTable}>
<thead>
<tr>
<th />
<th />
<th>
<FormattedMessage id="app.solutionsTable.submissionDate" defaultMessage="Date of submission" />
</th>
<th className="text-center">
<FormattedMessage id="app.solutionsTable.solutionValidity" defaultMessage="Validity" />
</th>
<th className="text-center">
<FormattedMessage id="app.solutionsTable.receivedPoints" defaultMessage="Points" />
</th>
<th className="text-center">
<FormattedMessage id="app.solutionsTable.environment" defaultMessage="Target language" />
</th>
}) => {
const highlightsIndex = createHighlightsIndex(highlights);

{!compact && (
return (
<Table responsive className={styles.solutionsTable}>
<thead>
<tr>
<th />
<th />
<th>
<FormattedMessage id="app.solutionsTable.note" defaultMessage="Note" />
<FormattedMessage id="app.solutionsTable.submissionDate" defaultMessage="Date of submission" />
</th>
<th className="text-center">
<FormattedMessage id="app.solutionsTable.solutionValidity" defaultMessage="Validity" />
</th>
<th className="text-center">
<FormattedMessage id="app.solutionsTable.receivedPoints" defaultMessage="Points" />
</th>
<th className="text-center">
<FormattedMessage id="app.solutionsTable.environment" defaultMessage="Target language" />
</th>
)}

{(!compact || showActionButtons) && (
<td className="text-right text-muted small">
{assignmentSolversLoading ? (
<LoadingIcon />
) : (
<>
{assignmentSolver &&
(assignmentSolver.get('lastAttemptIndex') > 5 ||
assignmentSolver.get('lastAttemptIndex') > solutions.size) && (
<>
{!compact && (
<FormattedMessage
id="app.solutionsTable.attemptsCount"
defaultMessage="Solutions submitted: {count}"
values={{ count: assignmentSolver.get('lastAttemptIndex') }}
/>
)}
{!compact && (
<th>
<FormattedMessage id="app.solutionsTable.note" defaultMessage="Note" />
</th>
)}

{assignmentSolver.get('lastAttemptIndex') > solutions.size && (
<span>
{!compact && <>&nbsp;&nbsp;</>}(
{(!compact || showActionButtons) && (
<td className="text-right text-muted small">
{assignmentSolversLoading ? (
<LoadingIcon />
) : (
<>
{assignmentSolver &&
(assignmentSolver.get('lastAttemptIndex') > 5 ||
assignmentSolver.get('lastAttemptIndex') > solutions.size) && (
<>
{!compact && (
<FormattedMessage
id="app.solutionsTable.attemptsDeleted"
defaultMessage="{deleted} deleted"
values={{ deleted: assignmentSolver.get('lastAttemptIndex') - solutions.size }}
id="app.solutionsTable.attemptsCount"
defaultMessage="Solutions submitted: {count}"
values={{ count: assignmentSolver.get('lastAttemptIndex') }}
/>
)
</span>
)}
</>
)}

{assignmentSolver.get('lastAttemptIndex') > solutions.size && (
<span>
{!compact && <>&nbsp;&nbsp;</>}(
<FormattedMessage
id="app.solutionsTable.attemptsDeleted"
defaultMessage="{deleted} deleted"
values={{ deleted: assignmentSolver.get('lastAttemptIndex') - solutions.size }}
/>
)
</span>
)}
</>
)}

{!compact && !assignmentSolver && solutions.size > 5 && (
<FormattedMessage
id="app.solutionsTable.rowsCount"
defaultMessage="Total records: {count}"
values={{ count: solutions.size }}
/>
)}
</>
)}
</td>
)}
</tr>
</thead>
{solutions.size === 0 ? (
<NoSolutionYetTableRow />
) : (
solutions.map((data, idx) => {
if (!data) {
return (
<tbody key={idx}>
<tr>
<td colSpan={compact ? 6 : 7} className="text-center">
<LoadingIcon size="xs" />
</td>
</tr>
</tbody>
);
}

const id = data.id;
const runtimeEnvironment =
data.runtimeEnvironmentId &&
runtimeEnvironments &&
runtimeEnvironments.find(({ id }) => id === data.runtimeEnvironmentId);

{!compact && !assignmentSolver && solutions.size > 5 && (
<FormattedMessage
id="app.solutionsTable.rowsCount"
defaultMessage="Total records: {count}"
values={{ count: solutions.size }}
/>
)}
</>
)}
</td>
)}
</tr>
</thead>
{solutions.size === 0 ? (
<NoSolutionYetTableRow />
) : (
solutions.map((data, idx) => {
if (!data) {
return (
<tbody key={idx}>
<tr>
<td colSpan={compact ? 6 : 7} className="text-center">
<LoadingIcon size="xs" />
</td>
</tr>
</tbody>
<SolutionsTableRow
key={id}
id={id}
status={data.lastSubmission ? data.lastSubmission.evaluationStatus : null}
runtimeEnvironment={runtimeEnvironment}
assignmentId={assignmentId}
groupId={groupId}
noteMaxlen={noteMaxlen}
compact={compact}
selected={id === selected}
highlighted={highlightsIndex.has(id)}
showActionButtons={showActionButtons}
onSelect={onSelect}
{...data}
/>
);
}

const id = data.id;
const runtimeEnvironment =
data.runtimeEnvironmentId &&
runtimeEnvironments &&
runtimeEnvironments.find(({ id }) => id === data.runtimeEnvironmentId);

return (
<SolutionsTableRow
key={id}
id={id}
status={data.lastSubmission ? data.lastSubmission.evaluationStatus : null}
runtimeEnvironment={runtimeEnvironment}
assignmentId={assignmentId}
groupId={groupId}
noteMaxlen={noteMaxlen}
compact={compact}
selected={id === selected}
showActionButtons={showActionButtons}
onSelect={onSelect}
{...data}
/>
);
})
)}
</Table>
);
})
)}
</Table>
);
};

SolutionsTable.propTypes = {
assignmentId: PropTypes.string.isRequired,
Expand All @@ -143,6 +153,7 @@ SolutionsTable.propTypes = {
noteMaxlen: PropTypes.number,
compact: PropTypes.bool,
selected: PropTypes.string,
highlights: PropTypes.array,
assignmentSolver: ImmutablePropTypes.map,
assignmentSolversLoading: PropTypes.bool,
showActionButtons: PropTypes.bool,
Expand Down
48 changes: 34 additions & 14 deletions src/components/Assignments/SolutionsTable/SolutionsTableRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import DeleteSolutionButtonContainer from '../../../containers/DeleteSolutionBut
import AcceptSolutionContainer from '../../../containers/AcceptSolutionContainer';
import ReviewSolutionContainer from '../../../containers/ReviewSolutionContainer';

import { DetailIcon } from '../../icons';
import { DetailIcon, CodeFileIcon } from '../../icons';
import DateTime from '../../widgets/DateTime';
import OptionalTooltipWrapper from '../../widgets/OptionalTooltipWrapper';
import Button, { TheButtonGroup } from '../../widgets/TheButton';
Expand Down Expand Up @@ -43,9 +43,10 @@ const SolutionsTableRow = ({
noteMaxlen = null,
compact = false,
selected = false,
highlighted = false,
showActionButtons = true,
onSelect = null,
links: { SOLUTION_DETAIL_URI_FACTORY },
links: { SOLUTION_DETAIL_URI_FACTORY, SOLUTION_SOURCE_CODES_URI_FACTORY },
intl: { locale },
}) => {
const trimmedNote = note && note.trim();
Expand All @@ -63,7 +64,11 @@ const SolutionsTableRow = ({
return (
<tbody>
<tr
className={selected ? 'table-active' : onSelect ? 'clickable' : ''}
className={classnames({
'table-primary': selected,
clickable: !selected && onSelect,
'table-warning': highlighted,
})}
onClick={!selected && onSelect ? () => onSelect(id) : null}>
<td className="text-nowrap valign-middle text-bold">{attemptIndex}.</td>

Expand Down Expand Up @@ -132,17 +137,31 @@ const SolutionsTableRow = ({
rowSpan={splitOnTwoLines ? 2 : 1}>
<TheButtonGroup>
{permissionHints && permissionHints.viewDetail && (
<OptionalTooltipWrapper
tooltip={<FormattedMessage id="generic.detail" defaultMessage="Detail" />}
hide={!compact}
tooltipId={`detail-${id}`}>
<Link to={SOLUTION_DETAIL_URI_FACTORY(assignmentId, id)}>
<Button size="xs" variant="secondary" disabled={selected}>
<DetailIcon gapRight={!compact} />
{!compact && <FormattedMessage id="generic.detail" defaultMessage="Detail" />}
</Button>
</Link>
</OptionalTooltipWrapper>
<>
<OptionalTooltipWrapper
tooltip={<FormattedMessage id="generic.detail" defaultMessage="Detail" />}
hide={!compact}
tooltipId={`detail-${id}`}>
<Link to={SOLUTION_DETAIL_URI_FACTORY(assignmentId, id)}>
<Button size="xs" variant="secondary" disabled={selected}>
<DetailIcon gapRight={!compact} />
{!compact && <FormattedMessage id="generic.detail" defaultMessage="Detail" />}
</Button>
</Link>
</OptionalTooltipWrapper>

<OptionalTooltipWrapper
tooltip={<FormattedMessage id="app.navigation.solutionFiles" defaultMessage="Submitted Files" />}
hide={!compact}
tooltipId={`codes-${id}`}>
<Link to={SOLUTION_SOURCE_CODES_URI_FACTORY(assignmentId, id)}>
<Button size="xs" variant="primary" disabled={selected}>
<CodeFileIcon fixedWidth gapRight={!compact} />
{!compact && <FormattedMessage id="generic.files" defaultMessage="Files" />}
</Button>
</Link>
</OptionalTooltipWrapper>
</>
)}

{permissionHints && permissionHints.setFlag && (
Expand Down Expand Up @@ -207,6 +226,7 @@ SolutionsTableRow.propTypes = {
noteMaxlen: PropTypes.number,
compact: PropTypes.bool.isRequired,
selected: PropTypes.bool,
highlighted: PropTypes.bool,
showActionButtons: PropTypes.bool,
onSelect: PropTypes.func,
links: PropTypes.object,
Expand Down
Loading

0 comments on commit c72fc43

Please sign in to comment.