Index: lams_central/src/java/org/lamsfoundation/lams/web/qb/ImsQtiController.java =================================================================== diff -u -r594d93694800975ab84a9b61190aa8a65a057077 -r126d6740a7a106bc9dbb91f4ea6a4c54be636858 --- lams_central/src/java/org/lamsfoundation/lams/web/qb/ImsQtiController.java (.../ImsQtiController.java) (revision 594d93694800975ab84a9b61190aa8a65a057077) +++ lams_central/src/java/org/lamsfoundation/lams/web/qb/ImsQtiController.java (.../ImsQtiController.java) (revision 126d6740a7a106bc9dbb91f4ea6a4c54be636858) @@ -9,6 +9,7 @@ import java.util.TreeSet; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -129,13 +130,12 @@ qbQuestion.setVersion(1); int questionMark = 1; + boolean isMultipleChoice = Question.QUESTION_TYPE_MULTIPLE_CHOICE.equals(question.getType()); + boolean isMarkHedgingType = Question.QUESTION_TYPE_MARK_HEDGING.equals(question.getType()); + boolean isVsaType = Question.QUESTION_TYPE_FILL_IN_BLANK.contentEquals(question.getType()); // options are different depending on the type - if (Question.QUESTION_TYPE_MULTIPLE_CHOICE.equals(question.getType()) - || Question.QUESTION_TYPE_FILL_IN_BLANK.equals(question.getType()) - || Question.QUESTION_TYPE_MARK_HEDGING.equals(question.getType())) { - boolean isMultipleChoice = Question.QUESTION_TYPE_MULTIPLE_CHOICE.equals(question.getType()); - boolean isMarkHedgingType = Question.QUESTION_TYPE_MARK_HEDGING.equals(question.getType()); + if (isMultipleChoice || isMarkHedgingType || isVsaType) { // setting answers is very similar in both types, so they were put together here if (isMarkHedgingType) { @@ -147,7 +147,7 @@ qbQuestion.setShuffle(false); qbQuestion.setPrefixAnswersWithLetters(false); - } else { + } else if (isVsaType) { qbQuestion.setType(QbQuestion.TYPE_VERY_SHORT_ANSWERS); qbQuestion.setCaseSensitive(false); } @@ -164,6 +164,11 @@ continue; } QbOption option = new QbOption(); + if (isVsaType) { + // convert comma-separated answers to ones accepted by QB VSA questions + answerText = Stream.of(answerText.split(",")).map(String::strip) + .collect(Collectors.joining("\r\n")); + } option.setName(answerText); option.setDisplayOrder(orderId++); option.setFeedback(answer.getFeedback()); @@ -293,56 +298,6 @@ qbQuestion.setType(QbQuestion.TYPE_ESSAY); qbQuestion.setAllowRichEditor(false); - } else if (Question.QUESTION_TYPE_ESSAY.equals(question.getType())) { - qbQuestion.setType(QbQuestion.TYPE_MULTIPLE_CHOICE); - qbQuestion.setShuffle(false); - qbQuestion.setPrefixAnswersWithLetters(false); - - String correctAnswer = null; - if (question.getAnswers() != null) { - TreeSet optionList = new TreeSet<>(); - int orderId = 1; - for (Answer answer : question.getAnswers()) { - String answerText = QuestionParser.processHTMLField(answer.getText(), false, contentFolderID, - question.getResourcesFolderPath()); - if ((correctAnswer != null) && correctAnswer.equals(answerText)) { - log.warn("Skipping an answer with same text as the correct answer: " + answerText); - continue; - } - QbOption option = new QbOption(); - option.setName(answerText); - option.setDisplayOrder(orderId++); - option.setFeedback(answer.getFeedback()); - option.setQbQuestion(qbQuestion); - - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - // for fill in blanks question all answers are correct and get full mark - if (correctAnswer == null) { - // whatever the correct answer holds, it becomes the question score - questionMark = Double.valueOf(Math.ceil(answer.getScore())).intValue(); - // 100% goes to the correct answer - option.setMaxMark(1); - correctAnswer = answerText; - } else { - log.warn("Choosing only first correct answer, despite another one was found: " - + answerText); - option.setMaxMark(0); - } - } else { - option.setMaxMark(0); - } - - optionList.add(option); - } - - qbQuestion.setQbOptions(new ArrayList<>(optionList)); - } - - if (correctAnswer == null) { - log.warn("No correct answer found for question: " + question.getText()); - continue; - } - } else { log.warn("Unknow QTI question type: " + question.getType()); continue; Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionWordParser.java =================================================================== diff -u -r52f835246c157f47eed9df1d47f9e7bb0c16315d -r126d6740a7a106bc9dbb91f4ea6a4c54be636858 --- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionWordParser.java (.../QuestionWordParser.java) (revision 52f835246c157f47eed9df1d47f9e7bb0c16315d) +++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionWordParser.java (.../QuestionWordParser.java) (revision 126d6740a7a106bc9dbb91f4ea6a4c54be636858) @@ -63,6 +63,8 @@ private final static String QUESTION_TAG = "question:"; private final static String ANSWER_TAG = "answer:"; + private final static String CORRECT_TAG = "correct:"; + private final static String INCORRECT_TAG = "incorrect:"; private final static String FEEDBACK_TAG = "feedback:"; private final static String LEARNING_OUTCOME_TAG = "lo:"; private static final String CUSTOM_IMAGE_TAG_REGEX = "\\[IMAGE: .*?]"; @@ -144,6 +146,8 @@ String description = null; String feedback = null; List answers = new ArrayList<>(); + Answer correctVsaAnswer = null; + Answer incorrectVsaAnswer = null; List learningOutcomes = new ArrayList<>(); boolean optionsStarted = false; @@ -177,11 +181,16 @@ continue; } - // check if correct answer line is found + // check if MCQ answer line is found if (text.startsWith(ANSWER_TAG)) { + answerTagFound = true; + if (correctVsaAnswer != null || incorrectVsaAnswer != null) { + // question is malformed, display an error after this loop + break; + } + optionsStarted = true; feedbackStarted = false; - answerTagFound = true; String correctAnswerLetters = text.replaceAll("(?i)" + ANSWER_TAG, "").replaceAll("\\s", ""); String[] correctAnswersTable = correctAnswerLetters.split(","); @@ -202,6 +211,29 @@ continue; } + // check if VSA answer line is found + if (text.startsWith(CORRECT_TAG) || text.startsWith(INCORRECT_TAG)) { + optionsStarted = true; + feedbackStarted = false; + + String vsaAnswers = WebUtil.removeHTMLtags(formattedText).replaceAll("(?i)" + CORRECT_TAG, "") + .replaceAll("(?i)" + INCORRECT_TAG, "").strip(); + + Answer answer = new Answer(); + answer.setText(vsaAnswers); + // there are only two answers, correct one and incorrect one + if (text.startsWith(CORRECT_TAG)) { + answer.setDisplayOrder(1); + answer.setScore(1F); + correctVsaAnswer = answer; + } else { + answer.setDisplayOrder(2); + incorrectVsaAnswer = answer; + } + + continue; + } + if (text.startsWith(LEARNING_OUTCOME_TAG)) { optionsStarted = true; feedbackStarted = false; @@ -257,8 +289,22 @@ continue; } + if (answerTagFound && (correctVsaAnswer != null || incorrectVsaAnswer != null)) { + log.error( + "ANSWER tag found, but also CORRECT and/or INCORRECT tag found. Can not categorise the question as MCQ or VSA."); + continue; + } + Question question = new Question(); - if (answers.isEmpty()) { + if (correctVsaAnswer != null) { + if (!QuestionParser.isQuestionTypeAcceptable(Question.QUESTION_TYPE_FILL_IN_BLANK, limitType, + question)) { + continue; + } + answers.add(correctVsaAnswer); + answers.add(incorrectVsaAnswer); + question.setAnswers(answers); + } else if (answers.isEmpty()) { if (!QuestionParser.isQuestionTypeAcceptable(Question.QUESTION_TYPE_ESSAY, limitType, question)) { continue; }