fix: make max attempts setting fallback to default

The max attempts setting for a problem xblock should be set to null for
course default max attempt setting to take effect. This makes sure that
xblock setting is updated if course default is updated.
This commit is contained in:
Navin Karkera
2023-10-06 20:35:51 +05:30
parent 564d724d5b
commit c70679da54
7 changed files with 35 additions and 26 deletions

View File

@@ -115,29 +115,29 @@ export const scoringCardHooks = (scoring, updateSettings, defaultValue) => {
const isUnlimited = event.target.checked;
if (isUnlimited) {
setAttemptDisplayValue('');
updateSettings({ scoring: { ...scoring, attempts: { number: '', unlimited: true } } });
updateSettings({ scoring: { ...scoring, attempts: { number: null, unlimited: true } } });
} else {
setAttemptDisplayValue(`${defaultValue} (Default)`);
updateSettings({ scoring: { ...scoring, attempts: { number: defaultValue, unlimited: false } } });
updateSettings({ scoring: { ...scoring, attempts: { number: null, unlimited: false } } });
}
};
const handleMaxAttemptChange = (event) => {
let unlimitedAttempts = false;
let attemptNumber = parseInt(event.target.value);
const { value } = event.target;
// TODO: impove below condition handling
if (_.isNaN(attemptNumber)) {
if (value === '') {
attemptNumber = defaultValue;
if (value === '' && !_.isNil(defaultValue)) {
attemptNumber = null;
setAttemptDisplayValue(`${defaultValue} (Default)`);
} else {
attemptNumber = '';
} else if (_.isNil(defaultValue)) {
attemptNumber = null;
unlimitedAttempts = true;
}
} else if (attemptNumber <= 0) {
attemptNumber = 0;
} else if (attemptNumber === defaultValue) {
const attemptNumberStr = value.replace(' (Default)');
attemptNumber = parseInt(attemptNumberStr);
attemptNumber = null;
}
updateSettings({ scoring: { ...scoring, attempts: { number: attemptNumber, unlimited: unlimitedAttempts } } });
};

View File

@@ -1,4 +1,5 @@
import React from 'react';
import _ from 'lodash-es';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -71,6 +72,7 @@ export const ScoringCard = ({
className="mt-3 decoration-control-label"
checked={scoring.attempts.unlimited}
onChange={handleUnlimitedChange}
disabled={!_.isNil(defaultValue)}
>
<div className="x-small">
<FormattedMessage {...messages.unlimitedAttemptsCheckboxLabel} />

View File

@@ -25,7 +25,7 @@ export const onSelect = ({ selected, updateField, setBlockTitle }) => () => {
setBlockTitle(AdvanceProblems[selected].title);
} else {
const newOLX = ProblemTypes[selected].template;
const { settings, ...newState } = getDataFromOlx({ rawOLX: newOLX, rawSettings: {} });
const { settings, ...newState } = getDataFromOlx({ rawOLX: newOLX, rawSettings: {}, defaultSettings: {} });
updateField({ ...newState });
setBlockTitle(ProblemTypes[selected].title);
}

View File

@@ -19,7 +19,7 @@ class ReactStateSettingsParser {
let settings = {};
const stateSettings = this.problem.settings;
settings = popuplateItem(settings, 'number', 'max_attempts', stateSettings.scoring.attempts);
settings = popuplateItem(settings, 'number', 'max_attempts', stateSettings.scoring.attempts, true);
settings = popuplateItem(settings, 'weight', 'weight', stateSettings.scoring);
settings = popuplateItem(settings, 'on', 'showanswer', stateSettings.showAnswer);
settings = popuplateItem(settings, 'afterAttempts', 'attempts_before_showanswer_button', stateSettings.showAnswer);

View File

@@ -2,21 +2,31 @@ import _ from 'lodash-es';
import { ShowAnswerTypes, RandomizationTypesKeys } from '../../../data/constants/problem';
export const popuplateItem = (parentObject, itemName, statekey, metadata) => {
export const popuplateItem = (parentObject, itemName, statekey, metadata, allowNull=false) => {
let parent = parentObject;
const item = _.get(metadata, itemName, null);
if (!_.isNil(item)) {
if (!_.isNil(item) || allowNull) {
parent = { ...parentObject, [statekey]: item };
}
return parent;
};
export const parseScoringSettings = (metadata) => {
export const parseScoringSettings = (metadata, defaultSettings) => {
let scoring = {};
let attempts = popuplateItem({}, 'max_attempts', 'number', metadata);
if (_.isEmpty(attempts) || _.isNaN(attempts.number)) {
// TODO: impove below condition handling
if ((_.isEmpty(attempts) || _.isNaN(attempts.number)) && _.isNaN(defaultSettings.max_attempts)) {
attempts = { unlimited: true, number: '' };
} else if (!_.isEmpty(attempts) && !_.isNaN(attempts.number)) {
attempts.unlimited = false;
attempts.number = attempts.number;
if (attempts.number < 0) {
attempts.number = 0;
}
} else if (!_.isNaN(defaultSettings.max_attempts)) {
attempts.unlimited = false;
attempts.number = defaultSettings.max_attempts;
} else {
attempts.unlimited = false;
if (attempts.number < 0) {
@@ -43,14 +53,14 @@ export const parseShowAnswer = (metadata) => {
return showAnswer;
};
export const parseSettings = (metadata) => {
export const parseSettings = (metadata, defaultSettings) => {
let settings = {};
if (_.isNil(metadata) || _.isEmpty(metadata)) {
return settings;
}
const scoring = parseScoringSettings(metadata);
const scoring = parseScoringSettings(metadata, defaultSettings);
if (!_.isEmpty(scoring)) {
settings = { ...settings, scoring };
}

View File

@@ -21,8 +21,8 @@ const initialState = {
scoring: {
weight: 1,
attempts: {
unlimited: true,
number: '',
unlimited: false,
number: null,
},
},
hints: [],
@@ -210,9 +210,6 @@ const problem = createSlice({
setEnableTypeSelection: (state, { payload }) => {
const { maxAttempts, showanswer, showResetButton } = payload;
const attempts = { number: maxAttempts, unlimited: false };
if (!maxAttempts) {
attempts.unlimited = true;
}
return {
...state,
settings: {

View File

@@ -25,7 +25,7 @@ export const isBlankProblem = ({ rawOLX }) => {
return false;
};
export const getDataFromOlx = ({ rawOLX, rawSettings }) => {
export const getDataFromOlx = ({ rawOLX, rawSettings, defaultSettings }) => {
let olxParser;
let parsedProblem;
try {
@@ -33,13 +33,13 @@ export const getDataFromOlx = ({ rawOLX, rawSettings }) => {
parsedProblem = olxParser.getParsedOLXData();
} catch (error) {
console.error('The Problem Could Not Be Parsed from OLX. redirecting to Advanced editor.', error);
return { problemType: ProblemTypeKeys.ADVANCED, rawOLX, settings: parseSettings(rawSettings) };
return { problemType: ProblemTypeKeys.ADVANCED, rawOLX, settings: parseSettings(rawSettings, defaultSettings) };
}
if (parsedProblem?.problemType === ProblemTypeKeys.ADVANCED) {
return { problemType: ProblemTypeKeys.ADVANCED, rawOLX, settings: parseSettings(rawSettings) };
return { problemType: ProblemTypeKeys.ADVANCED, rawOLX, settings: parseSettings(rawSettings, defaultSettings) };
}
const { settings, ...data } = parsedProblem;
const parsedSettings = { ...settings, ...parseSettings(rawSettings) };
const parsedSettings = { ...settings, ...parseSettings(rawSettings, defaultSettings) };
if (!_.isEmpty(rawOLX) && !_.isEmpty(data)) {
return { ...data, rawOLX, settings: parsedSettings };
}
@@ -50,7 +50,7 @@ export const loadProblem = ({ rawOLX, rawSettings, defaultSettings }) => (dispat
if (isBlankProblem({ rawOLX })) {
dispatch(actions.problem.setEnableTypeSelection(camelizeKeys(defaultSettings)));
} else {
dispatch(actions.problem.load(getDataFromOlx({ rawOLX, rawSettings })));
dispatch(actions.problem.load(getDataFromOlx({ rawOLX, rawSettings, defaultSettings })));
}
};