Index: lams_tool_assessment/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r002f9275f5ec93dc0f76d1eaff245a121781ea1a -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 002f9275f5ec93dc0f76d1eaff245a121781ea1a) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -78,8 +78,14 @@ label.authoring.ordering.add.ordering = Add question label.authoring.advance.allow.students.overall.feedback = Display overall feedback at the end of each attempt label.authoring.advance.time.limit = Time limit (minutes) +label.authoring.advance.question.distribution = Question distribution label.authoring.advance.questions.per.page = Questions per page label.authoring.advance.all.in.one.page = All in one page +label.authoring.advance.sections = Sections +label.authoring.advance.section.name = Name +label.authoring.advance.section.questions.count = Questions in section +label.authoring.advance.section.add = Add section +label.authoring.advance.section.remove = Remove section label.authoring.advance.shuffle.questions = Shuffle questions label.authoring.advance.attempts.allowed = Attempts allowed label.authoring.advance.unlimited = Unlimited Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20230202.sql =================================================================== diff -u --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20230202.sql (revision 0) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dbupdates/patch20230202.sql (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -0,0 +1,23 @@ +-- Turn off autocommit, so nothing is committed if there is an error +SET AUTOCOMMIT = 0; +SET FOREIGN_KEY_CHECKS=0; +-- Put all sql statements below here + +-- LDEV-5359 Add question sections +CREATE TABLE tl_laasse10_section( + uid MEDIUMINT UNSIGNED NOT NULL auto_increment, + assessment_uid BIGINT, + display_order TINYINT UNSIGNED NOT NULL DEFAULT '1', + name VARCHAR(100), + question_count TINYINT UNSIGNED NOT NULL, + CONSTRAINT FK_tl_laasse10_section_1 FOREIGN KEY (assessment_uid) REFERENCES tl_laasse10_assessment(uid) + ON UPDATE CASCADE ON DELETE CASCADE, + PRIMARY KEY (uid) +); + +-- Put all sql statements above here + +-- If there were no errors, commit and restore autocommit to on +COMMIT; +SET AUTOCOMMIT = 1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/Assessment.java =================================================================== diff -u -r6fc452ae5cdbca9476f14a97ddc933a23fd8776a -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/Assessment.java (.../Assessment.java) (revision 6fc452ae5cdbca9476f14a97ddc933a23fd8776a) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/Assessment.java (.../Assessment.java) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -51,8 +51,6 @@ import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.log4j.Logger; -import org.hibernate.annotations.Cache; -import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.SortComparator; import org.lamsfoundation.lams.qb.model.QbToolQuestion; @@ -102,6 +100,12 @@ @Column(name = "questions_per_page") private int questionsPerPage; + // Question section + @OneToMany(cascade = CascadeType.ALL) + @JoinColumn(name = "assessment_uid") + @SortComparator(AssessmentSection.AssessmentSectionComparator.class) + private Set sections = new TreeSet<>(); + @Column(name = "attempts_allowed") private int attemptsAllowed; @@ -223,7 +227,7 @@ try { assessment = (Assessment) super.clone(); assessment.setUid(null); - + // clone questions if (questions != null) { Iterator iter = questions.iterator(); @@ -276,7 +280,7 @@ if (createdBy != null) { assessment.setCreatedBy((AssessmentUser) createdBy.clone()); } - + assessment.setAbsoluteTimeLimit(null); assessment.setTimeLimitAdjustments(new HashMap<>(this.getTimeLimitAdjustments())); } catch (CloneNotSupportedException e) { @@ -647,10 +651,16 @@ this.questionsPerPage = questionsPerPage; } + public Set getSections() { + return sections; + } + + public void setSections(Set sections) { + this.sections = sections; + } + /** * number of allow students attempts - * - * @return */ public int getAttemptsAllowed() { return attemptsAllowed; @@ -683,8 +693,6 @@ /** * If this is checked, then in learner we display the numbering for learners. - * - * @return */ public boolean isNumbered() { return numbered; @@ -725,4 +733,4 @@ public void setQuestionEtherpadEnabled(boolean questionEtherpadEnabled) { this.questionEtherpadEnabled = questionEtherpadEnabled; } -} +} \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/AssessmentSection.java =================================================================== diff -u --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/AssessmentSection.java (revision 0) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/model/AssessmentSection.java (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -0,0 +1,110 @@ +package org.lamsfoundation.lams.tool.assessment.model; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.Objects; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "tl_laasse10_section") +public class AssessmentSection implements Serializable, Comparable { + + private static final long serialVersionUID = -8252609064125180392L; + + public static final Comparator COMPARATOR = Comparator + .comparing(AssessmentSection::getDisplayOrder); + + // it makes sense to put comparator here as an internal class, so we do not need to look for it in other classes + public static class AssessmentSectionComparator implements Comparator { + @Override + public int compare(AssessmentSection o1, AssessmentSection o2) { + return COMPARATOR.compare(o1, o2); + } + } + + @Id + @Column + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long uid; + + @Column(name = "display_order") + int displayOrder; + + @Column + String name; + + @Column(name = "question_count") + int questionCount; + + public AssessmentSection() { + this.displayOrder = 1; + } + + public Long getUid() { + return uid; + } + + public void setUid(Long uid) { + this.uid = uid; + } + + public int getDisplayOrder() { + return displayOrder; + } + + public void setDisplayOrder(int displayOrder) { + this.displayOrder = displayOrder; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getQuestionCount() { + return questionCount; + } + + public void setQuestionCount(int questionCount) { + this.questionCount = questionCount; + } + + @Override + public int compareTo(AssessmentSection o) { + return COMPARATOR.compare(this, o); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("AssessmentSection [uid=").append(uid).append(", displayOrder=").append(displayOrder) + .append(", name=").append(name).append(", questionCount=").append(questionCount).append("]"); + return builder.toString(); + } + + @Override + public int hashCode() { + return Objects.hash(uid); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AssessmentSection)) { + return false; + } + AssessmentSection other = (AssessmentSection) obj; + return Objects.equals(uid, other.uid); + } +} \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -re9f21c562efc19cd5b5f9025f6d47165de059b39 -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision e9f21c562efc19cd5b5f9025f6d47165de059b39) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -112,6 +112,7 @@ import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionResult; import org.lamsfoundation.lams.tool.assessment.model.AssessmentResult; +import org.lamsfoundation.lams.tool.assessment.model.AssessmentSection; import org.lamsfoundation.lams.tool.assessment.model.AssessmentSession; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; @@ -495,6 +496,11 @@ } @Override + public void deleteSection(Long uid) { + assessmentDao.removeObject(AssessmentSection.class, uid); + } + + @Override public Assessment getAssessmentBySessionId(Long sessionId) { AssessmentSession session = assessmentSessionDao.getSessionBySessionId(sessionId); // to skip CGLib problem Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java =================================================================== diff -u -re9f21c562efc19cd5b5f9025f6d47165de059b39 -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision e9f21c562efc19cd5b5f9025f6d47165de059b39) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -191,6 +191,8 @@ void deleteQuestionReference(Long uid); + void deleteSection(Long uid); + /** * Get assessment which is relative with the special toolSession. * Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/AuthoringController.java =================================================================== diff -u -r96bbde00d20c05475d9444bfec5e98568bb03f09 -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/AuthoringController.java (.../AuthoringController.java) (revision 96bbde00d20c05475d9444bfec5e98568bb03f09) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/AuthoringController.java (.../AuthoringController.java) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -52,6 +52,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; +import org.hibernate.Hibernate; import org.lamsfoundation.lams.qb.QbConstants; import org.lamsfoundation.lams.qb.form.QbQuestionForm; import org.lamsfoundation.lams.qb.model.QbCollection; @@ -62,6 +63,7 @@ import org.lamsfoundation.lams.tool.assessment.model.Assessment; import org.lamsfoundation.lams.tool.assessment.model.AssessmentOverallFeedback; import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; +import org.lamsfoundation.lams.tool.assessment.model.AssessmentSection; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; import org.lamsfoundation.lams.tool.assessment.service.IAssessmentService; @@ -173,6 +175,7 @@ // init RandomPoolQuestions List randomPoolQuestions = getRandomPoolQuestions(sessionMap); randomPoolQuestions.clear(); + for (AssessmentQuestion question : assessment.getQuestions()) { // since we are iterating anyway, here fill version information to each question qbService.fillVersionMap(question.getQbQuestion()); @@ -202,6 +205,8 @@ boolean questionEtherpadEnabled = StringUtils.isNotBlank(Configuration.get(ConfigurationKeys.ETHERPAD_API_KEY)); sessionMap.put(AssessmentConstants.ATTR_IS_QUESTION_ETHERPAD_ENABLED, questionEtherpadEnabled); + Hibernate.initialize(assessment.getSections()); + return "pages/authoring/start"; } @@ -241,6 +246,7 @@ Set oldQuestions = (assessmentPO == null) ? new HashSet<>() : assessmentPO.getQuestions(); Set oldReferences = (assessmentPO == null) ? new HashSet<>() : assessmentPO.getQuestionReferences(); + Set oldSections = (assessmentPO == null) ? new HashSet<>() : assessmentPO.getSections(); AssessmentUser assessmentUser = null; @@ -258,6 +264,9 @@ for (QuestionReference reference : oldReferences) { service.releaseFromCache(reference); } + for (AssessmentSection section : oldSections) { + service.releaseFromCache(section); + } Long uid = assessmentPO.getUid(); assessmentUser = assessmentPO.getCreatedBy(); @@ -324,6 +333,66 @@ TreeSet overallFeedbackList = getOverallFeedbacksFromForm(request, true); assessmentPO.setOverallFeedbacks(overallFeedbackList); + // ************************* Handle sections ******************* + String questionDistributionType = request.getParameter("questionDistributionType"); + + // check which sections were added / updated + Set sections = new TreeSet<>(); + int sectionCounter = 1; + Integer sectionQuestionCount = null; + do { + sectionQuestionCount = WebUtil.readIntParam(request, "sectionQuestionCount" + sectionCounter, true); + if (sectionQuestionCount != null) { + String sectionName = request.getParameter("sectionName" + sectionCounter); + int existingSectionCounter = 0; + AssessmentSection section = null; + for (AssessmentSection existingSection : oldSections) { + existingSectionCounter++; + if (existingSectionCounter < sectionCounter) { + continue; + } + section = existingSection; + break; + } + if (section == null) { + section = new AssessmentSection(); + } + section.setDisplayOrder(sectionCounter); + section.setName(StringUtils.isBlank(sectionName) ? null : sectionName); + section.setQuestionCount(sectionQuestionCount); + sections.add(section); + sectionCounter++; + } + } while (sectionQuestionCount != null); + + assessmentPO.setSections(sections); + + switch (questionDistributionType) { + case "all": + assessmentPO.setQuestionsPerPage(0); + break; + case "section": + if (sections.isEmpty()) { + assessmentPO.setQuestionsPerPage(0); + } else { + assessmentPO.setQuestionsPerPage(-1); + } + break; + } + + // remove unnecessary sections + if (sectionCounter <= oldSections.size()) { + Iterator sectionIterator = oldSections.iterator(); + int existingSectionCounter = 1; + while (sectionIterator.hasNext()) { + AssessmentSection section = sectionIterator.next(); + if (existingSectionCounter >= sectionCounter) { + service.deleteSection(section.getUid()); + } + existingSectionCounter++; + } + } + // ********************************************** // finally persist assessmentPO again service.saveOrUpdateAssessment(assessmentPO); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/form/AssessmentForm.java =================================================================== diff -u -r6b89d0c84a5695fb1ad02d5525eb240a9f4d3134 -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/form/AssessmentForm.java (.../AssessmentForm.java) (revision 6b89d0c84a5695fb1ad02d5525eb240a9f4d3134) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/form/AssessmentForm.java (.../AssessmentForm.java) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -39,10 +39,13 @@ private Assessment assessment; + private String questionDistributionType; + public AssessmentForm() { assessment = new Assessment(); assessment.setTitle("Shared Assessment"); currentTab = 1; + questionDistributionType = "all"; } public void setAssessment(Assessment assessment) { @@ -91,4 +94,11 @@ this.contentFolderID = contentFolderID; } -} + public String getQuestionDistributionType() { + return questionDistributionType; + } + + public void setQuestionDistributionType(String questionDistributionType) { + this.questionDistributionType = questionDistributionType; + } +} \ No newline at end of file Index: lams_tool_assessment/web/pages/authoring/advance.jsp =================================================================== diff -u -r5ada1d7bd1c23215a124e16d27ceb8daf9600977 -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/web/pages/authoring/advance.jsp (.../advance.jsp) (revision 5ada1d7bd1c23215a124e16d27ceb8daf9600977) +++ lams_tool_assessment/web/pages/authoring/advance.jsp (.../advance.jsp) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -62,6 +62,42 @@ $('#confidence-levels-type-area').css('display', 'none'); + + // sections add/remove functionality + let sectionAddButton = $('#sectionAddButton').click(function(){ + let sectionsContainer = $('#sectionsContainer'), + sections = $('.sectionEntry', sectionsContainer), + sectionSuffix = sections.length + 1, + section = $('#sectionTemplate').clone().attr('id', 'sectionEntry' + sectionSuffix).removeClass('hidden'); + $('.sectionRemoveButton', sections).addClass('hidden'); + section.appendTo(sectionsContainer); + $('input[type="text"]', section).attr('name', 'sectionName' + sectionSuffix); + $('select', section).attr('name', 'sectionQuestionCount' + sectionSuffix); + $('.sectionRemoveButton', section).removeClass('hidden').click(function(){ + section.remove(); + $('.sectionEntry:last .sectionRemoveButton', sectionsContainer).removeClass('hidden'); + }); + }); + + $('input[name="questionDistributionType"]').change(function(){ + let distributionType = $('input[name="questionDistributionType"]:checked').val(), + questionsPerPageSelect = $('#questionsPerPage'); + if (distributionType === 'all') { + questionsPerPageSelect.val(0).prop('disabled', true); + sectionAddButton.prop('disabled', true); + return; + } + if (distributionType === 'page') { + questionsPerPageSelect.prop('disabled', false); + sectionAddButton.prop('disabled', true); + return; + } + if (distributionType === 'section') { + questionsPerPageSelect.val(-1).prop('disabled', true); + sectionAddButton.prop('disabled', false); + return; + } + }).first().change(); }); @@ -90,40 +126,103 @@ - -
- -
- -
- -
- -
- -
- +
+ +
+ +
+ +
+ + + +
+
+ +
+ +
+ +
+ +
+ +
+
+ + + +
+ + + + +
+
+
+ + +
+
Index: lams_tool_assessment/web/pages/authoring/authoring.jsp =================================================================== diff -u -r1a6408137fed60838b5744155f0884381c37d2c0 -r09cb8620b7ebc847ae6a700d11ba7c24435a279a --- lams_tool_assessment/web/pages/authoring/authoring.jsp (.../authoring.jsp) (revision 1a6408137fed60838b5744155f0884381c37d2c0) +++ lams_tool_assessment/web/pages/authoring/authoring.jsp (.../authoring.jsp) (revision 09cb8620b7ebc847ae6a700d11ba7c24435a279a) @@ -44,8 +44,9 @@ //serialize overallFeedbackForm $("#overallFeedbackList").val($('#advancedInputArea').contents().find('#overallFeedbackForm').serialize(true)); - //enable checkbox to allow its value been submitted + // enable input to allow its value been submitted $("#display-summary").removeAttr("disabled", "disabled"); + $("#questionsPerPage").prop('disabled', false); return true; }