Skip to content

Commit

Permalink
Updating edit assignment form to pre-initialize the send notification…
Browse files Browse the repository at this point in the history
… checkbox with respect to whether an async job (with the notification) is scheduled or not.
  • Loading branch information
krulis-martin committed Jul 29, 2023
1 parent 9e9943e commit 2ee3498
Show file tree
Hide file tree
Showing 20 changed files with 176 additions and 83 deletions.
55 changes: 29 additions & 26 deletions src/components/forms/EditAssignmentForm/EditAssignmentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,28 +43,31 @@ const sanitizeInputNumber = (value, defValue) => {
* If the object holds `groups` property, it prepares multi-assign form.
*/
export const prepareInitialValues = defaultMemoize(
({
groups = null,
localizedTexts = null,
firstDeadline,
maxPointsBeforeFirstDeadline = 10,
allowSecondDeadline = false,
secondDeadline,
maxPointsBeforeSecondDeadline = 5,
maxPointsDeadlineInterpolation = false,
submissionsCountLimit = 50,
pointsPercentualThreshold = 0,
solutionFilesLimit = null,
solutionSizeLimit = null,
canViewLimitRatios = true,
canViewJudgeStdout = false,
canViewJudgeStderr = false,
isBonus = false,
disabledRuntimeEnvironmentIds = [],
runtimeEnvironmentIds,
isPublic = false,
visibleFrom = null,
}) => ({
(
{
groups = null,
localizedTexts = null,
firstDeadline,
maxPointsBeforeFirstDeadline = 10,
allowSecondDeadline = false,
secondDeadline,
maxPointsBeforeSecondDeadline = 5,
maxPointsDeadlineInterpolation = false,
submissionsCountLimit = 50,
pointsPercentualThreshold = 0,
solutionFilesLimit = null,
solutionSizeLimit = null,
canViewLimitRatios = true,
canViewJudgeStdout = false,
canViewJudgeStderr = false,
isBonus = false,
disabledRuntimeEnvironmentIds = [],
runtimeEnvironmentIds,
isPublic = false,
visibleFrom = null,
},
hasNotificationAsyncJob = false
) => ({
groups,
localizedTexts: localizedTexts && getLocalizedTextsInitialValues(localizedTexts, localizedTextDefaults),
firstDeadline: firstDeadline !== undefined ? moment.unix(firstDeadline) : moment().add(2, 'weeks').endOf('day'),
Expand Down Expand Up @@ -96,7 +99,7 @@ export const prepareInitialValues = defaultMemoize(
),
visibility: isPublic ? (visibleFrom ? 'visibleFrom' : 'visible') : 'hidden',
visibleFrom: visibleFrom ? moment.unix(visibleFrom) : moment().endOf('day'),
sendNotification: true,
sendNotification: !isPublic || (visibleFrom && moment().unix() < visibleFrom && hasNotificationAsyncJob),
deadlines: allowSecondDeadline ? (maxPointsDeadlineInterpolation ? 'interpolated' : 'dual') : 'single',
})
);
Expand Down Expand Up @@ -318,7 +321,7 @@ class EditAssignmentForm extends Component {
deadlines,
runtimeEnvironments,
visibility,
assignmentIsPublic,
showSendNotification,
submitButtonMessages = SUBMIT_BUTTON_MESSAGES_DEFAULT,
mergeJudgeLogs,
intl: { locale },
Expand Down Expand Up @@ -764,7 +767,7 @@ class EditAssignmentForm extends Component {
/>
)}

{visibility !== 'hidden' && (!assignmentIsPublic || visibility === 'visibleFrom') && (
{visibility !== 'hidden' && showSendNotification && (
<Field
name="sendNotification"
component={CheckboxField}
Expand Down Expand Up @@ -859,7 +862,7 @@ EditAssignmentForm.propTypes = {
deadlines: PropTypes.string,
runtimeEnvironments: PropTypes.array,
visibility: PropTypes.string,
assignmentIsPublic: PropTypes.bool,
showSendNotification: PropTypes.bool,
submitButtonMessages: PropTypes.object,
mergeJudgeLogs: PropTypes.bool.isRequired,
intl: PropTypes.object.isRequired,
Expand Down
3 changes: 3 additions & 0 deletions src/components/layout/Page/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Page = ({
windowTitle = null,
icon = null,
resource,
forceLoading = false,
loadingTitle = (
<span>
<LoadingIcon gapRight />
Expand Down Expand Up @@ -49,6 +50,7 @@ const Page = ({
}) => (
<ResourceRenderer
resource={resource}
forceLoading={forceLoading}
loading={<PageContent title={loadingTitle} description={loadingDescription} />}
failed={<PageContent title={failedTitle}>{failedDescription}</PageContent>}>
{(...resources) => (
Expand All @@ -72,6 +74,7 @@ const stringOrFormattedMessage = PropTypes.oneOfType([

Page.propTypes = {
resource: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
forceLoading: PropTypes.bool,
loadingTitle: stringOrFormattedMessage,
loadingDescription: stringOrFormattedMessage,
failedTitle: stringOrFormattedMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ AssignmentNameContainer.propTypes = {
export default withLinks(
connect(
(state, { assignmentId }) => ({
assignment: getAssignment(state)(assignmentId),
assignment: getAssignment(state, assignmentId),
}),
(dispatch, { assignmentId }) => ({
loadAssignmentIfNeeded: () => dispatch(fetchAssignmentIfNeeded(assignmentId)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ DeleteAssignmentButtonContainer.propTypes = {

export default connect(
(state, { id }) => ({
assignment: getAssignment(state)(id),
assignment: getAssignment(state, id),
}),
(dispatch, { id, onDeleted }) => ({
deleteAssignment: () => dispatch(deleteAssignment(id)).then(() => onDeleted && onDeleted()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ ResubmitAllSolutionsContainer.propTypes = {
};

const mapStateToProps = (state, { assignmentId }) => ({
isFetchPending: isResubmitAllFetchPending(assignmentId)(state),
pendingJob: getResubmitAllPendingJob(assignmentId)(state),
failedJob: getResubmitAllFailedJob(assignmentId)(state),
isFetchPending: isResubmitAllFetchPending(state, assignmentId),
pendingJob: getResubmitAllPendingJob(state, assignmentId),
failedJob: getResubmitAllFailedJob(state, assignmentId),
});

const mapDispatchToProps = (dispatch, { assignmentId }) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const mapStateToProps = (state, { id }) => {
const assignmentId = solution && solution.getIn(['data', 'assignmentId']);
return {
solution,
assignment: assignmentId && getAssignment(state)(assignmentId),
assignment: assignmentId && getAssignment(state, assignmentId),
pointsPending: isPointsUpdatePending(state, id),
acceptPending: isSetFlagPending(state, id, 'accepted'),
updatePending: isSolutionReviewUpdatePending(state, id),
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Assignment/Assignment.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export default connect(
) => {
const loggedInUserId = loggedInUserIdSelector(state);
return {
assignment: getAssignment(state)(assignmentId),
assignment: getAssignment(state, assignmentId),
submitting: isSubmitting(state),
runtimeEnvironments: assignmentEnvironmentsSelector(state)(assignmentId),
userId,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/AssignmentSolutions/AssignmentSolutions.js
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ export default withLinks(
},
}
) => {
const assignment = getAssignment(state)(assignmentId);
const assignment = getAssignment(state, assignmentId);
const getStudentsIds = groupId => studentsIdsOfGroup(groupId)(state);
const readyUsers = usersSelector(state).toArray().filter(isReady);

Expand Down
55 changes: 46 additions & 9 deletions src/pages/EditAssignment/EditAssignment.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FormattedMessage } from 'react-intl';
import { Col, Row } from 'react-bootstrap';
import { connect } from 'react-redux';
import { reset, formValueSelector, SubmissionError } from 'redux-form';
import moment from 'moment';

import Page from '../../components/layout/Page';
import { AssignmentNavigation } from '../../components/layout/Navigation';
Expand All @@ -19,8 +20,18 @@ import ResourceRenderer from '../../components/helpers/ResourceRenderer';
import { LocalizedExerciseName } from '../../components/helpers/LocalizedNames';

import { loggedInUserIdSelector } from '../../redux/selectors/auth';
import { fetchAssignment, editAssignment, syncWithExercise, validateAssignment } from '../../redux/modules/assignments';
import { getAssignment } from '../../redux/selectors/assignments';
import {
fetchAssignment,
editAssignment,
syncWithExercise,
validateAssignment,
fetchAssignmentAsyncJobs,
} from '../../redux/modules/assignments';
import {
getAssignment,
getFetchAssignmentAsyncJobsPending,
hasPendingNotificationAsyncJob,
} from '../../redux/selectors/assignments';
import { runtimeEnvironmentsSelector } from '../../redux/selectors/runtimeEnvironments';
import { fetchRuntimeEnvironments } from '../../redux/modules/runtimeEnvironments';
import { ResubmitAllSolutionsContainer } from '../../containers/ResubmitSolutionContainer';
Expand All @@ -29,6 +40,17 @@ import { hasPermissions } from '../../helpers/common';

import withLinks from '../../helpers/withLinks';

const showSendNotification = (assignment, visibility, visibleFrom) => {
const isCurrentlyVisible =
assignment.isPublic && (!assignment.visibleFrom || assignment.visibleFrom * 1000 <= Date.now());
const willBecomeVisible = visibility !== 'hidden';
const willBeVisibleInFuture =
visibility === 'visibleFrom' && moment.isMoment(visibleFrom) && moment().unix() < visibleFrom.unix();

// either transitions from hidden to visible, or is visible now and will be hidden + scheduled for later
return isCurrentlyVisible ? willBeVisibleInFuture : willBecomeVisible;
};

class EditAssignment extends Component {
componentDidMount = () => this.props.loadAsync();

Expand All @@ -40,7 +62,11 @@ class EditAssignment extends Component {
}

static loadAsync = ({ assignmentId }, dispatch) =>
Promise.all([dispatch(fetchAssignment(assignmentId)), dispatch(fetchRuntimeEnvironments())]);
Promise.all([
dispatch(fetchAssignment(assignmentId)),
dispatch(fetchRuntimeEnvironments()),
dispatch(fetchAssignmentAsyncJobs(assignmentId)),
]);

editAssignmentSubmitHandler = formData => {
const { assignment, editAssignment, validateAssignment } = this.props;
Expand Down Expand Up @@ -72,16 +98,20 @@ class EditAssignment extends Component {
},
history: { replace },
assignment,
asyncJobsLoading = false,
hasNotificationAsyncJob = false,
userId,
deadlines,
exerciseSync,
runtimeEnvironments,
visibility,
visibleFrom,
} = this.props;

return (
<Page
resource={assignment}
forceLoading={asyncJobsLoading}
icon={<EditAssignmentIcon />}
title={<FormattedMessage id="app.editAssignment.title" defaultMessage="Edit Assignment Settings" />}>
{assignment =>
Expand Down Expand Up @@ -145,14 +175,14 @@ class EditAssignment extends Component {
form="editAssignment"
userId={userId}
editTexts
initialValues={assignment ? prepareEditFormInitialValues(assignment) : {}}
initialValues={
assignment ? prepareEditFormInitialValues(assignment, hasNotificationAsyncJob) : {}
}
onSubmit={this.editAssignmentSubmitHandler}
deadlines={deadlines}
runtimeEnvironments={envs}
visibility={visibility}
assignmentIsPublic={
assignment.isPublic && (!assignment.visibleFrom || assignment.visibleFrom * 1000 <= Date.now())
}
showSendNotification={showSendNotification(assignment, visibility, visibleFrom)}
mergeJudgeLogs={assignment.mergeJudgeLogs}
/>
)}
Expand Down Expand Up @@ -205,9 +235,12 @@ EditAssignment.propTypes = {
assignment: ImmutablePropTypes.map,
userId: PropTypes.string.isRequired,
runtimeEnvironments: ImmutablePropTypes.map,
asyncJobsLoading: PropTypes.bool,
hasNotificationAsyncJob: PropTypes.bool,
editAssignment: PropTypes.func.isRequired,
deadlines: PropTypes.string,
visibility: PropTypes.string,
visibleFrom: PropTypes.object,
allowVisibleFrom: PropTypes.bool,
exerciseSync: PropTypes.func.isRequired,
validateAssignment: PropTypes.func.isRequired,
Expand All @@ -226,14 +259,17 @@ export default withLinks(
},
}
) => {
const assignment = getAssignment(state)(assignmentId);
const assignment = getAssignment(state, assignmentId);
return {
assignment,
userId: loggedInUserIdSelector(state),
runtimeEnvironments: runtimeEnvironmentsSelector(state),
asyncJobsLoading: getFetchAssignmentAsyncJobsPending(state, assignmentId),
hasNotificationAsyncJob: hasPendingNotificationAsyncJob(state, assignmentId),
deadlines: editAssignmentFormSelector(state, 'deadlines'),
visibility: editAssignmentFormSelector(state, 'visibility'),
allowVisibleFrom: editAssignmentFormSelector(state, 'allowVisibleFrom'),
visibleFrom: editAssignmentFormSelector(state, 'visibleFrom'),
};
},
(
Expand All @@ -246,7 +282,8 @@ export default withLinks(
) => ({
reset: () => dispatch(reset('editAssignment')),
loadAsync: () => EditAssignment.loadAsync({ assignmentId }, dispatch),
editAssignment: data => dispatch(editAssignment(assignmentId, data)),
editAssignment: data =>
dispatch(editAssignment(assignmentId, data)).then(() => dispatch(fetchAssignmentAsyncJobs(assignmentId))),
exerciseSync: () => dispatch(syncWithExercise(assignmentId)),
validateAssignment: version => dispatch(validateAssignment(assignmentId, version)),
})
Expand Down
2 changes: 1 addition & 1 deletion src/pages/ExerciseAssignments/ExerciseAssignments.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class ExerciseAssignments extends Component {
runtimeEnvironments={exercise.runtimeEnvironments}
deadlines={deadlines}
visibility={visibility}
assignmentIsPublic={false}
showSendNotification
submitButtonMessages={SUBMIT_BUTTON_MESSAGES}
defaultIcon={<SaveIcon gapRight />}
mergeJudgeLogs={exercise.mergeJudgeLogs}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Solution/Solution.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ export default connect(
solution: getSolution(state, solutionId),
files: getSolutionFiles(state, solutionId),
userSolutionsSelector: getUserSolutionsSortedData(state),
assignment: getAssignment(state)(assignmentId),
assignment: getAssignment(state, assignmentId),
evaluations: evaluationsForSubmissionSelector(state, solutionId),
runtimeEnvironments: assignmentEnvironmentsSelector(state)(assignmentId),
fetchStatus: fetchManyStatus(solutionId)(state),
Expand Down
2 changes: 1 addition & 1 deletion src/pages/SolutionPlagiarisms/SolutionPlagiarisms.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ export default withLinks(
reviewComments: getSolutionReviewComments(state, solutionId),
fileContentsSelector: getFilesContentSelector(state),
userSolutionsSelector: getUserSolutionsSortedData(state),
assignment: getAssignment(state)(assignmentId),
assignment: getAssignment(state, assignmentId),
plagiarisms: solution ? plagiarismsSelector(state, solution.getIn(['data', 'plagiarism']), solutionId) : null,
};
},
Expand Down
4 changes: 2 additions & 2 deletions src/pages/SolutionSourceCodes/SolutionSourceCodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -735,10 +735,10 @@ export default withLinks(
secondSolutionId && secondSolutionId !== solutionId ? getSolutionFiles(state, secondSolutionId) : null,
fileContentsSelector: getFilesContentSelector(state),
userSolutionsSelector: getUserSolutionsSortedData(state),
assignment: getAssignment(state)(assignmentId),
assignment: getAssignment(state, assignmentId),
secondAssignment:
secondSolution && secondSolution.getIn(['data', 'assignmentId'])
? getAssignment(state)(secondSolution.getIn(['data', 'assignmentId']))
? getAssignment(state, secondSolution.getIn(['data', 'assignmentId']))
: null,
loggedUserId: loggedInUserIdSelector(state),
effectiveRole: getLoggedInUserEffectiveRole(state),
Expand Down
2 changes: 1 addition & 1 deletion src/pages/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export const pathRelatedGroupSelector = (state, urlPath) => {
}

if (matchRes.params.assignmentId) {
const assignment = getAssignment(state)(matchRes.params.assignmentId);
const assignment = getAssignment(state, matchRes.params.assignmentId);
const groupId = assignment && assignment.getIn(['data', 'groupId']);
if (groupId) {
return groupId;
Expand Down

0 comments on commit 2ee3498

Please sign in to comment.