Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java =================================================================== diff -u -r14f0555e4fe91e89ebaa9c9e7d29f0666e681437 -r5aabc63ac1eb20bb16e7b586981129b3208323e3 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 14f0555e4fe91e89ebaa9c9e7d29f0666e681437) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 5aabc63ac1eb20bb16e7b586981129b3208323e3) @@ -37,6 +37,7 @@ import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionResult; import org.lamsfoundation.lams.tool.assessment.model.AssessmentResult; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; +import org.lamsfoundation.lams.tool.assessment.util.AssessmentEscapeUtils; import org.lamsfoundation.lams.usermanagement.User; import org.springframework.stereotype.Repository; @@ -152,14 +153,17 @@ + " AS q, " + AssessmentResult.class.getName() + " AS r " + " WHERE q.assessmentResult.uid = r.uid AND q.qbToolQuestion.qbQuestion.uid =:qbQuestionUid AND (r.finishDate != null) "; if (StringUtils.isNotBlank(answer)) { - FIND_BY_QBQUESTION_AND_FINISHED += "AND TRIM(q.answer) = TRIM(:answer) "; + + FIND_BY_QBQUESTION_AND_FINISHED += "AND REGEXP_REPLACE(q.answer, '" + + AssessmentEscapeUtils.VSA_ANSWER_NORMALISE_SQL_REG_EXP + "', '') = :answer"; } FIND_BY_QBQUESTION_AND_FINISHED += "ORDER BY r.startDate ASC"; Query q = getSession().createQuery(FIND_BY_QBQUESTION_AND_FINISHED, AssessmentResult.class); q.setParameter("qbQuestionUid", qbQuestionUid); if (StringUtils.isNotBlank(answer)) { - q.setParameter("answer", answer); + String normalisedAnswer = AssessmentEscapeUtils.normaliseVSAnswer(answer); + q.setParameter("answer", normalisedAnswer); } return q.list(); } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r06b79ec91753e0d1195e70ea75b516e8c11a936f -r5aabc63ac1eb20bb16e7b586981129b3208323e3 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 06b79ec91753e0d1195e70ea75b516e8c11a936f) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 5aabc63ac1eb20bb16e7b586981129b3208323e3) @@ -28,7 +28,6 @@ import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; @@ -787,47 +786,35 @@ } } else if (questionDto.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS) { + //clear previous answer questionResult.setQbOption(null); - for (OptionDTO optionDto : questionDto.getOptionDtos()) { - String[] optionAnswers = optionDto.getName().strip().split("\\r\\n"); - boolean isAnswerMatchedCurrentOption = false; - for (String optionAnswer : optionAnswers) { - optionAnswer = optionAnswer.strip(); + if (questionDto.getAnswer() != null) { + boolean isQuestionCaseSensitive = questionDto.isCaseSensitive(); + String normalisedQuestionAnswer = AssessmentEscapeUtils.normaliseVSAnswer(questionDto.getAnswer()); - //prepare regex which takes into account only * special character - String regexWithOnlyAsteriskSymbolActive = "\\Q"; - for (int i = 0; i < optionAnswer.length(); i++) { - //everything in between \\Q and \\E are taken literally no matter which characters it contains - if (optionAnswer.charAt(i) == '*') { - regexWithOnlyAsteriskSymbolActive += "\\E.*\\Q"; - } else { - regexWithOnlyAsteriskSymbolActive += optionAnswer.charAt(i); + for (OptionDTO optionDto : questionDto.getOptionDtos()) { + Collection optionAnswers = AssessmentEscapeUtils.normaliseVSOption(optionDto.getName()); + boolean isAnswerMatchedCurrentOption = false; + for (String optionAnswer : optionAnswers) { + String normalisedOptionAnswer = AssessmentEscapeUtils.normaliseVSAnswer(optionAnswer); + + // check is item unraveled + if (isQuestionCaseSensitive ? normalisedQuestionAnswer.equals(normalisedOptionAnswer) + : normalisedQuestionAnswer.equalsIgnoreCase(normalisedOptionAnswer)) { + isAnswerMatchedCurrentOption = true; + break; } } - regexWithOnlyAsteriskSymbolActive += "\\E"; - //check whether answer matches regex - Pattern pattern; - if (questionDto.isCaseSensitive()) { - pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive); - } else { - pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive, - java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.UNICODE_CASE); - } - if (questionDto.getAnswer() != null && pattern.matcher(questionDto.getAnswer().strip()).matches()) { - isAnswerMatchedCurrentOption = true; + if (isAnswerMatchedCurrentOption) { + mark = optionDto.getMaxMark() * maxMark; + QbOption qbOption = qbService.getOptionByUid(optionDto.getUid()); + questionResult.setQbOption(qbOption); break; } } - - if (isAnswerMatchedCurrentOption) { - mark = optionDto.getMaxMark() * maxMark; - QbOption qbOption = qbService.getOptionByUid(optionDto.getUid()); - questionResult.setQbOption(qbOption); - break; - } } } else if (questionDto.getType() == QbQuestion.TYPE_NUMERICAL) { @@ -1395,14 +1382,20 @@ } boolean isAnswerAllocated = false; + boolean isQuestionCaseSensitive = question.getQbQuestion().isCaseSensitive(); + String normalisedAnswer = AssessmentEscapeUtils.normaliseVSAnswer(answer); for (QbOption option : qbQuestion.getQbOptions()) { - String[] alternatives = option.getName().split("\r\n"); + Collection alternatives = AssessmentEscapeUtils.normaliseVSOption(option.getName()); for (String alternative : alternatives) { - if (AssessmentServiceImpl.isAnswersEqual(question, answer, alternative)) { + if (isQuestionCaseSensitive ? normalisedAnswer.equals(alternative) + : normalisedAnswer.equalsIgnoreCase(alternative)) { isAnswerAllocated = true; break; } } + if (isAnswerAllocated) { + break; + } } if (!isAnswerAllocated) { @@ -1423,23 +1416,27 @@ if (answer1 == null || answer2 == null) { return false; } + String normalisedAnswer1 = AssessmentEscapeUtils.normaliseVSAnswer(answer1); + String normalisedAnswer2 = AssessmentEscapeUtils.normaliseVSAnswer(answer2); - return question.getQbQuestion().isCaseSensitive() ? answer1.equals(answer2) : answer1.equalsIgnoreCase(answer2); + return question.getQbQuestion().isCaseSensitive() ? normalisedAnswer1.equals(normalisedAnswer2) + : normalisedAnswer1.equalsIgnoreCase(normalisedAnswer2); } @Override public Long allocateAnswerToOption(Long questionUid, Long targetOptionUid, Long previousOptionUid, String answer) { AssessmentQuestion assessmentQuestion = assessmentQuestionDao.getByUid(questionUid); QbQuestion qbQuestion = assessmentQuestion.getQbQuestion(); + String normalisedAnswer = AssessmentEscapeUtils.normaliseVSAnswer(answer); //adding if (previousOptionUid.equals(-1L)) { //search for duplicates and, if found, return false QbOption targetOption = null; for (QbOption option : qbQuestion.getQbOptions()) { String name = option.getName(); - String[] alternatives = name.split("\r\n"); - if (Arrays.asList(alternatives).contains(answer)) { + Collection alternatives = AssessmentEscapeUtils.normaliseVSOption(name); + if (alternatives.contains(normalisedAnswer)) { return option.getUid(); } if (option.getUid().equals(targetOptionUid)) { @@ -1464,11 +1461,12 @@ for (QbOption previousOption : qbQuestion.getQbOptions()) { if (previousOption.getUid().equals(previousOptionUid)) { String name = previousOption.getName(); - String[] alternatives = name.split("\r\n"); + String[] alternatives = name.split(","); StringBuilder nameWithoutUserAnswer = new StringBuilder(); for (String alternative : alternatives) { - if (!alternative.equals(answer)) { + String normalisedAlternative = AssessmentEscapeUtils.normaliseVSAnswer(alternative); + if (!normalisedAlternative.equals(normalisedAnswer)) { nameWithoutUserAnswer.append(alternative).append("\r\n"); } } @@ -1496,11 +1494,12 @@ for (QbOption previousOption : qbQuestion.getQbOptions()) { if (previousOption.getUid().equals(previousOptionUid)) { String name = previousOption.getName(); - String[] alternatives = name.split("\r\n"); + String[] alternatives = name.split(","); StringBuilder nameWithoutUserAnswer = new StringBuilder(); for (String alternative : alternatives) { - if (!alternative.equals(answer)) { + String normalisedAlternative = AssessmentEscapeUtils.normaliseVSAnswer(alternative); + if (!normalisedAlternative.equals(normalisedAnswer)) { nameWithoutUserAnswer.append(alternative).append("\r\n"); } } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/AssessmentEscapeUtils.java =================================================================== diff -u -r456feb2acb9bf40e61af9a8f304ed807f7a43bad -r5aabc63ac1eb20bb16e7b586981129b3208323e3 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/AssessmentEscapeUtils.java (.../AssessmentEscapeUtils.java) (revision 456feb2acb9bf40e61af9a8f304ed807f7a43bad) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/AssessmentEscapeUtils.java (.../AssessmentEscapeUtils.java) (revision 5aabc63ac1eb20bb16e7b586981129b3208323e3) @@ -24,6 +24,8 @@ import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; @@ -40,6 +42,9 @@ public class AssessmentEscapeUtils { + public static final String VSA_ANSWER_NORMALISE_JAVA_REG_EXP = "\\W"; + public static final String VSA_ANSWER_NORMALISE_SQL_REG_EXP = "[^[:alpha:][:alnum:]_]"; + public static class AssessmentExcelCell { public Object value; public boolean isHighlighted; @@ -361,4 +366,13 @@ return new AssessmentExcelCell(StringUtils.isBlank(ret) ? null : ret, highlightCell); } + + public static String normaliseVSAnswer(String answer) { + return answer == null ? null : answer.replaceAll(VSA_ANSWER_NORMALISE_JAVA_REG_EXP, ""); + } + + public static Set normaliseVSOption(String option) { + return Stream.of(option.split("\r\n")).collect( + Collectors.mapping(answer -> AssessmentEscapeUtils.normaliseVSAnswer(answer), Collectors.toSet())); + } } \ No newline at end of file Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/dao/hibernate/ScratchieSessionDAOHibernate.java =================================================================== diff -u -r2db58f73e88ecef930d486c60cc53fb3fa483415 -r5aabc63ac1eb20bb16e7b586981129b3208323e3 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/dao/hibernate/ScratchieSessionDAOHibernate.java (.../ScratchieSessionDAOHibernate.java) (revision 2db58f73e88ecef930d486c60cc53fb3fa483415) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/dao/hibernate/ScratchieSessionDAOHibernate.java (.../ScratchieSessionDAOHibernate.java) (revision 5aabc63ac1eb20bb16e7b586981129b3208323e3) @@ -37,6 +37,7 @@ import org.lamsfoundation.lams.tool.scratchie.model.ScratchieAnswerVisitLog; import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem; import org.lamsfoundation.lams.tool.scratchie.model.ScratchieSession; +import org.lamsfoundation.lams.tool.scratchie.service.IScratchieService; import org.lamsfoundation.lams.tool.scratchie.util.ScratchieSessionComparator; import org.springframework.stereotype.Repository; @@ -116,11 +117,13 @@ + ScratchieItem.class.getName() + " AS item, " + ScratchieSession.class.getName() + " AS session, " + ScratchieAnswerVisitLog.class.getName() + " AS visitLog WHERE session.scratchie.uid = item.scratchieUid AND item.qbQuestion.uid =:qbQuestionUid" - + " AND session.sessionId = visitLog.sessionId AND TRIM(visitLog.answer) = TRIM(:answer)"; + + " AND session.sessionId = visitLog.sessionId AND REGEXP_REPLACE(visitLog.answer, '" + + IScratchieService.VSA_ANSWER_NORMALISE_SQL_REG_EXP + "', '') = :answer"; Query q = getSession().createQuery(FIND_BY_QBQUESTION_AND_FINISHED, Long.class); q.setParameter("qbQuestionUid", qbQuestionUid); - q.setParameter("answer", answer); + String normalisedAnswer = answer.replaceAll(IScratchieService.VSA_ANSWER_NORMALISE_JAVA_REG_EXP, ""); + q.setParameter("answer", normalisedAnswer); return q.list(); } } \ No newline at end of file Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/IScratchieService.java =================================================================== diff -u -ra05bb8ff92c659cc340b037de664fd0d9b103c96 -r5aabc63ac1eb20bb16e7b586981129b3208323e3 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/IScratchieService.java (.../IScratchieService.java) (revision a05bb8ff92c659cc340b037de664fd0d9b103c96) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/IScratchieService.java (.../IScratchieService.java) (revision 5aabc63ac1eb20bb16e7b586981129b3208323e3) @@ -56,6 +56,9 @@ */ public interface IScratchieService extends ICommonToolService { + public static final String VSA_ANSWER_NORMALISE_JAVA_REG_EXP = "\\W"; + public static final String VSA_ANSWER_NORMALISE_SQL_REG_EXP = "[^[:alpha:][:alnum:]_]"; + /** * Get Scratchie by toolContentID. * Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java =================================================================== diff -u -rff37a5fb4bf9a24adde51ee67eb408f0790e128f -r5aabc63ac1eb20bb16e7b586981129b3208323e3 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision ff37a5fb4bf9a24adde51ee67eb408f0790e128f) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision 5aabc63ac1eb20bb16e7b586981129b3208323e3) @@ -41,8 +41,8 @@ import java.util.TreeSet; import java.util.UUID; import java.util.function.Function; -import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; @@ -1007,34 +1007,16 @@ QbOption correctAnswersGroup = qbQuestion.getQbOptions().get(0).isCorrect() ? qbQuestion.getQbOptions().get(0) : qbQuestion.getQbOptions().get(1); - String[] correctAnswers = correctAnswersGroup.getName().strip().split("\\r\\n"); - for (String correctAnswer : correctAnswers) { - correctAnswer = correctAnswer.strip(); + Collection correctAnswers = Stream.of(correctAnswersGroup.getName().split("\r\n")).collect(Collectors + .mapping(answer -> answer.replaceAll(VSA_ANSWER_NORMALISE_JAVA_REG_EXP, ""), Collectors.toSet())); - //prepare regex which takes into account only * special character - String regexWithOnlyAsteriskSymbolActive = "\\Q"; - for (int i = 0; i < correctAnswer.length(); i++) { - //everything in between \\Q and \\E are taken literally no matter which characters it contains - if (correctAnswer.charAt(i) == '*') { - regexWithOnlyAsteriskSymbolActive += "\\E.*\\Q"; - } else { - regexWithOnlyAsteriskSymbolActive += correctAnswer.charAt(i); - } - } - regexWithOnlyAsteriskSymbolActive += "\\E"; - - //check whether answer matches regex - Pattern pattern; - if (qbQuestion.isCaseSensitive()) { - pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive); - } else { - pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive, - java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.UNICODE_CASE); - } - + boolean isQuestionCaseSensitive = qbQuestion.isCaseSensitive(); + for (String correctAnswer : correctAnswers) { for (String userAnswer : userAnswers) { + String normalisedUserAnswer = userAnswer.replaceAll(VSA_ANSWER_NORMALISE_JAVA_REG_EXP, ""); // check is item unraveled - if (pattern.matcher(userAnswer.strip()).matches()) { + if (isQuestionCaseSensitive ? normalisedUserAnswer.equals(correctAnswer) + : normalisedUserAnswer.equalsIgnoreCase(correctAnswer)) { return true; } }