diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java index e2e5800211ebec77bd9e042656ff2c6d844682a2..63fd36db8c7e972807222e52a12f295bc933fadc 100644 --- a/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java +++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java @@ -104,7 +104,7 @@ public class AssessmentResponseDAO { .append(" where testSession.repositoryEntry.key=:repoEntryKey") .append(" and testSession.testEntry.key=:testEntryKey") .append(" and testSession.subIdent=:subIdent") - .append(" and testSession.terminationTime is not null") + .append(" and testSession.finishTime is not null") .append(" and ("); if(users) { sb.append(" testSession.identity.key is not null"); @@ -141,7 +141,7 @@ public class AssessmentResponseDAO { .append(" left join assessmentEntry.identity as ident") .append(" left join ident.user as usr") .append(" where testSession.testEntry.key=:testEntryKey") - .append(" and testSession.terminationTime is not null"); + .append(" and testSession.finishTime is not null"); if(courseEntry != null) { sb.append(" and testSession.repositoryEntry.key=:repoEntryKey"); } diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java index 7d2fc828375c71cc49da93e80efe5869b217c6ef..3d613327b1a729a0c62605e814385baaaae7a81c 100644 --- a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java +++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java @@ -335,7 +335,7 @@ public class AssessmentTestSessionDAO { .append(" left join fetch testEntry.olatResource testResource") .append(" inner join fetch session.identity assessedIdentity") .append(" inner join fetch assessedIdentity.user assessedUser") - .append(" where session.repositoryEntry.key=:repositoryEntryKey and session.terminationTime is null and session.testEntry.key=:testEntryKey"); + .append(" where session.repositoryEntry.key=:repositoryEntryKey and (session.finishTime is null or session.terminationTime is null) and session.testEntry.key=:testEntryKey"); if(StringHelper.containsNonWhitespace(courseSubIdent)) { sb.append(" and session.subIdent=:subIdent"); } else { @@ -357,7 +357,7 @@ public class AssessmentTestSessionDAO { sb.append("select session.key from qtiassessmenttestsession session") .append(" left join session.testEntry testEntry") .append(" left join testEntry.olatResource testResource") - .append(" where session.repositoryEntry.key=:repositoryEntryKey and session.terminationTime is null and session.testEntry.key=:testEntryKey"); + .append(" where session.repositoryEntry.key=:repositoryEntryKey and (session.finishTime is null or session.terminationTime is null) and session.testEntry.key=:testEntryKey"); if(StringHelper.containsNonWhitespace(courseSubIdent)) { sb.append(" and session.subIdent=:subIdent"); } else { diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java index 999e31413371c1833d7fc74046f7a8d2f155e8c1..9f657ab58eaf3516a1a5bb9e9fbdf6b501b3eb38 100644 --- a/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java +++ b/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java @@ -80,7 +80,7 @@ public class QTI21StatisticsManagerImpl implements QTI21StatisticsManager { @Autowired private DB dbInstance; - private StringBuilder decorateRSet(StringBuilder sb, QTI21StatisticSearchParams searchParams, boolean terminated) { + private StringBuilder decorateRSet(StringBuilder sb, QTI21StatisticSearchParams searchParams, boolean finished) { sb.append(" where asession.testEntry.key=:testEntryKey and asession.repositoryEntry.key=:repositoryEntryKey"); if(searchParams.getNodeIdent() != null ) { sb.append(" and asession.subIdent=:subIdent"); @@ -88,8 +88,8 @@ public class QTI21StatisticsManagerImpl implements QTI21StatisticsManager { sb.append(" and asession.subIdent is null"); } - if(terminated) { - sb.append(" and asession.terminationTime is not null"); + if(finished) { + sb.append(" and asession.finishTime is not null"); } sb.append(" and asession.lastModified = (select max(a2session.lastModified) from qtiassessmenttestsession a2session") diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java index bd4eb0460497e2b8ef831ef253efdcbbe64cb797..ef788e3d07cf92f1b37bffc80cb54caf57b00e2f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -155,7 +155,7 @@ public class AssessmentTestDisplayController extends BasicController implements private AssessmentResultController resultCtrl; private TestSessionController testSessionController; - private DialogBoxController advanceTestPartDialog; + private DialogBoxController advanceTestPartDialog, endTestPartDialog; private DialogBoxController confirmCancelDialog; private DialogBoxController confirmSuspendDialog; @@ -354,6 +354,11 @@ public class AssessmentTestDisplayController extends BasicController implements processAdvanceTestPart(ureq); } mainVC.setDirty(true); + } else if(endTestPartDialog == source) { + if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) { + processEndTestPart(ureq); + } + mainVC.setDirty(true); } else if(confirmCancelDialog == source) { if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) { doCancel(ureq); @@ -476,7 +481,7 @@ public class AssessmentTestDisplayController extends BasicController implements handleResponse(ureq, qe.getStringResponseMap(), qe.getFileResponseMap(), qe.getComment()); break; case endTestPart: - processEndTestPart(ureq); + confirmEndTestPart(ureq); break; case advanceTestPart: confirmAdvanceTestPart(ureq); @@ -845,12 +850,40 @@ public class AssessmentTestDisplayController extends BasicController implements itemSession.setPassed(pass); } } + + private void confirmEndTestPart(UserRequest ureq) { + TestPlanNode nextTestPart = testSessionController.findNextEnterableTestPart(); + + if(nextTestPart == null) { + String title = translate("confirm.close.test.title"); + String text = translate("confirm.close.test.text"); + endTestPartDialog = activateOkCancelDialog(ureq, title, text, endTestPartDialog); + } else { + TestPart currentTestPart = testSessionController.getCurrentTestPart(); + if(currentTestPart == null) { + processEndTestPart(ureq); + } else { + String title = translate("confirm.end.testpart.title"); + String text = translate("confirm.end.testpart.text"); + endTestPartDialog = activateOkCancelDialog(ureq, title, text, endTestPartDialog); + } + } + } //public CandidateSession endCurrentTestPart(final CandidateSessionContext candidateSessionContext) private void processEndTestPart(UserRequest ureq) { /* Update state */ final Date requestTimestamp = ureq.getRequestTimestamp(); testSessionController.endCurrentTestPart(requestTimestamp); + + TestSessionState testSessionState = testSessionController.getTestSessionState(); + + // Record current result state + final AssessmentResult assessmentResult = computeAndRecordTestAssessmentResult(ureq, testSessionState, false); + + if(testSessionController.findNextEnterableTestPart() == null) { + candidateSession = qtiService.finishTestSession(candidateSession, testSessionState, assessmentResult, requestTimestamp); + } } /** diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21TestSessionTableModel.java b/src/main/java/org/olat/ims/qti21/ui/QTI21TestSessionTableModel.java index a14a76ed41cbd7e8c840ae1aea95a7989039807d..a629e1759b64bab5637e2ac905b6efcdf3d0cb61 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21TestSessionTableModel.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21TestSessionTableModel.java @@ -61,20 +61,20 @@ public class QTI21TestSessionTableModel extends DefaultFlexiTableDataModel<Asses switch(TSCols.values()[col]) { case lastModified: return session.getLastModified(); case duration: { - if(session.getTerminationTime() != null) { + if(session.getFinishTime() != null) { return Formatter.formatDuration(session.getDuration().longValue()); } return "<span class='o_ochre'>" + translator.translate("assessment.test.open") + "</span>"; } case results: { - if(session.getTerminationTime() != null) { + if(session.getFinishTime() != null) { return AssessmentHelper.getRoundedScore(session.getScore()); } return "<span class='o_ochre'>" + translator.translate("assessment.test.notReleased") + "</span>"; } case open: { - Date terminated = session.getTerminationTime(); - return terminated == null ? Boolean.FALSE : Boolean.TRUE; + Date finished = session.getFinishTime(); + return finished == null ? Boolean.FALSE : Boolean.TRUE; } case correction: { return (lastSession != null && lastSession.equals(session)); diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestCorrectionController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestCorrectionController.java index 6ad9e3b2e058f980b89cb5ed97d22321d4d39401..b6d6b4708bb3fb027317d1eaaacdbf4390235d25 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestCorrectionController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestCorrectionController.java @@ -237,15 +237,15 @@ public class IdentitiesAssessmentTestCorrectionController extends BasicControlle continue; } - Date tDate = session.getTerminationTime(); - if(tDate == null) { + Date fDate = session.getFinishTime(); + if(fDate == null) { //not terminated } else { if(identityToSessions.containsKey(assessedIdentity)) { AssessmentTestSession currentSession = identityToSessions.get(assessedIdentity); - Date currentTDate = currentSession.getTerminationTime(); - if(tDate.after(currentTDate)) { + Date currentFDate = currentSession.getFinishTime(); + if(fDate.after(currentFDate)) { identityToSessions.put(assessedIdentity, session); } } else { diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestAndTestPartOptionsEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestAndTestPartOptionsEditorController.java new file mode 100644 index 0000000000000000000000000000000000000000..8b33d7ed6bb1f3955e3aecdaddb279d95992a1d0 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestAndTestPartOptionsEditorController.java @@ -0,0 +1,180 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ims.qti21.ui.editor; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.SingleSelection; +import org.olat.core.gui.components.form.flexible.elements.TextElement; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.util.StringHelper; +import org.olat.course.assessment.AssessmentHelper; +import org.olat.ims.qti21.model.xml.AssessmentTestBuilder; +import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent; + +import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; +import uk.ac.ed.ph.jqtiplus.node.test.NavigationMode; +import uk.ac.ed.ph.jqtiplus.node.test.TestPart; + +/** + * + * Initial date: 22.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AssessmentTestAndTestPartOptionsEditorController extends ItemSessionControlController { + + private static final String[] yesnoKeys = new String[] { "y", "n" }; + private static final String[] navigationKeys = new String[]{ + NavigationMode.LINEAR.name(), NavigationMode.NONLINEAR.name() + }; + + private TextElement titleEl, maxScoreEl, cutValueEl; + private SingleSelection exportScoreEl, navigationModeEl; + + private final TestPart testPart; + private final AssessmentTest assessmentTest; + private final AssessmentTestBuilder testBuilder; + + public AssessmentTestAndTestPartOptionsEditorController(UserRequest ureq, WindowControl wControl, + AssessmentTest assessmentTest, TestPart testPart, AssessmentTestBuilder testBuilder, boolean restrictedEdit) { + super(ureq, wControl, testPart, restrictedEdit); + this.assessmentTest = assessmentTest; + this.testBuilder = testBuilder; + this.testPart = testPart; + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + setFormContextHelp("Test and Questionnaire Editor in Detail#details_testeditor_test_konf"); + + String title = assessmentTest.getTitle(); + titleEl = uifactory.addTextElement("title", "form.metadata.title", 255, title, formLayout); + titleEl.setMandatory(true); + + //export score + String[] yesnoValues = new String[] { translate("yes"), translate("no") }; + exportScoreEl = uifactory.addRadiosHorizontal("form.test.export.score", formLayout, yesnoKeys, yesnoValues); + exportScoreEl.setEnabled(!restrictedEdit); + if(testBuilder.isExportScore()) { + exportScoreEl.select(yesnoKeys[0], true); + } else { + exportScoreEl.select(yesnoKeys[1], false); + } + + //score + String maxScore = testBuilder.getMaxScore() == null ? "" : AssessmentHelper.getRoundedScore(testBuilder.getMaxScore()); + maxScoreEl = uifactory.addTextElement("max.score", "max.score", 8, maxScore, formLayout); + maxScoreEl.setEnabled(false); + + Double cutValue = testBuilder.getCutValue(); + String cutValueStr = cutValue == null ? "" : cutValue.toString(); + cutValueEl = uifactory.addTextElement("cut.value", "cut.value", 8, cutValueStr, formLayout); + cutValueEl.setEnabled(!restrictedEdit); + + uifactory.addSpacerElement("space-test-part", formLayout, false); + + String[] navigationValues = new String[] { + translate("form.testPart.navigationMode.linear"), translate("form.testPart.navigationMode.nonlinear") + }; + String mode = testPart.getNavigationMode() == null ? NavigationMode.LINEAR.name() : testPart.getNavigationMode().name(); + navigationModeEl = uifactory.addRadiosHorizontal("navigationMode", "form.testPart.navigationMode", formLayout, navigationKeys, navigationValues); + navigationModeEl.select(mode, true); + navigationModeEl.setEnabled(!restrictedEdit); + + super.initForm(formLayout, listener, ureq); + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + formLayout.add(buttonsCont); + uifactory.addFormSubmitButton("save", "save", buttonsCont); + } + + @Override + protected void doDispose() { + // + } + + public String getTitle() { + return titleEl.getValue(); + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = true; + + titleEl.clearError(); + if(!StringHelper.containsNonWhitespace(titleEl.getValue())) { + titleEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + + cutValueEl.clearError(); + if(StringHelper.containsNonWhitespace(cutValueEl.getValue())) { + String cutValue = cutValueEl.getValue(); + try { + double val = Double.parseDouble(cutValue); + if(val < 0.0) { + cutValueEl.setErrorKey("form.error.nointeger", null); + allOk &= false; + } + } catch (NumberFormatException e) { + cutValueEl.setErrorKey("form.error.nointeger", null); + allOk &= false; + } + } + + navigationModeEl.clearError(); + if(!navigationModeEl.isOneSelected()) { + navigationModeEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + + return allOk & super.validateFormLogic(ureq); + } + + @Override + protected void formOK(UserRequest ureq) { + super.formOK(ureq); + + String title = titleEl.getValue(); + assessmentTest.setTitle(title); + + testBuilder.setExportScore(exportScoreEl.isOneSelected() && exportScoreEl.isSelected(0)); + + String cutValue = cutValueEl.getValue(); + if(StringHelper.containsNonWhitespace(cutValue)) { + testBuilder.setCutValue(new Double(cutValue)); + } else { + testBuilder.setCutValue(null); + } + + // navigation mode + if(navigationModeEl.isOneSelected() && navigationModeEl.isSelected(0)) { + testPart.setNavigationMode(NavigationMode.LINEAR); + } else { + testPart.setNavigationMode(NavigationMode.NONLINEAR); + } + + fireEvent(ureq, AssessmentTestEvent.ASSESSMENT_TEST_CHANGED_EVENT); + } +} diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java index 7e8c19407c0d00828349d0704d64cf696c9f990a..de81eb6ab3c5462c77132867b0d88c2d2dd5c1e3 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java @@ -906,7 +906,9 @@ public class AssessmentTestComposerController extends MainLayoutBasicController Object uobject = selectedNode.getUserObject(); if(uobject instanceof AssessmentTest) { - currentEditorCtrl = new AssessmentTestEditorController(ureq, getWindowControl(), assessmentTestBuilder, restrictedEdit); + AssessmentTest test = (AssessmentTest)uobject; + TestPart uniqueTestPart = test.getTestParts().size() == 1 ? test.getTestParts().get(0) : null; + currentEditorCtrl = new AssessmentTestEditorController(ureq, getWindowControl(), assessmentTestBuilder, uniqueTestPart, restrictedEdit); } else if(uobject instanceof TestPart) { currentEditorCtrl = new AssessmentTestPartEditorController(ureq, getWindowControl(), (TestPart)uobject, restrictedEdit); } else if(uobject instanceof AssessmentSection) { @@ -930,8 +932,16 @@ public class AssessmentTestComposerController extends MainLayoutBasicController } if(deleteLink != null) { - deleteLink.setEnabled(uobject instanceof AssessmentSection || uobject instanceof AssessmentItemRef); + if(uobject instanceof AssessmentSection || uobject instanceof AssessmentItemRef) { + deleteLink.setEnabled(true); + } else if(uobject instanceof TestPart) { + TestPart testPart = (TestPart)uobject; + deleteLink.setEnabled(testPart.getParent().getTestParts().size() > 1); + } else { + deleteLink.setEnabled(false); + } } + if(copyLink != null) { copyLink.setEnabled(uobject instanceof AssessmentItemRef); } @@ -1002,7 +1012,14 @@ public class AssessmentTestComposerController extends MainLayoutBasicController if(uobject instanceof AssessmentTest) { showWarning("error.cannot.delete"); } else if(uobject instanceof TestPart) { - showWarning("error.cannot.delete"); + TestPart testPart = (TestPart)uobject; + if(testPart.getParent().getTestParts().size() == 1) { + showWarning("error.cannot.delete"); + } + + String msg = translate("delete.testPart"); + confirmDeleteCtrl = activateYesNoDialog(ureq, translate("tools.change.delete"), msg, confirmDeleteCtrl); + confirmDeleteCtrl.setUserObject(selectedNode); } else { String msg; if(uobject instanceof AssessmentSection) { @@ -1021,7 +1038,9 @@ public class AssessmentTestComposerController extends MainLayoutBasicController private void doDelete(UserRequest ureq, TreeNode selectedNode) { Object uobject = selectedNode.getUserObject(); - if(uobject instanceof AssessmentSection) { + if(uobject instanceof TestPart) { + doDeleteTestPart((TestPart)uobject); + } else if(uobject instanceof AssessmentSection) { doDeleteAssessmentSection((AssessmentSection)uobject); } else if(uobject instanceof AssessmentItemRef) { doDeleteAssessmentItemRef((AssessmentItemRef)uobject); @@ -1082,6 +1101,14 @@ public class AssessmentTestComposerController extends MainLayoutBasicController assessmentSection.getParent().getChildAbstractParts().remove(assessmentSection); } } + + private void doDeleteTestPart(TestPart testPart) { + List<AssessmentSection> sections = new ArrayList<>(testPart.getAssessmentSections()); + for(AssessmentSection section:sections) { + doDeleteAssessmentSection(section); + } + testPart.getParent().getTestParts().remove(testPart); + } private ResourceType getResourceType(AssessmentItemRef itemRef) { return getResourceType(manifestBuilder, itemRef); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java index b8187d602c6722ace54cc2de4181213c0e44f92b..4b37cfad01669e333fd54a3cf7e14f21c00c1e40 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorAndComposerTreeModel.java @@ -61,9 +61,17 @@ public class AssessmentTestEditorAndComposerTreeModel extends GenericTreeModel i //list all test parts List<TestPart> parts = test.getChildAbstractParts(); - int counter = 0; - for(TestPart part:parts) { - buildRecursively(part, ++counter, node); + if(parts.size() == 1) { + List<AssessmentSection> sections = parts.get(0).getAssessmentSections(); + for(AssessmentSection section:sections) { + buildRecursively(section, node); + } + + } else { + int counter = 0; + for(TestPart part:parts) { + buildRecursively(part, ++counter, node); + } } } diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java index aea0b22104db4df62dea7ea4d3471a449e14489b..3a88ebd22bee69a0022314a348b0d8cfd2e8bf9b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestEditorController.java @@ -34,6 +34,7 @@ import org.olat.ims.qti21.ui.AssessmentTestDisplayController; import org.olat.ims.qti21.ui.editor.events.AssessmentTestEvent; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; +import uk.ac.ed.ph.jqtiplus.node.test.TestPart; /** * @@ -46,17 +47,19 @@ public class AssessmentTestEditorController extends BasicController { private final TabbedPane tabbedPane; private final VelocityContainer mainVC; - private AssessmentTestOptionsEditorController optionsCtrl; + private Controller optionsCtrl; private AssessmentTestFeedbackEditorController feedbackCtrl; private final boolean restrictedEdit; + private final TestPart testPart; private final AssessmentTest assessmentTest; private final AssessmentTestBuilder testBuilder; public AssessmentTestEditorController(UserRequest ureq, WindowControl wControl, - AssessmentTestBuilder testBuilder, boolean restrictedEdit) { + AssessmentTestBuilder testBuilder, TestPart testPart, boolean restrictedEdit) { super(ureq, wControl, Util.createPackageTranslator(AssessmentTestDisplayController.class, ureq.getLocale())); this.testBuilder = testBuilder; + this.testPart = testPart; this.assessmentTest = testBuilder.getAssessmentTest(); this.restrictedEdit = restrictedEdit; @@ -77,8 +80,13 @@ public class AssessmentTestEditorController extends BasicController { private void initTestEditor(UserRequest ureq) { if(QTI21Constants.TOOLNAME.equals(assessmentTest.getToolName())) { - optionsCtrl = new AssessmentTestOptionsEditorController(ureq, getWindowControl(), assessmentTest, testBuilder, restrictedEdit); + if(testPart != null) { + optionsCtrl = new AssessmentTestAndTestPartOptionsEditorController(ureq, getWindowControl(), assessmentTest, testPart, testBuilder, restrictedEdit); + } else { + optionsCtrl = new AssessmentTestOptionsEditorController(ureq, getWindowControl(), assessmentTest, testBuilder, restrictedEdit); + } listenTo(optionsCtrl); + feedbackCtrl = new AssessmentTestFeedbackEditorController(ureq, getWindowControl(), testBuilder, restrictedEdit); listenTo(feedbackCtrl); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/ItemSessionControlController.java b/src/main/java/org/olat/ims/qti21/ui/editor/ItemSessionControlController.java index e7fa1c10f7b2d993ec884dd82b6df86eb5c5d1c3..cca1f464bcdfc27ccc0e185680b945701908fb31 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/ItemSessionControlController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/ItemSessionControlController.java @@ -48,7 +48,7 @@ public abstract class ItemSessionControlController extends FormBasicController { private static final String[] attemtpsKeys = new String[] { "y", "n", "inherit" }; private TextElement maxAttemptsEl /*, maxTimeEl */; - private SingleSelection limitAttemptsEl, allowCommentEl, showSolutionEl; + private SingleSelection limitAttemptsEl, allowCommentEl, allowReviewEl, showSolutionEl; protected final boolean restrictedEdit; private final AbstractPart part; @@ -112,6 +112,15 @@ public abstract class ItemSessionControlController extends FormBasicController { } else { allowCommentEl.select(yesnoKeys[1], false); } + + allowReviewEl = uifactory.addRadiosHorizontal("item.session.control.allow.review", formLayout, yesnoKeys, yesnoValues); + allowReviewEl.addActionListener(FormEvent.ONCHANGE); + allowReviewEl.setEnabled(!restrictedEdit); + if(itemSessionControl != null && itemSessionControl.getAllowReview() != null && itemSessionControl.getAllowReview().booleanValue()) { + allowReviewEl.select(yesnoKeys[0], true); + } else { + allowReviewEl.select(yesnoKeys[1], false); + } showSolutionEl = uifactory.addRadiosHorizontal("item.session.control.show.solution", formLayout, yesnoKeys, yesnoValues); showSolutionEl.addActionListener(FormEvent.ONCHANGE); @@ -138,6 +147,12 @@ public abstract class ItemSessionControlController extends FormBasicController { allOk &= false; } + allowReviewEl.clearError(); + if(!allowReviewEl.isOneSelected()) { + allowReviewEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + showSolutionEl.clearError(); if(!showSolutionEl.isOneSelected()) { showSolutionEl.setErrorKey("form.legende.mandatory", null); @@ -196,6 +211,12 @@ public abstract class ItemSessionControlController extends FormBasicController { itemSessionControl.setAllowComment(Boolean.FALSE); } + if(allowReviewEl.isOneSelected() && allowReviewEl.isSelected(0)) { + checkNotNull(itemSessionControl).setAllowReview(Boolean.TRUE); + } else if(itemSessionControl != null) { + itemSessionControl.setAllowReview(Boolean.FALSE); + } + if(showSolutionEl.isOneSelected() && showSolutionEl.isSelected(0)) { checkNotNull(itemSessionControl).setShowSolution(Boolean.TRUE); } else if(itemSessionControl != null) { diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties index 3c246253b8633d1ee7a5da3776ea05bb0ab3d116..d1ec0954b0be2b48cfab3eff70def81c8a2910f4 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties @@ -5,6 +5,7 @@ correct.answers=Korrekt cut.value=Notwendige Punktzahl f\u00FCr "Bestanden" delete.item=$org.olat.ims.qti.editor\:delete.item delete.section=$org.olat.ims.qti.editor\:delete.section +delete.testPart=Wollen Sie das Test Part mit all ihren Fragen wirklich l\u00F6schen? editor.sc.title=Single choice editor.unkown.title=Unbekanntes interaction error.cannot.create.section=Sie k\u00F6nnen nicht eine Sektion hier erstellen. @@ -87,6 +88,7 @@ form.testPart.navigationMode.linear=Linear form.testPart.navigationMode.nonlinear=Nicht linear form.unkown=Unbekannt item.session.control.allow.comment=Kommentar erlauben +item.session.control.allow.review=Review erlauben item.session.control.attempts=Versuch item.session.control.show.solution=L\u00F6sung anzeigen max.score=Maximal erreichbare Punktzahl diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties index 6e7bacd7ee7190e6aa2174cfeb07f2a84b5406cc..ebbec1a65fa074fa8718475be3c8bcc5e728309f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties @@ -5,6 +5,7 @@ correct.answers=Correct cut.value=Cut value delete.item=$org.olat.ims.qti.editor\:delete.item delete.section=$org.olat.ims.qti.editor\:delete.section +delete.testPart=Do you really want to delete the test part along with all its questions? editor.sc.title=Single choice editor.unkown.title=Unkown interaction error.cannot.create.section=A section cannot be created everywhere\! @@ -87,6 +88,7 @@ form.testPart.navigationMode.nonlinear=Non linear form.unkown=Unkown inherit=Inherit item.session.control.allow.comment=Allow comments +item.session.control.allow.review=Allow review of questions item.session.control.attempts=Attempts item.session.control.show.solution=Show solution max.score=Max. score diff --git a/src/test/java/org/olat/selenium/page/qti/QTI21Page.java b/src/test/java/org/olat/selenium/page/qti/QTI21Page.java index 060a03139f06773047cfaf9798fa2dea90704b02..2d162861fbe794f6481c8460815ddf6eedb5f678 100644 --- a/src/test/java/org/olat/selenium/page/qti/QTI21Page.java +++ b/src/test/java/org/olat/selenium/page/qti/QTI21Page.java @@ -170,6 +170,7 @@ public class QTI21Page { By endBy = By.cssSelector("a.o_sel_end_testpart"); browser.findElement(endBy).click(); OOGraphene.waitBusy(browser); + confirm(); return this; } @@ -177,12 +178,19 @@ public class QTI21Page { By endBy = By.cssSelector("a.o_sel_close_test"); browser.findElement(endBy).click(); OOGraphene.waitBusy(browser); - + confirm(); + return this; + } + + /** + * Yes in a dialog box controller. + */ + private void confirm() { // confirm By confirmButtonBy = By.cssSelector("div.modal-dialog div.modal-footer a"); + OOGraphene.waitElement(confirmButtonBy, 5, browser); List<WebElement> buttonsEl = browser.findElements(confirmButtonBy); buttonsEl.get(0).click(); OOGraphene.waitBusy(browser); - return this; } }