feat: update question parser to preserve text structure (#232)
This commit is contained in:
@@ -24,7 +24,7 @@ exports[`SelectTypeWrapper snapshot 1`] = `
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body
|
||||
classname="pb-6"
|
||||
className="pb-6"
|
||||
>
|
||||
<h1>
|
||||
test child
|
||||
|
||||
@@ -31,7 +31,7 @@ export const SelectTypeWrapper = ({
|
||||
</div>
|
||||
</ModalDialog.Title>
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body classname="pb-6">
|
||||
<ModalDialog.Body className="pb-6">
|
||||
{children}
|
||||
</ModalDialog.Body>
|
||||
<SelectTypeFooter
|
||||
|
||||
@@ -31,15 +31,26 @@ export const nonQuestionKeys = [
|
||||
export class OLXParser {
|
||||
constructor(olxString) {
|
||||
this.problem = {};
|
||||
this.questionData = {};
|
||||
const questionOptions = {
|
||||
ignoreAttributes: false,
|
||||
alwaysCreateTextNode: true,
|
||||
preserveOrder: true,
|
||||
};
|
||||
const options = {
|
||||
ignoreAttributes: false,
|
||||
alwaysCreateTextNode: true,
|
||||
// preserveOrder: true
|
||||
};
|
||||
// There are two versions of the parsed XLM because the question requires the order of the
|
||||
// parsed data to be preserved. However, all the other widgets need the data grouped by
|
||||
// the wrapping tag.
|
||||
const questionParser = new XMLParser(questionOptions);
|
||||
const parser = new XMLParser(options);
|
||||
this.parsedOLX = parser.parse(olxString);
|
||||
this.parsedQuestionOLX = questionParser.parse(olxString);
|
||||
if (_.has(this.parsedOLX, 'problem')) {
|
||||
this.problem = this.parsedOLX.problem;
|
||||
this.questionData = this.parsedQuestionOLX[0].problem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,37 +287,25 @@ export class OLXParser {
|
||||
parseQuestions(problemType) {
|
||||
const options = {
|
||||
ignoreAttributes: false,
|
||||
preserveOrder: true,
|
||||
};
|
||||
const builder = new XMLBuilder(options);
|
||||
const problemObject = _.get(this.problem, problemType);
|
||||
let questionObject = {};
|
||||
/* TODO: How do we uniquely identify the label and description?
|
||||
In order to parse label and description, there should be two states
|
||||
const problemArray = _.get(this.questionData[0], problemType) || this.questionData;
|
||||
/* TODO: How do we uniquely identify the description?
|
||||
In order to parse description, there should be two states
|
||||
and settings should be introduced to edit the label and description.
|
||||
In turn editing the settings update the state and then it can be added to
|
||||
the parsed OLX.
|
||||
*/
|
||||
const tagMap = {
|
||||
description: 'em',
|
||||
};
|
||||
|
||||
/* Only numerical response has different ways to generate OLX, test with
|
||||
numericInputWithFeedbackAndHintsOLXException and numericInputWithFeedbackAndHintsOLX
|
||||
shows the different ways the olx can be generated.
|
||||
*/
|
||||
if (_.isArray(problemObject)) {
|
||||
questionObject = _.omitBy(problemObject[0], (value, key) => _.includes(nonQuestionKeys, key));
|
||||
} else {
|
||||
questionObject = _.omitBy(problemObject, (value, key) => _.includes(nonQuestionKeys, key));
|
||||
}
|
||||
// Check if problem tag itself will have question and descriptions.
|
||||
if (_.isEmpty(questionObject)) {
|
||||
questionObject = _.omitBy(this.problem, (value, key) => _.includes(nonQuestionKeys, key));
|
||||
}
|
||||
const serializedQuestion = _.mapKeys(questionObject, (value, key) => _.get(tagMap, key, key));
|
||||
|
||||
const questionString = builder.build(serializedQuestion);
|
||||
return questionString;
|
||||
const questionArray = [];
|
||||
problemArray.forEach(tag => {
|
||||
const tagName = Object.keys(tag)[0];
|
||||
if (!nonQuestionKeys.includes(tagName)) {
|
||||
questionArray.push(tag);
|
||||
}
|
||||
});
|
||||
const questionString = builder.build(questionArray);
|
||||
return questionString.replace(/<description>/gm, '<em>').replace(/<\/description>/gm, '</em>');
|
||||
}
|
||||
|
||||
getHints() {
|
||||
|
||||
@@ -4,10 +4,21 @@ import { ProblemTypeKeys } from '../../../data/constants/problem';
|
||||
|
||||
class ReactStateOLXParser {
|
||||
constructor(problemState) {
|
||||
const parserOptions = {
|
||||
// const parserOptions = {
|
||||
// ignoreAttributes: false,
|
||||
// alwaysCreateTextNode: true,
|
||||
// };
|
||||
const questionParserOptions = {
|
||||
ignoreAttributes: false,
|
||||
alwaysCreateTextNode: true,
|
||||
// preserveOrder: true
|
||||
preserveOrder: true,
|
||||
};
|
||||
const questionBuilderOptions = {
|
||||
ignoreAttributes: false,
|
||||
attributeNamePrefix: '@_',
|
||||
suppressBooleanAttributes: false,
|
||||
format: true,
|
||||
preserveOrder: true,
|
||||
};
|
||||
const builderOptions = {
|
||||
ignoreAttributes: false,
|
||||
@@ -15,8 +26,10 @@ class ReactStateOLXParser {
|
||||
suppressBooleanAttributes: false,
|
||||
format: true,
|
||||
};
|
||||
this.parser = new XMLParser(parserOptions);
|
||||
this.questionParser = new XMLParser(questionParserOptions);
|
||||
// this.parser = new XMLParser(parserOptions);
|
||||
this.builder = new XMLBuilder(builderOptions);
|
||||
this.questionBuilder = new XMLBuilder(questionBuilderOptions);
|
||||
this.problemState = problemState.problem;
|
||||
}
|
||||
|
||||
@@ -110,7 +123,7 @@ class ReactStateOLXParser {
|
||||
|
||||
addQuestion() {
|
||||
const { question } = this.problemState;
|
||||
const questionObject = this.parser.parse(question);
|
||||
const questionObject = this.questionParser.parse(question);
|
||||
return questionObject;
|
||||
}
|
||||
|
||||
@@ -123,14 +136,33 @@ class ReactStateOLXParser {
|
||||
const problemObject = {
|
||||
problem: {
|
||||
[problemType]: {
|
||||
...question,
|
||||
[widget]: widgetObject,
|
||||
},
|
||||
...demandhint,
|
||||
...solution,
|
||||
},
|
||||
};
|
||||
return this.builder.build(problemObject);
|
||||
|
||||
const problem = this.builder.build(problemObject);
|
||||
const questionString = this.questionBuilder.build(question);
|
||||
let problemTypeTag;
|
||||
switch (problemType) {
|
||||
case ProblemTypeKeys.MULTISELECT:
|
||||
[problemTypeTag] = problem.match(/<choiceresponse>|<choiceresponse.[^>]+>/);
|
||||
break;
|
||||
case ProblemTypeKeys.DROPDOWN:
|
||||
[problemTypeTag] = problem.match(/<optionresponse>|<optionresponse.[^>]+>/);
|
||||
break;
|
||||
case ProblemTypeKeys.SINGLESELECT:
|
||||
[problemTypeTag] = problem.match(/<multiplechoiceresponse>|<multiplechoiceresponse.[^>]+>/);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const updatedString = `${problemTypeTag}\n${questionString}`;
|
||||
const problemString = problem.replace(problemTypeTag, updatedString);
|
||||
|
||||
return problemString;
|
||||
}
|
||||
|
||||
buildTextInput() {
|
||||
@@ -142,14 +174,20 @@ class ReactStateOLXParser {
|
||||
const problemObject = {
|
||||
problem: {
|
||||
[ProblemTypeKeys.TEXTINPUT]: {
|
||||
...question,
|
||||
...answerObject,
|
||||
},
|
||||
...demandhint,
|
||||
...solution,
|
||||
},
|
||||
};
|
||||
return this.builder.build(problemObject);
|
||||
|
||||
const problem = this.builder.build(problemObject);
|
||||
const questionString = this.questionBuilder.build(question);
|
||||
const [problemTypeTag] = problem.match(/<stringresponse>|<stringresponse.[^>]+>/);
|
||||
const updatedString = `${problemTypeTag}\n${questionString}`;
|
||||
const problemString = problem.replace(problemTypeTag, updatedString);
|
||||
|
||||
return problemString;
|
||||
}
|
||||
|
||||
buildTextInputAnswersFeedback() {
|
||||
@@ -200,13 +238,19 @@ class ReactStateOLXParser {
|
||||
|
||||
const problemObject = {
|
||||
problem: {
|
||||
...question,
|
||||
[ProblemTypeKeys.NUMERIC]: answerObject,
|
||||
...demandhint,
|
||||
...solution,
|
||||
},
|
||||
};
|
||||
return this.builder.build(problemObject);
|
||||
|
||||
const problem = this.builder.build(problemObject);
|
||||
const questionString = this.questionBuilder.build(question);
|
||||
const [problemTypeTag] = problem.match(/<numericalresponse>|<numericalresponse.[^>]+>/);
|
||||
const updatedString = `${questionString}\n${problemTypeTag}`;
|
||||
const problemString = problem.replace(problemTypeTag, updatedString);
|
||||
|
||||
return problemString;
|
||||
}
|
||||
|
||||
buildNumericalResponse() {
|
||||
|
||||
@@ -16,48 +16,48 @@ describe('Check React Sate OLXParser problem', () => {
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toBe(checkboxesOLXWithFeedbackAndHintsOLX.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
test('Test dropdown with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(dropdownOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(dropdownOLXWithFeedbackAndHintsOLX.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toEqual(dropdownOLXWithFeedbackAndHintsOLX.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
test('Test string response with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(textInputWithFeedbackAndHintsOLX.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toEqual(textInputWithFeedbackAndHintsOLX.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
test('Test multiple choice with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(multipleChoiceWithFeedbackAndHintsOLX.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toEqual(multipleChoiceWithFeedbackAndHintsOLX.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
test('Test numerical response with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(numericInputWithFeedbackAndHintsOLX.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toEqual(numericInputWithFeedbackAndHintsOLX.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
test('Test numerical response with feedback and hints problem type with exception', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLXException.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(numericInputWithFeedbackAndHintsOLXException.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toEqual(numericInputWithFeedbackAndHintsOLXException.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
test('Test string response with feedback and hints, multiple answers', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.buildOLX);
|
||||
expect(buildOLX.replace(/\s/g, '')).toEqual(textInputWithFeedbackAndHintsOLXWithMultipleAnswers.buildOLX.replace(/\s/g, ''));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,8 +26,8 @@ export const getDataFromOlx = ({ rawOLX, rawSettings }) => {
|
||||
try {
|
||||
olxParser = new OLXParser(rawOLX);
|
||||
parsedProblem = olxParser.getParsedOLXData();
|
||||
} catch {
|
||||
console.error('The Problem Could Not Be Parsed from OLX. redirecting to Advanced editor.');
|
||||
} 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) };
|
||||
}
|
||||
if (parsedProblem?.problemType === ProblemTypeKeys.ADVANCED) {
|
||||
|
||||
Reference in New Issue
Block a user