From 3dc10157972c6d7d8ed110b915c56522da23597c Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Mon, 30 Apr 2018 17:18:03 +0200 Subject: [PATCH] OO-3457: propagate correction results to the course element results for every item / identity saved --- .../IQIdentityListCourseNodeController.java | 28 ++++++++++++- ...IdentityListCourseNodeToolsController.java | 2 +- .../ui/QTI21AssessmentDetailsController.java | 5 ++- ...orrectionAssessmentItemListController.java | 6 +++ ...ctionIdentityAssessmentItemController.java | 39 +++++++++++++++++++ ...nIdentityAssessmentItemListController.java | 2 + ...ityAssessmentItemNavigationController.java | 2 + .../CorrectionIdentityListController.java | 6 +++ .../CorrectionOverviewController.java | 8 +++- .../assessment/CorrectionOverviewModel.java | 34 ++++++++++++---- 10 files changed, 120 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java index bda45a853fa..9ec929a8235 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQIdentityListCourseNodeController.java @@ -31,6 +31,7 @@ import java.util.Map; import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.IdentityRef; import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.impl.FormEvent; @@ -39,6 +40,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFle import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiCellRenderer; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.stack.PopEvent; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; @@ -108,6 +110,8 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo private ConfirmExtraTimeController extraTimeCtrl; private ValidationXmlSignatureController validationCtrl; private CorrectionOverviewController correctionIdentitiesCtrl; + + private boolean modelDirty = false; @Autowired private QTI21Service qtiService; @@ -119,6 +123,9 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo RepositoryEntry courseEntry, BusinessGroup group, IQTESTCourseNode courseNode, UserCourseEnvironment coachCourseEnv, AssessmentToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) { super(ureq, wControl, stackPanel, courseEntry, group, courseNode, coachCourseEnv, toolContainer, assessmentCallback); + if(stackPanel != null) { + stackPanel.addListener(this); + } } @Override @@ -241,6 +248,8 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo boolean enabled = isTestRunning(); pullButton.setEnabled(enabled); } + + this.modelDirty = true; } /** @@ -268,7 +277,19 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo } return identityToExtraTime; } - + + @Override + public void event(UserRequest ureq, Component source, Event event) { + if(stackPanel == source) { + if(event instanceof PopEvent) { + PopEvent pe = (PopEvent)event; + if(modelDirty && pe.getController() == correctionIdentitiesCtrl) { + loadModel(ureq); + } + } + } + super.event(ureq, source, event); + } @Override public void event(UserRequest ureq, Controller source, Event event) { @@ -290,7 +311,10 @@ public class IQIdentityListCourseNodeController extends IdentityListCourseNodeCo CompleteAssessmentTestSessionEvent catse = (CompleteAssessmentTestSessionEvent)event; doUpdateCourseNode(catse.getTestSessions(), catse.getAssessmentTest(), catse.getStatus()); loadModel(ureq); - stackPanel.popController(correctionIdentitiesCtrl); + stackPanel.popController(correctionIdentitiesCtrl); + fireEvent(ureq, Event.CHANGED_EVENT); + } else if(event == Event.CHANGED_EVENT) { + modelDirty = true; fireEvent(ureq, Event.CHANGED_EVENT); } } else if(source instanceof QTI21IdentityListCourseNodeToolsController) { diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java b/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java index 2c93413e94d..097eec75147 100644 --- a/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java +++ b/src/main/java/org/olat/course/nodes/iq/QTI21IdentityListCourseNodeToolsController.java @@ -236,7 +236,7 @@ public class QTI21IdentityListCourseNodeToolsController extends AbstractToolsCon lastSessionMap.put(assessedIdentity, lastSession); Map<Identity, TestSessionState> testSessionStates = new HashMap<>(); testSessionStates.put(assessedIdentity, testSessionState); - CorrectionOverviewModel model = new CorrectionOverviewModel(courseEntry, testCourseNode.getIdent(), testEntry, + CorrectionOverviewModel model = new CorrectionOverviewModel(courseEntry, testCourseNode, testEntry, resolvedAssessmentTest, manifestBuilder, lastSessionMap, testSessionStates); correctionCtrl = new CorrectionIdentityAssessmentItemListController(ureq, getWindowControl(), stackPanel, model, assessedIdentity); listenTo(correctionCtrl); 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 f929284b82d..b58161a1e77 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -295,6 +295,9 @@ public class QTI21AssessmentDetailsController extends FormBasicController { fireEvent(ureq, Event.CHANGED_EVENT); stackPanel.popController(correctionCtrl); cleanUp(); + } else if(event == Event.CHANGED_EVENT) { + updateModel(); + fireEvent(ureq, Event.CHANGED_EVENT); } else if(event == Event.CANCELLED_EVENT) { stackPanel.popController(correctionCtrl); cleanUp(); @@ -367,7 +370,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { lastSessions.put(assessedIdentity, session); Map<Identity, TestSessionState> testSessionStates = new HashMap<>(); testSessionStates.put(assessedIdentity, testSessionState); - CorrectionOverviewModel model = new CorrectionOverviewModel(entry, subIdent, testEntry, + CorrectionOverviewModel model = new CorrectionOverviewModel(entry, courseNode, testEntry, resolvedAssessmentTest, manifestBuilder, lastSessions, testSessionStates); correctionCtrl = new CorrectionIdentityAssessmentItemListController(ureq, getWindowControl(), stackPanel, model, assessedIdentity); listenTo(correctionCtrl); diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java index 5c6ea9622e1..151037f62c6 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionAssessmentItemListController.java @@ -165,6 +165,10 @@ public class CorrectionAssessmentItemListController extends FormBasicController saveTestsButton.setElementCssClass("o_sel_correction_save_tests"); } + public void reloadModel() { + loadModel(true, true); + } + private void loadModel(boolean reset, boolean lastSessions) { if(lastSessions) { model.loadLastSessions(); @@ -284,6 +288,8 @@ public class CorrectionAssessmentItemListController extends FormBasicController stackPanel.popController(identityItemCtrl); SelectAssessmentItemEvent saie = (SelectAssessmentItemEvent)event; doSelect(ureq, saie.getListEntry(), identityItemCtrl.getAssessmentEntryList()); + } else if(event == Event.CHANGED_EVENT) { + fireEvent(ureq, Event.CHANGED_EVENT); } } else if(confirmSaveTestCtrl == source) { if(event == Event.DONE_EVENT) { diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java index 49389501467..de4e2eef23c 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemController.java @@ -21,6 +21,7 @@ package org.olat.ims.qti21.ui.assessment; import java.io.File; import java.io.IOException; +import java.math.BigDecimal; import java.net.URI; import java.util.HashMap; import java.util.List; @@ -38,6 +39,11 @@ import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.util.StringHelper; +import org.olat.course.assessment.AssessmentHelper; +import org.olat.course.nodes.IQTESTCourseNode; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.run.scoring.ScoreEvaluation; +import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.fileresource.FileResourceManager; import org.olat.ims.qti21.AssessmentItemSession; import org.olat.ims.qti21.AssessmentSessionAuditLogger; @@ -45,13 +51,16 @@ import org.olat.ims.qti21.AssessmentTestHelper; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.model.ParentPartItemRefs; +import org.olat.ims.qti21.model.xml.QtiNodesExtractor; import org.olat.ims.qti21.ui.ResourcesMapper; import org.olat.ims.qti21.ui.assessment.event.NextAssessmentItemEvent; import org.olat.ims.qti21.ui.assessment.model.AssessmentItemCorrection; import org.olat.ims.qti21.ui.assessment.model.AssessmentItemListEntry; +import org.olat.modules.assessment.Role; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; +import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest; import uk.ac.ed.ph.jqtiplus.state.TestPlanNodeKey; import uk.ac.ed.ph.jqtiplus.state.TestSessionState; @@ -151,6 +160,7 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl @Override protected void formOK(UserRequest ureq) { doSave(); + fireEvent(ureq, Event.CHANGED_EVENT); identityInteractionsCtrl.updateStatus(); } @@ -163,6 +173,7 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(saveNextQuestionButton == source) { doSave(); + fireEvent(ureq, Event.CHANGED_EVENT); fireEvent(ureq, new NextAssessmentItemEvent()); } else { super.formInnerEvent(ureq, source, event); @@ -193,8 +204,36 @@ public class CorrectionIdentityAssessmentItemController extends FormBasicControl candidateSession = qtiService.recalculateAssessmentTestSessionScores(candidateSession.getKey()); itemCorrection.setTestSession(candidateSession); model.updateLastSession(itemCorrection.getAssessedIdentity(), candidateSession); + + if(model.getCourseNode() != null && model.getCourseEnvironment() != null) { + doUpdateCourseNode(candidateSession, model.getCourseNode(), model.getCourseEnvironment()); + } } catch(IOException e) { logError("", e); } } + + private void doUpdateCourseNode(AssessmentTestSession testSession, IQTESTCourseNode courseNode, CourseEnvironment courseEnv) { + if(testSession == null) return; + + AssessmentTest assessmentTest = model.getResolvedAssessmentTest().getRootNodeLookup().extractIfSuccessful(); + Double cutValue = QtiNodesExtractor.extractCutValue(assessmentTest); + + UserCourseEnvironment assessedUserCourseEnv = AssessmentHelper + .createAndInitUserCourseEnvironment(testSession.getIdentity(), courseEnv); + ScoreEvaluation scoreEval = courseNode.getUserScoreEvaluation(assessedUserCourseEnv); + + BigDecimal finalScore = testSession.getFinalScore(); + Float score = finalScore == null ? null : finalScore.floatValue(); + Boolean passed = scoreEval.getPassed(); + if(testSession.getManualScore() != null && finalScore != null && cutValue != null) { + boolean calculated = finalScore.compareTo(BigDecimal.valueOf(cutValue.doubleValue())) >= 0; + passed = Boolean.valueOf(calculated); + } + + ScoreEvaluation manualScoreEval = new ScoreEvaluation(score, passed, + scoreEval.getAssessmentStatus(), scoreEval.getUserVisible(), scoreEval.getFullyAssessed(), + scoreEval.getCurrentRunCompletion(), scoreEval.getCurrentRunStatus(), testSession.getKey()); + courseNode.updateUserScoreEvaluation(manualScoreEval, assessedUserCourseEnv, getIdentity(), false, Role.coach); + } } diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java index 469d9eda22e..597bd9c5051 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemListController.java @@ -256,6 +256,8 @@ public class CorrectionIdentityAssessmentItemListController extends FormBasicCon stackPanel.popController(identityItemCtrl); SelectAssessmentItemEvent saie = (SelectAssessmentItemEvent)event; doSelect(ureq, (CorrectionIdentityAssessmentItemRow)saie.getListEntry()); + } else if(event == Event.CHANGED_EVENT) { + fireEvent(ureq, event); } } super.event(ureq, source, event); diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java index 8cbae849871..b26ffb03478 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityAssessmentItemNavigationController.java @@ -118,6 +118,8 @@ public class CorrectionIdentityAssessmentItemNavigationController extends BasicC if(itemCtrl == source) { if(event instanceof NextAssessmentItemEvent) { doNext(ureq); + } else if(event == Event.CHANGED_EVENT) { + fireEvent(ureq, Event.CHANGED_EVENT); } else if(event == Event.CANCELLED_EVENT) { fireEvent(ureq, Event.CANCELLED_EVENT); } diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityListController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityListController.java index 6025335391c..d3e9dd26117 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityListController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionIdentityListController.java @@ -176,6 +176,10 @@ public class CorrectionIdentityListController extends FormBasicController { saveTestsButton = uifactory.addFormLink("save.tests", formLayout, Link.BUTTON); } + public void reloadModel() { + loadModel(true, true); + } + private void loadModel(boolean reset, boolean lastSessions) { if(lastSessions) { model.loadLastSessions(); @@ -289,6 +293,8 @@ public class CorrectionIdentityListController extends FormBasicController { doUnlock(); loadModel(false, true); stackPanel.popController(identityItemListCtrl); + } else if(event == Event.CHANGED_EVENT) { + fireEvent(ureq, Event.CHANGED_EVENT); } } else if(confirmSaveTestCtrl == source) { if(event == Event.DONE_EVENT) { diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewController.java index 00fc91ff44a..e6f8d8aaf8f 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewController.java @@ -108,7 +108,7 @@ public class CorrectionOverviewController extends BasicController implements Too } List<Identity> assessedIdentities = getAssessedIdentities(); - model = new CorrectionOverviewModel(courseEntry, courseNode.getIdent(), testEntry, + model = new CorrectionOverviewModel(courseEntry, courseNode, testEntry, resolvedAssessmentTest, manifestBuilder, assessedIdentities); segmentButtonsCmp = new ButtonGroupComponent("segments"); @@ -155,6 +155,8 @@ public class CorrectionOverviewController extends BasicController implements Too if(assessmentItemsCtrl == source || identityListCtrl == source) { if(event instanceof CompleteAssessmentTestSessionEvent) { fireEvent(ureq, event); + } else if(event == Event.CHANGED_EVENT) { + fireEvent(ureq, Event.CHANGED_EVENT); } } } @@ -174,6 +176,8 @@ public class CorrectionOverviewController extends BasicController implements Too if(assessmentItemsCtrl == null) { assessmentItemsCtrl = new CorrectionAssessmentItemListController(ureq, getWindowControl(), stackPanel, model); listenTo(assessmentItemsCtrl); + } else { + assessmentItemsCtrl.reloadModel(); } stackPanel.popUpToController(this); mainPanel.setContent(assessmentItemsCtrl.getInitialComponent()); @@ -183,6 +187,8 @@ public class CorrectionOverviewController extends BasicController implements Too if(identityListCtrl == null) { identityListCtrl = new CorrectionIdentityListController(ureq, getWindowControl(), stackPanel, model); listenTo(identityListCtrl); + } else { + identityListCtrl.reloadModel(); } stackPanel.popUpToController(this); mainPanel.setContent(identityListCtrl.getInitialComponent()); diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewModel.java b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewModel.java index d23bbe31248..43283150b7e 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewModel.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/CorrectionOverviewModel.java @@ -30,6 +30,9 @@ import java.util.concurrent.ConcurrentHashMap; import org.olat.core.CoreSpringFactory; import org.olat.core.id.Identity; +import org.olat.course.CourseFactory; +import org.olat.course.nodes.IQTESTCourseNode; +import org.olat.course.run.environment.CourseEnvironment; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.model.xml.ManifestBuilder; @@ -54,9 +57,11 @@ import uk.ac.ed.ph.jqtiplus.state.TestSessionState; */ public class CorrectionOverviewModel { - private final String subIdent; + private CourseEnvironment courseEnv; + private final RepositoryEntry testEntry; private final RepositoryEntry courseEntry; + private final IQTESTCourseNode courseNode; private final List<Identity> assessedIdentities; private final ManifestBuilder manifestBuilder; private final ResolvedAssessmentTest resolvedAssessmentTest; @@ -68,12 +73,12 @@ public class CorrectionOverviewModel { private final QTI21Service qtiService; - public CorrectionOverviewModel(RepositoryEntry courseEntry, String subIdent, RepositoryEntry testEntry, + public CorrectionOverviewModel(RepositoryEntry courseEntry, IQTESTCourseNode courseNode, RepositoryEntry testEntry, ResolvedAssessmentTest resolvedAssessmentTest, ManifestBuilder manifestBuilder, Map<Identity,AssessmentTestSession> lastSessions, Map<Identity, TestSessionState> testSessionStates) { qtiService = CoreSpringFactory.getImpl(QTI21Service.class); this.courseEntry = courseEntry; - this.subIdent = subIdent; + this.courseNode = courseNode; this.testEntry = testEntry; this.manifestBuilder = manifestBuilder; this.resolvedAssessmentTest = resolvedAssessmentTest; @@ -86,12 +91,12 @@ public class CorrectionOverviewModel { } } - public CorrectionOverviewModel(RepositoryEntry courseEntry, String subIdent, RepositoryEntry testEntry, + public CorrectionOverviewModel(RepositoryEntry courseEntry, IQTESTCourseNode courseNode, RepositoryEntry testEntry, ResolvedAssessmentTest resolvedAssessmentTest, ManifestBuilder manifestBuilder, List<Identity> assessedIdentities) { qtiService = CoreSpringFactory.getImpl(QTI21Service.class); this.courseEntry = courseEntry; - this.subIdent = subIdent; + this.courseNode = courseNode; this.testEntry = testEntry; this.manifestBuilder = manifestBuilder; this.resolvedAssessmentTest = resolvedAssessmentTest; @@ -101,7 +106,22 @@ public class CorrectionOverviewModel { } public String getSubIdent() { - return subIdent; + return courseNode == null ? null : courseNode.getIdent(); + } + + public IQTESTCourseNode getCourseNode() { + return courseNode; + } + + public CourseEnvironment getCourseEnvironment() { + if(courseEnv != null) { + return courseEnv; + } + + if(courseEntry != null && "CourseModule".equals(courseEntry.getOlatResource().getResourceableTypeName())) { + courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment(); + } + return courseEnv; } public RepositoryEntry getTestEntry() { @@ -149,7 +169,7 @@ public class CorrectionOverviewModel { public Map<Identity,AssessmentTestSession> loadLastSessions() { Set<Identity> identitiesSet = new HashSet<>(assessedIdentities); List<AssessmentTestSession> sessions = CoreSpringFactory.getImpl(QTI21Service.class) - .getAssessmentTestSessions(courseEntry, subIdent, testEntry); + .getAssessmentTestSessions(courseEntry, getSubIdent(), testEntry); Map<Identity,AssessmentTestSession> identityToSessions = new HashMap<>(); for(AssessmentTestSession session:sessions) { //filter last session / user -- GitLab