Index: lams_tool_assessment/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -r4d547f8d6d94c21faecb89f723c362bf399f4614 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 4d547f8d6d94c21faecb89f723c362bf399f4614) +++ lams_tool_assessment/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -338,6 +338,7 @@ label.disclose.all.correct.answers =Disclose all correct answers label.disclose.all.groups.answers =Disclose all groups' answers label.prefix.sequential.letters.for.each.answer =Prefix sequential letters for each answer +label.edit.in.monitor.warning =Attention: while you edit this assessment students don't have access to it. You must save your changes so students can re-attempt this assessment again. #======= End labels: Exported 332 labels for en AU ===== Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/AssessmentConstants.java =================================================================== diff -u -r7a37ae5049a14049cacb3d0d3423362ce92993e6 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/AssessmentConstants.java (.../AssessmentConstants.java) (revision 7a37ae5049a14049cacb3d0d3423362ce92993e6) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/AssessmentConstants.java (.../AssessmentConstants.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -68,6 +68,8 @@ public static final String PARAM_FILE_UUID = "fileUuid"; public static final String PARAM_QUESTION_INDEX = "questionIndex"; + + public static final String PARAM_QUESTION_SEQUENCE_ID = "questionSequenceId"; public static final String PARAM_QUESTION_REFERENCE_INDEX = "questionReferenceIndex"; @@ -86,6 +88,12 @@ public static final String PARAM_QUESTION_RESULT_UID = "questionResultUid"; // for request attribute name + + /** + * used to signify edit in monitor when assessment has been attempted already + */ + public static final String ATTR_IS_AUTHORING_RESTRICTED = "isAuthoringRestricted"; + public static final String ATTR_GROUP_USERS = "groupUsers"; public static final String ATTR_IS_USER_LEADER = "isUserLeader"; @@ -111,6 +119,8 @@ public static final String ATTR_OPTION_ACCEPTED_ERROR_PREFIX = "optionAcceptedError"; public static final String ATTR_OPTION_FEEDBACK_PREFIX = "optionFeedback"; + + public static final String ATTR_OPTION_UID_PREFIX = "optionUid"; public static final String ATTR_OPTION_SEQUENCE_ID_PREFIX = "optionSequenceId"; Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentResultDAO.java =================================================================== diff -u -r7475d08afc280b5e2e5ddf04e8bf35e3166aaf80 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentResultDAO.java (.../AssessmentResultDAO.java) (revision 7475d08afc280b5e2e5ddf04e8bf35e3166aaf80) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentResultDAO.java (.../AssessmentResultDAO.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -88,6 +88,11 @@ List getLeadersLastFinishedAssessmentResults(Long contentId); int getAssessmentResultCount(Long assessmentUid, Long userId); + + /** + * Checks whether anyone has attempted this assessment. + */ + boolean isAssessmentAttempted(Long assessmentUid); AssessmentResult getAssessmentResultByUid(Long assessmentResultUid); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java =================================================================== diff -u -rdcdc1487609bd4f00afaa93c09272d84ab0cd325 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision dcdc1487609bd4f00afaa93c09272d84ab0cd325) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -66,6 +66,9 @@ private static final String FIND_ASSESSMENT_RESULT_COUNT_BY_ASSESSMENT_AND_USER = "select COUNT(*) FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId=? AND r.assessment.uid=? AND (r.finishDate != null)"; + + private static final String IS_ASSESSMENT_RESULT_EXIST_BY_ASSESSMENT = "select COUNT(*) > 0 FROM " + + AssessmentResult.class.getName() + " AS r WHERE r.assessment.uid=:assessmentUid"; private static final String LAST_ASSESSMENT_RESULT_GRADE = "select r.grade FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId=:userId AND r.assessment.uid=:assessmentUid AND (r.finishDate != null) AND r.latest=1"; @@ -265,6 +268,13 @@ return ((Number) list.get(0)).intValue(); } } + + @Override + public boolean isAssessmentAttempted(Long assessmentUid) { + Query q = getSession().createQuery(IS_ASSESSMENT_RESULT_EXIST_BY_ASSESSMENT, Boolean.class); + q.setParameter("assessmentUid", assessmentUid); + return q.uniqueResult(); + } @Override public AssessmentResult getAssessmentResultByUid(Long assessmentResultUid) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -rdcdc1487609bd4f00afaa93c09272d84ab0cd325 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision dcdc1487609bd4f00afaa93c09272d84ab0cd325) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -31,7 +31,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -61,6 +60,8 @@ import org.lamsfoundation.lams.notebook.model.NotebookEntry; import org.lamsfoundation.lams.notebook.service.CoreNotebookConstants; import org.lamsfoundation.lams.notebook.service.ICoreNotebookService; +import org.lamsfoundation.lams.questions.Answer; +import org.lamsfoundation.lams.questions.Question; import org.lamsfoundation.lams.rest.RestTags; import org.lamsfoundation.lams.rest.ToolRestManager; import org.lamsfoundation.lams.tool.ToolCompletionStatus; @@ -96,6 +97,7 @@ import org.lamsfoundation.lams.tool.assessment.model.AssessmentUnit; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; +import org.lamsfoundation.lams.tool.assessment.util.AnswerIntComparator; import org.lamsfoundation.lams.tool.assessment.util.AssessmentEscapeUtils; import org.lamsfoundation.lams.tool.assessment.util.AssessmentQuestionResultComparator; import org.lamsfoundation.lams.tool.assessment.util.AssessmentSessionComparator; @@ -366,8 +368,7 @@ @Override public Assessment getAssessmentByContentId(Long contentId) { - Assessment rs = assessmentDao.getByContentId(contentId); - return rs; + return assessmentDao.getByContentId(contentId); } @Override @@ -553,12 +554,6 @@ /* * Auxiliary method for setAttemptStarted(). Simply init new AssessmentQuestionResult object and fills it in with * values. - * - * @param question - * - * @param questionResults - * - * @return */ private AssessmentQuestionResult createQuestionResultObject(AssessmentQuestion question) { AssessmentQuestionResult questionResult = new AssessmentQuestionResult(); @@ -574,16 +569,53 @@ return questionResult; } - + + @Override - public boolean storeUserAnswers(Assessment assessment, Long userId, List> pagedQuestions, - Long singleMarkHedgingQuestionUid, boolean isAutosave) + public void storeSingleMarkHedgingQuestion(Assessment assessment, Long userId, + List> pagedQuestions, Long singleMarkHedgingQuestionUid) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { + AssessmentResult result = assessmentResultDao.getLastAssessmentResult(assessment.getUid(), userId); - int maximumGrade = 0; - float grade = 0; + // prohibit users from submitting (or autosubmitting) answers after result is finished but Resubmit button is + // not pressed (e.g. using 2 browsers) + if (result.getFinishDate() != null) { + return; + } - Set questions = assessment.getQuestions(); + // search for the question corresponding to single MarkHedging question + QuestionDTO questionDto = null; + for (Set questionsForOnePage : pagedQuestions) { + for (QuestionDTO questionDtoIter : questionsForOnePage) { + if (questionDtoIter.getUid().equals(singleMarkHedgingQuestionUid)) { + questionDto = questionDtoIter; + } + } + } + + AssessmentQuestionResult questionResult = storeUserAnswer(result, questionDto); + questionResult.setFinishDate(new Date()); + + float mark = 0; + for (OptionDTO optionDto : questionDto.getOptionDtos()) { + if (optionDto.isCorrect()) { + //if hedgingMark is a default '-1', change it to '0' + int hedgingMark = optionDto.getAnswerInt() == -1 ? 0 : optionDto.getAnswerInt(); + mark += hedgingMark; + break; + } + } + questionResult.setMark(mark); + questionResult.setMaxMark((float) questionDto.getGrade()); + assessmentResultDao.saveObject(questionResult); + + //for displaying purposes calculate mark and set it to questionDto + questionDto.setMark(mark); + } + + @Override + public boolean storeUserAnswers(Assessment assessment, Long userId, List> pagedQuestions, + boolean isAutosave) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { AssessmentResult result = assessmentResultDao.getLastAssessmentResult(assessment.getUid(), userId); // prohibit users from submitting (or autosubmitting) answers after result is finished but Resubmit button is @@ -595,51 +627,34 @@ // store all answers (in all pages) for (Set questionsForOnePage : pagedQuestions) { for (QuestionDTO questionDto : questionsForOnePage) { + storeUserAnswer(result, questionDto); + } + } - // in case single MarkHedging question needs to be stored -- search for that question - if ((singleMarkHedgingQuestionUid != null) - && !questionDto.getUid().equals(singleMarkHedgingQuestionUid)) { - continue; - } + // store grades and finished date only on user hitting submit all answers button (and not submit mark hedging + // question) + if (!isAutosave) { + int maximumGrade = 0; + float grade = 0; - // In case if assessment was modified in monitor after result has been started check question still exists in DB as - // it could be deleted - if (assessment.isContentModifiedInMonitor(result.getStartDate())) { - - Set references = assessment.getQuestionReferences(); - - boolean isQuestionExists = false; - for (QuestionReference reference : references) { - if (!reference.isRandomQuestion() - && reference.getQuestion().getUid().equals(questionDto.getUid())) { - isQuestionExists = true; - break; + //sum up user grade and max grade for all questions + for (Set questionsForOnePage : pagedQuestions) { + for (QuestionDTO questionDto : questionsForOnePage) { + // get questionResult from DB instance of AssessmentResult + AssessmentQuestionResult questionResult = null; + for (AssessmentQuestionResult questionResultIter : result.getQuestionResults()) { + if (questionDto.getUid().equals(questionResultIter.getAssessmentQuestion().getUid())) { + questionResult = questionResultIter; } - if (reference.isRandomQuestion()) { - for (AssessmentQuestion questionDb : questions) { - if (questionDb.getUid().equals(questionDto.getUid())) { - isQuestionExists = true; - break; - } - } - } } - if (!isQuestionExists) { - continue; - } + calculateAnswerMark(assessment.getUid(), userId, questionResult, questionDto); + questionResult.setFinishDate(new Date()); + + grade += questionResult.getMark(); + maximumGrade += questionDto.getGrade(); } - - float userQeustionGrade = storeUserAnswer(result, questionDto, isAutosave); - grade += userQeustionGrade; - - maximumGrade += questionDto.getGrade(); } - } - - // store grades and finished date only on user hitting submit all answers button (and not submit mark hedging - // question) - boolean isStoreResult = !isAutosave && (singleMarkHedgingQuestionUid == null); - if (isStoreResult) { + result.setMaximumGrade(maximumGrade); result.setGrade(grade); result.setFinishDate(new Timestamp(new Date().getTime())); @@ -651,33 +666,24 @@ /** * Stores given AssessmentQuestion's answer. - * - * @param isAutosave - * in case of autosave there is no need to calculate marks - * @return grade that user scored by answering that question - * @throws NoSuchMethodException - * @throws InvocationTargetException - * @throws IllegalAccessException */ - private float storeUserAnswer(AssessmentResult assessmentResult, QuestionDTO questionDto, boolean isAutosave) + private AssessmentQuestionResult storeUserAnswer(AssessmentResult assessmentResult, QuestionDTO questionDto) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { Assessment assessment = assessmentResult.getAssessment(); - AssessmentQuestionResult questionResult = null; // get questionResult from DB instance of AssessmentResult + AssessmentQuestionResult questionResult = null; for (AssessmentQuestionResult questionResultIter : assessmentResult.getQuestionResults()) { if (questionDto.getUid().equals(questionResultIter.getAssessmentQuestion().getUid())) { questionResult = questionResultIter; } } - //if teacher edited content in monitor (modified question) it led to removal if autosaved questionResult - if (assessment.isContentModifiedInMonitor(assessmentResult.getStartDate()) && questionResult == null) { - //update questionDto + //if teacher modified question in monitor - update questionDto now + if (assessment.isContentModifiedInMonitor(assessmentResult.getStartDate())) { AssessmentQuestion modifiedQuestion = assessmentQuestionDao.getByUid(questionDto.getUid()); QuestionDTO updatedQuestionDto = modifiedQuestion.getQuestionDTO(); PropertyUtils.copyProperties(questionDto, updatedQuestionDto); - return 0; } // store question answer values @@ -708,7 +714,16 @@ if (assessment.isEnableConfidenceLevels()) { questionResult.setConfidenceLevel(questionDto.getConfidenceLevel()); } - + + return questionResult; + } + + /** + * + * @return grade that user scored by answering that question + */ + private void calculateAnswerMark(Long assessmentUid, Long userId, AssessmentQuestionResult questionResult, + QuestionDTO questionDto) { //calculate both mark and maxMark float mark = 0; float maxMark = questionDto.getGrade(); @@ -852,49 +867,127 @@ } } } + + //total mark can't be more than maxMark + if (mark > maxMark) { + mark = maxMark; - // we start calculating and storing marks only in case it's not an autosave request - if (!isAutosave) { + // in case options have negative grades (<0), their total mark can't be less than -maxMark + } else if (mark < -maxMark) { + mark = -maxMark; + } - questionResult.setFinishDate(new Date()); + // calculate penalty + if (mark > 0) { + // calculate number of wrong answers + int numberWrongAnswers = assessmentQuestionResultDao.getNumberWrongAnswersDoneBefore(assessmentUid, + userId, questionDto.getUid()); - if (mark > maxMark) { - mark = maxMark; + // calculate penalty itself + float penalty = questionDto.getPenaltyFactor() * numberWrongAnswers; + mark -= penalty; + if (penalty > maxMark) { + penalty = maxMark; + } + questionResult.setPenalty(penalty); - // in case options have negative grades (<0), their total mark can't be less than -maxMark - } else if (mark < -maxMark) { - mark = -maxMark; + // don't let penalty make mark less than 0 + if (mark < 0) { + mark = 0; } + } - // calculate penalty - if (mark > 0) { - // calculate number of wrong answers - Long assessmentUid = assessmentResult.getAssessment().getUid(); - Long userId = assessmentResult.getUser().getUserId(); - int numberWrongAnswers = assessmentQuestionResultDao.getNumberWrongAnswersDoneBefore(assessmentUid, - userId, questionDto.getUid()); + questionResult.setMark(mark); + questionResult.setMaxMark(maxMark); + } + + @Override + public void loadupLastAttempt(Long assessmentUid, Long userId, List> pagedQuestionDtos) { + //get the latest result (it can be unfinished one) + AssessmentResult lastResult = getLastAssessmentResult(assessmentUid, userId); + //if there is no results yet - no action required + if (lastResult == null) { + return; + } - // calculate penalty itself - float penalty = questionDto.getPenaltyFactor() * numberWrongAnswers; - mark -= penalty; - if (penalty > maxMark) { - penalty = maxMark; + //get the latest finished result (required for mark hedging type of questions only) + AssessmentResult lastFinishedResult = null; + if (lastResult.getFinishDate() == null) { + lastFinishedResult = getLastFinishedAssessmentResult(assessmentUid, userId); + } + + for (Set questionsForOnePage : pagedQuestionDtos) { + for (QuestionDTO questionDto : questionsForOnePage) { + + //load last finished results for hedging type of questions (in order to prevent retry) + Set questionResults = lastResult.getQuestionResults(); + if ((questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) + && (lastResult.getFinishDate() == null) && (lastFinishedResult != null)) { + questionResults = lastFinishedResult.getQuestionResults(); } - questionResult.setPenalty(penalty); - // don't let penalty make mark less than 0 - if (mark < 0) { - mark = 0; + for (AssessmentQuestionResult questionResult : questionResults) { + if (questionDto.getUid().equals(questionResult.getAssessmentQuestion().getUid())) { + loadupQuestionResultIntoQuestionDto(questionDto, questionResult); + break; + } } } + } + } + + /** + * Loads up all information from questionResult into questionDto. + */ + private void loadupQuestionResultIntoQuestionDto(QuestionDTO questionDto, AssessmentQuestionResult questionResult) { + questionDto.setAnswerBoolean(questionResult.getAnswerBoolean()); + questionDto.setAnswerFloat(questionResult.getAnswerFloat()); + questionDto.setAnswerString(questionResult.getAnswerString()); + questionDto.setMark(questionResult.getMark()); + questionDto.setResponseSubmitted(questionResult.getFinishDate() != null); + questionDto.setPenalty(questionResult.getPenalty()); + questionDto.setConfidenceLevel(questionResult.getConfidenceLevel()); - questionResult.setMark(mark); - questionResult.setMaxMark(maxMark); - // for displaying purposes in case of submitSingleMarkHedgingQuestion() Ajax call - questionDto.setMark(mark); + for (OptionDTO optionDto : questionDto.getOptionDtos()) { + + for (AssessmentOptionAnswer optionAnswer : questionResult.getOptionAnswers()) { + if (optionDto.getUid().equals(optionAnswer.getOptionUid())) { + optionDto.setAnswerBoolean(optionAnswer.getAnswerBoolean()); + optionDto.setAnswerInt(optionAnswer.getAnswerInt()); + break; + } + } } - return mark; + //sort ordering type of question in order to show how learner has sorted them + if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) { + + //don't sort ordering type of questions that haven't been submitted to not break their shuffled order + boolean isOptionAnswersNeverSubmitted = true; + for (OptionDTO optionDto : questionDto.getOptionDtos()) { + if (optionDto.getAnswerInt() != 0) { + isOptionAnswersNeverSubmitted = false; + } + } + + if (!isOptionAnswersNeverSubmitted) { + TreeSet orderedSet = new TreeSet<>(new AnswerIntComparator()); + orderedSet.addAll(questionDto.getOptionDtos()); + questionDto.setOptionDtos(orderedSet); + } + } + + // set answerTotalGrade to let jsp know whether the question was answered correctly/partly/incorrectly even if mark=0 + if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { + float totalGrade = 0; + for (OptionDTO optionDto : questionDto.getOptionDtos()) { + if (optionDto.getAnswerBoolean()) { + totalGrade += optionDto.getGrade(); + } + } + questionDto.setAnswerTotalGrade(totalGrade); + } + } @Override @@ -982,6 +1075,11 @@ public int getAssessmentResultCount(Long assessmentUid, Long userId) { return assessmentResultDao.getAssessmentResultCount(assessmentUid, userId); } + + @Override + public boolean isAssessmentAttempted(Long assessmentUid) { + return assessmentResultDao.isAssessmentAttempted(assessmentUid); + } @Override public AssessmentQuestionResult getAssessmentQuestionResultByUid(Long questionResultUid) { @@ -2075,25 +2173,30 @@ } + @SuppressWarnings("unchecked") @Override public void recalculateUserAnswers(final Long assessmentUid, final Long toolContentId, Set oldQuestions, Set newQuestions, - List deletedQuestions, Set oldReferences, - Set newReferences, List deletedReferences) { + Set oldReferences, Set newReferences) { // create list of modified questions List modifiedQuestions = new ArrayList<>(); for (AssessmentQuestion oldQuestion : oldQuestions) { + + if (AssessmentConstants.QUESTION_TYPE_ESSAY == oldQuestion.getType() + || AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS == oldQuestion.getType()) { + continue; + } for (AssessmentQuestion newQuestion : newQuestions) { if (oldQuestion.getUid().equals(newQuestion.getUid())) { boolean isQuestionModified = false; - // title or question is different - if (!oldQuestion.getTitle().equals(newQuestion.getTitle()) - || !oldQuestion.getQuestion().equals(newQuestion.getQuestion()) - || (oldQuestion.getCorrectAnswer() != newQuestion.getCorrectAnswer())) { + // title or question is different - do nothing. Also question grade can't be changed + + //AssessmentConstants.QUESTION_TYPE_TRUE_FALSE + if (oldQuestion.getCorrectAnswer() != newQuestion.getCorrectAnswer()) { isQuestionModified = true; } @@ -2104,21 +2207,25 @@ for (AssessmentQuestionOption newOption : newOptions) { if (oldOption.getUid().equals(newOption.getUid())) { + //ordering if (((oldQuestion.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) && (oldOption.getSequenceId() != newOption.getSequenceId())) - || !StringUtils.equals(oldOption.getQuestion(), newOption.getQuestion()) - || !StringUtils.equals(oldOption.getOptionString(), newOption.getOptionString()) + //short answer + || ((oldQuestion.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER) + && !StringUtils.equals(oldOption.getOptionString(), + newOption.getOptionString())) + //numbering || (oldOption.getOptionFloat() != newOption.getOptionFloat()) || (oldOption.getAcceptedError() != newOption.getAcceptedError()) - || (oldOption.getGrade() != newOption.getGrade())) { + //option grade + || (oldOption.getGrade() != newOption.getGrade()) + //changed correct option + || (oldOption.isCorrect() != newOption.isCorrect())) { isQuestionModified = true; } } } } - if (oldOptions.size() != newOptions.size()) { - isQuestionModified = true; - } if (isQuestionModified) { modifiedQuestions.add(newQuestion); @@ -2127,7 +2234,7 @@ } } - // create list of modified references + // create list of references with modified grades. // modifiedReferences holds pairs newReference -> oldReference.getDefaultGrade() Map modifiedReferences = new HashMap<>(); for (QuestionReference oldReference : oldReferences) { @@ -2139,23 +2246,6 @@ } } - // create list of added references - List addedReferences = new ArrayList<>(); - for (QuestionReference newReference : newReferences) { - boolean isNewReferenceMetInOldReferences = false; - - for (QuestionReference oldReference : oldReferences) { - if (oldReference.getUid().equals(newReference.getUid())) { - isNewReferenceMetInOldReferences = true; - } - } - - // if the new reference was not met in old references then it's the newly added reference - if (!isNewReferenceMetInOldReferences) { - addedReferences.add(newReference); - } - } - List sessionList = assessmentSessionDao.getByContentId(toolContentId); for (AssessmentSession session : sessionList) { Long toolSessionId = session.getSessionId(); @@ -2179,135 +2269,92 @@ float assessmentMark = assessmentResult.getGrade(); int assessmentMaxMark = assessmentResult.getMaximumGrade(); + Set questionResults = assessmentResult.getQuestionResults(); + + // [+] if the question is modified + for (AssessmentQuestionResult questionResult : questionResults) { + AssessmentQuestion question = questionResult.getAssessmentQuestion(); - Set questionAnswers = assessmentResult.getQuestionResults(); - Iterator iter = questionAnswers.iterator(); - while (iter.hasNext()) { - AssessmentQuestionResult questionAnswer = iter.next(); - AssessmentQuestion question = questionAnswer.getAssessmentQuestion(); + //check whether according question was modified + for (AssessmentQuestion modifiedQuestion : modifiedQuestions) { + if (question.getUid().equals(modifiedQuestion.getUid())) { + Float oldQuestionAnswerMark = questionResult.getMark(); - boolean isRemoveQuestionResult = false; - - // [+] if the question reference was removed - for (QuestionReference deletedReference : deletedReferences) { - if (!deletedReference.isRandomQuestion() - && question.getUid().equals(deletedReference.getQuestion().getUid())) { - isRemoveQuestionResult = true; - assessmentMaxMark -= deletedReference.getDefaultGrade(); + //actually recalculate marks + QuestionDTO questionDto = question.getQuestionDTO(); + questionDto.setGrade(questionResult.getMaxMark().intValue()); + loadupQuestionResultIntoQuestionDto(questionDto, questionResult); + calculateAnswerMark(assessmentUid, user.getUserId(), questionResult, questionDto); + assessmentQuestionResultDao.saveObject(questionResult); + + float newQuestionAnswerMark = questionResult.getMark(); + assessmentMark += newQuestionAnswerMark - oldQuestionAnswerMark; break; } } - - // [+] if the question reference mark is modified + } + + // [+] if the question reference mark is modified + for (AssessmentQuestionResult questionResult:questionResults) { + Long questionUid = questionResult.getAssessmentQuestion().getUid(); + for (QuestionReference modifiedReference : modifiedReferences.keySet()) { if (!modifiedReference.isRandomQuestion() - && question.getUid().equals(modifiedReference.getQuestion().getUid())) { + && questionUid.equals(modifiedReference.getQuestion().getUid())) { int newReferenceGrade = modifiedReference.getDefaultGrade(); int oldReferenceGrade = modifiedReferences.get(modifiedReference); // update question answer's mark - Float oldQuestionAnswerMark = questionAnswer.getMark(); + Float oldQuestionAnswerMark = questionResult.getMark(); float newQuestionAnswerMark = (oldQuestionAnswerMark * newReferenceGrade) / oldReferenceGrade; - questionAnswer.setMark(newQuestionAnswerMark); - questionAnswer.setMaxMark((float) newReferenceGrade); - assessmentQuestionResultDao.saveObject(questionAnswer); + questionResult.setMark(newQuestionAnswerMark); + questionResult.setMaxMark((float) newReferenceGrade); + assessmentQuestionResultDao.saveObject(questionResult); assessmentMark += newQuestionAnswerMark - oldQuestionAnswerMark; assessmentMaxMark += newReferenceGrade - oldReferenceGrade; break; } - } - - // [+] if the question is modified - for (AssessmentQuestion modifiedQuestion : modifiedQuestions) { - if (question.getUid().equals(modifiedQuestion.getUid())) { - isRemoveQuestionResult = true; - break; - } - } - - // [+] if the question was removed - for (AssessmentQuestion deletedQuestion : deletedQuestions) { - if (question.getUid().equals(deletedQuestion.getUid())) { - isRemoveQuestionResult = true; - break; - } - } - - if (isRemoveQuestionResult) { - - assessmentMark -= questionAnswer.getMark(); - iter.remove(); - assessmentQuestionResultDao.removeObject(AssessmentQuestionResult.class, - questionAnswer.getUid()); - } - - // [+] doing nothing if the new question was added - } - // find all question answers from random question reference - ArrayList nonRandomQuestionAnswers = new ArrayList<>(); - for (AssessmentQuestionResult questionAnswer : questionAnswers) { + // find all question results from random question references + ArrayList nonRandomQuestionResults = new ArrayList<>(); + for (AssessmentQuestionResult questionResult : questionResults) { for (QuestionReference reference : newReferences) { - if (!reference.isRandomQuestion() && questionAnswer.getAssessmentQuestion().getUid() + if (!reference.isRandomQuestion() && questionResult.getAssessmentQuestion().getUid() .equals(reference.getQuestion().getUid())) { - nonRandomQuestionAnswers.add(questionAnswer); + nonRandomQuestionResults.add(questionResult); } } } - Collection randomQuestionAnswers = CollectionUtils - .subtract(questionAnswers, nonRandomQuestionAnswers); + Collection randomQuestionResults = CollectionUtils + .subtract(questionResults, nonRandomQuestionResults); - // [+] if the question reference was removed (in case of random question references) - for (QuestionReference deletedReference : deletedReferences) { - - // in case of random question reference - search for the answer with the same maxmark - if (deletedReference.isRandomQuestion()) { - - Iterator iter2 = randomQuestionAnswers.iterator(); - while (iter2.hasNext()) { - AssessmentQuestionResult randomQuestionAnswer = iter2.next(); - if (randomQuestionAnswer.getMaxMark().intValue() == deletedReference - .getDefaultGrade()) { - - assessmentMark -= randomQuestionAnswer.getMark(); - assessmentMaxMark -= deletedReference.getDefaultGrade(); - iter2.remove(); - questionAnswers.remove(randomQuestionAnswer); - assessmentQuestionResultDao.removeObject(AssessmentQuestionResult.class, - randomQuestionAnswer.getUid()); - break; - } - } - } - } - // [+] if the question reference mark is modified (in case of random question references) for (QuestionReference modifiedReference : modifiedReferences.keySet()) { - // in case of random question reference - search for the answer with the same maxmark + // in case of random question reference - search for the answer with the same maxmark (it does not matter to which random reference this question belong originally as the only thing that differentiate those references is defaultGrade) if (modifiedReference.isRandomQuestion()) { - for (AssessmentQuestionResult randomQuestionAnswer : randomQuestionAnswers) { + for (AssessmentQuestionResult randomQuestionResult : randomQuestionResults) { int newReferenceGrade = modifiedReference.getDefaultGrade(); int oldReferenceGrade = modifiedReferences.get(modifiedReference); - if (randomQuestionAnswer.getMaxMark().intValue() == oldReferenceGrade) { + if (randomQuestionResult.getMaxMark().intValue() == oldReferenceGrade) { // update question answer's mark - Float oldQuestionAnswerMark = randomQuestionAnswer.getMark(); - float newQuestionAnswerMark = (oldQuestionAnswerMark * newReferenceGrade) + Float oldQuestionResultMark = randomQuestionResult.getMark(); + float newQuestionResultMark = (oldQuestionResultMark * newReferenceGrade) / oldReferenceGrade; - randomQuestionAnswer.setMark(newQuestionAnswerMark); - randomQuestionAnswer.setMaxMark((float) newReferenceGrade); - assessmentQuestionResultDao.saveObject(randomQuestionAnswer); + randomQuestionResult.setMark(newQuestionResultMark); + randomQuestionResult.setMaxMark((float) newReferenceGrade); + assessmentQuestionResultDao.saveObject(randomQuestionResult); - nonRandomQuestionAnswers.add(randomQuestionAnswer); + nonRandomQuestionResults.add(randomQuestionResult); - assessmentMark += newQuestionAnswerMark - oldQuestionAnswerMark; + assessmentMark += newQuestionResultMark - oldQuestionResultMark; assessmentMaxMark += newReferenceGrade - oldReferenceGrade; break; } @@ -2316,11 +2363,6 @@ } - // [+] if the new question reference was added - for (QuestionReference addedReference : addedReferences) { - assessmentMaxMark += addedReference.getDefaultGrade(); - } - // store new mark and maxMark if they were changed if ((assessmentResult.getGrade() != assessmentMark) || (assessmentResult.getMaximumGrade() != assessmentMaxMark)) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java =================================================================== diff -u -rdcdc1487609bd4f00afaa93c09272d84ab0cd325 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision dcdc1487609bd4f00afaa93c09272d84ab0cd325) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -228,26 +228,26 @@ * @param assessmentResult */ void setAttemptStarted(Assessment assessment, AssessmentUser assessmentUser, Long toolSessionId); + + void storeSingleMarkHedgingQuestion(Assessment assessment, Long userId, List> pagedQuestions, + Long singleMarkHedgingQuestionUid) + throws IllegalAccessException, InvocationTargetException, NoSuchMethodException; /** * Store user answers into DB. It can be autosave and non-autosave requests. * * @param assessment * @param userId * @param pagedQuestions - * @param singleMarkHedgingQuestionUid - * - if provided - means only that current single MarkHedging question needs to be stored * @param isAutosave * indicates whether it's autosave request * * @return whether storing results is allowed, false otherwise - * @throws NoSuchMethodException - * @throws InvocationTargetException - * @throws IllegalAccessException */ boolean storeUserAnswers(Assessment assessment, Long userId, List> pagedQuestions, - Long singleMarkHedgingQuestionUid, boolean isAutosave) - throws IllegalAccessException, InvocationTargetException, NoSuchMethodException; + boolean isAutosave) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException; + + void loadupLastAttempt(Long assessmentUid, Long userId, List> pagedQuestionDtos); /** * Return the latest result (it can be unfinished). @@ -351,6 +351,11 @@ * @return */ int getAssessmentResultCount(Long assessmentUid, Long userId); + + /** + * Checks whether anyone has attempted this assessment. + */ + boolean isAssessmentAttempted(Long assessmentUid); AssessmentQuestionResult getAssessmentQuestionResultByUid(Long questionResultUid); @@ -525,8 +530,7 @@ */ void recalculateUserAnswers(final Long assessmentUid, final Long toolContentId, Set oldQuestions, Set newQuestions, - List deletedQuestions, Set oldReferences, - Set newReferences, List deletedReferences); + Set oldReferences, Set newReferences); void releaseFromCache(Object object); @@ -539,4 +543,5 @@ * to refresh page because new data is available */ void notifyLearnersOnAnswerDisclose(long toolContentId); + } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/QTIUtil.java =================================================================== diff -u --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/QTIUtil.java (revision 0) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/util/QTIUtil.java (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -0,0 +1,433 @@ +package org.lamsfoundation.lams.tool.assessment.util; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.questions.Answer; +import org.lamsfoundation.lams.questions.Question; +import org.lamsfoundation.lams.questions.QuestionParser; +import org.lamsfoundation.lams.tool.assessment.AssessmentConstants; +import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; +import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionOption; + +public class QTIUtil { + private static Logger log = Logger.getLogger(QTIUtil.class); + + public static List exportQTI(SortedSet questionList) { + List questions = new LinkedList<>(); + for (AssessmentQuestion assessmentQuestion : questionList) { + Question question = new Question(); + List answers = new ArrayList<>(); + + switch (assessmentQuestion.getType()) { + + case AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE: + + if (assessmentQuestion.isMultipleAnswersAllowed()) { + question.setType(Question.QUESTION_TYPE_MULTIPLE_RESPONSE); + int correctAnswerCount = 0; + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { + if (assessmentAnswer.getGrade() > 0) { + correctAnswerCount++; + } + } + + Float correctAnswerScore = correctAnswerCount > 0 + ? Integer.valueOf(100 / correctAnswerCount).floatValue() + : null; + int incorrectAnswerCount = assessmentQuestion.getOptions().size() - correctAnswerCount; + Float incorrectAnswerScore = incorrectAnswerCount > 0 + ? Integer.valueOf(-100 / incorrectAnswerCount).floatValue() + : null; + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { + Answer answer = new Answer(); + boolean isCorrectAnswer = assessmentAnswer.getGrade() > 0; + + answer.setText(assessmentAnswer.getOptionString()); + answer.setScore(isCorrectAnswer ? correctAnswerScore : incorrectAnswerScore); + answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + + answers.add(assessmentAnswer.getSequenceId(), answer); + } + + } else { + question.setType(Question.QUESTION_TYPE_MULTIPLE_CHOICE); + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { + Answer answer = new Answer(); + boolean isCorrectAnswer = assessmentAnswer.getGrade() == 1F; + + answer.setText(assessmentAnswer.getOptionString()); + answer.setScore( + isCorrectAnswer ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() + : 0); + answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + + answers.add(assessmentAnswer.getSequenceId(), answer); + } + } + break; + + case AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER: + question.setType(Question.QUESTION_TYPE_FILL_IN_BLANK); + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { + // only answer which has more than 0% is considered a correct one + if (assessmentAnswer.getGrade() > 0) { + Answer answer = new Answer(); + answer.setText(assessmentAnswer.getOptionString()); + answer.setScore(Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue()); + + answers.add(answer); + } + } + break; + + case AssessmentConstants.QUESTION_TYPE_TRUE_FALSE: + question.setType(Question.QUESTION_TYPE_TRUE_FALSE); + boolean isTrueCorrect = assessmentQuestion.getCorrectAnswer(); + + // true/false question is basically the same for QTI, just with special answers + Answer trueAnswer = new Answer(); + trueAnswer.setText("True"); + trueAnswer.setScore( + isTrueCorrect ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() : 0); + trueAnswer.setFeedback(isTrueCorrect ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + answers.add(trueAnswer); + + Answer falseAnswer = new Answer(); + falseAnswer.setText("False"); + falseAnswer.setScore( + !isTrueCorrect ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() : 0); + falseAnswer.setFeedback(!isTrueCorrect ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + answers.add(falseAnswer); + break; + + case AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS: + question.setType(Question.QUESTION_TYPE_MATCHING); + + int answerIndex = 0; + float score = assessmentQuestion.getDefaultGrade() / assessmentQuestion.getOptions().size(); + question.setMatchAnswers(new ArrayList(assessmentQuestion.getOptions().size())); + question.setMatchMap(new TreeMap()); + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { + Answer answer = new Answer(); + + answer.setText(assessmentAnswer.getQuestion()); + answer.setScore(score); + answer.setFeedback(assessmentAnswer.getFeedback()); + answers.add(answer); + + Answer matchingAnswer = new Answer(); + matchingAnswer.setText(assessmentAnswer.getOptionString()); + question.getMatchAnswers().add(matchingAnswer); + question.getMatchMap().put(answerIndex, answerIndex); + answerIndex++; + } + + break; + + case AssessmentConstants.QUESTION_TYPE_ESSAY: + // not much to do with essay + question.setType(Question.QUESTION_TYPE_ESSAY); + answers = null; + break; + + case AssessmentConstants.QUESTION_TYPE_MARK_HEDGING: + + question.setType(Question.QUESTION_TYPE_MARK_HEDGING); + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { + Answer answer = new Answer(); + boolean isCorrectAnswer = assessmentAnswer.isCorrect(); + + answer.setText(assessmentAnswer.getOptionString()); + answer.setScore( + isCorrectAnswer ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() : 0); + answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + + answers.add(assessmentAnswer.getSequenceId(), answer); + } + break; + + default: + continue; + } + + question.setTitle(assessmentQuestion.getTitle()); + question.setText(assessmentQuestion.getQuestion()); + question.setFeedback(assessmentQuestion.getGeneralFeedback()); + question.setAnswers(answers); + + questions.add(question); + } + + return questions; + } + + public static void saveQTI(HttpServletRequest request, SortedSet questionList, + String contentFolderID) throws UnsupportedEncodingException { + Question[] questions = QuestionParser.parseQuestionChoiceForm(request); + for (Question question : questions) { + AssessmentQuestion assessmentQuestion = new AssessmentQuestion(); + int maxSeq = 0; + if ((questionList != null) && (questionList.size() > 0)) { + AssessmentQuestion last = questionList.last(); + maxSeq = last.getSequenceId() + 1; + } + assessmentQuestion.setSequenceId(maxSeq); + assessmentQuestion.setTitle(question.getTitle()); + assessmentQuestion.setQuestion(QuestionParser.processHTMLField(question.getText(), false, contentFolderID, + question.getResourcesFolderPath())); + assessmentQuestion.setGeneralFeedback(QuestionParser.processHTMLField(question.getFeedback(), false, + contentFolderID, question.getResourcesFolderPath())); + assessmentQuestion.setPenaltyFactor(0); + + int questionGrade = 1; + + // 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()); + + // setting answers is very similar in both types, so they were put together here + if (isMarkHedgingType) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MARK_HEDGING); + + } else if (isMultipleChoice) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE); + assessmentQuestion.setMultipleAnswersAllowed(false); + assessmentQuestion.setShuffle(false); + assessmentQuestion.setPrefixAnswersWithLetters(false); + + } else { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER); + assessmentQuestion.setCaseSensitive(false); + } + + String correctAnswer = null; + if (question.getAnswers() != null) { + TreeSet optionList = new TreeSet<>(new SequencableComparator()); + int orderId = 0; + 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; + } + AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); + assessmentAnswer.setOptionString(answerText); + assessmentAnswer.setSequenceId(orderId++); + assessmentAnswer.setFeedback(answer.getFeedback()); + + if ((answer.getScore() != null) && (answer.getScore() > 0)) { + // for fill in blanks question all answers are correct and get full grade + if (!isMultipleChoice && !isMarkHedgingType || correctAnswer == null) { + // whatever the correct answer holds, it becomes the question score + questionGrade = Double.valueOf(Math.ceil(answer.getScore())).intValue(); + // 100% goes to the correct answer + assessmentAnswer.setGrade(1); + correctAnswer = answerText; + } else { + log.warn("Choosing only first correct answer, despite another one was found: " + + answerText); + assessmentAnswer.setGrade(0); + } + } else { + assessmentAnswer.setGrade(0); + } + + optionList.add(assessmentAnswer); + } + + assessmentQuestion.setOptions(optionList); + } + + if (correctAnswer == null) { + log.warn("No correct answer found for question: " + question.getText()); + continue; + } + + } else if (Question.QUESTION_TYPE_MULTIPLE_RESPONSE.equals(question.getType())) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE); + assessmentQuestion.setMultipleAnswersAllowed(true); + assessmentQuestion.setShuffle(false); + assessmentQuestion.setPrefixAnswersWithLetters(false); + + if (question.getAnswers() != null) { + float totalScore = 0; + for (Answer answer : question.getAnswers()) { + if ((answer.getScore() != null) && (answer.getScore() > 0)) { + // the question score information is stored as sum of answer scores + totalScore += answer.getScore(); + } + } + questionGrade = Double.valueOf(Math.round(totalScore)).intValue(); + + TreeSet optionList = new TreeSet<>(new SequencableComparator()); + int orderId = 1; + for (Answer answer : question.getAnswers()) { + String answerText = answer.getText(); + AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); + assessmentAnswer.setOptionString(answerText); + assessmentAnswer.setSequenceId(orderId++); + assessmentAnswer.setFeedback(answer.getFeedback()); + + if ((answer.getScore() != null) && (answer.getScore() > 0)) { + // set the factor of score for correct answers + assessmentAnswer.setGrade(answer.getScore() / totalScore); + } else { + assessmentAnswer.setGrade(0); + } + + optionList.add(assessmentAnswer); + } + + assessmentQuestion.setOptions(optionList); + } + + } else if (Question.QUESTION_TYPE_TRUE_FALSE.equals(question.getType())) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_TRUE_FALSE); + + if (question.getAnswers() == null) { + log.warn("Answers missing from true-false question: " + question.getText()); + continue; + } else { + for (Answer answer : question.getAnswers()) { + if ((answer.getScore() != null) && (answer.getScore() > 0)) { + assessmentQuestion.setCorrectAnswer(Boolean.parseBoolean(answer.getText())); + questionGrade = Double.valueOf(Math.ceil(answer.getScore())).intValue(); + } + if (!StringUtils.isBlank(answer.getFeedback())) { + // set feedback for true/false answers + if (Boolean.parseBoolean(answer.getText())) { + assessmentQuestion.setFeedbackOnCorrect(answer.getFeedback()); + } else { + assessmentQuestion.setFeedbackOnIncorrect(answer.getFeedback()); + } + } + } + } + } else if (Question.QUESTION_TYPE_MATCHING.equals(question.getType())) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS); + assessmentQuestion.setShuffle(true); + + if (question.getAnswers() != null) { + // the question score information is stored as sum of answer scores + float totalScore = 0; + for (Answer answer : question.getAnswers()) { + if ((answer.getScore() != null) && (answer.getScore() > 0)) { + totalScore += answer.getScore(); + } + } + questionGrade = Double.valueOf(Math.round(totalScore)).intValue(); + + TreeSet optionList = new TreeSet<>(new SequencableComparator()); + int orderId = 1; + for (int answerIndex = 0; answerIndex < question.getAnswers().size(); answerIndex++) { + // QTI allows answers without a match, but LAMS assessment tool does not + Integer matchAnswerIndex = question.getMatchMap() == null ? null + : question.getMatchMap().get(answerIndex); + Answer matchAnswer = (matchAnswerIndex == null) || (question.getMatchAnswers() == null) ? null + : question.getMatchAnswers().get(matchAnswerIndex); + if (matchAnswer != null) { + Answer answer = question.getAnswers().get(answerIndex); + String answerText = answer.getText(); + AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); + assessmentAnswer.setQuestion(answerText); + assessmentAnswer.setOptionString(matchAnswer.getText()); + assessmentAnswer.setSequenceId(orderId++); + assessmentAnswer.setFeedback(answer.getFeedback()); + + optionList.add(assessmentAnswer); + } + } + + assessmentQuestion.setOptions(optionList); + } + } else if (Question.QUESTION_TYPE_ESSAY.equals(question.getType())) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_ESSAY); + assessmentQuestion.setAllowRichEditor(false); + + } else if (Question.QUESTION_TYPE_ESSAY.equals(question.getType())) { + assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE); + assessmentQuestion.setShuffle(false); + assessmentQuestion.setPrefixAnswersWithLetters(false); + + String correctAnswer = null; + if (question.getAnswers() != null) { + TreeSet optionList = new TreeSet<>(new SequencableComparator()); + 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; + } + AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); + assessmentAnswer.setOptionString(answerText); + assessmentAnswer.setSequenceId(orderId++); + assessmentAnswer.setFeedback(answer.getFeedback()); + + if ((answer.getScore() != null) && (answer.getScore() > 0)) { + // for fill in blanks question all answers are correct and get full grade + if (correctAnswer == null) { + // whatever the correct answer holds, it becomes the question score + questionGrade = Double.valueOf(Math.ceil(answer.getScore())).intValue(); + // 100% goes to the correct answer + assessmentAnswer.setGrade(1); + correctAnswer = answerText; + } else { + log.warn("Choosing only first correct answer, despite another one was found: " + + answerText); + assessmentAnswer.setGrade(0); + } + } else { + assessmentAnswer.setGrade(0); + } + + optionList.add(assessmentAnswer); + } + + assessmentQuestion.setOptions(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; + } + + assessmentQuestion.setDefaultGrade(questionGrade); + + questionList.add(assessmentQuestion); + if (log.isDebugEnabled()) { + log.debug("Added question: " + assessmentQuestion.getTitle()); + } + } + } +} Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/AuthoringController.java =================================================================== diff -u -rdcdc1487609bd4f00afaa93c09272d84ab0cd325 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/AuthoringController.java (.../AuthoringController.java) (revision dcdc1487609bd4f00afaa93c09272d84ab0cd325) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/AuthoringController.java (.../AuthoringController.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -69,6 +69,7 @@ import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; import org.lamsfoundation.lams.tool.assessment.service.IAssessmentService; +import org.lamsfoundation.lams.tool.assessment.util.QTIUtil; import org.lamsfoundation.lams.tool.assessment.util.SequencableComparator; import org.lamsfoundation.lams.tool.assessment.web.form.AssessmentForm; import org.lamsfoundation.lams.tool.assessment.web.form.AssessmentQuestionForm; @@ -114,8 +115,7 @@ public String start(@ModelAttribute("assessmentForm") AssessmentForm assessmentForm, HttpServletRequest request) throws ServletException { ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request); - request.setAttribute(AttributeNames.ATTR_MODE, mode.toString()); - return showStartPage(assessmentForm, request); + return readDatabaseData(assessmentForm, request, mode); } @RequestMapping("/definelater") @@ -131,14 +131,14 @@ //audit log the teacher has started editing activity in monitor service.auditLogStartEditingActivityInMonitor(contentId); - request.setAttribute(AttributeNames.ATTR_MODE, ToolAccessMode.TEACHER.toString()); - return showStartPage(assessmentForm, request); + return readDatabaseData(assessmentForm, request, ToolAccessMode.TEACHER); } /** * Common method for "start" and "defineLater" */ - private String showStartPage(AssessmentForm assessmentForm, HttpServletRequest request) throws ServletException { + private String readDatabaseData(AssessmentForm assessmentForm, HttpServletRequest request, ToolAccessMode mode) + throws ServletException { // save toolContentID into HTTPSession Long contentId = WebUtil.readLongParam(request, AssessmentConstants.PARAM_TOOL_CONTENT_ID); @@ -189,6 +189,10 @@ // init available questions reinitializeAvailableQuestions(sessionMap); + boolean isAssessmentAttempted = assessment.getUid() == null ? false + : service.isAssessmentAttempted(assessment.getUid()); + sessionMap.put(AssessmentConstants.ATTR_IS_AUTHORING_RESTRICTED, isAssessmentAttempted && mode.isTeacher()); + sessionMap.put(AttributeNames.ATTR_MODE, mode); sessionMap.put(AssessmentConstants.ATTR_ASSESSMENT_FORM, assessmentForm); return "pages/authoring/start"; } @@ -208,9 +212,6 @@ throw new ServletException(e); } - ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request); - request.setAttribute(AttributeNames.ATTR_MODE, mode.toString()); - return "pages/authoring/authoring"; } @@ -225,7 +226,7 @@ // get back sessionMAP SessionMap sessionMap = (SessionMap) request.getSession() .getAttribute(assessmentForm.getSessionMapID()); - ToolAccessMode mode = WebUtil.readToolAccessModeAuthorDefaulted(request); + ToolAccessMode mode = (ToolAccessMode) sessionMap.get(AttributeNames.ATTR_MODE); Assessment assessment = assessmentForm.getAssessment(); Assessment assessmentPO = service.getAssessmentByContentId(assessmentForm.getAssessment().getContentId()); @@ -290,17 +291,17 @@ } assessmentPO.setQuestions(newQuestions); - List deletedQuestions = getDeletedQuestionList(sessionMap); - Set newReferences = updateQuestionReferencesGrades(request, sessionMap, true); - List deletedReferences = getDeletedQuestionReferences(sessionMap); + Set newReferences = updateQuestionReferencesGrades(request, sessionMap, true); - //recalculate results in case content is edited from monitoring - if (mode.isTeacher()) { + //recalculate results in case content is edited from monitoring and it's been already attempted by a student + boolean isAuthoringRestricted = (boolean) sessionMap.get(AssessmentConstants.ATTR_IS_AUTHORING_RESTRICTED); + if (isAuthoringRestricted) { service.recalculateUserAnswers(assessmentPO.getUid(), assessmentPO.getContentId(), oldQuestions, - newQuestions, deletedQuestions, oldReferences, newReferences, deletedReferences); + newQuestions, oldReferences, newReferences); } - // delete References from database. + // delete References from database + List deletedReferences = getDeletedQuestionReferences(sessionMap); Iterator iterRef = deletedReferences.iterator(); while (iterRef.hasNext()) { QuestionReference reference = iterRef.next(); @@ -310,7 +311,8 @@ } } - // delete Questions from database. + // delete Questions from database + List deletedQuestions = getDeletedQuestionList(sessionMap); Iterator iter = deletedQuestions.iterator(); while (iter.hasNext()) { AssessmentQuestion question = iter.next(); @@ -334,7 +336,7 @@ assessmentForm.setAssessment(assessmentPO); request.setAttribute(CommonConstants.LAMS_AUTHORING_SUCCESS_FLAG, Boolean.TRUE); - request.setAttribute(AttributeNames.ATTR_MODE, mode.toString()); + request.setAttribute(AssessmentConstants.ATTR_SESSION_MAP_ID, sessionMap.getSessionID()); return "pages/authoring/authoring"; } @@ -349,6 +351,7 @@ updateQuestionReferencesGrades(request, sessionMap, false); String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID); questionForm.setSessionMapID(sessionMapID); + questionForm.setSequenceId(-1);//which signifies it's a new question questionForm.setContentFolderID(contentFolderID); questionForm.setDefaultGrade("1"); questionForm.setPenaltyFactor("0"); @@ -390,22 +393,27 @@ HttpServletRequest request) { SessionMap sessionMap = getSessionMap(request); updateQuestionReferencesGrades(request, sessionMap, false); - String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID); - int questionIdx = NumberUtils.toInt(request.getParameter(AssessmentConstants.PARAM_QUESTION_INDEX), -1); + SortedSet questionList = getQuestionList(sessionMap); + int questionSequenceId = WebUtil.readIntParam(request, AssessmentConstants.PARAM_QUESTION_SEQUENCE_ID); AssessmentQuestion question = null; - if (questionIdx != -1) { - SortedSet assessmentList = getQuestionList(sessionMap); - List rList = new ArrayList<>(assessmentList); - question = rList.get(questionIdx); - if (question != null) { - populateQuestionToForm(questionIdx, question, questionForm, request); - questionForm.setContentFolderID(contentFolderID); + for (AssessmentQuestion questionIter : questionList) { + if (questionIter.getSequenceId() == questionSequenceId) { + question = questionIter; + break; } } + if (question == null) { + throw new RuntimeException("Question with sequenceId:" + questionSequenceId + " was not found!"); + } + populateQuestionToForm(question, questionForm, request); + sessionMap.put(AssessmentConstants.ATTR_QUESTION_TYPE, question.getType()); + + String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID); + questionForm.setContentFolderID(contentFolderID); request.setAttribute(AttributeNames.PARAM_CONTENT_FOLDER_ID, contentFolderID); - return findForward(question == null ? -1 : question.getType()); + return findForward(question.getType()); } /** @@ -418,15 +426,25 @@ @RequestMapping("/saveOrUpdateQuestion") public String saveOrUpdateQuestion(@ModelAttribute("assessmentQuestionForm") AssessmentQuestionForm questionForm, HttpServletRequest request) { - extractFormToAssessmentQuestion(request, questionForm); - SessionMap sessionMap = (SessionMap) request.getSession() .getAttribute(questionForm.getSessionMapID()); + boolean isAuthoringRestricted = (boolean) sessionMap.get(AssessmentConstants.ATTR_IS_AUTHORING_RESTRICTED); + SortedSet questionList = getQuestionList(sessionMap); + + // check whether it is "edit(old Question)" or "add(new Question)" + extractFormToAssessmentQuestion(request, questionList, questionForm, isAuthoringRestricted); + reinitializeAvailableQuestions(sessionMap); // set session map ID so that questionlist.jsp can get sessionMAP request.setAttribute(AssessmentConstants.ATTR_SESSION_MAP_ID, questionForm.getSessionMapID()); - return "pages/authoring/parts/questionlist"; + + //in case of edit in monitor and at least one attempted user, we show authoring page with restricted options + if (isAuthoringRestricted) { + return "pages/authoring/parts/questionlistRestricted"; + } else { + return "pages/authoring/parts/questionlist"; + } } /** @@ -438,255 +456,9 @@ String contentFolderID = (String) sessionMap.get(AttributeNames.PARAM_CONTENT_FOLDER_ID); SortedSet questionList = getQuestionList(sessionMap); - Question[] questions = QuestionParser.parseQuestionChoiceForm(request); - for (Question question : questions) { - AssessmentQuestion assessmentQuestion = new AssessmentQuestion(); - int maxSeq = 0; - if ((questionList != null) && (questionList.size() > 0)) { - AssessmentQuestion last = questionList.last(); - maxSeq = last.getSequenceId() + 1; - } - assessmentQuestion.setSequenceId(maxSeq); - assessmentQuestion.setTitle(question.getTitle()); - assessmentQuestion.setQuestion(QuestionParser.processHTMLField(question.getText(), false, contentFolderID, - question.getResourcesFolderPath())); - assessmentQuestion.setGeneralFeedback(QuestionParser.processHTMLField(question.getFeedback(), false, - contentFolderID, question.getResourcesFolderPath())); - assessmentQuestion.setPenaltyFactor(0); + QTIUtil.saveQTI(request, questionList, contentFolderID); - int questionGrade = 1; - - // 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()); - - // setting answers is very similar in both types, so they were put together here - if (isMarkHedgingType) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MARK_HEDGING); - - } else if (isMultipleChoice) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE); - assessmentQuestion.setMultipleAnswersAllowed(false); - assessmentQuestion.setShuffle(false); - assessmentQuestion.setPrefixAnswersWithLetters(false); - - } else { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER); - assessmentQuestion.setCaseSensitive(false); - } - - String correctAnswer = null; - if (question.getAnswers() != null) { - TreeSet optionList = new TreeSet<>(new SequencableComparator()); - int orderId = 0; - 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; - } - AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); - assessmentAnswer.setOptionString(answerText); - assessmentAnswer.setSequenceId(orderId++); - assessmentAnswer.setFeedback(answer.getFeedback()); - - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - // for fill in blanks question all answers are correct and get full grade - if (!isMultipleChoice && !isMarkHedgingType || correctAnswer == null) { - // whatever the correct answer holds, it becomes the question score - questionGrade = Double.valueOf(Math.ceil(answer.getScore())).intValue(); - // 100% goes to the correct answer - assessmentAnswer.setGrade(1); - correctAnswer = answerText; - } else { - log.warn("Choosing only first correct answer, despite another one was found: " - + answerText); - assessmentAnswer.setGrade(0); - } - } else { - assessmentAnswer.setGrade(0); - } - - optionList.add(assessmentAnswer); - } - - assessmentQuestion.setOptions(optionList); - } - - if (correctAnswer == null) { - log.warn("No correct answer found for question: " + question.getText()); - continue; - } - - } else if (Question.QUESTION_TYPE_MULTIPLE_RESPONSE.equals(question.getType())) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE); - assessmentQuestion.setMultipleAnswersAllowed(true); - assessmentQuestion.setShuffle(false); - assessmentQuestion.setPrefixAnswersWithLetters(false); - - if (question.getAnswers() != null) { - float totalScore = 0; - for (Answer answer : question.getAnswers()) { - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - // the question score information is stored as sum of answer scores - totalScore += answer.getScore(); - } - } - questionGrade = Double.valueOf(Math.round(totalScore)).intValue(); - - TreeSet optionList = new TreeSet<>(new SequencableComparator()); - int orderId = 1; - for (Answer answer : question.getAnswers()) { - String answerText = answer.getText(); - AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); - assessmentAnswer.setOptionString(answerText); - assessmentAnswer.setSequenceId(orderId++); - assessmentAnswer.setFeedback(answer.getFeedback()); - - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - // set the factor of score for correct answers - assessmentAnswer.setGrade(answer.getScore() / totalScore); - } else { - assessmentAnswer.setGrade(0); - } - - optionList.add(assessmentAnswer); - } - - assessmentQuestion.setOptions(optionList); - } - - } else if (Question.QUESTION_TYPE_TRUE_FALSE.equals(question.getType())) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_TRUE_FALSE); - - if (question.getAnswers() == null) { - log.warn("Answers missing from true-false question: " + question.getText()); - continue; - } else { - for (Answer answer : question.getAnswers()) { - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - assessmentQuestion.setCorrectAnswer(Boolean.parseBoolean(answer.getText())); - questionGrade = Double.valueOf(Math.ceil(answer.getScore())).intValue(); - } - if (!StringUtils.isBlank(answer.getFeedback())) { - // set feedback for true/false answers - if (Boolean.parseBoolean(answer.getText())) { - assessmentQuestion.setFeedbackOnCorrect(answer.getFeedback()); - } else { - assessmentQuestion.setFeedbackOnIncorrect(answer.getFeedback()); - } - } - } - } - } else if (Question.QUESTION_TYPE_MATCHING.equals(question.getType())) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS); - assessmentQuestion.setShuffle(true); - - if (question.getAnswers() != null) { - // the question score information is stored as sum of answer scores - float totalScore = 0; - for (Answer answer : question.getAnswers()) { - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - totalScore += answer.getScore(); - } - } - questionGrade = Double.valueOf(Math.round(totalScore)).intValue(); - - TreeSet optionList = new TreeSet<>(new SequencableComparator()); - int orderId = 1; - for (int answerIndex = 0; answerIndex < question.getAnswers().size(); answerIndex++) { - // QTI allows answers without a match, but LAMS assessment tool does not - Integer matchAnswerIndex = question.getMatchMap() == null ? null - : question.getMatchMap().get(answerIndex); - Answer matchAnswer = (matchAnswerIndex == null) || (question.getMatchAnswers() == null) ? null - : question.getMatchAnswers().get(matchAnswerIndex); - if (matchAnswer != null) { - Answer answer = question.getAnswers().get(answerIndex); - String answerText = answer.getText(); - AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); - assessmentAnswer.setQuestion(answerText); - assessmentAnswer.setOptionString(matchAnswer.getText()); - assessmentAnswer.setSequenceId(orderId++); - assessmentAnswer.setFeedback(answer.getFeedback()); - - optionList.add(assessmentAnswer); - } - } - - assessmentQuestion.setOptions(optionList); - } - } else if (Question.QUESTION_TYPE_ESSAY.equals(question.getType())) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_ESSAY); - assessmentQuestion.setAllowRichEditor(false); - - } else if (Question.QUESTION_TYPE_ESSAY.equals(question.getType())) { - assessmentQuestion.setType(AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE); - assessmentQuestion.setShuffle(false); - assessmentQuestion.setPrefixAnswersWithLetters(false); - - String correctAnswer = null; - if (question.getAnswers() != null) { - TreeSet optionList = new TreeSet<>(new SequencableComparator()); - 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; - } - AssessmentQuestionOption assessmentAnswer = new AssessmentQuestionOption(); - assessmentAnswer.setOptionString(answerText); - assessmentAnswer.setSequenceId(orderId++); - assessmentAnswer.setFeedback(answer.getFeedback()); - - if ((answer.getScore() != null) && (answer.getScore() > 0)) { - // for fill in blanks question all answers are correct and get full grade - if (correctAnswer == null) { - // whatever the correct answer holds, it becomes the question score - questionGrade = Double.valueOf(Math.ceil(answer.getScore())).intValue(); - // 100% goes to the correct answer - assessmentAnswer.setGrade(1); - correctAnswer = answerText; - } else { - log.warn("Choosing only first correct answer, despite another one was found: " - + answerText); - assessmentAnswer.setGrade(0); - } - } else { - assessmentAnswer.setGrade(0); - } - - optionList.add(assessmentAnswer); - } - - assessmentQuestion.setOptions(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; - } - - assessmentQuestion.setDefaultGrade(questionGrade); - - questionList.add(assessmentQuestion); - if (log.isDebugEnabled()) { - log.debug("Added question: " + assessmentQuestion.getTitle()); - } - } - reinitializeAvailableQuestions(sessionMap); - return "pages/authoring/parts/questionlist"; } @@ -697,163 +469,10 @@ public String exportQTI(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { SessionMap sessionMap = getSessionMap(request); - SortedSet questionList = getQuestionList(sessionMap); - List questions = new LinkedList<>(); - for (AssessmentQuestion assessmentQuestion : questionList) { - Question question = new Question(); - List answers = new ArrayList<>(); + + List questions = QTIUtil.exportQTI(questionList); - switch (assessmentQuestion.getType()) { - - case AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE: - - if (assessmentQuestion.isMultipleAnswersAllowed()) { - question.setType(Question.QUESTION_TYPE_MULTIPLE_RESPONSE); - int correctAnswerCount = 0; - - for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { - if (assessmentAnswer.getGrade() > 0) { - correctAnswerCount++; - } - } - - Float correctAnswerScore = correctAnswerCount > 0 - ? Integer.valueOf(100 / correctAnswerCount).floatValue() - : null; - int incorrectAnswerCount = assessmentQuestion.getOptions().size() - correctAnswerCount; - Float incorrectAnswerScore = incorrectAnswerCount > 0 - ? Integer.valueOf(-100 / incorrectAnswerCount).floatValue() - : null; - - for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { - Answer answer = new Answer(); - boolean isCorrectAnswer = assessmentAnswer.getGrade() > 0; - - answer.setText(assessmentAnswer.getOptionString()); - answer.setScore(isCorrectAnswer ? correctAnswerScore : incorrectAnswerScore); - answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() - : assessmentQuestion.getFeedbackOnIncorrect()); - - answers.add(assessmentAnswer.getSequenceId(), answer); - } - - } else { - question.setType(Question.QUESTION_TYPE_MULTIPLE_CHOICE); - - for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { - Answer answer = new Answer(); - boolean isCorrectAnswer = assessmentAnswer.getGrade() == 1F; - - answer.setText(assessmentAnswer.getOptionString()); - answer.setScore( - isCorrectAnswer ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() - : 0); - answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() - : assessmentQuestion.getFeedbackOnIncorrect()); - - answers.add(assessmentAnswer.getSequenceId(), answer); - } - } - break; - - case AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER: - question.setType(Question.QUESTION_TYPE_FILL_IN_BLANK); - - for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { - // only answer which has more than 0% is considered a correct one - if (assessmentAnswer.getGrade() > 0) { - Answer answer = new Answer(); - answer.setText(assessmentAnswer.getOptionString()); - answer.setScore(Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue()); - - answers.add(answer); - } - } - break; - - case AssessmentConstants.QUESTION_TYPE_TRUE_FALSE: - question.setType(Question.QUESTION_TYPE_TRUE_FALSE); - boolean isTrueCorrect = assessmentQuestion.getCorrectAnswer(); - - // true/false question is basically the same for QTI, just with special answers - Answer trueAnswer = new Answer(); - trueAnswer.setText("True"); - trueAnswer.setScore( - isTrueCorrect ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() : 0); - trueAnswer.setFeedback(isTrueCorrect ? assessmentQuestion.getFeedbackOnCorrect() - : assessmentQuestion.getFeedbackOnIncorrect()); - answers.add(trueAnswer); - - Answer falseAnswer = new Answer(); - falseAnswer.setText("False"); - falseAnswer.setScore( - !isTrueCorrect ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() : 0); - falseAnswer.setFeedback(!isTrueCorrect ? assessmentQuestion.getFeedbackOnCorrect() - : assessmentQuestion.getFeedbackOnIncorrect()); - answers.add(falseAnswer); - break; - - case AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS: - question.setType(Question.QUESTION_TYPE_MATCHING); - - int answerIndex = 0; - float score = assessmentQuestion.getDefaultGrade() / assessmentQuestion.getOptions().size(); - question.setMatchAnswers(new ArrayList(assessmentQuestion.getOptions().size())); - question.setMatchMap(new TreeMap()); - for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { - Answer answer = new Answer(); - - answer.setText(assessmentAnswer.getQuestion()); - answer.setScore(score); - answer.setFeedback(assessmentAnswer.getFeedback()); - answers.add(answer); - - Answer matchingAnswer = new Answer(); - matchingAnswer.setText(assessmentAnswer.getOptionString()); - question.getMatchAnswers().add(matchingAnswer); - question.getMatchMap().put(answerIndex, answerIndex); - answerIndex++; - } - - break; - - case AssessmentConstants.QUESTION_TYPE_ESSAY: - // not much to do with essay - question.setType(Question.QUESTION_TYPE_ESSAY); - answers = null; - break; - - case AssessmentConstants.QUESTION_TYPE_MARK_HEDGING: - - question.setType(Question.QUESTION_TYPE_MARK_HEDGING); - - for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getOptions()) { - Answer answer = new Answer(); - boolean isCorrectAnswer = assessmentAnswer.isCorrect(); - - answer.setText(assessmentAnswer.getOptionString()); - answer.setScore( - isCorrectAnswer ? Integer.valueOf(assessmentQuestion.getDefaultGrade()).floatValue() : 0); - answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() - : assessmentQuestion.getFeedbackOnIncorrect()); - - answers.add(assessmentAnswer.getSequenceId(), answer); - } - break; - - default: - continue; - } - - question.setTitle(assessmentQuestion.getTitle()); - question.setText(assessmentQuestion.getQuestion()); - question.setFeedback(assessmentQuestion.getGeneralFeedback()); - question.setAnswers(answers); - - questions.add(question); - } - String title = request.getParameter("title"); QuestionExporter exporter = new QuestionExporter(title, questions.toArray(Question.QUESTION_ARRAY_TYPE)); exporter.exportQTIPackage(request, response); @@ -870,47 +489,49 @@ SessionMap sessionMap = getSessionMap(request); updateQuestionReferencesGrades(request, sessionMap, false); - int questionIdx = NumberUtils.toInt(request.getParameter(AssessmentConstants.PARAM_QUESTION_INDEX), -1); - if (questionIdx != -1) { - SortedSet questionList = getQuestionList(sessionMap); - List rList = new ArrayList<>(questionList); - AssessmentQuestion question = rList.remove(questionIdx); - questionList.clear(); - questionList.addAll(rList); - // add to delList - List delList = getDeletedQuestionList(sessionMap); - delList.add(question); + int deletedQuestionSequenceId = NumberUtils.toInt(request.getParameter(AssessmentConstants.PARAM_QUESTION_SEQUENCE_ID), -1); + SortedSet questionList = getQuestionList(sessionMap); + Iterator iterator = questionList.iterator(); + AssessmentQuestion deletedQuestion = null; + while (iterator.hasNext()) { + AssessmentQuestion questionIter = iterator.next(); + if (questionIter.getSequenceId() == deletedQuestionSequenceId) { + deletedQuestion = questionIter; + iterator.remove(); + } + } + + // add to delList + List delList = getDeletedQuestionList(sessionMap); + delList.add(deletedQuestion); - // remove according questionReference, if exists - SortedSet questionReferences = getQuestionReferences(sessionMap); - QuestionReference questionReferenceToDelete = null; + // remove according questionReference, if exists + SortedSet questionReferences = getQuestionReferences(sessionMap); + QuestionReference questionReferenceToDelete = null; + for (QuestionReference questionReference : questionReferences) { + if ((questionReference.getQuestion() != null) + && (questionReference.getQuestion().getSequenceId() == deletedQuestionSequenceId)) { + questionReferenceToDelete = questionReference; + } + } + // check if we need to delete random question reference + if ((questionReferenceToDelete == null) && (questionReferences.size() > questionList.size())) { + // find the first random question for (QuestionReference questionReference : questionReferences) { - if ((questionReference.getQuestion() != null) - && (questionReference.getQuestion().getSequenceId() == question.getSequenceId())) { + if (questionReference.isRandomQuestion()) { questionReferenceToDelete = questionReference; + break; } } - // check if we need to delete random question reference - if ((questionReferenceToDelete == null) && (questionReferences.size() > questionList.size())) { - // find the first random question - for (QuestionReference questionReference : questionReferences) { - if (questionReference.isRandomQuestion()) { - questionReferenceToDelete = questionReference; - break; - } - } - } - if (questionReferenceToDelete != null) { - questionReferences.remove(questionReferenceToDelete); - // add to delList - List delReferencesList = getDeletedQuestionReferences(sessionMap); - delReferencesList.add(questionReferenceToDelete); - } - } + if (questionReferenceToDelete != null) { + questionReferences.remove(questionReferenceToDelete); + // add to delList + List delReferencesList = getDeletedQuestionReferences(sessionMap); + delReferencesList.add(questionReferenceToDelete); + } reinitializeAvailableQuestions(sessionMap); - return "pages/authoring/parts/questionlist"; } @@ -1030,7 +651,13 @@ references.addAll(rList); } - return "pages/authoring/parts/questionlist"; + //in case of edit in monitor and at least one attempted user, we show authoring page with restricted options + boolean isAuthoringRestricted = (boolean) sessionMap.get(AssessmentConstants.ATTR_IS_AUTHORING_RESTRICTED); + if (isAuthoringRestricted) { + return "pages/authoring/parts/questionlistRestricted"; + } else { + return "pages/authoring/parts/questionlist"; + } } /** @@ -1332,8 +959,6 @@ /** * refreshes set of all available questions for adding to question list - * - * @param sessionMap */ @SuppressWarnings("unchecked") private void reinitializeAvailableQuestions(SessionMap sessionMap) { @@ -1352,9 +977,6 @@ /** * List save current assessment questions. - * - * @param request - * @return */ @SuppressWarnings("unchecked") private SortedSet getQuestionList(SessionMap sessionMap) { @@ -1383,7 +1005,6 @@ /** * List save deleted assessment questions, which could be persisted or non-persisted questions. - */ @SuppressWarnings("unchecked") private List getDeletedQuestionList(SessionMap sessionMap) { @@ -1450,7 +1071,7 @@ /** * This method will populate assessment question information to its form for edit use. */ - private void populateQuestionToForm(int questionIdx, AssessmentQuestion question, AssessmentQuestionForm form, + private void populateQuestionToForm(AssessmentQuestion question, AssessmentQuestionForm form, HttpServletRequest request) { form.setTitle(question.getTitle()); form.setQuestion(question.getQuestion()); @@ -1472,9 +1093,7 @@ form.setMaxWordsLimit(question.getMaxWordsLimit()); form.setMinWordsLimit(question.getMinWordsLimit()); form.setHedgingJustificationEnabled(question.isHedgingJustificationEnabled()); - if (questionIdx >= 0) { - form.setQuestionIndex(String.valueOf(questionIdx)); - } + form.setSequenceId(question.getSequenceId()); short questionType = question.getType(); if ((questionType == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) @@ -1494,24 +1113,18 @@ /** * Extract web form content to assessment question. + * + * BE CAREFUL: This method will copy necessary info from request form to an old or new AssessmentQuestion + * instance. It gets all info EXCEPT AssessmentQuestion.createDate, which need be set when + * persisting this assessment Question. */ - @SuppressWarnings("unchecked") - private void extractFormToAssessmentQuestion(HttpServletRequest request, AssessmentQuestionForm questionForm) { - /* - * BE CAREFUL: This method will copy nessary info from request form to an old or new AssessmentQuestion - * instance. It - * gets all info EXCEPT AssessmentQuestion.createDate, which need be set when - * persisting - * this assessment Question. - */ - SessionMap sessionMap = (SessionMap) request.getSession() - .getAttribute(questionForm.getSessionMapID()); - // check whether it is "edit(old Question)" or "add(new Question)" - SortedSet questionList = getQuestionList(sessionMap); - int questionIdx = NumberUtils.toInt(questionForm.getQuestionIndex(), -1); - AssessmentQuestion question = null; + private void extractFormToAssessmentQuestion(HttpServletRequest request, SortedSet questionList, + AssessmentQuestionForm questionForm, boolean isAuthoringRestricted) { - if (questionIdx == -1) { // add + //find according question + AssessmentQuestion question = null; + // add + if (questionForm.getSequenceId() == -1) { question = new AssessmentQuestion(); int maxSeq = 1; if ((questionList != null) && (questionList.size() > 0)) { @@ -1520,17 +1133,26 @@ } question.setSequenceId(maxSeq); questionList.add(question); - } else { // edit - List rList = new ArrayList<>(questionList); - question = rList.get(questionIdx); + + // edit + } else { + for (AssessmentQuestion questionIter : questionList) { + if (questionIter.getSequenceId() == questionForm.getSequenceId()) { + question = questionIter; + break; + } + } } + short type = questionForm.getQuestionType(); - question.setType(questionForm.getQuestionType()); + question.setType(type); question.setTitle(questionForm.getTitle()); question.setQuestion(questionForm.getQuestion()); - question.setDefaultGrade(Integer.parseInt(questionForm.getDefaultGrade())); + if (!isAuthoringRestricted) { + question.setDefaultGrade(Integer.parseInt(questionForm.getDefaultGrade())); + } question.setGeneralFeedback(questionForm.getGeneralFeedback()); question.setAnswerRequired(questionForm.isAnswerRequired()); @@ -1645,35 +1267,38 @@ int questionType = WebUtil.readIntParam(request, AssessmentConstants.ATTR_QUESTION_TYPE); Integer correctOptionIndex = (paramMap.get(AssessmentConstants.ATTR_OPTION_CORRECT) == null) ? null : NumberUtils.toInt(paramMap.get(AssessmentConstants.ATTR_OPTION_CORRECT)); + TreeSet optionList = new TreeSet<>(new SequencableComparator()); for (int i = 0; i < count; i++) { + AssessmentQuestionOption option = new AssessmentQuestionOption(); + String uid = paramMap.get(AssessmentConstants.ATTR_OPTION_UID_PREFIX + i); + if (uid != null) { + option.setUid(NumberUtils.toLong(uid)); + } + String sequenceId = paramMap.get(AssessmentConstants.ATTR_OPTION_SEQUENCE_ID_PREFIX + i); + option.setSequenceId(NumberUtils.toInt(sequenceId)); + if ((questionType == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) || (questionType == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER)) { String optionString = paramMap.get(AssessmentConstants.ATTR_OPTION_STRING_PREFIX + i); if ((optionString == null) && isForSaving) { continue; } - AssessmentQuestionOption option = new AssessmentQuestionOption(); - String sequenceId = paramMap.get(AssessmentConstants.ATTR_OPTION_SEQUENCE_ID_PREFIX + i); - option.setSequenceId(NumberUtils.toInt(sequenceId)); option.setOptionString(optionString); float grade = Float.valueOf(paramMap.get(AssessmentConstants.ATTR_OPTION_GRADE_PREFIX + i)); option.setGrade(grade); option.setFeedback(paramMap.get(AssessmentConstants.ATTR_OPTION_FEEDBACK_PREFIX + i)); - optionList.add(option); + } else if (questionType == AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS) { String question = paramMap.get(AssessmentConstants.ATTR_OPTION_QUESTION_PREFIX + i); if ((question == null) && isForSaving) { continue; } - AssessmentQuestionOption option = new AssessmentQuestionOption(); - String sequenceId = paramMap.get(AssessmentConstants.ATTR_OPTION_SEQUENCE_ID_PREFIX + i); - option.setSequenceId(NumberUtils.toInt(sequenceId)); option.setOptionString(paramMap.get(AssessmentConstants.ATTR_OPTION_STRING_PREFIX + i)); option.setQuestion(question); - optionList.add(option); + } else if (questionType == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { String optionFloatStr = paramMap.get(AssessmentConstants.ATTR_OPTION_FLOAT_PREFIX + i); String acceptedErrorStr = paramMap.get(AssessmentConstants.ATTR_OPTION_ACCEPTED_ERROR_PREFIX + i); @@ -1683,9 +1308,6 @@ continue; } - AssessmentQuestionOption option = new AssessmentQuestionOption(); - String sequenceId = paramMap.get(AssessmentConstants.ATTR_OPTION_SEQUENCE_ID_PREFIX + i); - option.setSequenceId(NumberUtils.toInt(sequenceId)); try { float optionFloat = Float.valueOf(optionFloatStr); option.setOptionFloat(optionFloat); @@ -1701,36 +1323,31 @@ float grade = Float.valueOf(paramMap.get(AssessmentConstants.ATTR_OPTION_GRADE_PREFIX + i)); option.setGrade(grade); option.setFeedback(paramMap.get(AssessmentConstants.ATTR_OPTION_FEEDBACK_PREFIX + i)); - optionList.add(option); + } else if (questionType == AssessmentConstants.QUESTION_TYPE_ORDERING) { String optionString = paramMap.get(AssessmentConstants.ATTR_OPTION_STRING_PREFIX + i); if ((optionString == null) && isForSaving) { continue; } - AssessmentQuestionOption option = new AssessmentQuestionOption(); - String sequenceId = paramMap.get(AssessmentConstants.ATTR_OPTION_SEQUENCE_ID_PREFIX + i); - option.setSequenceId(NumberUtils.toInt(sequenceId)); option.setOptionString(optionString); //TODO check the following line is not required -// option.setAnswerInt(i); - optionList.add(option); + //option.setAnswerInt(i); + } else if (questionType == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) { String optionString = paramMap.get(AssessmentConstants.ATTR_OPTION_STRING_PREFIX + i); if ((optionString == null) && isForSaving) { continue; } - AssessmentQuestionOption option = new AssessmentQuestionOption(); - String sequenceId = paramMap.get(AssessmentConstants.ATTR_OPTION_SEQUENCE_ID_PREFIX + i); - option.setSequenceId(NumberUtils.toInt(sequenceId)); option.setOptionString(optionString); if ((correctOptionIndex != null) && correctOptionIndex.equals(Integer.valueOf(sequenceId))) { option.setCorrect(true); } option.setFeedback(paramMap.get(AssessmentConstants.ATTR_OPTION_FEEDBACK_PREFIX + i)); - optionList.add(option); } + + optionList.add(option); } return optionList; } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java =================================================================== diff -u -rdcdc1487609bd4f00afaa93c09272d84ab0cd325 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision dcdc1487609bd4f00afaa93c09272d84ab0cd325) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -58,7 +58,6 @@ import org.lamsfoundation.lams.tool.assessment.dto.QuestionDTO; import org.lamsfoundation.lams.tool.assessment.dto.QuestionSummary; import org.lamsfoundation.lams.tool.assessment.model.Assessment; -import org.lamsfoundation.lams.tool.assessment.model.AssessmentOptionAnswer; import org.lamsfoundation.lams.tool.assessment.model.AssessmentOverallFeedback; import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionResult; @@ -68,7 +67,6 @@ import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; import org.lamsfoundation.lams.tool.assessment.service.AssessmentApplicationException; import org.lamsfoundation.lams.tool.assessment.service.IAssessmentService; -import org.lamsfoundation.lams.tool.assessment.util.AnswerIntComparator; import org.lamsfoundation.lams.tool.assessment.util.AssessmentSessionComparator; import org.lamsfoundation.lams.tool.assessment.util.SequencableComparator; import org.lamsfoundation.lams.tool.assessment.web.form.ReflectionForm; @@ -353,8 +351,8 @@ sessionMap.put(AssessmentConstants.ATTR_PAGE_NUMBER, 1); sessionMap.put(AssessmentConstants.ATTR_ASSESSMENT, assessment); - // loadupLastAttempt for display purpose - loadupLastAttempt(sessionMap); + // loadupLastAttempt for displaying purposes + service.loadupLastAttempt(assessment.getUid(), user.getUserId(), pagedQuestionDtos); if (showResults) { @@ -509,13 +507,7 @@ // store results from sessionMap into DB Long singleMarkHedgingQuestionUid = WebUtil.readLongParam(request, "singleMarkHedgingQuestionUid"); - boolean isResultsStored = service.storeUserAnswers(assessment, userId, pagedQuestionDtos, - singleMarkHedgingQuestionUid, false); - // result was not stored in case user was prohibited from submitting (or autosubmitting) answers (e.g. when - // using 2 browsers). Then show last stored results - if (!isResultsStored) { - //loadupLastAttempt(sessionMap); - } + service.storeSingleMarkHedgingQuestion(assessment, userId, pagedQuestionDtos, singleMarkHedgingQuestionUid); //find according question in order to get its mark QuestionDTO questionDto = null; @@ -548,6 +540,7 @@ * @throws InvocationTargetException * @throws IllegalAccessException */ + @SuppressWarnings("unchecked") @RequestMapping("/submitAll") public String submitAll(HttpServletRequest request) throws ServletException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { @@ -573,7 +566,11 @@ // result was not stored in case user was prohibited from submitting (or autosubmitting) answers (e.g. when // using 2 browsers). Then show last stored results if (!isResultsStored) { - loadupLastAttempt(sessionMap); + List> pagedQuestionDtos = (List>) sessionMap + .get(AssessmentConstants.ATTR_PAGED_QUESTION_DTOS); + Long assessmentUid = ((Assessment) sessionMap.get(AssessmentConstants.ATTR_ASSESSMENT)).getUid(); + Long userId = ((AssessmentUser) sessionMap.get(AssessmentConstants.ATTR_USER)).getUserId(); + service.loadupLastAttempt(assessmentUid, userId, pagedQuestionDtos); } String redirectURL = "redirect:/learning/start.do"; @@ -1107,92 +1104,6 @@ sessionMap.put(AssessmentConstants.ATTR_IS_RESUBMIT_ALLOWED, isResubmitAllowed); } - @SuppressWarnings("unchecked") - private void loadupLastAttempt(SessionMap sessionMap) { - List> pagedQuestionDtos = (List>) sessionMap - .get(AssessmentConstants.ATTR_PAGED_QUESTION_DTOS); - Long assessmentUid = ((Assessment) sessionMap.get(AssessmentConstants.ATTR_ASSESSMENT)).getUid(); - Long userId = ((AssessmentUser) sessionMap.get(AssessmentConstants.ATTR_USER)).getUserId(); - //get the latest result (it can be unfinished one) - AssessmentResult lastResult = service.getLastAssessmentResult(assessmentUid, userId); - //if there is no results yet - no action required - if (lastResult == null) { - return; - } - - //get the latest finished result (required for mark hedging type of questions only) - AssessmentResult lastFinishedResult = null; - if (lastResult.getFinishDate() == null) { - lastFinishedResult = service.getLastFinishedAssessmentResult(assessmentUid, userId); - } - - for (Set questionsForOnePage : pagedQuestionDtos) { - for (QuestionDTO questionDto : questionsForOnePage) { - - //load last finished results for hedging type of questions (in order to prevent retry) - Set questionResults = lastResult.getQuestionResults(); - if ((questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) - && (lastResult.getFinishDate() == null) && (lastFinishedResult != null)) { - questionResults = lastFinishedResult.getQuestionResults(); - } - - for (AssessmentQuestionResult questionResult : questionResults) { - if (questionDto.getUid().equals(questionResult.getAssessmentQuestion().getUid())) { - questionDto.setAnswerBoolean(questionResult.getAnswerBoolean()); - questionDto.setAnswerFloat(questionResult.getAnswerFloat()); - questionDto.setAnswerString(questionResult.getAnswerString()); - questionDto.setMark(questionResult.getMark()); - questionDto.setResponseSubmitted(questionResult.getFinishDate() != null); - questionDto.setPenalty(questionResult.getPenalty()); - questionDto.setConfidenceLevel(questionResult.getConfidenceLevel()); - - for (OptionDTO optionDto : questionDto.getOptionDtos()) { - - for (AssessmentOptionAnswer optionAnswer : questionResult.getOptionAnswers()) { - if (optionDto.getUid().equals(optionAnswer.getOptionUid())) { - optionDto.setAnswerBoolean(optionAnswer.getAnswerBoolean()); - optionDto.setAnswerInt(optionAnswer.getAnswerInt()); - break; - } - } - } - - //sort ordering type of question in order to show how learner has sorted them - if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) { - - //don't sort ordering type of questions that haven't been submitted to not break their shuffled order - boolean isOptionAnswersNeverSubmitted = true; - for (OptionDTO optionDto : questionDto.getOptionDtos()) { - if (optionDto.getAnswerInt() != 0) { - isOptionAnswersNeverSubmitted = false; - } - } - - if (!isOptionAnswersNeverSubmitted) { - TreeSet orderedSet = new TreeSet<>(new AnswerIntComparator()); - orderedSet.addAll(questionDto.getOptionDtos()); - questionDto.setOptionDtos(orderedSet); - } - } - - // set answerTotalGrade to let jsp know whether the question was answered correctly/partly/incorrectly even if mark=0 - if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { - float totalGrade = 0; - for (OptionDTO optionDto : questionDto.getOptionDtos()) { - if (optionDto.getAnswerBoolean()) { - totalGrade += optionDto.getGrade(); - } - } - questionDto.setAnswerTotalGrade(totalGrade); - } - - break; - } - } - } - } - } - /** * Store user answers in DB in last unfinished attempt and notify teachers about it. */ @@ -1207,7 +1118,7 @@ ToolAccessMode mode = (ToolAccessMode) sessionMap.get(AttributeNames.ATTR_MODE); Assessment assessment = service.getAssessmentBySessionId(toolSessionId); - boolean isResultsStored = service.storeUserAnswers(assessment, userId, pagedQuestionDtos, null, isAutosave); + boolean isResultsStored = service.storeUserAnswers(assessment, userId, pagedQuestionDtos, isAutosave); // notify teachers if ((mode != null) && !mode.isTeacher() && !isAutosave && isResultsStored Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/form/AssessmentQuestionForm.java =================================================================== diff -u -r4353f26f9509808acdf41d0deef6cf5fd458a2b7 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/form/AssessmentQuestionForm.java (.../AssessmentQuestionForm.java) (revision 4353f26f9509808acdf41d0deef6cf5fd458a2b7) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/form/AssessmentQuestionForm.java (.../AssessmentQuestionForm.java) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -31,7 +31,7 @@ */ public class AssessmentQuestionForm { - private String questionIndex; + private int sequenceId; private String sessionMapID; private String contentFolderID; @@ -101,12 +101,12 @@ this.question = question; } - public String getQuestionIndex() { - return questionIndex; + public int getSequenceId() { + return sequenceId; } - public void setQuestionIndex(String questionIndex) { - this.questionIndex = questionIndex; + public void setSequenceId(int sequenceId) { + this.sequenceId = sequenceId; } public short getQuestionType() { Index: lams_tool_assessment/web/common/header.jsp =================================================================== diff -u -r4353f26f9509808acdf41d0deef6cf5fd458a2b7 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/common/header.jsp (.../header.jsp) (revision 4353f26f9509808acdf41d0deef6cf5fd458a2b7) +++ lams_tool_assessment/web/common/header.jsp (.../header.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -1,7 +1,5 @@ <%@ include file="/common/taglibs.jsp"%> - - Index: lams_tool_assessment/web/common/tabbedheader.jsp =================================================================== diff -u -r4353f26f9509808acdf41d0deef6cf5fd458a2b7 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/common/tabbedheader.jsp (.../tabbedheader.jsp) (revision 4353f26f9509808acdf41d0deef6cf5fd458a2b7) +++ lams_tool_assessment/web/common/tabbedheader.jsp (.../tabbedheader.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -1,7 +1,5 @@ <%@ include file="/common/taglibs.jsp"%> - - Index: lams_tool_assessment/web/pages/authoring/authoring.jsp =================================================================== diff -u -raced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/pages/authoring/authoring.jsp (.../authoring.jsp) (revision aced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194) +++ lams_tool_assessment/web/pages/authoring/authoring.jsp (.../authoring.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -1,6 +1,8 @@ <%@ include file="/common/taglibs.jsp"%> <%@ page import="org.lamsfoundation.lams.tool.assessment.AssessmentConstants"%> + + Index: lams_tool_assessment/web/pages/authoring/basic.jsp =================================================================== diff -u -r4353f26f9509808acdf41d0deef6cf5fd458a2b7 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/pages/authoring/basic.jsp (.../basic.jsp) (revision 4353f26f9509808acdf41d0deef6cf5fd458a2b7) +++ lams_tool_assessment/web/pages/authoring/basic.jsp (.../basic.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -2,7 +2,9 @@ - + + + + + + + + +
@@ -175,28 +183,37 @@
- <%@ include file="/pages/authoring/parts/questionlist.jsp"%> + + + <%@ include file="/pages/authoring/parts/questionlistRestricted.jsp"%> + + + <%@ include file="/pages/authoring/parts/questionlist.jsp"%> + +
- -
- - - - - + + +
+ -
-

+ + + +
+

+ + + + - - Index: lams_tool_assessment/web/pages/authoring/parts/addessay.jsp =================================================================== diff -u -raced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/pages/authoring/parts/addessay.jsp (.../addessay.jsp) (revision aced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194) +++ lams_tool_assessment/web/pages/authoring/parts/addessay.jsp (.../addessay.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -115,9 +115,11 @@ + + - +
-
- - -
+ +
+ + +
+
+ + - + @@ -134,13 +136,15 @@
-
- - -
+ +
+ + +
+
Index: lams_tool_assessment/web/pages/authoring/parts/addmatchingpairs.jsp =================================================================== diff -u -raced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/pages/authoring/parts/addmatchingpairs.jsp (.../addmatchingpairs.jsp) (revision aced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194) +++ lams_tool_assessment/web/pages/authoring/parts/addmatchingpairs.jsp (.../addmatchingpairs.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -104,15 +104,16 @@ + + - + -
- - + + + + +
+ + - + @@ -185,11 +187,14 @@
- - + + + + +
-
@@ -288,7 +292,6 @@ - Index: lams_tool_assessment/web/pages/authoring/parts/addnumerical.jsp =================================================================== diff -u -raced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194 -r67d7232f087b9f5c72ff41f7bbebe29cff81e099 --- lams_tool_assessment/web/pages/authoring/parts/addnumerical.jsp (.../addnumerical.jsp) (revision aced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194) +++ lams_tool_assessment/web/pages/authoring/parts/addnumerical.jsp (.../addnumerical.jsp) (revision 67d7232f087b9f5c72ff41f7bbebe29cff81e099) @@ -177,11 +177,13 @@ + + - +
@@ -208,11 +210,13 @@
- - + + + +
+ + - + @@ -139,11 +141,13 @@
- - + + + +
+ + - +
@@ -143,11 +145,13 @@
- - + + + +
+ + - +
- - + + + +