Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190110.sql =================================================================== diff -u -r31352ec47730fc3414d7bef7ce65ac695bb45a15 -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190110.sql (.../patch20190110.sql) (revision 31352ec47730fc3414d7bef7ce65ac695bb45a15) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20190110.sql (.../patch20190110.sql) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -9,7 +9,7 @@ `type` TINYINT NOT NULL, `question_id` INT NOT NULL, `version` SMALLINT NOT NULL DEFAULT 1, - `name` TEXT, + `name` TEXT NOT NULL, `mark` INT, `feedback` TEXT, PRIMARY KEY (uid), @@ -27,20 +27,45 @@ -- prepare MCQ's question table for referencing Question Bank's question ALTER TABLE tl_lamc11_que_content ADD COLUMN qb_question_uid BIGINT AFTER uid, - ADD CONSTRAINT FK_qb_question FOREIGN KEY (qb_question_uid) REFERENCES lams_qb_question (uid) ON UPDATE CASCADE; + ADD CONSTRAINT FK_tl_lamc11_que_content_2 FOREIGN KEY (qb_question_uid) REFERENCES lams_qb_question (uid) ON UPDATE CASCADE; -- find matching questions in Question Bank and set up references UPDATE tl_lamc11_que_content AS mcq, lams_qb_question AS qb SET mcq.qb_question_uid = qb.ui - WHERE TRIM(mcq.question) = qb.name AND mcq.mark = qb.mark + WHERE TRIM(mcq.question) = qb.name + AND mcq.mark = qb.mark AND (TRIM(mcq.feedback) = qb.feedback OR (IF(TRIM(mcq.feedback) = '', NULL, TRIM(mcq.feedback)) IS NULL AND qb.feedback IS NULL)) AND qb.question_id > @start_question_id; -- remove columns from MCQ which are duplicated in Question Bank ALTER TABLE tl_lamc11_que_content DROP COLUMN question, DROP COLUMN mark, DROP COLUMN feedback; + +-- create Question Bank option +CREATE TABLE lams_qb_option (`uid` BIGINT AUTO_INCREMENT, + `qb_question_uid` BIGINT NOT NULL, + `name` TEXT NOT NULL, + `correct` TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (uid), + CONSTRAINT FK_lams_qb_option_1 FOREIGN KEY (qb_question_uid) REFERENCES lams_qb_question (uid) ON DELETE CASCADE ON UPDATE CASCADE); +INSERT INTO lams_qb_option (qb_question_uid, name, correct) + SELECT DISTINCT q.qb_question_uid, TRIM(o.mc_que_option_text), o.correct_option + FROM tl_lamc11_que_content AS q JOIN tl_lamc11_options_content AS o ON q.uid = o.mc_que_content_id; + +ALTER TABLE tl_lamc11_options_content ADD COLUMN qb_option_uid BIGINT AFTER uid, + ADD CONSTRAINT FK_tl_lamc11_options_content_2 FOREIGN KEY (qb_option_uid) REFERENCES lams_qb_option (uid) ON UPDATE CASCADE; + +UPDATE tl_lamc11_options_content AS mco, tl_lamc11_que_content AS mcq, lams_qb_option AS qo + SET mco.qb_option_uid = qo.uid + WHERE TRIM(mco.mc_que_option_text) = qo.name + AND qo.qb_question_uid = mcq.qb_question_uid + AND mco.mc_que_content_id = mcq.uid; + +ALTER TABLE tl_lamc11_options_content DROP COLUMN mc_que_option_text, + DROP COLUMN correct_option; + ----------------------Put all sql statements above here------------------------- -- If there were no errors, commit and restore autocommit to on Index: lams_common/src/java/org/lamsfoundation/lams/qb/QbOption.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/qb/QbOption.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/qb/QbOption.java (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -0,0 +1,66 @@ +package org.lamsfoundation.lams.qb; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +/** + * One of possible answers for a question in Question Bank. + * + * @author Marcin Cieslak + */ +@Entity +@Table(name = "lams_qb_option") +public class QbOption implements Serializable, Cloneable { + private static final long serialVersionUID = -2354311780882736829L; + + @Id + @Column + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long uid; + + @Column + private String name; + + @Column + private boolean correct = false; + + @ManyToOne(optional = false) + @JoinColumn(name = "qb_question_uid") + private QbQuestion question; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isCorrect() { + return correct; + } + + public void setCorrect(boolean correct) { + this.correct = correct; + } + + public QbQuestion getQuestion() { + return question; + } + + public void setQuestion(QbQuestion question) { + this.question = question; + } + + public Long getUid() { + return uid; + } +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/qb/QbQuestion.java =================================================================== diff -u -rcd39ade0293bfdcdb89176284da7138266107689 -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_common/src/java/org/lamsfoundation/lams/qb/QbQuestion.java (.../QbQuestion.java) (revision cd39ade0293bfdcdb89176284da7138266107689) +++ lams_common/src/java/org/lamsfoundation/lams/qb/QbQuestion.java (.../QbQuestion.java) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -1,12 +1,17 @@ package org.lamsfoundation.lams.qb; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; +import javax.persistence.OneToMany; import javax.persistence.PostLoad; import javax.persistence.Table; import javax.persistence.Transient; @@ -62,6 +67,9 @@ @Column private String feedback; + @OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = CascadeType.ALL) + private List options = new ArrayList<>(); + // question state when it was loaded from DB @Transient private QbQuestion initialContent; @@ -159,4 +167,12 @@ public void setFeedback(String feedback) { this.feedback = StringUtils.isBlank(feedback) ? null : feedback.trim(); } + + public List getOptions() { + return options; + } + + public void setOptions(List options) { + this.options = options; + } } \ No newline at end of file Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dto/McOptionDTO.java =================================================================== diff -u -rd03a1456d2d0ee7a482273fe35412c67054f133d -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dto/McOptionDTO.java (.../McOptionDTO.java) (revision d03a1456d2d0ee7a482273fe35412c67054f133d) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dto/McOptionDTO.java (.../McOptionDTO.java) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -32,9 +32,10 @@ public class McOptionDTO implements Comparable { private Long uid; + private Long qbOptionUid; private String candidateAnswer; private String correct; - + /** * used for TBL monitoring */ @@ -47,6 +48,7 @@ public McOptionDTO(McOptsContent option) { this.uid = option.getUid(); this.candidateAnswer = option.getMcQueOptionText(); + this.qbOptionUid = option.getQbOption().getUid(); //this.correct = new Boolean(option.isCorrectOption()).toString(); if (option.isCorrectOption()) { @@ -71,6 +73,14 @@ this.uid = uid; } + public Long getQbOptionUid() { + return qbOptionUid; + } + + public void setQbOptionUid(Long qbOptionUid) { + this.qbOptionUid = qbOptionUid; + } + /** * @return Returns the candidateAnswer. */ @@ -100,11 +110,11 @@ public void setCorrect(String correct) { this.correct = correct; } - + public float getPercentage() { return percentage; } - + public void setPercentage(float percentage) { this.percentage = percentage; } Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/model/McOptsContent.java =================================================================== diff -u -r1ee503e3d0e0228ea8a45025fddf15d9623c0377 -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/model/McOptsContent.java (.../McOptsContent.java) (revision 1ee503e3d0e0228ea8a45025fddf15d9623c0377) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/model/McOptsContent.java (.../McOptsContent.java) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -24,6 +24,7 @@ import java.io.Serializable; +import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; @@ -36,6 +37,7 @@ import javax.persistence.Transient; import org.apache.commons.lang.builder.ToStringBuilder; +import org.lamsfoundation.lams.qb.QbOption; /** * Persistent object/bean that defines the content for the MCQ tool. Provides accessors and mutators to get/set @@ -53,15 +55,14 @@ @GeneratedValue(strategy = GenerationType.IDENTITY) private Long uid; - @Column(name = "correct_option") - private boolean correctOption; - - @Column(name = "mc_que_option_text") - private String mcQueOptionText; - @Column private Integer displayOrder; + @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = { CascadeType.DETACH, CascadeType.MERGE, + CascadeType.PERSIST, CascadeType.REFRESH }) + @JoinColumn(name = "qb_option_uid") + private QbOption qbOption = new QbOption(); + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "mc_que_content_id") private McQueContent mcQueContent; @@ -72,17 +73,15 @@ @Transient private String escapedOptionText; - public McOptsContent(Integer displayOrder, boolean correctOption, String mcQueOptionText, - McQueContent mcQueContent) { + public McOptsContent(Integer displayOrder, QbOption qbOption, McQueContent mcQueContent) { this.displayOrder = displayOrder; - this.correctOption = correctOption; - this.mcQueOptionText = mcQueOptionText; + this.qbOption = qbOption; this.mcQueContent = mcQueContent; } public static McOptsContent newInstance(McOptsContent mcOptsContent, McQueContent newMcQueContent) { - McOptsContent newMcOptsContent = new McOptsContent(mcOptsContent.getDisplayOrder(), - mcOptsContent.isCorrectOption(), mcOptsContent.getMcQueOptionText(), newMcQueContent); + McOptsContent newMcOptsContent = new McOptsContent(mcOptsContent.getDisplayOrder(), mcOptsContent.getQbOption(), + newMcQueContent); return newMcOptsContent; } @@ -99,19 +98,19 @@ } public boolean isCorrectOption() { - return this.correctOption; + return qbOption.isCorrect(); } public void setCorrectOption(boolean correctOption) { - this.correctOption = correctOption; + qbOption.setCorrect(correctOption); } public String getMcQueOptionText() { - return this.mcQueOptionText; + return qbOption.getName(); } public void setMcQueOptionText(String mcQueOptionText) { - this.mcQueOptionText = mcQueOptionText; + qbOption.setName(mcQueOptionText); } public McQueContent getMcQueContent() { @@ -120,6 +119,7 @@ public void setMcQueContent(McQueContent mcQueContent) { this.mcQueContent = mcQueContent; + this.qbOption.setQuestion(mcQueContent.getQbQuestion()); } @Override @@ -168,6 +168,14 @@ this.escapedOptionText = escapedOptionText; } + public QbOption getQbOption() { + return qbOption; + } + + public void setQbOption(QbOption qbOption) { + this.qbOption = qbOption; + } + public String formatPrefixLetter(int index) { return new String(Character.toChars(97 + index)) + ")"; } Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java =================================================================== diff -u -rcd39ade0293bfdcdb89176284da7138266107689 -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision cd39ade0293bfdcdb89176284da7138266107689) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -60,6 +60,7 @@ 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.qb.QbOption; import org.lamsfoundation.lams.qb.QbQuestion; import org.lamsfoundation.lams.rest.RestTags; import org.lamsfoundation.lams.rest.ToolRestManager; @@ -251,6 +252,10 @@ mcQueContentDAO.insertOrUpdate(mcQueContent.getQbQuestion()); + for (McOptsContent mcqOption : mcQueContent.getMcOptionsContents()) { + mcQueContentDAO.insertOrUpdate(mcqOption.getQbOption()); + } + mcQueContentDAO.saveOrUpdateMcQueContent(mcQueContent); } @@ -286,6 +291,9 @@ // if it exists, detach it so we do not change data on the existing entity, // otherwise it gets saved on transaction end with modified data mcQueContentDAO.releaseFromCache(qbQuestion); + for (QbOption option : qbQuestion.getOptions()) { + mcQueContentDAO.releaseFromCache(option); + } } // set question's data to current values qbQuestion.setName(currentQuestionText); @@ -329,11 +337,20 @@ //find persisted option if it exists McOptsContent option = new McOptsContent(); for (McOptsContent oldOption : oldOptions) { + mcQueContentDAO.releaseFromCache(oldOption.getQbOption()); + mcQueContentDAO.releaseFromCache(oldOption.getQbOption().getQuestion()); if (oldOption.getUid().equals(optionUid)) { option = oldOption; } } + for (QbOption qbOption : question.getQbQuestion().getOptions()) { + if (qbOption.getUid().equals(optionDTO.getQbOptionUid())) { + option.setQbOption(qbOption); + break; + } + } + option.setDisplayOrder(displayOrderOption); option.setCorrectOption(isCorrectOption); option.setMcQueOptionText(optionText); @@ -1970,10 +1987,11 @@ ArrayNode optionsData = JsonUtil.optArray(questionData, RestTags.ANSWERS); for (JsonNode optionData : optionsData) { - question.getMcOptionsContents() - .add(new McOptsContent(JsonUtil.optInt(optionData, RestTags.DISPLAY_ORDER), - JsonUtil.optBoolean(optionData, RestTags.CORRECT), - JsonUtil.optString(optionData, RestTags.ANSWER_TEXT), question)); + QbOption qbOption = new QbOption(); + qbOption.setName(JsonUtil.optString(optionData, RestTags.ANSWER_TEXT)); + qbOption.setCorrect(JsonUtil.optBoolean(optionData, RestTags.CORRECT)); + question.getMcOptionsContents().add( + new McOptsContent(JsonUtil.optInt(optionData, RestTags.DISPLAY_ORDER), qbOption, question)); } saveOrUpdateMcQueContent(question); } Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McPedagogicalPlannerController.java =================================================================== diff -u -rd03a1456d2d0ee7a482273fe35412c67054f133d -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McPedagogicalPlannerController.java (.../McPedagogicalPlannerController.java) (revision d03a1456d2d0ee7a482273fe35412c67054f133d) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McPedagogicalPlannerController.java (.../McPedagogicalPlannerController.java) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -34,6 +34,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; +import org.lamsfoundation.lams.qb.QbOption; import org.lamsfoundation.lams.tool.mc.McAppConstants; import org.lamsfoundation.lams.tool.mc.dto.McOptionDTO; import org.lamsfoundation.lams.tool.mc.model.McContent; @@ -134,9 +135,11 @@ for (int candidateAnswerDTOIndex = 0; candidateAnswerDTOIndex < candidateAnswerDTOList .size(); candidateAnswerDTOIndex++) { McOptionDTO answerDTO = candidateAnswerDTOList.get(candidateAnswerDTOIndex); - McOptsContent candidateAnswer = new McOptsContent(candidateAnswerDTOIndex + 1, - McAppConstants.CORRECT.equals(answerDTO.getCorrect()), - answerDTO.getCandidateAnswer(), mcQueContent); + QbOption qbOption = new QbOption(); + qbOption.setName(answerDTO.getCandidateAnswer()); + qbOption.setCorrect(McAppConstants.CORRECT.equals(answerDTO.getCorrect())); + McOptsContent candidateAnswer = new McOptsContent(candidateAnswerDTOIndex + 1, qbOption, + mcQueContent); candidateAnswer.setMcQueContent(mcQueContent); candidateAnswers.add(candidateAnswer); } @@ -147,8 +150,7 @@ } } while (questionIndex <= plannerForm.getQuestionCount()); for (; questionIndex <= mcContent.getMcQueContents().size(); questionIndex++) { - McQueContent mcQueContent = mcService.getQuestionByDisplayOrder(questionIndex, - mcContent.getUid()); + McQueContent mcQueContent = mcService.getQuestionByDisplayOrder(questionIndex, mcContent.getUid()); mcContent.getMcQueContents().remove(mcQueContent); mcService.removeMcQueContent(mcQueContent); } Index: lams_tool_lamc/web/authoring/candidateAnswersList.jsp =================================================================== diff -u -raced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194 -rfd46695dcabc301f28799564c23314aa53d72dc6 --- lams_tool_lamc/web/authoring/candidateAnswersList.jsp (.../candidateAnswersList.jsp) (revision aced7ba6c1e7c5a9a50d3f64d8cdd96dd7e76194) +++ lams_tool_lamc/web/authoring/candidateAnswersList.jsp (.../candidateAnswersList.jsp) (revision fd46695dcabc301f28799564c23314aa53d72dc6) @@ -39,6 +39,7 @@ +