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