Index: lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java =================================================================== diff -u -r2e9ee1c2451a05981f05e9edf89bb3356cf2b147 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision 2e9ee1c2451a05981f05e9edf89bb3356cf2b147) +++ lams_common/src/java/org/lamsfoundation/lams/qb/service/IQbService.java (.../IQbService.java) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -1,6 +1,18 @@ package org.lamsfoundation.lams.qb.service; public interface IQbService { + + // statuses of comparing QB question coming from authoring with data existing in DB + + // no change detected + static final int QUESTION_MODIFIED_NONE = 0; + // change is minor, so the original question will be modified + static final int QUESTION_MODIFIED_UPDATE = 1; + // it is a new version of the old question + static final int QUESTION_MODIFIED_VERSION_BUMP = 2; + // it is a new question + static final int QUESTION_MODIFIED_ID_BUMP = 3; + // finds next question ID for Question Bank question int getMaxQuestionId(); Index: lams_tool_lamc/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rdc41ab1a82d32dfecca77a3db6936fe116c164b7 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_tool_lamc/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision dc41ab1a82d32dfecca77a3db6936fe116c164b7) +++ lams_tool_lamc/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -234,6 +234,8 @@ outcome.authoring.existing.none =none warn.tool.output.change.none =This will delete all existing marks for this activity. Are you sure? output.desc.none =No score +message.qb.modified.update =The question in Question Bank will be updated +message.qb.modified.version =A new version of the question will be created in Question Bank +message.qb.modified.new =A new question will be created in Question Bank - #======= End labels: Exported 228 labels for en AU ===== Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dto/McQuestionDTO.java =================================================================== diff -u -rd9b3433f41c2b25011809144d241d33237d9b9d8 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dto/McQuestionDTO.java (.../McQuestionDTO.java) (revision d9b3433f41c2b25011809144d241d33237d9b9d8) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dto/McQuestionDTO.java (.../McQuestionDTO.java) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -25,6 +25,8 @@ import java.util.List; +import org.lamsfoundation.lams.qb.service.IQbService; + /** * DTO that holds users attempt history data for jsp purposes * @@ -33,6 +35,7 @@ public class McQuestionDTO implements Comparable { private Long uid; private Long qbQuestionUid; + private int qbQuestionModified = IQbService.QUESTION_MODIFIED_NONE; private String question; private Integer displayOrder; private String feedback; @@ -73,6 +76,14 @@ this.qbQuestionUid = qaQuestionId; } + public int getQbQuestionModified() { + return qbQuestionModified; + } + + public void setQbQuestionModified(int qbQuestionModified) { + this.qbQuestionModified = qbQuestionModified; + } + /** * @return Returns the displayOrder. */ Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/IMcService.java =================================================================== diff -u -rc87bb47eb670934f10192c08922f83367bc36230 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/IMcService.java (.../IMcService.java) (revision c87bb47eb670934f10192c08922f83367bc36230) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/IMcService.java (.../IMcService.java) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -267,4 +267,9 @@ */ List getSessionDtos(Long contentId, boolean includeStatistics); -} + /** + * Checks if data in DTO is the same as in the corresponding QB question and options in DB. + * Returns one of statuses from IQbService.QUESTION_MODIFIED_* + */ + int isQbQuestionModified(McQuestionDTO questionDTO); +} \ No newline at end of file Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java =================================================================== diff -u -r2e9ee1c2451a05981f05e9edf89bb3356cf2b147 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision 2e9ee1c2451a05981f05e9edf89bb3356cf2b147) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -300,45 +300,30 @@ qbQuestionClone.setMark(Integer.valueOf(currentMark)); qbQuestionClone.setFeedback(currentFeedback); - List optionDTOs = questionDTO.getOptionDtos(); - // basic question data does not match, skip option checking - boolean isModified = qbQuestion.isModified(qbQuestionClone) - || optionDTOs.size() != qbQuestionClone.getQbOptions().size(); - if (!isModified) { - // check if options changed - for (int i = 0; i < optionDTOs.size(); i++) { - McOptionDTO optionDTO = optionDTOs.get(i); - String optionText = optionDTO.getCandidateAnswer(); - boolean isCorrectOption = "Correct".equals(optionDTO.getCorrect()); - - //find persisted option if it exists - for (QbOption qbOption : qbQuestionClone.getQbOptions()) { - if (optionDTO.getQbOptionUid().equals(qbOption.getUid())) { - qbOption.setCorrect(isCorrectOption); - qbOption.setName(optionText); - qbOption.setDisplayOrder(i + 1); - break; - } - } - } - // run check again, this time with modified options - isModified = qbQuestion.isModified(qbQuestionClone); + // modification status was already set in McController#saveQuestion() + switch (questionDTO.getQbQuestionModified()) { + case IQbService.QUESTION_MODIFIED_NONE: + // if no modification was made, prevent clone from being persisted + releaseQbQuestionFromCache(qbQuestionClone); + break; + case IQbService.QUESTION_MODIFIED_UPDATE: + // simply accept the modified clone as new version of the question + // this option is not supported yet + qbQuestion = qbQuestionClone; + break; + case IQbService.QUESTION_MODIFIED_VERSION_BUMP: + // new version of the old question gets created + qbQuestion = qbQuestionClone; + qbQuestion.setVersion(qbService.getMaxQuestionVersion(qbQuestion.getQuestionId())); + break; + case IQbService.QUESTION_MODIFIED_ID_BUMP: + // new question gets created + qbQuestion = qbQuestionClone; + qbQuestion.setVersion(1); + qbQuestion.setQuestionId(qbService.getMaxQuestionId()); + break; } - if (isModified) { - // if vital data changed, the already modified clone becomes the new question - qbQuestion = qbQuestionClone; - // create a new question version instead of modifying existing one - int newVersion = qbService.getMaxQuestionVersion(qbQuestion.getQuestionId()); - qbQuestion.setVersion(newVersion); - } else { - // if no modification was made, prevent clone from being persisted - mcQueContentDAO.releaseFromCache(qbQuestionClone); - for (QbOption option : qbQuestionClone.getQbOptions()) { - mcQueContentDAO.releaseFromCache(option); - } - } - // in case question doesn't exist if (question == null) { question = new McQueContent(qbQuestion, null, new Integer(displayOrder), content); @@ -359,6 +344,7 @@ List newOptions = new ArrayList<>(); int displayOrderOption = 1; Set qbOptionsToRemove = new HashSet<>(qbQuestion.getQbOptions()); + List optionDTOs = questionDTO.getOptionDtos(); for (McOptionDTO optionDTO : optionDTOs) { String optionText = optionDTO.getCandidateAnswer(); @@ -376,6 +362,7 @@ if (optionDTO.getQbOptionUid() == null) { // it is a new option option = new QbOption(); + option.setQbQuestion(qbQuestion); } else { // match existing options with DTO data for (QbOption qbOption : qbQuestion.getQbOptions()) { @@ -402,9 +389,9 @@ // make removed options orphaned, so they will be removed from DB qbQuestion.getQbOptions().removeAll(qbOptionsToRemove); qbOptionsToRemove.clear(); - // if clone is the real question, clear its IDs so it is saved as a new question - // it needs to be only now as above we used option UID matching - if (isModified) { + // if clone is the real question, clear its IDs so it is saved as a new question or version + // it needs to happen only now as above we used option UID matching + if (questionDTO.getQbQuestionModified() > IQbService.QUESTION_MODIFIED_UPDATE) { qbQuestion.clearID(); } @@ -2054,4 +2041,69 @@ } } + + @Override + public int isQbQuestionModified(McQuestionDTO questionDTO) { + String currentQuestionText = questionDTO.getQuestion(); + + // skip empty questions + if (currentQuestionText.isEmpty()) { + return IQbService.QUESTION_MODIFIED_NONE; + } + QbQuestion baseLine = questionDTO.getQbQuestionUid() == null ? null + : (QbQuestion) mcQueContentDAO.find(QbQuestion.class, questionDTO.getQbQuestionUid()); + if (baseLine == null) { + return IQbService.QUESTION_MODIFIED_ID_BUMP; + } + + releaseQbQuestionFromCache(baseLine); + // make a clone to check if data changed + QbQuestion modifiedQuestion = baseLine.clone(); + releaseQbQuestionFromCache(modifiedQuestion); + + String currentFeedback = questionDTO.getFeedback(); + String currentMark = questionDTO.getMark(); + /* set the default mark in case it is not provided */ + if (currentMark == null) { + currentMark = "1"; + } + + // set clone's data to current values + modifiedQuestion.setName(currentQuestionText); + modifiedQuestion.setMark(Integer.valueOf(currentMark)); + modifiedQuestion.setFeedback(currentFeedback); + + List optionDTOs = questionDTO.getOptionDtos(); + boolean isModified = baseLine.isModified(modifiedQuestion) + || optionDTOs.size() != modifiedQuestion.getQbOptions().size(); + if (isModified) { + return IQbService.QUESTION_MODIFIED_VERSION_BUMP; + } + // check if options changed + for (int i = 0; i < optionDTOs.size(); i++) { + McOptionDTO optionDTO = optionDTOs.get(i); + String optionText = optionDTO.getCandidateAnswer(); + boolean isCorrectOption = "Correct".equals(optionDTO.getCorrect()); + + //find persisted option if it exists + for (QbOption qbOption : modifiedQuestion.getQbOptions()) { + if (optionDTO.getQbOptionUid().equals(qbOption.getUid())) { + qbOption.setCorrect(isCorrectOption); + qbOption.setName(optionText); + qbOption.setDisplayOrder(i + 1); + break; + } + } + } + // run check again, this time with modified options + return baseLine.isModified(modifiedQuestion) ? IQbService.QUESTION_MODIFIED_VERSION_BUMP + : IQbService.QUESTION_MODIFIED_NONE; + } + + private void releaseQbQuestionFromCache(QbQuestion qbQuestion) { + mcQueContentDAO.releaseFromCache(qbQuestion); + for (QbOption option : qbQuestion.getQbOptions()) { + mcQueContentDAO.releaseFromCache(option); + } + } } \ No newline at end of file Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McController.java =================================================================== diff -u -r650202864c49257c570cd5c4d496ca3789db7846 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McController.java (.../McController.java) (revision 650202864c49257c570cd5c4d496ca3789db7846) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McController.java (.../McController.java) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -36,6 +36,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.qb.service.IQbService; import org.lamsfoundation.lams.questions.Answer; import org.lamsfoundation.lams.questions.Question; import org.lamsfoundation.lams.questions.QuestionExporter; @@ -471,6 +472,8 @@ questionDTO.setDisplayOrder(maxDisplayOrder + 1); questionDTO.setOptionDtos(options); questionDTO.setMark(mark); + questionDTO.setQbQuestionModified(IQbService.QUESTION_MODIFIED_ID_BUMP); + request.setAttribute("qbQuestionModified", questionDTO.getQbQuestionModified()); questionDTOs.add(questionDTO); @@ -492,6 +495,9 @@ questionDto.setDisplayOrder(questionIndex); questionDto.setOptionDtos(options); questionDto.setMark(mark); + + questionDto.setQbQuestionModified(mcService.isQbQuestionModified(questionDto)); + request.setAttribute("qbQuestionModified", questionDto.getQbQuestionModified()); } } else { // entry blank, not adding Index: lams_tool_lamc/web/authoring/itemlist.jsp =================================================================== diff -u -r9e395fca5d7eb4a5ac4c9768642a336723a950f7 -rf24a82ae2ef1cbaa92dfd356226f6c3edcd5e404 --- lams_tool_lamc/web/authoring/itemlist.jsp (.../itemlist.jsp) (revision 9e395fca5d7eb4a5ac4c9768642a336723a950f7) +++ lams_tool_lamc/web/authoring/itemlist.jsp (.../itemlist.jsp) (revision f24a82ae2ef1cbaa92dfd356226f6c3edcd5e404) @@ -1,7 +1,28 @@ <%@ include file="/common/taglibs.jsp"%> +<%@ page import="org.lamsfoundation.lams.qb.service.IQbService" %> + +