From 71e7a4430abc87579c799208b46105f4ecd1d54e Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Tue, 13 Nov 2012 08:36:39 +0100 Subject: [PATCH] OO-417: refactor the submit process in QTI, all the back end jobs are done at the same time with delegates, course node attempts increments, save on the database, generating reports... --- .../course/nodes/iq/IQEditController.java | 2 +- .../course/nodes/iq/IQEditController.java | 2 +- .../olat/course/nodes/iq/IQRunController.java | 74 ++++++++------- .../ims/qti/QTIResultDetailsController.java | 32 ++++--- .../ims/qti/navigator/DefaultNavigator.java | 21 ++++- .../ims/qti/navigator/MenuItemNavigator.java | 4 +- .../qti/navigator/MenuSectionNavigator.java | 4 +- .../org/olat/ims/qti/navigator/Navigator.java | 1 + .../ims/qti/navigator/NavigatorDelegate.java | 34 +++++++ .../navigator/SequentialItemNavigator.java | 4 +- .../navigator/SequentialSectionNavigator.java | 4 +- .../ims/qti/process/AssessmentFactory.java | 11 ++- .../ims/qti/process/AssessmentInstance.java | 21 +++-- .../olat/modules/iq/IQDisplayController.java | 90 ++++++++++--------- .../java/org/olat/modules/iq/IQManager.java | 5 +- .../org/olat/modules/iq/IQSubmittedEvent.java | 38 +------- .../course/CourseAssessmentWebService.java | 2 +- 17 files changed, 199 insertions(+), 150 deletions(-) create mode 100644 src/main/java/org/olat/ims/qti/navigator/NavigatorDelegate.java diff --git a/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditController.java b/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditController.java index df8ed672182..b4465b95854 100644 --- a/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditController.java +++ b/src/main/java/de/bps/onyx/plugin/course/nodes/iq/IQEditController.java @@ -445,7 +445,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl // handle preview if (previewLayoutCtr != null) previewLayoutCtr.dispose(); Controller previewController = IQManager.getInstance().createIQDisplayController(moduleConfiguration, new IQPreviewSecurityCallback(), ureq, getWindowControl(), course - .getResourceableId().longValue(), courseNode.getIdent()); + .getResourceableId().longValue(), courseNode.getIdent(), null); previewLayoutCtr = new LayoutMain3ColsPreviewController(ureq, getWindowControl(), null, null, previewController.getInitialComponent(), null); previewLayoutCtr.addDisposableChildController(previewController); previewLayoutCtr.activate(); diff --git a/src/main/java/org/olat/course/nodes/iq/IQEditController.java b/src/main/java/org/olat/course/nodes/iq/IQEditController.java index c5bb084bdce..424f9d60bfc 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQEditController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQEditController.java @@ -386,7 +386,7 @@ public class IQEditController extends ActivateableTabbableDefaultController impl } else if (source == previewLink){ // handle preview Controller previewController = IQManager.getInstance().createIQDisplayController(moduleConfiguration, new IQPreviewSecurityCallback(), ureq, getWindowControl(), course - .getResourceableId().longValue(), courseNode.getIdent()); + .getResourceableId().longValue(), courseNode.getIdent(), null); previewLayoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), previewController); stackPanel.pushController(translate("preview"), previewLayoutCtr); diff --git a/src/main/java/org/olat/course/nodes/iq/IQRunController.java b/src/main/java/org/olat/course/nodes/iq/IQRunController.java index 90fb27c0603..34afc919de1 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQRunController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQRunController.java @@ -75,6 +75,8 @@ import org.olat.course.nodes.SelfAssessableCourseNode; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.ims.qti.QTIChangeLogMessage; +import org.olat.ims.qti.container.AssessmentContext; +import org.olat.ims.qti.navigator.NavigatorDelegate; import org.olat.ims.qti.process.AssessmentInstance; import org.olat.ims.qti.process.ImsRepositoryResolver; import org.olat.instantMessaging.InstantMessaging; @@ -95,7 +97,7 @@ import org.olat.util.logging.activity.LoggingResourceable; * Initial Date: Oct 13, 2004 * @author Felix Jost */ -public class IQRunController extends BasicController implements GenericEventListener, Activateable2 { +public class IQRunController extends BasicController implements GenericEventListener, Activateable2, NavigatorDelegate { private VelocityContainer myContent; @@ -384,7 +386,7 @@ public class IQRunController extends BasicController implements GenericEventList OLATResourceable ores = OresHelper.createOLATResourceableTypeWithoutCheck("test"); ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores)); WindowControl bwControl = addToHistory(ureq, ores, null); - Controller returnController = IQManager.getInstance().createIQDisplayController(modConfig, secCallback, ureq, bwControl, callingResId, callingResDetail); + Controller returnController = IQManager.getInstance().createIQDisplayController(modConfig, secCallback, ureq, bwControl, callingResId, callingResDetail, this); /* * either returnController is a MessageController or it is a IQDisplayController * this should not serve as pattern to be copy&pasted. @@ -454,44 +456,56 @@ public class IQRunController extends BasicController implements GenericEventList } } + @Override + public void submitAssessment(AssessmentInstance ai) { + if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) { + AssessmentContext ac = ai.getAssessmentContext(); + Float score = new Float(ac.getScore()); + Boolean passed = new Boolean(ac.isPassed()); + ScoreEvaluation sceval = new ScoreEvaluation(score, passed, new Long(ai.getAssessID())); + AssessableCourseNode acn = (AssessableCourseNode)courseNode; // assessment nodes are assesable + boolean incrementUserAttempts = true; + acn.updateUserScoreEvaluation(sceval, userCourseEnv, getIdentity(), incrementUserAttempts); + + // Mark publisher for notifications + AssessmentNotificationsHandler anh = AssessmentNotificationsHandler.getInstance(); + Long courseId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); + anh.markPublisherNews(getIdentity(), courseId); + if(!assessmentStopped) { + assessmentStopped = true; + AssessmentEvent assessmentStoppedEvent = new AssessmentEvent(AssessmentEvent.TYPE.STOPPED, userSession); + singleUserEventCenter.deregisterFor(this, assessmentInstanceOres); + singleUserEventCenter.fireEventToListenersOf(assessmentStoppedEvent, assessmentEventOres); + } + } else if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) { + // save number of attempts + // although this is not an assessable node we still use the assessment + // manager since this one uses caching + AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); + am.incrementNodeAttempts(courseNode, getIdentity(), userCourseEnv); + } else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)){ + AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); + am.incrementNodeAttempts(courseNode, getIdentity(), userCourseEnv); + } + } + + @Override + public void cancelAssessment(AssessmentInstance ai) { + // + } + /** * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event) */ public void event(UserRequest urequest, Controller source, Event event) { if (source == displayController) { if (event instanceof IQSubmittedEvent) { - IQSubmittedEvent se = (IQSubmittedEvent) event; - AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); - // Save results in case of test - if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) { - // update scoring overview for the user in the current course - Float score = new Float(se.getScore()); - Boolean passed = new Boolean(se.isPassed()); - ScoreEvaluation sceval = new ScoreEvaluation(score, passed, new Long(se.getAssessmentID())); - AssessableCourseNode acn = (AssessableCourseNode)courseNode; // assessment nodes are assesable - boolean incrementUserAttempts = true; - acn.updateUserScoreEvaluation(sceval, userCourseEnv, urequest.getIdentity(), incrementUserAttempts); - //userCourseEnv.getScoreAccounting().scoreInfoChanged(acn, sceval); + if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_ASSESS)) { exposeUserTestDataToVC(urequest); - - // Mark publisher for notifications - AssessmentNotificationsHandler anh = AssessmentNotificationsHandler.getInstance(); - Long courseId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); - anh.markPublisherNews(urequest.getIdentity(), courseId); - if(!assessmentStopped) { - assessmentStopped = true; - AssessmentEvent assessmentStoppedEvent = new AssessmentEvent(AssessmentEvent.TYPE.STOPPED, userSession); - singleUserEventCenter.deregisterFor(this, assessmentInstanceOres); - singleUserEventCenter.fireEventToListenersOf(assessmentStoppedEvent, assessmentEventOres); - } } // Save results in case of questionnaire else if (type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SURVEY)) { - // save number of attempts - // although this is not an assessable node we still use the assessment - // manager since this one uses caching - am.incrementNodeAttempts(courseNode, urequest.getIdentity(), userCourseEnv); exposeUserQuestionnaireDataToVC(); if(displayContainerController != null) { @@ -505,7 +519,7 @@ public class IQRunController extends BasicController implements GenericEventList // Don't save results in case of self-test // but do safe attempts ! else if(type.equals(AssessmentInstance.QMD_ENTRY_TYPE_SELF)){ - am.incrementNodeAttempts(courseNode, urequest.getIdentity(), userCourseEnv); + //am.incrementNodeAttempts(courseNode, urequest.getIdentity(), userCourseEnv); } } else if (event.equals(Event.DONE_EVENT)) { stopAssessment(urequest, event); diff --git a/src/main/java/org/olat/ims/qti/QTIResultDetailsController.java b/src/main/java/org/olat/ims/qti/QTIResultDetailsController.java index c2f45bf0dea..ec36cd7c8ff 100644 --- a/src/main/java/org/olat/ims/qti/QTIResultDetailsController.java +++ b/src/main/java/org/olat/ims/qti/QTIResultDetailsController.java @@ -156,19 +156,23 @@ public class QTIResultDetailsController extends BasicController { if (tEvent.getActionId().equals("sel")) { QTIResultSet resultSet = tableModel.getObject(tEvent.getRowId()); - Document doc = FilePersister.retreiveResultsReporting(assessedIdentity, type, resultSet.getAssessmentID()); - if (doc == null) { - showInfo("error.resreporting.na"); - return; - } - StringBuilder resultsHTML = LocalizedXSLTransformer.getInstance(ureq.getLocale()).renderResults(doc); - details.contextPut("reshtml", resultsHTML); - - removeAsListenerAndDispose(cmc); - cmc = new CloseableModalController(getWindowControl(), getTranslator().translate("close"), details); - listenTo(cmc); - - cmc.activate(); + try { + Document doc = FilePersister.retreiveResultsReporting(assessedIdentity, type, resultSet.getAssessmentID()); + if (doc == null) { + showInfo("error.resreporting.na"); + return; + } + StringBuilder resultsHTML = LocalizedXSLTransformer.getInstance(ureq.getLocale()).renderResults(doc); + details.contextPut("reshtml", resultsHTML); + + removeAsListenerAndDispose(cmc); + cmc = new CloseableModalController(getWindowControl(), getTranslator().translate("close"), details); + listenTo(cmc); + cmc.activate(); + } catch (Exception e) { + logError("", e); + showError("error.resreporting.na"); + } } else if(tEvent.getActionId().equals("ret")) { updateTableModel(); if(tableModel.isTestRunning()) { @@ -220,7 +224,7 @@ public class QTIResultDetailsController extends BasicController { ModuleConfiguration modConfig = testNode.getModuleConfiguration(); String resourcePathInfo = courseResourceableId + File.separator + nodeIdent; - AssessmentInstance ai = AssessmentFactory.createAssessmentInstance(assessedIdentity, "", modConfig, false, courseResourceableId, nodeIdent, resourcePathInfo); + AssessmentInstance ai = AssessmentFactory.createAssessmentInstance(assessedIdentity, "", modConfig, false, courseResourceableId, nodeIdent, resourcePathInfo, null); //close the test ai.close(); //persist the results diff --git a/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java b/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java index 16bd832384c..ec40fdf0d7b 100644 --- a/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java +++ b/src/main/java/org/olat/ims/qti/navigator/DefaultNavigator.java @@ -45,16 +45,20 @@ public class DefaultNavigator implements Serializable { private AssessmentInstance assessmentInstance; private Info info; + private transient NavigatorDelegate delegate; /** * */ - public DefaultNavigator(AssessmentInstance assessmentInstance) { + public DefaultNavigator(AssessmentInstance assessmentInstance, NavigatorDelegate delegate) { this.assessmentInstance = assessmentInstance; + this.delegate = delegate; info = new Info(); } - + public void setDelegate(NavigatorDelegate delegate) { + this.delegate = delegate; + } /** * @return AssessmentContext @@ -138,11 +142,12 @@ public class DefaultNavigator implements Serializable { } return sectionResult; } + /** * @see org.olat.qti.process.Navigator#submitAssessment() */ - public void submitAssessment() { + public final void submitAssessment() { Output pendingOutput = null; boolean pendingFeedback = getInfo().isFeedback(); boolean alreadyClosed = getAssessmentInstance().isClosed(); @@ -171,14 +176,22 @@ public class DefaultNavigator implements Serializable { info.setMessage(QTIConstants.MESSAGE_ASSESSMENT_SUBMITTED); info.setStatus(QTIConstants.ASSESSMENT_FINISHED); info.setRenderItems(false); + + if(delegate != null) { + delegate.submitAssessment(assessmentInstance); + } } - public void cancelAssessment() { + public final void cancelAssessment() { getAssessmentInstance().close(); info.clear(); info.setMessage(QTIConstants.MESSAGE_ASSESSMENT_CANCELED); info.setStatus(QTIConstants.ASSESSMENT_CANCELED); info.setRenderItems(false); + + if(delegate != null) { + delegate.cancelAssessment(assessmentInstance); + } } /** diff --git a/src/main/java/org/olat/ims/qti/navigator/MenuItemNavigator.java b/src/main/java/org/olat/ims/qti/navigator/MenuItemNavigator.java index 7db758687c3..42702177c76 100644 --- a/src/main/java/org/olat/ims/qti/navigator/MenuItemNavigator.java +++ b/src/main/java/org/olat/ims/qti/navigator/MenuItemNavigator.java @@ -47,8 +47,8 @@ public class MenuItemNavigator extends DefaultNavigator implements Navigator, Se /** * @param assessmentContext */ - public MenuItemNavigator(AssessmentInstance assessmentInstance) { - super(assessmentInstance); + public MenuItemNavigator(AssessmentInstance assessmentInstance, NavigatorDelegate delegate) { + super(assessmentInstance, delegate); } public void startAssessment() { diff --git a/src/main/java/org/olat/ims/qti/navigator/MenuSectionNavigator.java b/src/main/java/org/olat/ims/qti/navigator/MenuSectionNavigator.java index 1341bdd7204..8e064934c96 100644 --- a/src/main/java/org/olat/ims/qti/navigator/MenuSectionNavigator.java +++ b/src/main/java/org/olat/ims/qti/navigator/MenuSectionNavigator.java @@ -46,8 +46,8 @@ public class MenuSectionNavigator extends DefaultNavigator implements Navigator, /** * @param assessmentContext */ - public MenuSectionNavigator(AssessmentInstance assessmentInstance) { - super(assessmentInstance); + public MenuSectionNavigator(AssessmentInstance assessmentInstance, NavigatorDelegate delegate) { + super(assessmentInstance, delegate); } public void startAssessment() { diff --git a/src/main/java/org/olat/ims/qti/navigator/Navigator.java b/src/main/java/org/olat/ims/qti/navigator/Navigator.java index e9c18d14a4a..1f3a2e3852b 100644 --- a/src/main/java/org/olat/ims/qti/navigator/Navigator.java +++ b/src/main/java/org/olat/ims/qti/navigator/Navigator.java @@ -41,4 +41,5 @@ public interface Navigator { public void goToSection(int sectionPos); public Info getInfo(); + public void setDelegate(NavigatorDelegate delegate); } \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti/navigator/NavigatorDelegate.java b/src/main/java/org/olat/ims/qti/navigator/NavigatorDelegate.java new file mode 100644 index 00000000000..326b6511cd9 --- /dev/null +++ b/src/main/java/org/olat/ims/qti/navigator/NavigatorDelegate.java @@ -0,0 +1,34 @@ +/** + * <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.qti.navigator; + +import org.olat.ims.qti.process.AssessmentInstance; + +/** + * + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public interface NavigatorDelegate { + + public void submitAssessment(AssessmentInstance ai); + + public void cancelAssessment(AssessmentInstance ai); + +} diff --git a/src/main/java/org/olat/ims/qti/navigator/SequentialItemNavigator.java b/src/main/java/org/olat/ims/qti/navigator/SequentialItemNavigator.java index 1b43ecc1762..a4f6de42501 100644 --- a/src/main/java/org/olat/ims/qti/navigator/SequentialItemNavigator.java +++ b/src/main/java/org/olat/ims/qti/navigator/SequentialItemNavigator.java @@ -55,8 +55,8 @@ public class SequentialItemNavigator extends DefaultNavigator implements Navigat /** * @param assessmentInstance */ - public SequentialItemNavigator(AssessmentInstance assessmentInstance) { - super(assessmentInstance); + public SequentialItemNavigator(AssessmentInstance assessmentInstance, NavigatorDelegate delegate) { + super(assessmentInstance, delegate); } /** diff --git a/src/main/java/org/olat/ims/qti/navigator/SequentialSectionNavigator.java b/src/main/java/org/olat/ims/qti/navigator/SequentialSectionNavigator.java index d567f101ae4..3997e44540b 100644 --- a/src/main/java/org/olat/ims/qti/navigator/SequentialSectionNavigator.java +++ b/src/main/java/org/olat/ims/qti/navigator/SequentialSectionNavigator.java @@ -54,8 +54,8 @@ public class SequentialSectionNavigator extends DefaultNavigator implements Navi /** * @param assessmentInstance */ - public SequentialSectionNavigator(AssessmentInstance assessmentInstance) { - super(assessmentInstance); + public SequentialSectionNavigator(AssessmentInstance assessmentInstance, NavigatorDelegate delegate) { + super(assessmentInstance, delegate); } public void startAssessment() { diff --git a/src/main/java/org/olat/ims/qti/process/AssessmentFactory.java b/src/main/java/org/olat/ims/qti/process/AssessmentFactory.java index b9466cf893c..b231244be1b 100644 --- a/src/main/java/org/olat/ims/qti/process/AssessmentFactory.java +++ b/src/main/java/org/olat/ims/qti/process/AssessmentFactory.java @@ -30,6 +30,7 @@ import org.olat.core.logging.Tracing; import org.olat.core.util.CodeHelper; import org.olat.core.util.StringHelper; import org.olat.course.nodes.iq.IQEditController; +import org.olat.ims.qti.navigator.NavigatorDelegate; import org.olat.modules.ModuleConfiguration; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; @@ -47,7 +48,7 @@ public class AssessmentFactory { * @return */ public static AssessmentInstance createAssessmentInstance(Identity subj, String remoteAddr, ModuleConfiguration modConfig, boolean preview, - long callingResId, String callingResDetail, String resourcePathInfo) { + long callingResId, String callingResDetail, String resourcePathInfo, NavigatorDelegate delegate) { AssessmentInstance ai = null; Persister persister = null; @@ -74,7 +75,7 @@ public class AssessmentFactory { Resolver resolver = new ImsRepositoryResolver(re.getKey()); long aiID = CodeHelper.getForeverUniqueID(); try { - ai = new AssessmentInstance(subj, remoteAddr, re.getKey().longValue(), aiID, callingResId, callingResDetail, resolver, persister, modConfig); + ai = new AssessmentInstance(subj, remoteAddr, re.getKey().longValue(), aiID, callingResId, callingResDetail, resolver, persister, modConfig, delegate); } catch (Exception e) { return null; } } else { @@ -86,6 +87,7 @@ public class AssessmentFactory { ai.setAssessedIdentity(subj); ai.setCallingResId(callingResId); ai.setCallingResDetail(callingResDetail); + ai.setDelegate(delegate); if(StringHelper.containsNonWhitespace(ai.getRemoteAddr())) { ai.setRemoteAddr(remoteAddr); } @@ -101,9 +103,10 @@ public class AssessmentFactory { * @param doc * @return */ - public static AssessmentInstance createAssessmentInstance(Identity subj, String remoteAddr, long callingResId, String callingResDetail, Resolver resolver, Persister persister, ModuleConfiguration modConfig) { + public static AssessmentInstance createAssessmentInstance(Identity subj, String remoteAddr, long callingResId, String callingResDetail, Resolver resolver, Persister persister, + ModuleConfiguration modConfig, NavigatorDelegate delegate) { long aiID = CodeHelper.getForeverUniqueID(); - return new AssessmentInstance(null, remoteAddr, 0, aiID, callingResId, callingResDetail, resolver, persister, modConfig); + return new AssessmentInstance(null, remoteAddr, 0, aiID, callingResId, callingResDetail, resolver, persister, modConfig, delegate); } } diff --git a/src/main/java/org/olat/ims/qti/process/AssessmentInstance.java b/src/main/java/org/olat/ims/qti/process/AssessmentInstance.java index e990a288a61..80832407922 100644 --- a/src/main/java/org/olat/ims/qti/process/AssessmentInstance.java +++ b/src/main/java/org/olat/ims/qti/process/AssessmentInstance.java @@ -39,6 +39,7 @@ import org.olat.ims.qti.container.SectionContext; import org.olat.ims.qti.navigator.MenuItemNavigator; import org.olat.ims.qti.navigator.MenuSectionNavigator; import org.olat.ims.qti.navigator.Navigator; +import org.olat.ims.qti.navigator.NavigatorDelegate; import org.olat.ims.qti.navigator.SequentialItemNavigator; import org.olat.ims.qti.navigator.SequentialSectionNavigator; import org.olat.modules.ModuleConfiguration; @@ -139,7 +140,7 @@ public class AssessmentInstance implements Serializable { * @param modConfig */ public AssessmentInstance(Identity identity, String remoteAddr, long repositoryEntryKey, long assessID, long callingResId, String callingResDetail, - Resolver resolver, Persister persistor, ModuleConfiguration modConfig) { + Resolver resolver, Persister persistor, ModuleConfiguration modConfig, NavigatorDelegate delegate) { this.assessedIdentity = identity; this.remoteAddr = remoteAddr; this.callingResId = callingResId; @@ -211,7 +212,7 @@ public class AssessmentInstance implements Serializable { assessmentContext = new AssessmentContext(); assessmentContext.setUp(this); - createNavigator(); + createNavigator(delegate); } public Identity getAssessedIdentity() { @@ -274,21 +275,21 @@ public class AssessmentInstance implements Serializable { public boolean isSelfAssess() { return type == TYPE_SELF; } public boolean isSurvey() { return type == TYPE_SURVEY; } - private void createNavigator() { + private void createNavigator(NavigatorDelegate delegate) { if (menu) { if (sequence == SEQUENCE_SECTION) { - navigator = new MenuSectionNavigator(this); + navigator = new MenuSectionNavigator(this, delegate); } else { - navigator = new MenuItemNavigator(this); + navigator = new MenuItemNavigator(this, delegate); } } else { // not menu if (sequence == SEQUENCE_SECTION) { - navigator = new SequentialSectionNavigator(this); + navigator = new SequentialSectionNavigator(this, delegate); } else { - navigator = new SequentialItemNavigator(this); + navigator = new SequentialItemNavigator(this, delegate); } } } @@ -514,6 +515,12 @@ public class AssessmentInstance implements Serializable { preview = b; } + public void setDelegate(NavigatorDelegate delegate) { + if(navigator != null) { + navigator.setDelegate(delegate); + } + } + /* * For marking/flagging question items ... OLAT-5807 */ diff --git a/src/main/java/org/olat/modules/iq/IQDisplayController.java b/src/main/java/org/olat/modules/iq/IQDisplayController.java index 6dc21fc7666..998e3fbd89d 100644 --- a/src/main/java/org/olat/modules/iq/IQDisplayController.java +++ b/src/main/java/org/olat/modules/iq/IQDisplayController.java @@ -28,6 +28,7 @@ package org.olat.modules.iq; import java.io.File; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Set; import org.apache.log4j.Logger; @@ -65,6 +66,7 @@ import org.olat.ims.qti.container.AssessmentContext; import org.olat.ims.qti.container.ItemsInput; import org.olat.ims.qti.container.SectionContext; import org.olat.ims.qti.navigator.Navigator; +import org.olat.ims.qti.navigator.NavigatorDelegate; import org.olat.ims.qti.process.AssessmentFactory; import org.olat.ims.qti.process.AssessmentInstance; import org.olat.ims.qti.process.FilePersister; @@ -79,7 +81,7 @@ import org.olat.util.logging.activity.LoggingResourceable; /** * @author Felix Jost */ -public class IQDisplayController extends DefaultController implements GenericEventListener, Activateable2 { +public class IQDisplayController extends DefaultController implements GenericEventListener, Activateable2, NavigatorDelegate { private static final String PACKAGE = Util.getPackageName(IQDisplayController.class); private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(IQDisplayController.class); @@ -92,8 +94,11 @@ public class IQDisplayController extends DefaultController implements GenericEve private String repositorySoftkey = null; private Resolver resolver = null; private Persister persister = null; + private final Locale locale; private final Identity assessedIdentity; private volatile boolean retrievedFlag = false; + + private NavigatorDelegate delegate; private ProgressBar qtiscoreprogress, qtiquestionprogress; private IQComponent qticomp; @@ -123,10 +128,12 @@ public class IQDisplayController extends DefaultController implements GenericEve * @param callingResDetail */ IQDisplayController(ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, - WindowControl wControl, long callingResId, String callingResDetail) { + WindowControl wControl, long callingResId, String callingResDetail, NavigatorDelegate delegate) { super(wControl); this.assessedIdentity = ureq.getIdentity(); + this.locale = ureq.getLocale(); + this.delegate = delegate; ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_OPEN, getClass()); @@ -157,6 +164,7 @@ public class IQDisplayController extends DefaultController implements GenericEve ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_OPEN, getClass()); this.assessedIdentity = ureq.getIdentity(); + this.locale = ureq.getLocale(); this.modConfig = new ModuleConfiguration(); modConfig.set(IQEditController.CONFIG_KEY_ENABLEMENU, Boolean.TRUE); modConfig.set(IQEditController.CONFIG_KEY_TYPE, type); @@ -252,26 +260,16 @@ public class IQDisplayController extends DefaultController implements GenericEve // get the assessment AssessmentInstance ai = null; - // - // IQManagers synchronizes display controller creation with qti editor, please see comment in qti editor - // - //synchronized (QTIEditorMainController.IS_SAVING) { - //QTIEditorMainController.IS_SAVING_RWL.readLock().lock(); - //lock is now checked in the IQManager -> see there - //try{ - if (repositorySoftkey != null) { // instantiate from repository - // build path information which will be used to store tempory qti file - String resourcePathInfo = callingResId + File.separator + callingResDetail; - ai = AssessmentFactory.createAssessmentInstance(ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr(), - modConfig, iqsec.isPreview(), callingResId, callingResDetail, resourcePathInfo); - } else if (resolver != null) { // instantiate from given resolver - ai = AssessmentFactory.createAssessmentInstance(ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr(), - callingResId, callingResDetail, resolver, persister, modConfig); - } - //}finally{ - //QTIEditorMainController.IS_SAVING_RWL.readLock().unlock(); - //} - //} + if (repositorySoftkey != null) { // instantiate from repository + // build path information which will be used to store tempory qti file + String resourcePathInfo = callingResId + File.separator + callingResDetail; + ai = AssessmentFactory.createAssessmentInstance(ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr(), + modConfig, iqsec.isPreview(), callingResId, callingResDetail, resourcePathInfo, this); + } else if (resolver != null) { // instantiate from given resolver + ai = AssessmentFactory.createAssessmentInstance(ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr(), + callingResId, callingResDetail, resolver, persister, modConfig, this); + } + // check for null instance or instance with no items if (ai == null || ai.getAssessmentContext().getSectionContext(0).getItemContextCount() == 0) throw new AssertException( "Assessment Instance was null or no sections/items found."); @@ -523,12 +521,11 @@ public class IQDisplayController extends DefaultController implements GenericEve } } - /** - * Persist data in all cases: test, selftest, surveys except previews - * In case of survey, data will be anonymized when reading from the - * table (using the archiver) - */ - protected void postSubmitAssessment(UserRequest ureq, AssessmentInstance ai) { + + + + @Override + public void submitAssessment(AssessmentInstance ai) { if (!qtistatus.isPreview()) { //iqm.persistResults(ai, callingResId, callingResDetail, ureq.getIdentity(), ureq.getHttpReq().getRemoteAddr()); getWindowControl().setInfo(translator.translate("status.results.saved")); @@ -536,25 +533,37 @@ public class IQDisplayController extends DefaultController implements GenericEve getWindowControl().setInfo(translator.translate("status.results.notsaved")); } - if (!qtistatus.isSurvey()) { + if (!qtistatus.isSurvey() && !iqsec.isPreview()) { // for test and self-assessment, generate detailed results - generateDetailsResults(ureq, ai); - } else { + Document docResReporting = iqm.getResultsReporting(ai, assessedIdentity, locale); + FilePersister.createResultsReporting(docResReporting, assessedIdentity, ai.getFormattedType(), ai.getAssessID()); + } + + if(delegate != null) { + delegate.submitAssessment(ai); + } + } + + @Override + public void cancelAssessment(AssessmentInstance ai) { + // + } + + /** + * Persist data in all cases: test, selftest, surveys except previews + * In case of survey, data will be anonymized when reading from the + * table (using the archiver) + */ + protected void postSubmitAssessment(UserRequest ureq, AssessmentInstance ai) { + if (qtistatus.isSurvey()) { // Send also finished event in case of survey fireEvent(ureq, new IQSubmittedEvent()); } } protected void generateDetailsResults(UserRequest ureq, AssessmentInstance ai) { - Document docResReporting = iqm.getResultsReporting(ai, ureq.getIdentity(), ureq.getLocale()); if (!iqsec.isPreview()) { - FilePersister.createResultsReporting(docResReporting, ureq.getIdentity(), ai.getFormattedType(), ai.getAssessID()); - // Send score and passed to parent controller. Maybe it is necessary - // to save some data there - // Do this now and not later, maybe user will never click on - // 'close'... - AssessmentContext ac = ai.getAssessmentContext(); - fireEvent(ureq, new IQSubmittedEvent(ac.getScore(), ac.isPassed(), ai.getAssessID())); + fireEvent(ureq, new IQSubmittedEvent()); } Boolean showResultsOnFinishObj = (Boolean)modConfig.get(IQEditController.CONFIG_KEY_RESULT_ON_FINISH); @@ -563,13 +572,12 @@ public class IQDisplayController extends DefaultController implements GenericEve // do not display results reporting myContent.contextPut("displayreporting", Boolean.FALSE); } else { // display results reporting + Document docResReporting = iqm.getResultsReporting(ai, ureq.getIdentity(), ureq.getLocale()); String resReporting = iqm.transformResultsReporting(docResReporting, ureq.getLocale(), ai.getSummaryType() ); myContent.contextPut("resreporting", resReporting); myContent.contextPut("displayreporting", Boolean.TRUE); } myContent.setPage(VELOCITY_ROOT + "/result.html"); - - } /** diff --git a/src/main/java/org/olat/modules/iq/IQManager.java b/src/main/java/org/olat/modules/iq/IQManager.java index 378debd9b1c..82d22632b5f 100644 --- a/src/main/java/org/olat/modules/iq/IQManager.java +++ b/src/main/java/org/olat/modules/iq/IQManager.java @@ -78,6 +78,7 @@ import org.olat.ims.qti.container.ItemContext; import org.olat.ims.qti.container.ItemInput; import org.olat.ims.qti.container.ItemsInput; import org.olat.ims.qti.container.SectionContext; +import org.olat.ims.qti.navigator.NavigatorDelegate; import org.olat.ims.qti.process.AssessmentInstance; import org.olat.ims.qti.process.FilePersister; import org.olat.ims.qti.process.Resolver; @@ -121,7 +122,7 @@ public class IQManager extends BasicManager implements UserDataDeletable { * */ public Controller createIQDisplayController(ModuleConfiguration moduleConfiguration, IQSecurityCallback secCallback, UserRequest ureq, - WindowControl wControl, long callingResId, String callingResDetail) { + WindowControl wControl, long callingResId, String callingResDetail, NavigatorDelegate delegate) { //two cases: // -- VERY RARE CASE -- 1) qti is open in an editor session right now on the screen (or session on the way to timeout) @@ -136,7 +137,7 @@ public class IQManager extends BasicManager implements UserDataDeletable { translator.translate("status.currently.locked", new String[] {lockResult.getOwner().getName()})); }else{ ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrap(re, OlatResourceableType.iq)); - return new IQDisplayController(moduleConfiguration, secCallback, ureq, wControl, callingResId, callingResDetail); + return new IQDisplayController(moduleConfiguration, secCallback, ureq, wControl, callingResId, callingResDetail, delegate); } } diff --git a/src/main/java/org/olat/modules/iq/IQSubmittedEvent.java b/src/main/java/org/olat/modules/iq/IQSubmittedEvent.java index cf0c891063e..5f046aae691 100644 --- a/src/main/java/org/olat/modules/iq/IQSubmittedEvent.java +++ b/src/main/java/org/olat/modules/iq/IQSubmittedEvent.java @@ -33,10 +33,8 @@ import org.olat.core.gui.control.Event; * @author gnaegi */ public class IQSubmittedEvent extends Event { - private float score = 0; - private boolean passed = false; - private long assessmentID; + private static final long serialVersionUID = -7510782040615263505L; /** * constructor for a finished survey event @@ -44,38 +42,4 @@ public class IQSubmittedEvent extends Event { public IQSubmittedEvent() { super("iqfinished"); } - - /** - * Constructor for a finished test or selftest event - * @param score - * @param passed - */ - public IQSubmittedEvent(float score, boolean passed, long assessmentID) { - super("iqfinished"); - this.score = score; - this.passed = passed; - this.assessmentID = assessmentID; - } - - - /** - * @return Returns the passed. - */ - public boolean isPassed() { - return passed; - } - /** - * @return Returns the score. - */ - public float getScore() { - return score; - } - - /** - * - * @return Returns the (last) assessmentID. - */ - public long getAssessmentID() { - return assessmentID; - } } diff --git a/src/main/java/org/olat/restapi/repository/course/CourseAssessmentWebService.java b/src/main/java/org/olat/restapi/repository/course/CourseAssessmentWebService.java index f41508d5be2..5d9d205955d 100644 --- a/src/main/java/org/olat/restapi/repository/course/CourseAssessmentWebService.java +++ b/src/main/java/org/olat/restapi/repository/course/CourseAssessmentWebService.java @@ -362,7 +362,7 @@ public class CourseAssessmentWebService { // The consequence is that we must loop on section and items and set the // navigator on // the right position before submitting the inputs. - AssessmentInstance ai = AssessmentFactory.createAssessmentInstance(identity, "", modConfig, false, course.getResourceableId(), courseNode.getIdent(), resourcePathInfo); + AssessmentInstance ai = AssessmentFactory.createAssessmentInstance(identity, "", modConfig, false, course.getResourceableId(), courseNode.getIdent(), resourcePathInfo, null); Navigator navigator = ai.getNavigator(); navigator.startAssessment(); // The type of the navigator depends on the setting of the course node -- GitLab