diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java index 02700af84f3f40b983e599228fe5cbb18da377e4..bd13f235dc46eea076a757efd82d3c2376a50df0 100644 --- a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentRunController.java @@ -19,6 +19,8 @@ */ package org.olat.course.nodes.iq; +import java.io.File; +import java.net.URI; import java.text.DateFormat; import java.util.Date; import java.util.List; @@ -47,6 +49,7 @@ import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.resource.OresHelper; import org.olat.course.DisposedCourseRestartController; import org.olat.course.assessment.AssessmentHelper; +import org.olat.course.assessment.AssessmentManager; import org.olat.course.auditing.UserNodeAuditManager; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.IQSELFCourseNode; @@ -54,14 +57,18 @@ import org.olat.course.nodes.IQTESTCourseNode; import org.olat.course.nodes.QTICourseNode; import org.olat.course.run.scoring.ScoreEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.fileresource.FileResourceManager; import org.olat.ims.qti.process.AssessmentInstance; +import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.OutcomesListener; import org.olat.ims.qti21.QTI21DeliveryOptions; import org.olat.ims.qti21.QTI21DeliveryOptions.ShowResultsOnFinish; import org.olat.ims.qti21.QTI21LoggingAction; import org.olat.ims.qti21.QTI21Service; +import org.olat.ims.qti21.ui.AssessmentResultController; import org.olat.ims.qti21.ui.AssessmentTestDisplayController; import org.olat.ims.qti21.ui.QTI21Event; +import org.olat.ims.qti21.ui.ResourcesMapper; import org.olat.instantMessaging.InstantMessagingService; import org.olat.modules.ModuleConfiguration; import org.olat.modules.assessment.AssessmentEntry; @@ -81,7 +88,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen private static final OLATResourceable assessmentEventOres = OresHelper.createOLATResourceableType(AssessmentEvent.class); private static final OLATResourceable assessmentInstanceOres = OresHelper.createOLATResourceableType(AssessmentInstance.class); - private Link startButton; + private Link startButton, showResultsButton, hideResultsButton; private final VelocityContainer mainVC; private boolean assessmentStopped = true; @@ -97,6 +104,7 @@ public class QTI21AssessmentRunController extends BasicController implements Gen // The test is really assessment not a self test or a survey private final boolean assessmentType = true; + private AssessmentResultController resultCtrl; private AssessmentTestDisplayController displayCtrl; private LayoutMain3ColsController displayContainerController; private AtomicBoolean incrementAttempts = new AtomicBoolean(true); @@ -222,13 +230,23 @@ public class QTI21AssessmentRunController extends BasicController implements Gen private void exposeResults(UserRequest ureq) { //migration: check if old tests have no summary configured boolean showResultsOnHomePage = config.getBooleanSafe(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE); - String configuredSummary = config.getStringValue(IQEditController.CONFIG_KEY_SUMMARY); + + ShowResultsOnFinish showSummary = deliveryOptions.getShowResultsOnFinish(); + String defaultConfSummary = showSummary == null ? AssessmentInstance.QMD_ENTRY_SUMMARY_COMPACT : showSummary.getIQEquivalent(); + String configuredSummary = config.getStringValue(IQEditController.CONFIG_KEY_SUMMARY, defaultConfSummary); boolean noSummary = configuredSummary == null || (configuredSummary!=null && configuredSummary.equals(AssessmentInstance.QMD_ENTRY_SUMMARY_NONE)); if(!noSummary) { - mainVC.contextPut("showResultsOnHomePage",new Boolean(showResultsOnHomePage)); - boolean dateRelatedVisibility = AssessmentHelper.isResultVisible(config); + mainVC.contextPut("showResultsOnHomePage", new Boolean(showResultsOnHomePage)); + boolean dateRelatedVisibility = isResultVisible(config); if(showResultsOnHomePage && dateRelatedVisibility) { mainVC.contextPut("showResultsVisible",Boolean.TRUE); + showResultsButton = LinkFactory.createLink("command.showResults", "command.showResults", getTranslator(), mainVC, this, Link.LINK | Link.NONTRANSLATED); + showResultsButton.setCustomDisplayText(translate("showResults.title")); + showResultsButton.setIconLeftCSS("o_icon o_icon-fw o_icon_open_togglebox"); + + hideResultsButton = LinkFactory.createLink("command.hideResults", "command.hideResults", getTranslator(), mainVC, this, Link.LINK | Link.NONTRANSLATED); + hideResultsButton.setCustomDisplayText(translate("showResults.title")); + hideResultsButton.setIconLeftCSS("o_icon o_icon-fw o_icon_close_togglebox"); } else if(showResultsOnHomePage) { Date startDate = config.getDateValue(IQEditController.CONFIG_KEY_RESULTS_START_DATE); Date endDate = config.getDateValue(IQEditController.CONFIG_KEY_RESULTS_END_DATE); @@ -248,6 +266,22 @@ public class QTI21AssessmentRunController extends BasicController implements Gen mainVC.contextPut("showChangelog", showResultsOnHomePage); } + private boolean isResultVisible(ModuleConfiguration modConfig) { + boolean isVisible = false; + boolean showResultsActive = modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS); + if(showResultsActive) { + Date startDate = modConfig.getDateValue(IQEditController.CONFIG_KEY_RESULTS_START_DATE); + Date endDate = modConfig.getDateValue(IQEditController.CONFIG_KEY_RESULTS_END_DATE); + Date currentDate = new Date(); + if(startDate != null && currentDate.after(startDate) && (endDate == null || currentDate.before(endDate))) { + isVisible = true; + } + } else { + isVisible = true; + } + return isVisible; + } + @Override protected void doDispose() { if (assessmentType) { @@ -274,6 +308,10 @@ public class QTI21AssessmentRunController extends BasicController implements Gen protected void event(UserRequest ureq, Component source, Event event) { if(startButton == source && startButton.isEnabled() && startButton.isVisible()) { doStart(ureq); + }else if(source == showResultsButton) { + doShowResults(ureq); + } else if (source == hideResultsButton) { + doHideResults(); } } @@ -303,6 +341,34 @@ public class QTI21AssessmentRunController extends BasicController implements Gen } super.event(ureq, source, event); } + + private void doShowResults(UserRequest ureq) { + removeAsListenerAndDispose(resultCtrl); + + AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager(); + AssessmentEntry assessmentEntry = am.getAssessmentEntry(courseNode, getIdentity()); + AssessmentTestSession session = qtiService.getAssessmentTestSession(assessmentEntry.getAssessmentId()); + if(session == null) { + mainVC.contextPut("showResults", Boolean.FALSE); + } else { + FileResourceManager frm = FileResourceManager.getInstance(); + File fUnzippedDirRoot = frm.unzipFileResource(session.getTestEntry().getOlatResource()); + URI assessmentObjectUri = qtiService.createAssessmentObjectUri(fUnzippedDirRoot); + File submissionDir = qtiService.getAssessmentResultFile(session); + String mapperUri = registerCacheableMapper(null, "QTI21CNResults::" + session.getTestEntry().getKey(), + new ResourcesMapper(assessmentObjectUri, submissionDir)); + + resultCtrl = new AssessmentResultController(ureq, getWindowControl(), getIdentity(), true, + session, getDeliveryOptions().getShowResultsOnFinish(), fUnzippedDirRoot, mapperUri, false, false); + listenTo(resultCtrl); + mainVC.put("resultReport", resultCtrl.getInitialComponent()); + mainVC.contextPut("showResults", Boolean.TRUE); + } + } + + private void doHideResults() { + mainVC.contextPut("showResults", Boolean.FALSE); + } private void doStart(UserRequest ureq) { removeAsListenerAndDispose(displayCtrl); diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java b/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java index 7875bb3fb4fbbde1c2f5bab7b7b0c56fa5aa1c82..151d41684164f2e0bdf6f89a304d19329b3a78b8 100644 --- a/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21EditForm.java @@ -19,9 +19,12 @@ */ package org.olat.course.nodes.iq; +import java.util.Date; + import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.DateChooser; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.elements.SelectionElement; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; @@ -50,6 +53,8 @@ public class QTI21EditForm extends FormBasicController { private SelectionElement fullWindowEl; private SingleSelection correctionModeEl; + private SelectionElement showResultsOnHomePage; + private SelectionElement scoreInfo, showResultsDateDependentButton; private MultipleSelectionElement showTitlesEl, showMenuEl; private MultipleSelectionElement personalNotesEl; private MultipleSelectionElement enableCancelEl, enableSuspendEl; @@ -58,6 +63,7 @@ public class QTI21EditForm extends FormBasicController { private MultipleSelectionElement showResultsOnFinishEl; private MultipleSelectionElement allowAnonymEl; private SingleSelection typeShowResultsOnFinishEl; + private DateChooser startDateElement, endDateElement; private TextElement maxAttemptsEl; @@ -170,6 +176,33 @@ public class QTI21EditForm extends FormBasicController { if(enableCancel) { enableCancelEl.select(onKeys[0], true); } + + //Show score informations on start page + boolean enableScoreInfos = modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_ENABLESCOREINFO); + scoreInfo = uifactory.addCheckboxesHorizontal("qti_scoreInfo", "qti.form.scoreinfo", formLayout, new String[]{"xx"}, new String[]{null}); + scoreInfo.select("xx", enableScoreInfos); + scoreInfo.addActionListener(FormEvent.ONCLICK); + + boolean showResultOnHomePage = modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE); + showResultsOnHomePage = uifactory.addCheckboxesHorizontal("qti_enableResultsOnHomePage", "qti.form.results.onhomepage", formLayout, new String[]{"xx"}, new String[]{null}); + showResultsOnHomePage.select("xx", showResultOnHomePage); + showResultsOnHomePage.addActionListener(FormEvent.ONCLICK); + + boolean showResultsDateDependent = modConfig.getBooleanSafe(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS); + showResultsDateDependentButton = uifactory.addCheckboxesHorizontal("qti_showresult", "qti.form.show.results", formLayout, new String[]{"xx"}, new String[]{null}); + showResultsDateDependentButton.select("xx", showResultsDateDependent); + showResultsDateDependentButton.addActionListener(FormEvent.ONCLICK); + + Date startDate = modConfig.getDateValue(IQEditController.CONFIG_KEY_RESULTS_START_DATE); + startDateElement = uifactory.addDateChooser("qti_form_start_date", "qti.form.date.start", null, formLayout); + startDateElement.setDateChooserTimeEnabled(true); + startDateElement.setDate(startDate); + startDateElement.setMandatory(true); + + Date endDate = modConfig.getDateValue(IQEditController.CONFIG_KEY_RESULTS_END_DATE); + endDateElement = uifactory.addDateChooser("qti_form_end_date", "qti.form.date.end", null, formLayout); + endDateElement.setDateChooserTimeEnabled(true); + endDateElement.setDate(endDate); showResultsOnFinishEl = uifactory.addCheckboxesHorizontal("resultOnFinish", "qti.form.results.onfinish", formLayout, onKeys, onValues); showResultsOnFinishEl.addActionListener(FormEvent.ONCHANGE); @@ -199,6 +232,8 @@ public class QTI21EditForm extends FormBasicController { } uifactory.addFormSubmitButton("submit", formLayout); + + update(); } @Override @@ -231,12 +266,33 @@ public class QTI21EditForm extends FormBasicController { @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(limitAttemptsEl == source) { - maxAttemptsEl.setVisible(limitAttemptsEl.isAtLeastSelected(1)); + update(); } else if(showResultsOnFinishEl == source) { - typeShowResultsOnFinishEl.setVisible(showResultsOnFinishEl.isAtLeastSelected(1)); + update(); + } else if(showResultsDateDependentButton == source || showResultsOnHomePage == source) { + update(); } super.formInnerEvent(ureq, source, event); } + + private void update() { + maxAttemptsEl.setVisible(limitAttemptsEl.isAtLeastSelected(1)); + + showResultsDateDependentButton.setVisible(showResultsOnHomePage.isSelected(0)); + typeShowResultsOnFinishEl.setVisible(showResultsOnFinishEl.isAtLeastSelected(1) || showResultsOnHomePage.isSelected(0)); + + if (!startDateElement.isVisible()) { + startDateElement.setValue(""); + } + startDateElement.clearError(); + startDateElement.setVisible(showResultsDateDependentButton.isVisible() && showResultsDateDependentButton.isSelected(0)); + + endDateElement.clearError(); + if (!endDateElement.isVisible()){ + endDateElement.setValue(""); + } + endDateElement.setVisible(startDateElement.isVisible()); + } @Override protected void formOK(UserRequest ureq) { @@ -260,7 +316,13 @@ public class QTI21EditForm extends FormBasicController { modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_SCOREPROGRESS, displayScoreProgressEl.isSelected(0)); modConfig.setBooleanEntry(IQEditController.CONFIG_ALLOW_ANONYM, allowAnonymEl.isSelected(0)); - if(showResultsOnFinishEl.isAtLeastSelected(1)) { + modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_ENABLESCOREINFO, scoreInfo.isSelected(0)); + modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_DATE_DEPENDENT_RESULTS, showResultsDateDependentButton.isSelected(0)); + modConfig.setDateValue(IQEditController.CONFIG_KEY_RESULTS_START_DATE, startDateElement.getDate()); + modConfig.setDateValue(IQEditController.CONFIG_KEY_RESULTS_END_DATE, endDateElement.getDate()); + modConfig.setBooleanEntry(IQEditController.CONFIG_KEY_RESULT_ON_HOME_PAGE, showResultsOnHomePage.isSelected(0)); + + if(showResultsOnFinishEl.isAtLeastSelected(1) || showResultsOnHomePage.isSelected(0)) { if(typeShowResultsOnFinishEl.isOneSelected()) { modConfig.set(IQEditController.CONFIG_KEY_SUMMARY, typeShowResultsOnFinishEl.getSelectedKey()); } else { diff --git a/src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html b/src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html index f3a4ae268808167afb503e9d7d9321a60e36aa02..66b5812c8867f8c52f096df27132e5204fbed657 100644 --- a/src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html +++ b/src/main/java/org/olat/course/nodes/iq/_content/assessment_run.html @@ -80,6 +80,27 @@ #end #end +#if($showResultsOnHomePage && $attempts > 0) + <div class="panel panel-default o_results"> + <div class="panel-heading"> + <h4 class="panel-title">#if ($showResults && $r.available("command.hideResults")) + $r.render("command.hideResults") + #elseif($r.available("command.showResults")) + $r.render("command.showResults") + #else$r.translate("showResults.title")#end</h4> + </div> + #if($showResultsVisible) + #if($showResults) + <div class="panel-collapse collapse in"><div class="panel-body"> + <div id="o_qti_results" class="small">$r.render("resultReport")</div> + </div></div> + #end + #else + <div class="panel-collapse collapse in"><div class="panel-body">$visibilityPeriod</div></div> + #end + </div> +#end + #if (($attemptsConfig && $attemptsConfig > 0 && ($attemptsConfig <= $attempts)) || $blockAfterSuccess) <div class="o_statusinfo"> diff --git a/src/main/java/org/olat/ims/qti/resultexport/QTI21ResultsExportMediaResource.java b/src/main/java/org/olat/ims/qti/resultexport/QTI21ResultsExportMediaResource.java index 4357f9b831553dc31071c8d01e1ee4199d60dba4..b9531e38217ad418287f8458675172e9148ed8b3 100644 --- a/src/main/java/org/olat/ims/qti/resultexport/QTI21ResultsExportMediaResource.java +++ b/src/main/java/org/olat/ims/qti/resultexport/QTI21ResultsExportMediaResource.java @@ -193,7 +193,7 @@ public class QTI21ResultsExportMediaResource implements MediaResource { Controller assessmentResultController = new AssessmentResultController( ureq, mockwControl, identity, false, session, - ShowResultsOnFinish.details, fUnzippedDirRoot, null, false); + ShowResultsOnFinish.details, fUnzippedDirRoot, null, false, true); Component component = assessmentResultController.getInitialComponent(); String componentHTML = createResultHTML(component); diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentResultController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentResultController.java index a6bcd9a3ed140deb236634096bb7438dee69a568..3c3e82d93fba305242c08b9770a4400dd981abf2 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentResultController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentResultController.java @@ -94,6 +94,7 @@ public class AssessmentResultController extends FormBasicController { private final boolean anonym; private final boolean withPrint; + private final boolean withTitle; private final Identity assessedIdentity; private final TestSessionState testSessionState; private final AssessmentResult assessmentResult; @@ -113,12 +114,13 @@ public class AssessmentResultController extends FormBasicController { public AssessmentResultController(UserRequest ureq, WindowControl wControl, Identity assessedIdentity, boolean anonym, AssessmentTestSession candidateSession, ShowResultsOnFinish resultsOnfinish, File fUnzippedDirRoot, String mapperUri, - boolean withPrint) { + boolean withPrint, boolean withTitle) { super(ureq, wControl, "assessment_results"); this.anonym = anonym; this.mapperUri = mapperUri; this.withPrint = withPrint; + this.withTitle = withTitle; this.resultsOnfinish = resultsOnfinish; this.assessedIdentity = assessedIdentity; this.candidateSession = candidateSession; @@ -142,12 +144,13 @@ public class AssessmentResultController extends FormBasicController { initForm(ureq); } - + @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(formLayout instanceof FormLayoutContainer) { FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("title", new Boolean(withTitle)); layoutCont.contextPut("print", new Boolean(withPrint)); layoutCont.contextPut("printCommand", Boolean.FALSE); if(withPrint) { @@ -328,7 +331,7 @@ public class AssessmentResultController extends FormBasicController { @Override public Controller createController(UserRequest uureq, WindowControl wwControl) { AssessmentResultController printViewCtrl = new AssessmentResultController(uureq, wwControl, assessedIdentity, anonym, - candidateSession, resultsOnfinish, fUnzippedDirRoot, mapperUri, false); + candidateSession, resultsOnfinish, fUnzippedDirRoot, mapperUri, false, true); printViewCtrl.flc.contextPut("printCommand", Boolean.TRUE); listenTo(printViewCtrl); return printViewCtrl; 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 6d1c66ed9b6c3e10f584429fe5384c9dd0f7c370..1019f544d4b22705d07bd65947bd0b935481b10c 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -1478,7 +1478,7 @@ public class AssessmentTestDisplayController extends BasicController implements removeAsListenerAndDispose(resultCtrl); resultCtrl = new AssessmentResultController(ureq, getWindowControl(), assessedIdentity, anonym, AssessmentTestDisplayController.this.getCandidateSession(), - deliveryOptions.getShowResultsOnFinish(), fUnzippedDirRoot, mapperUri, false); + deliveryOptions.getShowResultsOnFinish(), fUnzippedDirRoot, mapperUri, false, true); listenTo(resultCtrl); flc.add("qtiResults", resultCtrl.getInitialFormItem()); resultsVisible = true; diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java index f33c6dbf8ac9319014da94625020e2c5e859f32d..f3c312dcdfb015a4fe3ef18c43228ad301ceaf10 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -343,7 +343,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { new ResourcesMapper(assessmentObjectUri, submissionDir)); resultCtrl = new AssessmentResultController(ureq, getWindowControl(), assessedIdentity, false, session, - ShowResultsOnFinish.details, fUnzippedDirRoot, mapperUri, true); + ShowResultsOnFinish.details, fUnzippedDirRoot, mapperUri, true, true); listenTo(resultCtrl); cmc = new CloseableModalController(getWindowControl(), "close", resultCtrl.getInitialComponent(), true, translate("table.header.results")); diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html b/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html index 6a19d9297d2ccad9abf5b1697285bf4d667f2d4b..f92ca714a74807836a34d8898c18aa2420e731b2 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html +++ b/src/main/java/org/olat/ims/qti21/ui/_content/assessment_results.html @@ -1,5 +1,7 @@ <div class="o_header_with_buttons"> + #if($title) <h3>$r.translate("head.assessment.details")</h3> + #end #if($print) <div class="o_button_group o_button_group_right"> <script type="text/javascript">