From 59ef4161c0d768ddd5f1ca6634dc1559747bfb2b Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Mon, 2 Sep 2019 11:33:06 +0200
Subject: [PATCH] OO-4207: Calculate the done date of a course node

---
 .../DefaultLinearStatusEvaluator.java         | 21 +++++---
 .../evaluation/LearningPathEvaluator.java     | 23 +++-----
 .../evaluation/StatusEvaluator.java           | 23 ++++++--
 .../evaluation/StatusEvaluatorResult.java     | 53 +++++++++++++++++++
 .../learningpath/STLinearStatusEvaluator.java | 35 +++++++++---
 .../run/scoring/AssessmentEvaluation.java     | 17 +++---
 .../course/run/scoring/ScoreAccounting.java   | 18 ++++++-
 .../DefaultLinearStatusEvaluatorTest.java     | 49 ++++++++++++++---
 .../STLinearStatusEvaluatorTest.java          | 49 ++++++++++++++---
 9 files changed, 229 insertions(+), 59 deletions(-)
 create mode 100644 src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluatorResult.java

diff --git a/src/main/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluator.java b/src/main/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluator.java
index bd2acf26be1..6aebf71046f 100644
--- a/src/main/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluator.java
+++ b/src/main/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluator.java
@@ -19,6 +19,7 @@
  */
 package org.olat.course.learningpath.evaluation;
 
+import java.util.Date;
 import java.util.List;
 
 import org.apache.logging.log4j.Logger;
@@ -26,6 +27,7 @@ import org.olat.core.logging.Tracing;
 import org.olat.course.learningpath.LearningPathObligation;
 import org.olat.course.learningpath.LearningPathStatus;
 import org.olat.course.learningpath.ui.LearningPathTreeNode;
+import org.olat.course.run.scoring.AssessmentEvaluation;
 import org.olat.modules.assessment.model.AssessmentEntryStatus;
 
 /**
@@ -44,26 +46,29 @@ public class DefaultLinearStatusEvaluator implements StatusEvaluator {
 	}
 	
 	@Override
-	public LearningPathStatus getStatus(LearningPathTreeNode previousNode, AssessmentEntryStatus statusCurrentNode) {
+	public Result getStatus(LearningPathTreeNode previousNode, AssessmentEvaluation assessmentEvaluation) {
 		LearningPathStatus status = LearningPathStatus.notAccessible;
-		if (AssessmentEntryStatus.done.equals(statusCurrentNode)) {
+		AssessmentEntryStatus assessmentStatus = assessmentEvaluation.getAssessmentStatus();
+		if (AssessmentEntryStatus.done.equals(assessmentStatus)) {
 			status = LearningPathStatus.done;
-		} else if (AssessmentEntryStatus.inProgress.equals(statusCurrentNode) || AssessmentEntryStatus.inReview.equals(statusCurrentNode)) {
+		} else if (AssessmentEntryStatus.inProgress.equals(assessmentStatus) || AssessmentEntryStatus.inReview.equals(assessmentStatus)) {
 			status = LearningPathStatus.inProgress;
 		} else if (previousNode == null) {
 			status = LearningPathStatus.ready;
 		} else if (LearningPathStatus.done.equals(previousNode.getStatus())) {
 			status = LearningPathStatus.ready;
-		} else if (!LearningPathObligation.mandatory.equals(previousNode.getObligation()) && LearningPathStatus.isAccessible(previousNode.getStatus())) {
+		} else if (LearningPathObligation.optional.equals(previousNode.getObligation()) && LearningPathStatus.isAccessible(previousNode.getStatus())) {
 			status = LearningPathStatus.ready;
 		}
 		log.debug("previous node type: {}, previous learning path status: {}, previous obligation: {}, current assessment status: {}, current learning path status: {}"
-				, previousNode != null? previousNode.getCourseNode().getType(): null
+				, previousNode != null && previousNode.getCourseNode() != null? previousNode.getCourseNode().getType(): null
 				, previousNode != null? previousNode.getStatus(): null
 				, previousNode != null? previousNode.getObligation(): null
-				, statusCurrentNode
+				, assessmentEvaluation
 				, status);
-		return status;
+
+		Date dateDone = LearningPathStatus.done.equals(status)? assessmentEvaluation.getAssessmentDone(): null;
+		return StatusEvaluator.result(status, dateDone);
 	}
 
 	@Override
@@ -72,7 +77,7 @@ public class DefaultLinearStatusEvaluator implements StatusEvaluator {
 	}
 
 	@Override
-	public LearningPathStatus getStatus(LearningPathTreeNode currentNode, List<LearningPathTreeNode> children) {
+	public Result getStatus(LearningPathTreeNode currentNode, List<LearningPathTreeNode> children) {
 		return null;
 	}
 
diff --git a/src/main/java/org/olat/course/learningpath/evaluation/LearningPathEvaluator.java b/src/main/java/org/olat/course/learningpath/evaluation/LearningPathEvaluator.java
index 1e266509cef..0c087574f9c 100644
--- a/src/main/java/org/olat/course/learningpath/evaluation/LearningPathEvaluator.java
+++ b/src/main/java/org/olat/course/learningpath/evaluation/LearningPathEvaluator.java
@@ -26,11 +26,10 @@ import org.olat.core.gui.components.tree.GenericTreeModel;
 import org.olat.core.gui.components.tree.TreeNode;
 import org.olat.core.util.nodes.INode;
 import org.olat.course.learningpath.LearningPathObligation;
-import org.olat.course.learningpath.LearningPathStatus;
+import org.olat.course.learningpath.evaluation.StatusEvaluator.Result;
 import org.olat.course.learningpath.ui.LearningPathTreeNode;
 import org.olat.course.run.scoring.AssessmentEvaluation;
 import org.olat.course.run.scoring.ScoreAccounting;
-import org.olat.modules.assessment.model.AssessmentEntryStatus;
 
 /**
  * 
@@ -94,20 +93,13 @@ public class LearningPathEvaluator {
 	private void refreshStatus(LearningPathTreeNode currentNode, LearningPathTreeNode previousNode) {
 		StatusEvaluator evaluator = statusEvaluatorProvider.getEvaluator(currentNode.getCourseNode());
 		if (evaluator.isStatusDependingOnPreviousNode()) {
-			AssessmentEntryStatus assessmentStatus = getAssessmentStatus(currentNode);
-			LearningPathStatus status = evaluator.getStatus(previousNode, assessmentStatus);
-			currentNode.setStatus(status);
+			AssessmentEvaluation assessmentEvaluation = scoreAccounting.getScoreEvaluation(currentNode.getCourseNode());
+			Result result = evaluator.getStatus(previousNode, assessmentEvaluation);
+			currentNode.setStatus(result.getStatus());
+			currentNode.setDateDone(result.getDoneDate());
 		}
 	}
 
-	private AssessmentEntryStatus getAssessmentStatus(LearningPathTreeNode currentNode) {
-		AssessmentEvaluation scoreEvaluation = scoreAccounting.getScoreEvaluation(currentNode.getCourseNode());
-		AssessmentEntryStatus assessmentStatus = scoreEvaluation != null
-				? scoreEvaluation.getAssessmentStatus()
-				: AssessmentEntryStatus.notStarted;
-		return assessmentStatus;
-	}
-
 	private void refreshDuration(LearningPathTreeNode currentNode) {
 		DurationEvaluator evaluator = durationEvaluatorProvider.getEvaluator(currentNode.getCourseNode());
 		if (evaluator.isDependingOnCurrentNode()) {
@@ -120,8 +112,9 @@ public class LearningPathEvaluator {
 			List<LearningPathTreeNode> children) {
 		StatusEvaluator evaluator = statusEvaluatorProvider.getEvaluator(currentNode.getCourseNode());
 		if (evaluator.isStatusDependingOnChildNodes()) {
-			LearningPathStatus status = evaluator.getStatus(currentNode, children);
-			currentNode.setStatus(status);
+			Result result = evaluator.getStatus(currentNode, children);
+			currentNode.setStatus(result.getStatus());
+			currentNode.setDateDone(result.getDoneDate());
 		}
 	}
 
diff --git a/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluator.java b/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluator.java
index e07b7e697da..c1232b593cf 100644
--- a/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluator.java
+++ b/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluator.java
@@ -19,11 +19,12 @@
  */
 package org.olat.course.learningpath.evaluation;
 
+import java.util.Date;
 import java.util.List;
 
 import org.olat.course.learningpath.LearningPathStatus;
 import org.olat.course.learningpath.ui.LearningPathTreeNode;
-import org.olat.modules.assessment.model.AssessmentEntryStatus;
+import org.olat.course.run.scoring.AssessmentEvaluation;
 
 /**
  * 
@@ -33,12 +34,24 @@ import org.olat.modules.assessment.model.AssessmentEntryStatus;
  */
 public interface StatusEvaluator {
 	
-	boolean isStatusDependingOnPreviousNode();
+	public boolean isStatusDependingOnPreviousNode();
 	
-	LearningPathStatus getStatus(LearningPathTreeNode previousNode, AssessmentEntryStatus statusCurrentNode);
+	public Result getStatus(LearningPathTreeNode previousNode, AssessmentEvaluation assessmentEvaluation);
 	
-	boolean isStatusDependingOnChildNodes();
+	public boolean isStatusDependingOnChildNodes();
 	
-	LearningPathStatus getStatus(LearningPathTreeNode currentNode, List<LearningPathTreeNode>children);
+	public Result getStatus(LearningPathTreeNode currentNode, List<LearningPathTreeNode>children);
+	
+	public static Result result(LearningPathStatus status, Date doneDate) {
+		return new StatusEvaluatorResult(status, doneDate);
+	}
+	
+	public interface Result {
+		
+		public LearningPathStatus getStatus();
+		
+		public Date getDoneDate();
+		
+	}
 
 }
diff --git a/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluatorResult.java b/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluatorResult.java
new file mode 100644
index 00000000000..f584008cded
--- /dev/null
+++ b/src/main/java/org/olat/course/learningpath/evaluation/StatusEvaluatorResult.java
@@ -0,0 +1,53 @@
+/**
+ * <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.course.learningpath.evaluation;
+
+import java.util.Date;
+
+import org.olat.course.learningpath.LearningPathStatus;
+import org.olat.course.learningpath.evaluation.StatusEvaluator.Result;
+
+/**
+ * 
+ * Initial date: 2 Sep 2019<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+class StatusEvaluatorResult implements Result {
+	
+	private final LearningPathStatus status;
+	private final Date doneDate;
+	
+	StatusEvaluatorResult(LearningPathStatus status, Date doneDate) {
+		this.status = status;
+		this.doneDate = doneDate;
+	}
+
+	@Override
+	public LearningPathStatus getStatus() {
+		return status;
+	}
+
+	@Override
+	public Date getDoneDate() {
+		return doneDate;
+	}
+
+}
diff --git a/src/main/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluator.java b/src/main/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluator.java
index 0d1882c8aab..c9547bac342 100644
--- a/src/main/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluator.java
+++ b/src/main/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluator.java
@@ -19,6 +19,7 @@
  */
 package org.olat.course.nodes.st.learningpath;
 
+import java.util.Date;
 import java.util.List;
 
 import org.olat.course.learningpath.LearningPathObligation;
@@ -26,7 +27,7 @@ import org.olat.course.learningpath.LearningPathStatus;
 import org.olat.course.learningpath.evaluation.DefaultLinearStatusEvaluator;
 import org.olat.course.learningpath.evaluation.StatusEvaluator;
 import org.olat.course.learningpath.ui.LearningPathTreeNode;
-import org.olat.modules.assessment.model.AssessmentEntryStatus;
+import org.olat.course.run.scoring.AssessmentEvaluation;
 
 /**
  * 
@@ -44,8 +45,8 @@ class STLinearStatusEvaluator implements StatusEvaluator {
 	}
 
 	@Override
-	public LearningPathStatus getStatus(LearningPathTreeNode previousNode, AssessmentEntryStatus statusCurrentNode) {
-		return previousEvaluator.getStatus(previousNode, statusCurrentNode);
+	public Result getStatus(LearningPathTreeNode previousNode, AssessmentEvaluation assessmentEvaluation) {
+		return previousEvaluator.getStatus(previousNode, assessmentEvaluation);
 	}
 
 	@Override
@@ -54,9 +55,10 @@ class STLinearStatusEvaluator implements StatusEvaluator {
 	}
 
 	@Override
-	public LearningPathStatus getStatus(LearningPathTreeNode currentNode, List<LearningPathTreeNode> children) {
+	public Result getStatus(LearningPathTreeNode currentNode, List<LearningPathTreeNode> children) {
 		boolean allDone = true;
 		boolean inProgress = false;
+		Date latestDoneDate = currentNode.getDateDone();
 		for (LearningPathTreeNode child : children) {
 			if (allDone && isNotOptional(child) && isNotDone(child)) {
 				allDone = false;
@@ -64,11 +66,24 @@ class STLinearStatusEvaluator implements StatusEvaluator {
 			if (isInProgess(child)) {
 				inProgress = true;
 			}
+			if (isDone(child) && isDoneLater(child, latestDoneDate)) {
+				latestDoneDate = child.getDateDone();
+			}
 		}
 		
-		if (allDone)     return LearningPathStatus.done;
-		if (inProgress)  return LearningPathStatus.inProgress;
-		                 return currentNode.getStatus();
+		LearningPathStatus status = currentNode.getStatus();
+		if (allDone) {
+			status = LearningPathStatus.done;
+		} else if (inProgress) {
+			status =  LearningPathStatus.inProgress;
+		}
+		return StatusEvaluator.result(status, latestDoneDate);
+	}
+
+	private boolean isDoneLater(LearningPathTreeNode child, Date latestDoneDate) {
+		if (latestDoneDate == null) return true;
+		
+		return child.getDateDone() != null && child.getDateDone().after(latestDoneDate);
 	}
 
 	private boolean isNotOptional(LearningPathTreeNode node) {
@@ -80,8 +95,12 @@ class STLinearStatusEvaluator implements StatusEvaluator {
 				|| LearningPathStatus.done.equals(node.getStatus());
 	}
 
+	private boolean isDone(LearningPathTreeNode node) {
+		return LearningPathStatus.done.equals(node.getStatus());
+	}
+
 	private boolean isNotDone(LearningPathTreeNode node) {
-		return !LearningPathStatus.done.equals(node.getStatus());
+		return !isDone(node);
 	}
 
 }
diff --git a/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java b/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java
index 940f43cdbb5..478f50458f5 100644
--- a/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java
+++ b/src/main/java/org/olat/course/run/scoring/AssessmentEvaluation.java
@@ -44,7 +44,7 @@ public class AssessmentEvaluation extends ScoreEvaluation {
 	private final Date lastModified;
 	private final Date lastUserModified;
 	private final Date lastCoachModified;
-	
+	private final Date assessmentDone;
 
 	public AssessmentEvaluation(Float score, Boolean passed) {
 		this(score, passed, null, null);
@@ -55,17 +55,17 @@ public class AssessmentEvaluation extends ScoreEvaluation {
 	}
 	
 	public AssessmentEvaluation(Float score, Boolean passed, Boolean fullyAssessed, Long assessmentID) {
-		this(score, passed, null, null, null, fullyAssessed, null, null, assessmentID, null, null, -1, null, null, null);
+		this(score, passed, null, null, null, fullyAssessed, null, null, assessmentID, null, null, -1, null, null, null, null);
 	}
 	
 	public AssessmentEvaluation(Date lastModified, Date lastUserModified, Date lastCoachModified) {
-		this(null, null, null, null, null, null, null, null, null, null, null, -1, lastModified, lastUserModified, lastCoachModified);
+		this(null, null, null, null, null, null, null, null, null, null, null, -1, lastModified, lastUserModified, lastCoachModified, null);
 	}
 	
 	public AssessmentEvaluation(Float score, Boolean passed, Integer attempts, AssessmentEntryStatus assessmentStatus, Boolean userVisibility,
 			Boolean fullyAssessed, Double currentRunCompletion, AssessmentRunStatus runStatus, Long assessmentID,
 			String comment, String coachComment, int numOfAssessmentDocs,
-			Date lastModified, Date lastUserModified, Date lastCoachModified) {
+			Date lastModified, Date lastUserModified, Date lastCoachModified, Date assessmentDone) {
 		super(score, passed, assessmentStatus, userVisibility, fullyAssessed, currentRunCompletion, runStatus, assessmentID);
 		this.attempts = attempts;
 		this.comment = comment;
@@ -74,6 +74,7 @@ public class AssessmentEvaluation extends ScoreEvaluation {
 		this.lastModified = lastModified;
 		this.lastUserModified = lastUserModified;
 		this.lastCoachModified = lastCoachModified;
+		this.assessmentDone = assessmentDone;
 	}
 	
 	/**
@@ -86,7 +87,7 @@ public class AssessmentEvaluation extends ScoreEvaluation {
 		this(eval.getScore(), eval.getPassed(), eval.getAttempts(), assessmentStatus, eval.getUserVisible(),
 				eval.getFullyAssessed(), eval.getCurrentRunCompletion(), eval.getCurrentRunStatus(),  eval.getAssessmentID(),
 				eval.getComment(), eval.getCoachComment(), -1,
-				eval.getLastModified(), eval.getLastUserModified(), eval.getLastCoachModified());
+				eval.getLastModified(), eval.getLastUserModified(), eval.getLastCoachModified(), eval.getAssessmentDone());
 	}
 
 	public Integer getAttempts() {
@@ -117,6 +118,10 @@ public class AssessmentEvaluation extends ScoreEvaluation {
 		return lastCoachModified;
 	}
 
+	public Date getAssessmentDone() {
+		return assessmentDone;
+	}
+
 	public static final AssessmentEvaluation toAssessmentEvaluation(AssessmentEntry entry, AssessmentConfig assessmentConfig) {
 		if(entry == null) {
 			return AssessmentEvaluation.EMPTY_EVAL;
@@ -152,6 +157,6 @@ public class AssessmentEvaluation extends ScoreEvaluation {
 		return new AssessmentEvaluation(score, passed, attempts, entry.getAssessmentStatus(), entry.getUserVisibility(),
 				entry.getFullyAssessed(), currentRunCompletion, runStatus, entry.getAssessmentId(),
 				comment, entry.getCoachComment(), entry.getNumberOfAssessmentDocuments(),
-				entry.getLastModified(), entry.getLastUserModified(), entry.getLastCoachModified());
+				entry.getLastModified(), entry.getLastUserModified(), entry.getLastCoachModified(), entry.getAssessmentDone());
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java
index 13032db95d8..14d9cf5aedf 100644
--- a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java
+++ b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java
@@ -238,9 +238,11 @@ public class ScoreAccounting {
 				
 				LastModifications lastModifications = new LastModifications();
 				updateLastModified(cNode, lastModifications);
+				Date assessmentDone = aetAssessmentDone(entry, assessmentStatus);
 				se = new AssessmentEvaluation(score, passed, null, assessmentStatus, userVisibility, null,
-						currentRunCompletion, runStatus, assessmendId, null, null, numOfAssessmentDocs,
-						lastModified, lastModifications.getLastUserModified(), lastModifications.getLastCoachModified());
+						currentRunCompletion, runStatus, assessmendId, null, null, numOfAssessmentDocs, lastModified,
+						lastModifications.getLastUserModified(), lastModifications.getLastCoachModified(),
+						assessmentDone);
 				
 				if(entry == null) {
 					Identity assessedIdentity = userCourseEnvironment.getIdentityEnvironment().getIdentity();
@@ -302,6 +304,18 @@ public class ScoreAccounting {
 			return se;
 		}
 		
+		private Date aetAssessmentDone(AssessmentEntry entry, AssessmentEntryStatus assessmentStatus) {
+			Date assessmentDone = null;
+			if (AssessmentEntryStatus.done.equals(assessmentStatus)) {
+				if (entry != null && entry.getAssessmentDone() != null) {
+					assessmentDone = entry.getAssessmentDone();
+				} else {
+					assessmentDone = new Date();
+				}
+			}
+			return assessmentDone;
+		}
+
 		private RepositoryEntryLifecycle getRepositoryEntryLifecycle() {
 			CourseGroupManager cgm = userCourseEnvironment.getCourseEnvironment().getCourseGroupManager();
 			try {
diff --git a/src/test/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluatorTest.java b/src/test/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluatorTest.java
index 62533f8ded2..db525a3fc51 100644
--- a/src/test/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluatorTest.java
+++ b/src/test/java/org/olat/course/learningpath/evaluation/DefaultLinearStatusEvaluatorTest.java
@@ -23,11 +23,15 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.olat.course.learningpath.LearningPathObligation.mandatory;
 import static org.olat.course.learningpath.LearningPathObligation.optional;
 
+import java.util.Date;
+import java.util.GregorianCalendar;
+
 import org.junit.Test;
 import org.olat.course.learningpath.LearningPathObligation;
 import org.olat.course.learningpath.LearningPathStatus;
-import org.olat.course.learningpath.evaluation.DefaultLinearStatusEvaluator;
+import org.olat.course.learningpath.evaluation.StatusEvaluator.Result;
 import org.olat.course.learningpath.ui.LearningPathTreeNode;
+import org.olat.course.run.scoring.AssessmentEvaluation;
 import org.olat.modules.assessment.model.AssessmentEntryStatus;
 
 /**
@@ -52,16 +56,20 @@ public class DefaultLinearStatusEvaluatorTest {
 
 	@Test
 	public void shouldReturnDoneIfAssessmentStatusIsDone() {
-		LearningPathStatus status = sut.getStatus(null, AssessmentEntryStatus.done);
+		AssessmentEvaluation assessmentEvaluation = getAssessmentEvaluation(AssessmentEntryStatus.done, null);
+		
+		Result result = sut.getStatus(null, assessmentEvaluation);
 		
-		assertThat(status).isEqualTo(LearningPathStatus.done);
+		assertThat(result.getStatus()).isEqualTo(LearningPathStatus.done);
 	}
 	
 	@Test
 	public void shouldReturnReadyIfIsRootNodeAndNotAlreadyDone() {
-		LearningPathStatus status = sut.getStatus(null, AssessmentEntryStatus.notStarted);
+		AssessmentEvaluation assessmentEvaluation = getAssessmentEvaluation(AssessmentEntryStatus.notStarted, null);;
+		
+		Result result = sut.getStatus(null, assessmentEvaluation);
 		
-		assertThat(status).isEqualTo(LearningPathStatus.ready);
+		assertThat(result.getStatus()).isEqualTo(LearningPathStatus.ready);
 	}
 
 	@Test
@@ -109,10 +117,37 @@ public class DefaultLinearStatusEvaluatorTest {
 		LearningPathTreeNode previousNode = new LearningPathTreeNode(null, 0);
 		previousNode.setStatus(previousStatus);
 		previousNode.setObligation(previousObligation);
+		AssessmentEvaluation assessmentEvaluation = getAssessmentEvaluation(currentStatus, null);
+		
+		Result result = sut.getStatus(previousNode, assessmentEvaluation);
+		
+		assertThat(result.getStatus()).isEqualTo(expected);
+	}
+	
+	@Test
+	public void shouldReturnDateDone() {
+		LearningPathTreeNode previousNode = new LearningPathTreeNode(null, 0);
+		Date dateDone = new GregorianCalendar(2019, 3, 1).getTime();
+		AssessmentEvaluation assessmentEvaluation = getAssessmentEvaluation(AssessmentEntryStatus.done, dateDone);
 		
-		LearningPathStatus status = sut.getStatus(previousNode, currentStatus);
+		Result result = sut.getStatus(previousNode, assessmentEvaluation);
 		
-		assertThat(status).isEqualTo(expected);
+		assertThat(result.getDoneDate()).isEqualTo(dateDone);
+	}
+	
+	@Test
+	public void shouldReturnDateDoneOnlyIfStatusIsDone() {
+		LearningPathTreeNode previousNode = new LearningPathTreeNode(null, 0);
+		Date dateDone = new GregorianCalendar(2019, 3, 1).getTime();
+		AssessmentEvaluation assessmentEvaluation = getAssessmentEvaluation(AssessmentEntryStatus.inProgress, dateDone);
+		
+		Result result = sut.getStatus(previousNode, assessmentEvaluation);
+		
+		assertThat(result.getDoneDate()).isEqualTo(null);
+	}
+
+	private AssessmentEvaluation getAssessmentEvaluation(AssessmentEntryStatus assessmentStatus, Date assessmentDone) {
+		return new AssessmentEvaluation(null, null, null, assessmentStatus, null, null, null, null, null, null, null, 0, null, null, null, assessmentDone);
 	}
 
 }
diff --git a/src/test/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluatorTest.java b/src/test/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluatorTest.java
index 8a292041bf6..82ca62d1a67 100644
--- a/src/test/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluatorTest.java
+++ b/src/test/java/org/olat/course/nodes/st/learningpath/STLinearStatusEvaluatorTest.java
@@ -22,11 +22,14 @@ package org.olat.course.nodes.st.learningpath;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
 import java.util.List;
 
 import org.junit.Test;
 import org.olat.course.learningpath.LearningPathObligation;
 import org.olat.course.learningpath.LearningPathStatus;
+import org.olat.course.learningpath.evaluation.StatusEvaluator.Result;
 import org.olat.course.learningpath.ui.LearningPathTreeNode;
 
 /**
@@ -68,9 +71,9 @@ public class STLinearStatusEvaluatorTest {
 		child3.setObligation(LearningPathObligation.optional);
 		children.add(child3);
 		
-		LearningPathStatus status = sut.getStatus(currentNode, children);
+		Result result = sut.getStatus(currentNode, children);
 		
-		assertThat(status).isEqualTo(LearningPathStatus.done);
+		assertThat(result.getStatus()).isEqualTo(LearningPathStatus.done);
 	}
 	
 	@Test
@@ -88,9 +91,9 @@ public class STLinearStatusEvaluatorTest {
 		child3.setStatus(LearningPathStatus.notAccessible);
 		children.add(child3);
 		
-		LearningPathStatus status = sut.getStatus(currentNode, children);
+		Result result = sut.getStatus(currentNode, children);
 		
-		assertThat(status).isEqualTo(LearningPathStatus.inProgress);
+		assertThat(result.getStatus()).isEqualTo(LearningPathStatus.inProgress);
 	}
 	
 	@Test
@@ -108,9 +111,9 @@ public class STLinearStatusEvaluatorTest {
 		child3.setStatus(LearningPathStatus.notAccessible);
 		children.add(child3);
 		
-		LearningPathStatus status = sut.getStatus(currentNode, children);
+		Result result = sut.getStatus(currentNode, children);
 		
-		assertThat(status).isEqualTo(LearningPathStatus.inProgress);
+		assertThat(result.getStatus()).isEqualTo(LearningPathStatus.inProgress);
 	}
 
 	@Test
@@ -126,8 +129,38 @@ public class STLinearStatusEvaluatorTest {
 		child2.setStatus(LearningPathStatus.notAccessible);
 		children.add(child2);
 		
-		LearningPathStatus status = sut.getStatus(currentNode, children);
+		Result result = sut.getStatus(currentNode, children);
 		
-		assertThat(status).isEqualTo(statusBeforeChildrenEvaluation);
+		assertThat(result.getStatus()).isEqualTo(statusBeforeChildrenEvaluation);
 	}
+	
+	@Test
+	public void shouldReturnLatestDoneDate() {
+		LearningPathTreeNode currentNode = new LearningPathTreeNode(null, 0);
+		currentNode.setStatus(LearningPathStatus.ready);
+		List<LearningPathTreeNode> children = new ArrayList<>();
+		LearningPathTreeNode child1 = new LearningPathTreeNode(null, 1);
+		child1.setStatus(LearningPathStatus.done);
+		child1.setDateDone(new GregorianCalendar(2013,1,1).getTime());
+		children.add(child1);
+		LearningPathTreeNode child2 = new LearningPathTreeNode(null, 1);
+		child2.setStatus(LearningPathStatus.done);
+		Date latest = new GregorianCalendar(2013,2,1).getTime();
+		child2.setDateDone(latest);
+		children.add(child2);
+		// test status / date mismatch
+		LearningPathTreeNode child3 = new LearningPathTreeNode(null, 1);
+		child3.setStatus(LearningPathStatus.notAccessible);
+		child3.setDateDone(new GregorianCalendar(2013,9,1).getTime());
+		children.add(child3);
+		// no date
+		LearningPathTreeNode child4 = new LearningPathTreeNode(null, 1);
+		child4.setStatus(LearningPathStatus.done);
+		children.add(child4);
+		
+		Result result = sut.getStatus(currentNode, children);
+		
+		assertThat(result.getDoneDate()).isEqualTo(latest);
+	}
+
 }
-- 
GitLab