From 49405fdd758a0f957093fa779a097b07ab91b7f9 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Fri, 4 Dec 2015 17:59:49 +0100
Subject: [PATCH] OO-1593: assessment entry for structure course element

---
 .../course/assessment/AssessmentHelper.java   |  19 +-
 .../course/assessment/AssessmentManager.java  |   4 +
 .../IdentityAssessmentOverviewController.java |   2 +-
 .../manager/CourseAssessmentManagerImpl.java  |  47 +-
 .../manager/EfficiencyStatementManager.java   | 142 +++---
 .../ui/tool/AssessedIdentityController.java   |  36 ++
 .../assessment/ui/tool/AssessmentForm.java    | 413 ++++++++++++++++++
 .../AssessmentIdentitiesCourseController.java | 399 -----------------
 .../AssessmentIdentitiesCourseTableModel.java | 115 -----
 ...essmentIdentitiesCourseTreeController.java |  12 +-
 .../AssessmentIdentityCourseController.java   |  23 +-
 ...ssessmentIdentityCourseNodeController.java |  16 +-
 .../ui/tool/AssessmentToolController.java     |   5 +-
 .../ui/tool/CourseToolContainer.java          |  48 ++
 ... => IdentityListCourseNodeController.java} |  63 ++-
 ... => IdentityListCourseNodeTableModel.java} |  15 +-
 .../nodes/CalculatedAssessableCourseNode.java |   4 +
 .../org/olat/course/nodes/STCourseNode.java   |   6 +
 .../run/preview/PreviewAssessmentManager.java |  10 +
 .../course/run/scoring/ScoreAccounting.java   | 114 ++++-
 .../modules/assessment/AssessmentService.java |  13 +
 .../manager/AssessmentEntryDAO.java           |  18 +
 .../manager/AssessmentServiceImpl.java        |   6 +
 .../org/olat/upgrade/OLATUpgrade_11_0_0.java  | 134 ++++--
 24 files changed, 1004 insertions(+), 660 deletions(-)
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java
 delete mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java
 delete mode 100644 src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java
 create mode 100644 src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java
 rename src/main/java/org/olat/course/assessment/ui/tool/{AssessmentIdentitiesCourseNodeController.java => IdentityListCourseNodeController.java} (86%)
 rename src/main/java/org/olat/course/assessment/ui/tool/{AssessmentIdentitiesCourseNodeTableModel.java => IdentityListCourseNodeTableModel.java} (88%)

diff --git a/src/main/java/org/olat/course/assessment/AssessmentHelper.java b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
index 6b2c4b37b8d..ddf4c264e5b 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentHelper.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
@@ -401,8 +401,25 @@ public class AssessmentHelper {
 		return data;
 	}
 	
+	/**
+	 * Calculated 
+	 * 
+	 * 
+	 * @param evaluatedScoreAccounting
+	 * @param userCourseEnv
+	 * @param discardEmptyNodes
+	 * @param discardComments
+	 * @return
+	 */
+	public static List<AssessmentNodeData> getAssessmentNodeDataList(ScoreAccounting evaluatedScoreAccounting, UserCourseEnvironment userCourseEnv, boolean discardEmptyNodes, boolean discardComments) {
+		List<AssessmentNodeData> data = new ArrayList<AssessmentNodeData>(50);
+		getAssessmentNodeDataList(0, userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode(),
+				evaluatedScoreAccounting, userCourseEnv, discardEmptyNodes, discardComments, data);
+		return data;
+	}
+	
 	
-	private static int getAssessmentNodeDataList(int recursionLevel, CourseNode courseNode, ScoreAccounting scoreAccounting,
+	public static int getAssessmentNodeDataList(int recursionLevel, CourseNode courseNode, ScoreAccounting scoreAccounting,
 			UserCourseEnvironment userCourseEnv, boolean discardEmptyNodes, boolean discardComments, List<AssessmentNodeData> data) {
 		// 1) Get list of children data using recursion of this method
 		AssessmentNodeData assessmentNodeData = new AssessmentNodeData(recursionLevel, courseNode);
diff --git a/src/main/java/org/olat/course/assessment/AssessmentManager.java b/src/main/java/org/olat/course/assessment/AssessmentManager.java
index 02cbfc59231..1961a3a3a6f 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentManager.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentManager.java
@@ -195,6 +195,10 @@ public interface AssessmentManager {
 	
 
 	public AssessmentEntry getAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, String referenceSoftKey);
+	
+	public AssessmentEntry createAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, ScoreEvaluation scoreEvaluation);
+	
+	public AssessmentEntry updateAssessmentEntry(AssessmentEntry assessmentEntry);
 
 	public List<AssessmentEntry> getAssessmentEntries(CourseNode courseNode);
 	
diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java
index d87f9cba9b0..44485f7778c 100644
--- a/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java
+++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentOverviewController.java
@@ -237,7 +237,7 @@ public class IdentityAssessmentOverviewController extends BasicController {
 		}
 	}
 
-	private void doIdentityAssessmentOverview(UserRequest ureq) {
+	public void doIdentityAssessmentOverview(UserRequest ureq) {
 		List<AssessmentNodeData> nodesTableList;
 		if (loadNodesFromCourse) {
 			// get list of course node and user data and populate table data model 	
diff --git a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java
index 2ffa0830b59..23c07572df9 100644
--- a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java
+++ b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java
@@ -20,10 +20,12 @@
 package org.olat.course.assessment.manager;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.logging.activity.StringResourceableType;
@@ -35,14 +37,18 @@ import org.olat.core.util.resource.OresHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.course.assessment.AssessmentChangedEvent;
+import org.olat.course.assessment.AssessmentHelper;
 import org.olat.course.assessment.AssessmentLoggingAction;
 import org.olat.course.assessment.AssessmentManager;
+import org.olat.course.assessment.model.AssessmentNodeData;
 import org.olat.course.auditing.UserNodeAuditManager;
 import org.olat.course.certificate.CertificateTemplate;
 import org.olat.course.certificate.CertificatesManager;
 import org.olat.course.certificate.model.CertificateInfos;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
+import org.olat.course.run.environment.CourseEnvironment;
+import org.olat.course.run.scoring.ScoreAccounting;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.group.BusinessGroup;
@@ -98,6 +104,27 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 		return assessmentService.loadAssessmentEntries(assessedGoup, courseEntry, courseNode.getIdent());
 	}
 
+	@Override
+	public AssessmentEntry createAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, ScoreEvaluation scoreEvaluation) {
+		RepositoryEntry referenceEntry = null;
+		if(courseNode.needsReferenceToARepositoryEntry()) {
+			referenceEntry = courseNode.getReferencedRepositoryEntry();
+		}
+		Float score = null;
+		Boolean passed = null;
+		if(scoreEvaluation != null) {
+			score = scoreEvaluation.getScore();
+			passed = scoreEvaluation.getPassed();
+		}
+		return assessmentService
+				.createAssessmentEntry(assessedIdentity, courseEntry, courseNode.getIdent(), referenceEntry, score, passed);
+	}
+
+	@Override
+	public AssessmentEntry updateAssessmentEntry(AssessmentEntry assessmentEntry) {
+		return assessmentService.updateAssessmentEntry(assessmentEntry);
+	}
+
 	@Override
 	public void saveNodeAttempts(CourseNode courseNode, Identity identity, Identity assessedIdentity, Integer attempts) {
 		ICourse course = CourseFactory.loadCourse(courseEntry.getOlatResource());
@@ -209,7 +236,8 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 	public void saveScoreEvaluation(AssessableCourseNode courseNode, Identity identity, Identity assessedIdentity,
 			ScoreEvaluation scoreEvaluation, UserCourseEnvironment userCourseEnv,
 			boolean incrementUserAttempts) {
-		ICourse course = CourseFactory.loadCourse(courseEntry.getOlatResource());
+		final ICourse course = CourseFactory.loadCourse(courseEntry);
+		final CourseEnvironment courseEnv = userCourseEnv.getCourseEnvironment();
 		
 		Float score = scoreEvaluation.getScore();
 		Boolean passed = scoreEvaluation.getPassed();
@@ -233,9 +261,14 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 			assessmentEntry.setAttempts(attempts);
 		}
 		assessmentEntry = assessmentService.updateAssessmentEntry(assessmentEntry);
+		DBFactory.getInstance().commit();//commit before sending events
+		//reevalute the tree
+		ScoreAccounting scoreAccounting = userCourseEnv.getScoreAccounting();
+		scoreAccounting.evaluateAll(true);
+		DBFactory.getInstance().commit();//commit before sending events
 		
 		// node log
-		UserNodeAuditManager am = course.getCourseEnvironment().getAuditManager();
+		UserNodeAuditManager am = courseEnv.getAuditManager();
 		am.appendToUserNodeLog(courseNode, identity, assessedIdentity,  "score set to: " + String.valueOf(scoreEvaluation.getScore()));
 		if(scoreEvaluation.getPassed()!=null) {
 			am.appendToUserNodeLog(courseNode, identity, assessedIdentity, "passed set to: " + scoreEvaluation.getPassed().toString());
@@ -277,9 +310,13 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 					LoggingResourceable.wrapNonOlatResource(StringResourceableType.qtiAttempts, "", String.valueOf(attempts)));	
 		}
 		
-		userCourseEnv.getScoreAccounting().evaluateAll();//.scoreInfoChanged(courseNode, scoreEvaluation);
-		// Update users efficiency statement
-		efficiencyStatementManager.updateUserEfficiencyStatement(userCourseEnv);
+		// write only when enabled for this course
+		if (courseEnv.getCourseConfig().isEfficencyStatementEnabled()) {
+			List<AssessmentNodeData> data = new ArrayList<AssessmentNodeData>(50);
+			AssessmentHelper.getAssessmentNodeDataList(0, courseEnv.getRunStructure().getRootNode(),
+					scoreAccounting, userCourseEnv, true, true, data);
+			efficiencyStatementManager.updateUserEfficiencyStatement(assessedIdentity, courseEnv, data, courseEntry);
+		}
 
 		if(passed != null && passed.booleanValue() && course.getCourseConfig().isAutomaticCertificationEnabled()) {
 			if(certificatesManager.isCertificationAllowed(assessedIdentity, courseEntry)) {
diff --git a/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java b/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java
index 625c7c48a0f..91dadd4c40b 100644
--- a/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java
+++ b/src/main/java/org/olat/course/assessment/manager/EfficiencyStatementManager.java
@@ -37,7 +37,8 @@ import org.olat.basesecurity.IdentityRef;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
-import org.olat.core.manager.BasicManager;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.coordinate.SyncerExecutor;
 import org.olat.core.util.resource.OresHelper;
@@ -56,6 +57,7 @@ import org.olat.course.assessment.model.UserEfficiencyStatementImpl;
 import org.olat.course.assessment.model.UserEfficiencyStatementLight;
 import org.olat.course.assessment.model.UserEfficiencyStatementStandalone;
 import org.olat.course.config.CourseConfig;
+import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
@@ -78,7 +80,9 @@ import com.thoughtworks.xstream.XStream;
  * @author gnaegi
  */
 @Service
-public class EfficiencyStatementManager extends BasicManager implements UserDataDeletable {
+public class EfficiencyStatementManager implements UserDataDeletable {
+	
+	private static final OLog log = Tracing.createLoggerFor(EfficiencyStatementManager.class);
 
 	public static final String KEY_ASSESSMENT_NODES = "assessmentNodes";
 	public static final String KEY_COURSE_TITLE = "courseTitle";
@@ -97,10 +101,8 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * @param userCourseEnv
 	 */
 	public void updateUserEfficiencyStatement(UserCourseEnvironment userCourseEnv) {
-		Long courseResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); 
-		OLATResourceable courseOres = OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId);
 		RepositoryEntry re = userCourseEnv.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
-		updateUserEfficiencyStatement(userCourseEnv, re, courseOres);
+		updateUserEfficiencyStatement(userCourseEnv, re);
 	}
 
 	public UserEfficiencyStatement createUserEfficiencyStatement(Date creationDate, Float score, Boolean passed, Identity identity, OLATResource resource) {
@@ -158,63 +160,69 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * @param repoEntryKey
 	 * @param courseOres
 	 */
-	private void updateUserEfficiencyStatement(final UserCourseEnvironment userCourseEnv, final RepositoryEntry repoEntry, OLATResourceable courseOres) {
+	private void updateUserEfficiencyStatement(final UserCourseEnvironment userCourseEnv, final RepositoryEntry repoEntry) {
     //	o_clusterOK: by ld
 		CourseConfig cc = userCourseEnv.getCourseEnvironment().getCourseConfig();
 		// write only when enabled for this course
 		if (cc.isEfficencyStatementEnabled()) {
 			Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity();				
 			List<AssessmentNodeData> assessmentNodeList = AssessmentHelper.getAssessmentNodeDataList(userCourseEnv, true, true);
-			List<Map<String,Object>> assessmentNodes = AssessmentHelper.assessmentNodeDataListToMap(assessmentNodeList);
-					
-			EfficiencyStatement efficiencyStatement = new EfficiencyStatement();
-			efficiencyStatement.setAssessmentNodes(assessmentNodes);
-			efficiencyStatement.setCourseTitle(userCourseEnv.getCourseEnvironment().getCourseTitle());
-			efficiencyStatement.setCourseRepoEntryKey(repoEntry.getKey());
-			String userInfos = userManager.getUserDisplayName(identity);
-			efficiencyStatement.setDisplayableUserInfo(userInfos);
-			efficiencyStatement.setLastUpdated(System.currentTimeMillis());
-							
-			UserEfficiencyStatementImpl efficiencyProperty = getUserEfficiencyStatementFull(repoEntry, identity);
-			if (assessmentNodes != null) {				
-				if (efficiencyProperty == null) {
-					// create new
-					efficiencyProperty = new UserEfficiencyStatementImpl();
-					efficiencyProperty.setIdentity(identity);
+			updateUserEfficiencyStatement(identity, userCourseEnv.getCourseEnvironment(), assessmentNodeList, repoEntry);
+		}
+	}
+	
+	public void updateUserEfficiencyStatement(Identity assessedIdentity, final CourseEnvironment courseEnv, List<AssessmentNodeData> assessmentNodeList, final RepositoryEntry repoEntry) {
+		List<Map<String,Object>> assessmentNodes = AssessmentHelper.assessmentNodeDataListToMap(assessmentNodeList);
+				
+		EfficiencyStatement efficiencyStatement = new EfficiencyStatement();
+		efficiencyStatement.setAssessmentNodes(assessmentNodes);
+		efficiencyStatement.setCourseTitle(courseEnv.getCourseTitle());
+		efficiencyStatement.setCourseRepoEntryKey(repoEntry.getKey());
+		String userInfos = userManager.getUserDisplayName(assessedIdentity);
+		efficiencyStatement.setDisplayableUserInfo(userInfos);
+		efficiencyStatement.setLastUpdated(System.currentTimeMillis());
+		
+		boolean debug = log.isDebug();
+		UserEfficiencyStatementImpl efficiencyProperty = getUserEfficiencyStatementFull(repoEntry, assessedIdentity);
+		if (assessmentNodes != null) {				
+			if (efficiencyProperty == null) {
+				// create new
+				efficiencyProperty = new UserEfficiencyStatementImpl();
+				efficiencyProperty.setIdentity(assessedIdentity);
+				efficiencyProperty.setCourseRepoKey(repoEntry.getKey());
+				if(repoEntry != null) {
+					efficiencyProperty.setResource(repoEntry.getOlatResource());
 					efficiencyProperty.setCourseRepoKey(repoEntry.getKey());
-					if(repoEntry != null) {
-						efficiencyProperty.setResource(repoEntry.getOlatResource());
-						efficiencyProperty.setCourseRepoKey(repoEntry.getKey());
-					}
-					
-					fillEfficiencyStatement(efficiencyStatement, efficiencyProperty);
-					dbInstance.getCurrentEntityManager().persist(efficiencyProperty);
-					if (isLogDebugEnabled()) {
-						logDebug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntry.getKey());
-					}				
-				} else {
-					// update existing
-					if (isLogDebugEnabled()) {
-						logDebug("updating efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntry.getKey());
-					}	
-					fillEfficiencyStatement(efficiencyStatement, efficiencyProperty);
-					dbInstance.getCurrentEntityManager().merge(efficiencyProperty);
 				}
+				
+				fillEfficiencyStatement(efficiencyStatement, efficiencyProperty);
+				dbInstance.getCurrentEntityManager().persist(efficiencyProperty);
+				if (debug) {
+					log.debug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + assessedIdentity.getName() + " repoEntry::" + repoEntry.getKey());
+				}				
 			} else {
-				if (efficiencyProperty != null) {
-					// remove existing since now empty efficiency statements
-					if (isLogDebugEnabled()) {
-						logDebug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::"	+ identity.getName() + " repoEntry::" + repoEntry.getKey() + " since empty");
-					}
-					dbInstance.getCurrentEntityManager().remove(efficiencyProperty);
+				// update existing
+				if (debug) {
+					log.debug("updating efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + assessedIdentity.getName() + " repoEntry::" + repoEntry.getKey());
+				}	
+				fillEfficiencyStatement(efficiencyStatement, efficiencyProperty);
+				dbInstance.getCurrentEntityManager().merge(efficiencyProperty);
+			}
+		} else {
+			if (efficiencyProperty != null) {
+				// remove existing since now empty efficiency statements
+				if (debug) {
+					log.debug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::"	+ assessedIdentity.getName() + " repoEntry::" + repoEntry.getKey() + " since empty");
 				}
-				// else nothing to create and nothing to delete
-			}					
-			
-			// send modified event to everybody
-			AssessmentChangedEvent ace = new AssessmentChangedEvent(AssessmentChangedEvent.TYPE_EFFICIENCY_STATEMENT_CHANGED, identity);
-			CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(ace, courseOres);
-		}
+				dbInstance.getCurrentEntityManager().remove(efficiencyProperty);
+			}
+			// else nothing to create and nothing to delete
+		}					
+		
+		// send modified event to everybody
+		AssessmentChangedEvent ace = new AssessmentChangedEvent(AssessmentChangedEvent.TYPE_EFFICIENCY_STATEMENT_CHANGED, assessedIdentity);
+		OLATResourceable courseOres = OresHelper.createOLATResourceableInstance(CourseModule.class, courseEnv.getCourseResourceableId());
+		CoordinatorManager.getInstance().getCoordinator().getEventBus().fireEventToListenersOf(ace, courseOres);
 	}
 	
 	public void fillEfficiencyStatement(EfficiencyStatement efficiencyStatement, UserEfficiencyStatementImpl efficiencyProperty) {
@@ -270,7 +278,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 				Boolean passed = (Boolean)nodeData.get(AssessmentHelper.KEY_PASSED);
 				Integer attempts = (Integer)nodeData.get(AssessmentHelper.KEY_ATTEMPTS);
 				String attemptsStr = attempts==null ? null : String.valueOf(attempts.intValue());				
-				logInfo("title: " + title + " score: " + score + " passed: " + passed + " attempts: " + attemptsStr);				
+				log.info("title: " + title + " score: " + score + " passed: " + passed + " attempts: " + attemptsStr);				
 			}
 		}		
 	}
@@ -324,7 +332,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			}
 			return statement.get(0);
 		} catch (Exception e) {
-			logError("Cannot retrieve efficiency statement: " + courseRepoEntry.getKey() + " from " + identity, e);
+			log.error("Cannot retrieve efficiency statement: " + courseRepoEntry.getKey() + " from " + identity, e);
 			return null;
 		}
 	}
@@ -346,7 +354,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			}
 			return statement.get(0);
 		} catch (Exception e) {
-			logError("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e);
+			log.error("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e);
 			return null;
 		}
 	}
@@ -383,7 +391,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			}
 			return statement.get(0);
 		} catch (Exception e) {
-			logError("Cannot retrieve efficiency statement: " + courseRepo.getKey() + " from " + identity, e);
+			log.error("Cannot retrieve efficiency statement: " + courseRepo.getKey() + " from " + identity, e);
 			return null;
 		}
 	}
@@ -404,7 +412,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			}
 			return statement.get(0);
 		} catch (Exception e) {
-			logError("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e);
+			log.error("Cannot retrieve efficiency statement: " + resourceKey + " from " + identity, e);
 			return null;
 		}
 	}
@@ -424,7 +432,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			}
 			return (EfficiencyStatement)xstream.fromXML(statement.get(0).getStatementXml());
 		} catch (Exception e) {
-			logError("Cannot retrieve efficiency statement: " + key, e);
+			log.error("Cannot retrieve efficiency statement: " + key, e);
 			return null;
 		}
 	}
@@ -549,7 +557,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			}
 
 		} catch (Exception e) {
-			logError("findEfficiencyStatements: " + identity, e);
+			log.error("findEfficiencyStatements: " + identity, e);
 		}
 		return efficiencyStatements;
 	}
@@ -566,7 +574,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 					.setParameter("identityKey", identity.getKey())
 					.getResultList();
 		} catch (Exception e) {
-			logError("findEfficiencyStatements: " + identity, e);
+			log.error("findEfficiencyStatements: " + identity, e);
 			return Collections.emptyList();
 		}
 	}
@@ -601,7 +609,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 					.setParameter("repoKey", courseRepoEntryKey)
 					.getResultList();
 		} catch (Exception e) {
-			logError("findIdentitiesWithEfficiencyStatements: " + courseRepoEntryKey, e);
+			log.error("findIdentitiesWithEfficiencyStatements: " + courseRepoEntryKey, e);
 			return Collections.emptyList();
 		}
 	}
@@ -625,7 +633,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 				dbInstance.deleteObject(statement);
 			}
 		} catch (Exception e) {
-			logError("deleteEfficiencyStatementsFromCourse: " + courseRepoEntryKey, e);
+			log.error("deleteEfficiencyStatementsFromCourse: " + courseRepoEntryKey, e);
 		}
 	}
 
@@ -661,7 +669,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	public void updateEfficiencyStatements(final RepositoryEntry courseEntry, List<Identity> identities) {
 		if (identities.size() > 0) {
 			final ICourse course = CourseFactory.loadCourse(courseEntry);
-			logAudit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU");
+			log.audit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU");
 
 			// preload cache to speed up things
 			long start = System.currentTimeMillis();
@@ -674,7 +682,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 					public void execute() {					
 						// create temporary user course env
 						UserCourseEnvironment uce = AssessmentHelper.createAndInitUserCourseEnvironment(identity, course);
-						updateUserEfficiencyStatement(uce, courseEntry, course);
+						updateUserEfficiencyStatement(uce, courseEntry);
 					}
 				});
 				if (Thread.interrupted()) {
@@ -682,9 +690,9 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 				}
 			}
 			
-			if (isLogDebugEnabled()) {
+			if (log.isDebug()) {
 				long end = System.currentTimeMillis();
-				logDebug("Updated efficiency statements for course::" + course.getResourceableId() 
+				log.debug("Updated efficiency statements for course::" + course.getResourceableId() 
 					 + "ms; Updating statements: " + (end-start) + "ms; Users: " + identities.size());
 			}
 		}
@@ -704,7 +712,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 		for (Iterator<EfficiencyStatement> iter = efficiencyStatements.iterator(); iter.hasNext();) {
 			deleteEfficiencyStatement(identity, iter.next());
 		}
-		logDebug("All efficiency statements deleted for identity=" + identity);
+		log.debug("All efficiency statements deleted for identity=" + identity);
 	}
 	
 }
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java
new file mode 100644
index 00000000000..406c037be9d
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessedIdentityController.java
@@ -0,0 +1,36 @@
+/**
+ * <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.assessment.ui.tool;
+
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.generic.dtabs.Activateable2;
+import org.olat.core.id.Identity;
+
+/**
+ * 
+ * Initial date: 04.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface AssessedIdentityController extends Controller, Activateable2 {
+
+	public Identity getAssessedIdentity();
+	
+}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java
new file mode 100644
index 00000000000..835881965ad
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentForm.java
@@ -0,0 +1,413 @@
+/**
+* OLAT - Online Learning and Training<br>
+* http://www.olat.org
+* <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
+* <p>
+* http://www.apache.org/licenses/LICENSE-2.0
+* <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>
+* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
+* University of Zurich, Switzerland.
+* <hr>
+* <a href="http://www.openolat.org">
+* OpenOLAT - Online Learning and Training</a><br>
+* This file has been modified by the OpenOLAT community. Changes are licensed
+* under the Apache 2.0 license as the original file.
+*/
+
+package org.olat.course.assessment.ui.tool;
+
+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.FormLink;
+import org.olat.core.gui.components.form.flexible.elements.IntegerElement;
+import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
+import org.olat.core.gui.components.form.flexible.elements.StaticTextElement;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.link.Link;
+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.core.util.Util;
+import org.olat.course.assessment.AssessmentHelper;
+import org.olat.course.assessment.AssessmentMainController;
+import org.olat.course.nodes.AssessableCourseNode;
+import org.olat.course.run.scoring.ScoreEvaluation;
+import org.olat.course.run.userview.UserCourseEnvironment;
+
+
+/**
+ * Initial Date:  Jun 24, 2004
+ *
+ * @author gnaegi
+ */
+public class AssessmentForm extends FormBasicController {
+	
+	private TextElement score;
+	private IntegerElement attempts;
+	private StaticTextElement cutVal;
+	private SingleSelection passed;
+	private TextElement userComment, coachComment;
+	private FormLink saveAndCloseLink;
+	
+	private final boolean saveAndClose;
+
+	private final boolean hasScore, hasPassed, hasComment, hasAttempts;
+	private Float min, max, cut;
+
+	private final UserCourseEnvironment assessedUserCourseEnv;
+	private final AssessableCourseNode assessableCourseNode;
+	
+	private Integer attemptsValue;
+	private Float   scoreValue;
+	private String  userCommentValue;
+	private String  coachCommentValue;
+	
+	/**
+	 * Constructor for an assessment detail form. The form will be configured according
+	 * to the assessable course node parameters
+	 * @param name The form name
+	 * @param assessableCourseNode The course node
+	 * @param assessedIdentityWrapper The wrapped identity
+	 * @param trans The package translator
+	 */
+	public AssessmentForm(UserRequest ureq, WindowControl wControl, AssessableCourseNode assessableCourseNode,
+			UserCourseEnvironment assessedUserCourseEnv, boolean saveAndClose) {
+		super(ureq, wControl);
+		setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator()));
+		
+		this.saveAndClose = saveAndClose;
+		
+		hasAttempts = assessableCourseNode.hasAttemptsConfigured();
+		hasScore = assessableCourseNode.hasScoreConfigured();
+		hasPassed = assessableCourseNode.hasPassedConfigured();
+		hasComment = assessableCourseNode.hasCommentConfigured();
+		
+		this.assessedUserCourseEnv = assessedUserCourseEnv;
+		this.assessableCourseNode = assessableCourseNode;
+
+		initForm(ureq);
+	}
+
+	public boolean isAttemptsDirty() {
+		return hasAttempts && attemptsValue.intValue() != attempts.getIntValue();
+	}
+	
+	public int getAttempts() {
+		return attempts.getIntValue();
+	}
+
+
+	public Float getCut() {
+		return cut;
+	}
+
+	public StaticTextElement getCutVal() {
+		return cutVal;
+	}
+
+	public boolean isHasAttempts() {
+		return hasAttempts;
+	}
+
+	public boolean isHasComment() {
+		return hasComment;
+	}
+
+	public boolean isHasPassed() {
+		return hasPassed;
+	}
+
+	public boolean isHasScore() {
+		return hasScore;
+	}
+
+	public SingleSelection getPassed() {
+		return passed;
+	}
+
+	public boolean isScoreDirty() {
+		if (!hasScore) return false;
+		if (scoreValue == null) return !score.getValue().equals("");
+		return parseFloat(score) != scoreValue.floatValue();
+	}
+	
+	public Float getScore() {
+		return parseFloat(score);
+	}
+
+	public boolean isUserCommentDirty () {
+		return hasComment && !userComment.getValue().equals(userCommentValue);
+	}
+	public TextElement getUserComment() {
+		return userComment;
+	}
+	
+	public boolean isCoachCommentDirty () {
+		return !coachComment.getValue().equals(coachCommentValue);
+	}
+	
+	public TextElement getCoachComment() {
+		return coachComment;
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(saveAndCloseLink == source) {
+			if(validateFormLogic(ureq)) {
+				doUpdateAssessmentData();
+				fireEvent(ureq, Event.DONE_EVENT);
+			}
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		doUpdateAssessmentData();
+		if(saveAndClose) {
+			fireEvent(ureq, Event.CHANGED_EVENT);
+		} else {
+			fireEvent(ureq, Event.DONE_EVENT);
+		}
+	}
+
+	@Override
+	protected void formCancelled (UserRequest ureq) {
+		fireEvent(ureq, Event.CANCELLED_EVENT);
+	}
+
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		if (hasScore) {
+			try {
+				if(parseFloat(score) == null) {
+					score.setErrorKey("form.error.wrongFloat", null);
+					return false;
+				}
+			} catch (NumberFormatException e) {
+				score.setErrorKey("form.error.wrongFloat", null);
+				return false;
+			}
+			
+			Float fscore = parseFloat(score);
+			if ((min != null && fscore < min.floatValue()) 
+					|| fscore < AssessmentHelper.MIN_SCORE_SUPPORTED) {
+				score.setErrorKey("form.error.scoreOutOfRange", null);
+				return false;
+			}
+			if ((max != null && fscore > max.floatValue())
+					|| fscore > AssessmentHelper.MAX_SCORE_SUPPORTED) {
+				score.setErrorKey("form.error.scoreOutOfRange", null);
+				return false;
+			}
+		}	
+		return true;
+	}
+	
+	private Float parseFloat(TextElement textEl) throws NumberFormatException {
+		String scoreStr = textEl.getValue();
+		if(!StringHelper.containsNonWhitespace(scoreStr)) {
+			return null;
+		}
+		int index = scoreStr.indexOf(',');
+		if(index >= 0) {
+			scoreStr = scoreStr.replace(',', '.');
+			return Float.parseFloat(scoreStr);
+		}
+		return Float.parseFloat(scoreStr);
+	}
+	
+	protected void doUpdateAssessmentData() {
+		ScoreEvaluation scoreEval = null;
+		Float newScore = null;
+		Boolean newPassed = null;
+		
+		if (isHasAttempts() && isAttemptsDirty()) {
+			assessableCourseNode.updateUserAttempts(new Integer(getAttempts()), assessedUserCourseEnv, getIdentity());
+		}
+
+		if (isHasScore() && isScoreDirty()) {
+			newScore = getScore();
+			// Update properties in db later... see
+			// courseNode.updateUserSocreAndPassed...
+		}
+		
+		if (isHasPassed()) {
+			if (getCut() != null && getScore() != null) {
+				newPassed = getScore() >= getCut().floatValue()
+				          ? Boolean.TRUE : Boolean.FALSE;
+			} else {
+        //"passed" info was changed or not 
+				String selectedKeyString = getPassed().getSelectedKey();
+				if("true".equalsIgnoreCase(selectedKeyString) || "false".equalsIgnoreCase(selectedKeyString)) {
+					newPassed = Boolean.valueOf(selectedKeyString);
+				}
+				else {
+          // "undefined" was choosen
+					newPassed = null;
+				}				
+			}
+		}
+		// Update score,passed properties in db
+		scoreEval = new ScoreEvaluation(newScore, newPassed);
+		assessableCourseNode.updateUserScoreEvaluation(scoreEval, assessedUserCourseEnv, getIdentity(), false);
+
+		if (isHasComment() && isUserCommentDirty()) {
+			String newComment = getUserComment().getValue();
+			// Update properties in db
+			assessableCourseNode.updateUserUserComment(newComment, assessedUserCourseEnv, getIdentity());
+		}
+
+		if (isCoachCommentDirty()) {
+			String newCoachComment = getCoachComment().getValue();
+			// Update properties in db
+			assessableCourseNode.updateUserCoachComment(newCoachComment, assessedUserCourseEnv);
+		}
+	}
+	
+	public void reloadData() {
+		ScoreEvaluation scoreEval = assessedUserCourseEnv.getScoreAccounting().evalCourseNode(assessableCourseNode);
+		if (scoreEval == null) scoreEval = new ScoreEvaluation(null, null);
+		
+		if (hasAttempts) {
+			attemptsValue = assessableCourseNode.getUserAttempts(assessedUserCourseEnv);
+			attempts.setIntValue(attemptsValue == null ? 0 : attemptsValue.intValue());
+		}
+		
+		if (hasScore) {
+			scoreValue = scoreEval.getScore();
+			if (scoreValue != null) {
+				score.setValue(AssessmentHelper.getRoundedScore(scoreValue));
+			} 
+		}
+		
+		if (hasPassed) {
+			Boolean passedValue = scoreEval.getPassed();
+			passed.select(passedValue == null ? "undefined" :passedValue.toString(), true);
+			passed.setEnabled(cut == null);
+		}
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		setFormTitle("form.title", null);
+		formLayout.setElementCssClass("o_sel_assessment_form");
+		
+		ScoreEvaluation scoreEval = assessedUserCourseEnv.getScoreAccounting().evalCourseNode(assessableCourseNode);
+		if (scoreEval == null) {
+			scoreEval = ScoreEvaluation.EMPTY_EVALUATION;
+		}
+
+		if (hasAttempts) {
+			attemptsValue = assessableCourseNode.getUserAttempts(assessedUserCourseEnv);
+			attempts = uifactory.addIntegerElement("attempts", "form.attempts", (attemptsValue == null ? 0 : attemptsValue.intValue()), formLayout);
+			attempts.setDisplaySize(3);
+			attempts.setMinValueCheck(0, null);
+		}
+
+		if (hasScore) {
+			min = assessableCourseNode.getMinScoreConfiguration();
+			max = assessableCourseNode.getMaxScoreConfiguration();
+			if (hasPassed) {
+				cut = assessableCourseNode.getCutValueConfiguration();
+			}
+			
+			String minStr = AssessmentHelper.getRoundedScore(min);
+			String maxStr = AssessmentHelper.getRoundedScore(max);
+			uifactory.addStaticTextElement("minval", "form.min", ((min == null) ? translate("form.valueUndefined") : minStr), formLayout);
+			uifactory.addStaticTextElement("maxval", "form.max", ((max == null) ? translate("form.valueUndefined") : maxStr), formLayout);
+
+			// Use init variables from wrapper, already loaded from db
+			scoreValue = scoreEval.getScore();
+			score = uifactory.addTextElement("score","form.score" , 10, "", formLayout);
+			score.setDisplaySize(4);
+			score.setElementCssClass("o_sel_assessment_form_score");
+			score.setExampleKey("form.score.rounded", null);
+			if (scoreValue != null) {
+				score.setValue(AssessmentHelper.getRoundedScore(scoreValue));
+			} 
+			// assessment overview with max score
+			score.setRegexMatchCheck("(\\d+)||(\\d+\\.\\d{1,3})||(\\d+\\,\\d{1,3})", "form.error.wrongFloat");
+		}
+
+		if (hasPassed) {
+			if (cut != null) {
+				// Display cut value if defined
+				cutVal = uifactory.addStaticTextElement(
+						"cutval","form.cut" ,
+						((cut == null) ? translate("form.valueUndefined") : AssessmentHelper.getRoundedScore(cut)),
+						formLayout
+				);
+			}
+			
+			String[] trueFalseKeys = new String[] { "undefined", "true", "false" };
+			String[] passedNotPassedValues = new String[] {
+					translate("form.passed.undefined"),
+					translate("form.passed.true"),
+					translate("form.passed.false")
+			};
+
+			//passed = new StaticSingleSelectionElement("form.passed", trueFalseKeys, passedNotPassedValues);
+			passed = uifactory.addRadiosVertical("passed", "form.passed", formLayout, trueFalseKeys, passedNotPassedValues);	
+			passed.setElementCssClass("o_sel_assessment_form_passed");
+			
+			Boolean passedValue = scoreEval.getPassed();
+			passed.select(passedValue == null ? "undefined" :passedValue.toString(), true);
+			// When cut value is defined, no manual passed possible
+			passed.setEnabled(cut == null);
+		}
+
+		if (hasComment) {
+			// Use init variables from db, not available from wrapper
+			userCommentValue = assessableCourseNode.getUserUserComment(assessedUserCourseEnv);
+			if (userCommentValue == null) {
+				userCommentValue = "";
+			}
+			userComment = uifactory.addTextAreaElement("usercomment", "form.usercomment", 2500, 5, 40, true, userCommentValue, formLayout);
+		}
+
+		coachCommentValue = assessableCourseNode.getUserCoachComment(assessedUserCourseEnv);
+		if (coachCommentValue == null) {
+			coachCommentValue = "";
+		}
+		coachComment = uifactory.addTextAreaElement("coachcomment", "form.coachcomment", 2500, 5, 40, true, coachCommentValue, formLayout);
+	
+		//why does the TextElement not use its default error key??? 
+		//userComment could be null for course elements of type Assessment (MSCourseNode)
+		if(userComment!=null) {
+		  userComment.setNotLongerThanCheck(2500, "input.toolong");
+		}
+		if(coachComment!=null) {
+		  coachComment.setNotLongerThanCheck(2500, "input.toolong");
+		}
+		
+		FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator());
+		formLayout.add(buttonGroupLayout);
+		
+		uifactory.addFormSubmitButton("save", buttonGroupLayout);
+		if(saveAndClose) {
+			saveAndCloseLink = uifactory.addFormLink("save.close", buttonGroupLayout, Link.BUTTON);
+			saveAndCloseLink.setElementCssClass("o_sel_assessment_form_save_and_close");
+		}
+		uifactory.addFormCancelButton("cancel", buttonGroupLayout, ureq, getWindowControl());
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java
deleted file mode 100644
index 713dba09fd7..00000000000
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseController.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/**
- * <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.assessment.ui.tool;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.BaseSecurityModule;
-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.FormItemContainer;
-import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
-import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter;
-import org.olat.core.gui.components.form.flexible.elements.FormLink;
-import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
-import org.olat.core.gui.components.form.flexible.impl.FormEvent;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableSearchEvent;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent;
-import org.olat.core.gui.components.link.Link;
-import org.olat.core.gui.components.link.LinkFactory;
-import org.olat.core.gui.components.stack.TooledStackedPanel;
-import org.olat.core.gui.components.stack.TooledStackedPanel.Align;
-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.gui.control.generic.wizard.Step;
-import org.olat.core.gui.control.generic.wizard.StepRunnerCallback;
-import org.olat.core.gui.control.generic.wizard.StepsMainRunController;
-import org.olat.core.gui.control.generic.wizard.StepsRunContext;
-import org.olat.core.id.Identity;
-import org.olat.core.util.StringHelper;
-import org.olat.core.util.Util;
-import org.olat.course.CourseFactory;
-import org.olat.course.ICourse;
-import org.olat.course.assessment.AssessedIdentityWrapper;
-import org.olat.course.assessment.AssessmentMainController;
-import org.olat.course.assessment.AssessmentToolManager;
-import org.olat.course.assessment.bulk.PassedCellRenderer;
-import org.olat.course.assessment.model.SearchAssessedIdentityParams;
-import org.olat.course.assessment.ui.tool.AssessmentIdentitiesCourseTableModel.IdentityCourseCols;
-import org.olat.course.certificate.CertificateLight;
-import org.olat.course.certificate.CertificateTemplate;
-import org.olat.course.certificate.CertificatesManager;
-import org.olat.course.certificate.model.CertificateInfos;
-import org.olat.course.certificate.ui.Certificates_1_SelectionStep;
-import org.olat.course.certificate.ui.DownloadCertificateCellRenderer;
-import org.olat.course.config.CourseConfig;
-import org.olat.group.BusinessGroup;
-import org.olat.modules.assessment.model.AssessmentEntryStatus;
-import org.olat.modules.assessment.ui.AssessmentToolSecurityCallback;
-import org.olat.modules.coach.CoachingService;
-import org.olat.modules.coach.model.EfficiencyStatementEntry;
-import org.olat.repository.RepositoryEntry;
-import org.olat.user.UserManager;
-import org.olat.user.propertyhandlers.UserPropertyHandler;
-import org.springframework.beans.factory.annotation.Autowired;
-
-/**
- * 
- * Initial date: 06.10.2015<br>
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class AssessmentIdentitiesCourseController extends FormBasicController {
-	
-	private final RepositoryEntry courseEntry;
-	private final boolean isAdministrativeUser;
-	private List<UserPropertyHandler> userPropertyHandlers;
-	private final AssessmentToolSecurityCallback assessmentCallback;
-
-	private Link nextLink, previousLink;
-	private FormLink generateCertificateButton;
-	private FlexiTableElement tableEl;
-	private TooledStackedPanel stackPanel;
-	private AssessmentIdentitiesCourseTableModel usersTableModel;
-	
-	private StepsMainRunController wizardCtrl;
-	private AssessmentIdentityCourseController currentIdentityCtrl;
-
-	@Autowired
-	private UserManager userManager;
-	@Autowired
-	private BaseSecurity securityManager;
-	@Autowired
-	private CoachingService coachingService;
-	@Autowired
-	private BaseSecurityModule securityModule;
-	@Autowired
-	private CertificatesManager certificatesManager;
-	@Autowired
-	private AssessmentToolManager assessmentToolManager;
-	
-	public AssessmentIdentitiesCourseController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
-			RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) {
-		super(ureq, wControl, "identity_course");
-		setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator()));
-		setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
-		
-		this.stackPanel = stackPanel;
-		this.courseEntry = courseEntry;
-		this.assessmentCallback = assessmentCallback;
-		
-		isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles());
-		userPropertyHandlers = userManager.getUserPropertyHandlersFor(AssessmentToolConstants.usageIdentifyer, isAdministrativeUser);
-
-		initForm(ureq);
-		loadModel(null, null, null);
-	}
-
-	@Override
-	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
-		//add the table
-		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
-		if(isAdministrativeUser) {
-			columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.username, "select"));
-		}
-		
-		int colIndex = AssessmentToolConstants.USER_PROPS_OFFSET;
-		for (int i = 0; i < userPropertyHandlers.size(); i++) {
-			UserPropertyHandler userPropertyHandler	= userPropertyHandlers.get(i);
-			boolean visible = UserManager.getInstance().isMandatoryUserProperty(AssessmentToolConstants.usageIdentifyer , userPropertyHandler);
-			columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colIndex, "select",
-					true, "userProp-" + colIndex));
-			colIndex++;
-		}
-	
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.passed, new PassedCellRenderer()));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.score));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.lastScoreUpdate));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseCols.certificate, new DownloadCertificateCellRenderer()));
-		
-		usersTableModel = new AssessmentIdentitiesCourseTableModel(columnsModel); 
-		tableEl = uifactory.addTableElement(getWindowControl(), "table", usersTableModel, 20, false, getTranslator(), formLayout);
-		tableEl.setExportEnabled(true);
-		tableEl.setSearchEnabled(new AssessedIdentityListProvider(getIdentity(), courseEntry, null, null, assessmentCallback), ureq.getUserSession());
-		
-		List<FlexiTableFilter> filters = new ArrayList<>();
-		filters.add(new FlexiTableFilter(translate("filter.passed"), "passed"));
-		filters.add(new FlexiTableFilter(translate("filter.failed"), "failed"));
-		filters.add(new FlexiTableFilter(translate("filter.inProgress"), "inProgress"));
-		filters.add(new FlexiTableFilter(translate("filter.inReview"), "inReview"));
-		filters.add(new FlexiTableFilter(translate("filter.done"), "done"));
-		tableEl.setFilters("", filters);
-
-		ICourse course = CourseFactory.loadCourse(courseEntry);
-		if(assessmentCallback.canAssessBusinessGoupMembers()) {
-			List<BusinessGroup> coachedGroups = null;
-			if(assessmentCallback.isAdmin()) {
-				coachedGroups = course.getCourseEnvironment().getCourseGroupManager().getAllBusinessGroups();
-			} else {
-				coachedGroups = assessmentCallback.getCoachedGroups(); 
-			}
-
-			if(coachedGroups.size() > 0) {
-				List<FlexiTableFilter> groupFilters = new ArrayList<>();
-				for(BusinessGroup coachedGroup:coachedGroups) {
-					groupFilters.add(new FlexiTableFilter(coachedGroup.getName(), coachedGroup.getKey().toString(), "o_icon o_icon_group"));
-				}
-				
-				tableEl.setExtendedFilterButton(translate("filter.groups"), groupFilters);
-			}
-		}
-		
-		CourseConfig courseConfig = course.getCourseConfig();
-		if(courseConfig.isManualCertificationEnabled()) {
-			generateCertificateButton = uifactory.addFormLink("generate.certificate", formLayout, Link.BUTTON);
-		}
-	}
-	
-	public List<EfficiencyStatementEntry> loadModel(String searchStr, List<FlexiTableFilter> filters, List<FlexiTableFilter> extendedFilters) {
-
-		SearchAssessedIdentityParams params = new SearchAssessedIdentityParams(courseEntry, null, null, assessmentCallback);
-		
-		List<AssessmentEntryStatus> assessmentStatus = null;
-		if(filters != null && filters.size() > 0) {
-			assessmentStatus = new ArrayList<>(filters.size());
-			for(FlexiTableFilter filter:filters) {
-				if("passed".equals(filter.getFilter())) {
-					params.setPassed(true);
-				} else if("failed".equals(filter.getFilter())) {
-					params.setFailed(true);
-				} else if(AssessmentEntryStatus.isValueOf(filter.getFilter())){
-					assessmentStatus.add(AssessmentEntryStatus.valueOf(filter.getFilter()));
-				}
-			}
-		}
-		params.setAssessmentStatus(assessmentStatus);
-		
-		List<Long> businessGroupKeys = null;
-		if(extendedFilters != null && extendedFilters.size() > 0) {
-			businessGroupKeys = new ArrayList<>(extendedFilters.size());
-			for(FlexiTableFilter extendedFilter:extendedFilters) {
-				if(StringHelper.isLong(extendedFilter.getFilter())) {
-					businessGroupKeys.add(Long.parseLong(extendedFilter.getFilter()));
-				}
-			}
-		}
-		params.setBusinessGroupKeys(businessGroupKeys);
-		params.setSearchString(searchStr);
-		
-		List<Identity> assessedIdentities = assessmentToolManager.getAssessedIdentities(getIdentity(), params);
-		List<EfficiencyStatementEntry> entries = coachingService.getCourse(getIdentity(), courseEntry);
-		Map<Long,EfficiencyStatementEntry> identityKeyToStatementMap = entries.stream()
-				.collect(Collectors.toMap(EfficiencyStatementEntry::getStudentKey, Function.identity()));
-
-		List<AssessedIdentityCourseRow> rows = new ArrayList<>(assessedIdentities.size());
-		for(Identity assessedIdentity: assessedIdentities) {
-			EfficiencyStatementEntry statement = identityKeyToStatementMap.get(assessedIdentity.getKey());
-			rows.add(new AssessedIdentityCourseRow(assessedIdentity, statement, userPropertyHandlers, getLocale()));
-		}
-
-		List<CertificateLight> certificates = certificatesManager.getLastCertificates(courseEntry.getOlatResource());
-		ConcurrentMap<Long, CertificateLight> certificateMap = new ConcurrentHashMap<>();
-		for(CertificateLight certificate:certificates) {
-			certificateMap.put(certificate.getIdentityKey(), certificate);
-		}
-		
-		usersTableModel.setCertificateMap(certificateMap);
-		usersTableModel.setObjects(rows);
-		tableEl.reloadData();
-		return entries;
-	}
-	
-	@Override
-	protected void doDispose() {
-		//
-	}
-
-	@Override
-	protected void formOK(UserRequest ureq) {
-		//
-	}
-	
-	@Override
-	public void event(UserRequest ureq, Component source, Event event) {
-		if(previousLink == source) {
-			doPrevious(ureq);
-		} else if(nextLink == source) {
-			doNext(ureq);
-		}
-		super.event(ureq, source, event);
-	}
-	
-	
-
-	@Override
-	protected void event(UserRequest ureq, Controller source, Event event) {
-		if(wizardCtrl == source) {
-			if(event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT || event == Event.CHANGED_EVENT) {
-				getWindowControl().pop();
-				removeAsListenerAndDispose(wizardCtrl);
-				wizardCtrl = null;
-			}
-		}
-		super.event(ureq, source, event);
-	}
-
-	@Override
-	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
-		if(tableEl == source) {
-			if(event instanceof SelectionEvent) {
-				SelectionEvent se = (SelectionEvent)event;
-				String cmd = se.getCommand();
-				AssessedIdentityCourseRow row = usersTableModel.getObject(se.getIndex());
-				if("select".equals(cmd)) {
-					doSelect(ureq, row);
-				}
-			} else if(event instanceof FlexiTableSearchEvent) {
-				FlexiTableSearchEvent ftse = (FlexiTableSearchEvent)event;
-				loadModel(ftse.getSearch(), ftse.getFilters(), ftse.getExtendedFilters());
-			}
-		} else if(generateCertificateButton == source) {
-			doGenerateCertificates(ureq);
-		}
-		
-		super.formInnerEvent(ureq, source, event);
-	}
-	
-	private void doGenerateCertificates(UserRequest ureq) {
-		ICourse course = CourseFactory.loadCourse(courseEntry);
-
-		List<AssessedIdentityWrapper> datas = new ArrayList<>();
-		Certificates_1_SelectionStep start = new Certificates_1_SelectionStep(ureq, courseEntry, datas, course.hasAssessableNodes());
-		StepRunnerCallback finish = new StepRunnerCallback() {
-			@Override
-			public Step execute(UserRequest uureq, WindowControl wControl, StepsRunContext runContext) {
-				@SuppressWarnings("unchecked")
-				List<CertificateInfos> assessedIdentitiesInfos = (List<CertificateInfos>)runContext.get("infos");
-				if(assessedIdentitiesInfos != null && assessedIdentitiesInfos.size() > 0) {
-					doGenerateCertificates(assessedIdentitiesInfos);
-					return StepsMainRunController.DONE_MODIFIED;
-				}
-				return StepsMainRunController.DONE_UNCHANGED;
-			}
-		};
-		
-		wizardCtrl = new StepsMainRunController(ureq, getWindowControl(), start, finish, null,
-				translate("certificates.wizard.title"), "o_sel_certificates_wizard");
-		listenTo(wizardCtrl);
-		getWindowControl().pushAsModalDialog(wizardCtrl.getInitialComponent());
-	}
-	
-	private void doGenerateCertificates(List<CertificateInfos> assessedIdentitiesInfos) {
-		ICourse course = CourseFactory.loadCourse(courseEntry);
-		Long templateKey = course.getCourseConfig().getCertificateTemplate();
-		CertificateTemplate template = null;
-		if(templateKey != null) {
-			template = certificatesManager.getTemplateById(templateKey);
-		}
-		
-		certificatesManager.generateCertificates(assessedIdentitiesInfos, courseEntry, template, true);
-	}
-	
-	private void doNext(UserRequest ureq) {
-		stackPanel.popController(currentIdentityCtrl);
-		
-		Identity currentIdentity = currentIdentityCtrl.getAssessedIdentity();
-		int index = getIndexOf(currentIdentity);
-		if(index >= 0) {
-			int nextIndex = index + 1;//next
-			if(nextIndex >= 0 && nextIndex < usersTableModel.getRowCount()) {
-				doSelect(ureq, usersTableModel.getObject(nextIndex));
-			} else if(usersTableModel.getRowCount() > 0) {
-				doSelect(ureq, usersTableModel.getObject(0));
-			}
-		}
-	}
-	
-	private void doPrevious(UserRequest ureq) {
-		stackPanel.popController(currentIdentityCtrl);
-		
-		Identity currentIdentity = currentIdentityCtrl.getAssessedIdentity();
-		int index = getIndexOf(currentIdentity);
-		if(index >= 0) {
-			int previousIndex = index - 1;//next
-			if(previousIndex >= 0 && previousIndex < usersTableModel.getRowCount()) {
-				doSelect(ureq, usersTableModel.getObject(previousIndex));
-			} else if(usersTableModel.getRowCount() > 0) {
-				doSelect(ureq, usersTableModel.getObject(usersTableModel.getRowCount() - 1));
-			}
-		}
-	}
-	
-	private int getIndexOf(Identity identity) {
-		int index = -1;
-		for(int i=usersTableModel.getRowCount(); i-->0; ) {
-			Long rowIdentityKey = usersTableModel.getObject(i).getIdentityKey();
-			if(rowIdentityKey.equals(identity.getKey())) {
-				return i;
-			}
-		}
-		return index;
-	}
-
-	private void doSelect(UserRequest ureq, AssessedIdentityCourseRow row) {
-		removeAsListenerAndDispose(currentIdentityCtrl);
-		
-		Identity assessedIdentity = securityManager.loadIdentityByKey(row.getIdentityKey());
-		String fullName = userManager.getUserDisplayName(assessedIdentity);
-		
-		currentIdentityCtrl = new AssessmentIdentityCourseController(ureq, getWindowControl(), stackPanel, courseEntry, assessedIdentity);
-		listenTo(currentIdentityCtrl);
-		stackPanel.pushController(fullName, currentIdentityCtrl);
-		
-		previousLink = LinkFactory.createToolLink("previouselement","", this, "o_icon_previous_toolbar");
-		previousLink.setTitle(translate("command.previous"));
-		stackPanel.addTool(previousLink, Align.rightEdge, false, "o_tool_previous");
-		nextLink = LinkFactory.createToolLink("nextelement","", this, "o_icon_next_toolbar");
-		nextLink.setTitle(translate("command.next"));
-		stackPanel.addTool(nextLink, Align.rightEdge, false, "o_tool_next");
-	}
-}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java
deleted file mode 100644
index aab936bd5ee..00000000000
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTableModel.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/**
- * <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.assessment.ui.tool;
-
-import java.util.List;
-import java.util.concurrent.ConcurrentMap;
-
-import org.olat.core.commons.persistence.SortKey;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate;
-import org.olat.course.certificate.CertificateLight;
-
-/**
- * 
- * Initial date: 07.10.2015<br>
- * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
- *
- */
-public class AssessmentIdentitiesCourseTableModel extends DefaultFlexiTableDataModel<AssessedIdentityCourseRow>
-	implements SortableFlexiTableDataModel<AssessedIdentityCourseRow> {
-	
-	private ConcurrentMap<Long, CertificateLight> certificateMap;
-	
-	public AssessmentIdentitiesCourseTableModel(FlexiTableColumnModel columnModel) {
-		super(columnModel);
-	}
-	
-	public void setCertificateMap(ConcurrentMap<Long, CertificateLight> certificateMap) {
-		this.certificateMap = certificateMap;
-	}
-
-	@Override
-	public void sort(SortKey orderBy) {
-		SortableFlexiTableModelDelegate<AssessedIdentityCourseRow> sorter
-				= new SortableFlexiTableModelDelegate<>(orderBy, this, null);
-		List<AssessedIdentityCourseRow> views = sorter.sort();
-		super.setObjects(views);
-	}
-	
-	@Override
-	public Object getValueAt(int row, int col) {
-		AssessedIdentityCourseRow entry = getObject(row);
-		return getValueAt(entry, col);
-	}
-
-	@Override
-	public Object getValueAt(AssessedIdentityCourseRow row, int col) {
-		if(col >= 0 && col < IdentityCourseCols.values().length) {
-	
-			switch(IdentityCourseCols.values()[col]) {
-				case username: return row.getIdentityName();
-				case certificate: return certificateMap.get(row.getIdentityKey());
-				case score: return row.getScore();
-				case passed: return row.getPassed();
-				case lastScoreUpdate: return row.getLastModified();
-			}
-		}
-		int propPos = col - AssessmentToolConstants.USER_PROPS_OFFSET;
-		return row.getIdentityProp(propPos);
-	}
-
-	@Override
-	public DefaultFlexiTableDataModel<AssessedIdentityCourseRow> createCopyWithEmptyList() {
-		return new AssessmentIdentitiesCourseTableModel(getTableColumnModel());
-	}
-	
-	public enum IdentityCourseCols implements FlexiSortableColumnDef {
-		username("table.header.name"),
-		passed("table.header.passed"),
-		certificate("table.header.certificate"),
-		score("table.header.score"),
-		lastScoreUpdate("table.header.lastScoreDate");
-
-		private final String i18nKey;
-		
-		private IdentityCourseCols(String i18nKey) {
-			this.i18nKey = i18nKey;
-		}
-		
-		@Override
-		public String i18nHeaderKey() {
-			return i18nKey;
-		}
-
-		@Override
-		public boolean sortable() {
-			return true;
-		}
-
-		@Override
-		public String sortKey() {
-			return name();
-		}
-	}
-}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java
index 0d56bd00e26..f60d5a0ca92 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseTreeController.java
@@ -60,16 +60,18 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im
 	private final MenuTree menuTree;
 	private final Panel mainPanel;
 	private final TooledStackedPanel stackPanel;
+	private final CourseToolContainer toolContainer;
 	private Controller currentCtrl;
 	
 	private final RepositoryEntry courseEntry;
 	private AssessmentToolSecurityCallback assessmentCallback;
 	
 	public AssessmentIdentitiesCourseTreeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
-			RepositoryEntry courseEntry, AssessmentToolSecurityCallback assessmentCallback) {
+			RepositoryEntry courseEntry, CourseToolContainer toolContainer, AssessmentToolSecurityCallback assessmentCallback) {
 		super(ureq, wControl);
 		this.courseEntry = courseEntry;
 		this.stackPanel = stackPanel;
+		this.toolContainer = toolContainer;
 		this.assessmentCallback = assessmentCallback;
 
 		ICourse course = CourseFactory.loadCourse(courseEntry);
@@ -119,18 +121,14 @@ public class AssessmentIdentitiesCourseTreeController extends BasicController im
 		
 		OLATResourceable ores = OresHelper.createOLATResourceableInstance("Node", new Long(courseNode.getIdent()));
 		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
-
-		ICourse course = CourseFactory.loadCourse(courseEntry);
-		if(course.getRunStructure().getRootNode().equals(courseNode)) {
-			currentCtrl = new AssessmentIdentitiesCourseController(ureq, bwControl, stackPanel, courseEntry, assessmentCallback);
-		} else if(courseNode instanceof AssessableCourseNode && ((AssessableCourseNode)courseNode).isAssessedBusinessGroups()) {
+		if(courseNode instanceof AssessableCourseNode && ((AssessableCourseNode)courseNode).isAssessedBusinessGroups()) {
 			if(courseNode instanceof GTACourseNode) {
 				CourseEnvironment courseEnv = CourseFactory.loadCourse(courseEntry).getCourseEnvironment();
 				currentCtrl = ((GTACourseNode)courseNode).getCoachedGroupListController(ureq, getWindowControl(), stackPanel,
 						courseEnv, assessmentCallback.isAdmin(), assessmentCallback.getCoachedGroups());
 			}
 		} else {
-			currentCtrl = new AssessmentIdentitiesCourseNodeController(ureq, bwControl, stackPanel, courseEntry, courseNode, assessmentCallback);
+			currentCtrl = new IdentityListCourseNodeController(ureq, bwControl, stackPanel, courseEntry, courseNode, toolContainer, assessmentCallback);
 		}
 		listenTo(currentCtrl);
 		mainPanel.setContent(currentCtrl.getInitialComponent());
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java
index 8acd7c50e9d..02b86c3357f 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java
@@ -19,6 +19,8 @@
  */
 package org.olat.course.assessment.ui.tool;
 
+import java.util.List;
+
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
@@ -35,6 +37,8 @@ import org.olat.core.gui.control.generic.closablewrapper.CloseableCalloutWindowC
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.Roles;
+import org.olat.core.id.context.ContextEntry;
+import org.olat.core.id.context.StateEntry;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.course.assessment.IdentityAssessmentOverviewController;
@@ -53,7 +57,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentIdentityCourseController extends BasicController {
+public class AssessmentIdentityCourseController extends BasicController implements AssessedIdentityController {
 	
 	private final Identity assessedIdentity;
 	private final RepositoryEntry courseEntry;
@@ -105,7 +109,8 @@ public class AssessmentIdentityCourseController extends BasicController {
 
 		putInitialPanel(identityAssessmentVC);
 	}
-	
+
+	@Override
 	public Identity getAssessedIdentity() {
 		return assessedIdentity;
 	}
@@ -115,6 +120,11 @@ public class AssessmentIdentityCourseController extends BasicController {
 		//
 	}
 
+	@Override
+	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
+		//
+	}
+
 	@Override
 	protected void event(UserRequest ureq, Controller source, Event event) {
 		if(treeOverviewCtrl == source) {
@@ -134,6 +144,15 @@ public class AssessmentIdentityCourseController extends BasicController {
 			cleanUp();
 		} else if(courseNodeChooserCalloutCtrl == source) {
 			cleanUp();
+		} else if(currentNodeCtrl == source) {
+			if(event == Event.DONE_EVENT) {
+				treeOverviewCtrl.doIdentityAssessmentOverview(ureq);
+				stackPanel.popController(currentNodeCtrl);
+			} else if(event == Event.CHANGED_EVENT) {
+				treeOverviewCtrl.doIdentityAssessmentOverview(ureq);
+			} else if(event == Event.CANCELLED_EVENT) {
+				stackPanel.popController(currentNodeCtrl);
+			}
 		}
 		super.event(ureq, source, event);
 	}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java
index 2d98fcd76bd..b5015ad9ebd 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseNodeController.java
@@ -19,6 +19,8 @@
  */
 package org.olat.course.assessment.ui.tool;
 
+import java.util.List;
+
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
@@ -31,10 +33,11 @@ import org.olat.core.gui.control.controller.BasicController;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.Roles;
+import org.olat.core.id.context.ContextEntry;
+import org.olat.core.id.context.StateEntry;
 import org.olat.core.util.Formatter;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
-import org.olat.course.assessment.AssessmentForm;
 import org.olat.course.assessment.OpenSubDetailsEvent;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
@@ -53,7 +56,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentIdentityCourseNodeController extends BasicController {
+public class AssessmentIdentityCourseNodeController extends BasicController implements AssessedIdentityController {
 	
 	private final TooledStackedPanel stackPanel;
 	private final VelocityContainer identityAssessmentVC;
@@ -96,6 +99,7 @@ public class AssessmentIdentityCourseNodeController extends BasicController {
 		Roles roles = securityManager.getRoles(assessedIdentity);
 		IdentityEnvironment identityEnv = new IdentityEnvironment(assessedIdentity, roles);
 		UserCourseEnvironment assessedUserCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment());
+		assessedUserCourseEnv.getScoreAccounting().evaluateAll();
 
 		// Add the assessment details form
 		if(courseNode instanceof AssessableCourseNode) {
@@ -116,6 +120,7 @@ public class AssessmentIdentityCourseNodeController extends BasicController {
 		putInitialPanel(identityAssessmentVC);
 	}
 	
+	@Override
 	public Identity getAssessedIdentity() {
 		return assessedIdentity;
 	}
@@ -128,6 +133,11 @@ public class AssessmentIdentityCourseNodeController extends BasicController {
 	protected void doDispose() {
 		//
 	}
+	
+	@Override
+	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
+		//
+	}
 
 	@Override
 	protected void event(UserRequest ureq, Controller source, Event event) {
@@ -145,6 +155,8 @@ public class AssessmentIdentityCourseNodeController extends BasicController {
 				listenTo(subDetailsController);
 				stackPanel.pushController(translate("sub.details"), subDetailsController);
 			}
+		} else if(assessmentForm == source) {
+			fireEvent(ureq, event);
 		}
 		super.event(ureq, source, event);
 	}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java
index 0ccf2a1a422..89bfbd04a6d 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentToolController.java
@@ -58,6 +58,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 	
 	private Link usersLink, efficiencyStatementsLink, bulkAssessmentLink;
 	private final TooledStackedPanel stackPanel;
+	private final CourseToolContainer toolContainer;
 	
 	private AssessmentCourseOverviewController overviewCtrl;
 	private AssessmentIdentitiesCourseTreeController currentCtl;
@@ -71,7 +72,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 		this.courseEntry = courseEntry;
 		this.stackPanel = stackPanel;
 		this.assessmentCallback = assessmentCallback;
-
+		toolContainer = new CourseToolContainer();
 		
 		overviewCtrl = new AssessmentCourseOverviewController(ureq, getWindowControl(), courseEntry, assessmentCallback);
 		listenTo(overviewCtrl);
@@ -152,7 +153,7 @@ public class AssessmentToolController extends MainLayoutBasicController implemen
 		WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
 		addToHistory(ureq, bwControl);
 		AssessmentIdentitiesCourseTreeController treeCtrl = new AssessmentIdentitiesCourseTreeController(ureq, bwControl, stackPanel,
-				courseEntry, assessmentCallback);
+				courseEntry, toolContainer, assessmentCallback);
 		listenTo(treeCtrl);
 		stackPanel.pushController(translate("users"), treeCtrl);
 		currentCtl = treeCtrl;
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java b/src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java
new file mode 100644
index 00000000000..4d84baf9681
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/ui/tool/CourseToolContainer.java
@@ -0,0 +1,48 @@
+/**
+ * <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.assessment.ui.tool;
+
+import java.util.concurrent.ConcurrentMap;
+
+import org.olat.course.certificate.CertificateLight;
+
+/**
+ * 
+ * 
+ * 
+ * Initial date: 04.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class CourseToolContainer {
+	
+	private ConcurrentMap<Long, CertificateLight> certificateMap;
+
+	public ConcurrentMap<Long, CertificateLight> getCertificateMap() {
+		return certificateMap;
+	}
+
+	public void setCertificateMap(ConcurrentMap<Long, CertificateLight> certificateMap) {
+		this.certificateMap = certificateMap;
+	}
+	
+	
+
+}
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
similarity index 86%
rename from src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java
rename to src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
index d7dcbd5d5e5..7fe2a93af3e 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeController.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeController.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityModule;
@@ -58,7 +60,10 @@ import org.olat.course.assessment.AssessmentMainController;
 import org.olat.course.assessment.AssessmentToolManager;
 import org.olat.course.assessment.bulk.PassedCellRenderer;
 import org.olat.course.assessment.model.SearchAssessedIdentityParams;
-import org.olat.course.assessment.ui.tool.AssessmentIdentitiesCourseNodeTableModel.IdentityCourseElementCols;
+import org.olat.course.assessment.ui.tool.IdentityListCourseNodeTableModel.IdentityCourseElementCols;
+import org.olat.course.certificate.CertificateLight;
+import org.olat.course.certificate.CertificatesManager;
+import org.olat.course.certificate.ui.DownloadCertificateCellRenderer;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.AssessmentToolOptions;
 import org.olat.course.nodes.CourseNode;
@@ -79,7 +84,7 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentIdentitiesCourseNodeController extends FormBasicController {
+public class IdentityListCourseNodeController extends FormBasicController {
 
 	private final BusinessGroup group;
 	private final CourseNode courseNode;
@@ -92,9 +97,10 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 	private Link nextLink, previousLink;
 	private FlexiTableElement tableEl;
 	private final TooledStackedPanel stackPanel;
-	private AssessmentIdentitiesCourseNodeTableModel usersTableModel;
+	private final CourseToolContainer toolContainer;
+	private IdentityListCourseNodeTableModel usersTableModel;
 	
-	private AssessmentIdentityCourseNodeController currentIdentityCtrl;
+	private AssessedIdentityController currentIdentityCtrl;
 	
 	@Autowired
 	private UserManager userManager;
@@ -105,10 +111,13 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 	@Autowired
 	private RepositoryService repositoryService;
 	@Autowired
+	private CertificatesManager certificatesManager;
+	@Autowired
 	private AssessmentToolManager assessmentToolManager;
 	
-	public AssessmentIdentitiesCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
-			RepositoryEntry courseEntry, CourseNode courseNode, AssessmentToolSecurityCallback assessmentCallback) {
+	public IdentityListCourseNodeController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel,
+			RepositoryEntry courseEntry, CourseNode courseNode, CourseToolContainer toolContainer,
+			AssessmentToolSecurityCallback assessmentCallback) {
 		super(ureq, wControl, "identity_courseelement");
 		setTranslator(Util.createPackageTranslator(AssessmentMainController.class, getLocale(), getTranslator()));
 		setTranslator(userManager.getPropertyHandlerTranslator(getTranslator()));
@@ -117,6 +126,7 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		this.courseNode = courseNode;
 		this.stackPanel = stackPanel;
 		this.courseEntry = courseEntry;
+		this.toolContainer = toolContainer;
 		this.assessmentCallback = assessmentCallback;
 		
 		if(courseNode.needsReferenceToARepositoryEntry()) {
@@ -173,8 +183,9 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.assessmentStatus, new AssessmentStatusCellRenderer()));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.initialLaunchDate, "select"));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.lastScoreUpdate, "select"));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(IdentityCourseElementCols.certificate, new DownloadCertificateCellRenderer()));
 
-		usersTableModel = new AssessmentIdentitiesCourseNodeTableModel(columnsModel, assessableNode); 
+		usersTableModel = new IdentityListCourseNodeTableModel(columnsModel, assessableNode); 
 		tableEl = uifactory.addTableElement(getWindowControl(), "table", usersTableModel, 20, false, getTranslator(), formLayout);
 		tableEl.setExportEnabled(true);
 		tableEl.setSearchEnabled(new AssessedIdentityListProvider(getIdentity(), courseEntry, referenceEntry, courseNode.getIdent(), assessmentCallback), ureq.getUserSession());
@@ -249,6 +260,16 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 				rows.add(new AssessedIdentityCourseElementRow(assessedIdentity, entry, userPropertyHandlers, getLocale()));
 			}
 		}
+		
+		if(toolContainer.getCertificateMap() == null) {
+			List<CertificateLight> certificates = certificatesManager.getLastCertificates(courseEntry.getOlatResource());
+			ConcurrentMap<Long, CertificateLight> certificateMap = new ConcurrentHashMap<>();
+			for(CertificateLight certificate:certificates) {
+				certificateMap.put(certificate.getIdentityKey(), certificate);
+			}
+			toolContainer.setCertificateMap(certificateMap);
+		}
+		usersTableModel.setCertificateMap(toolContainer.getCertificateMap());
 		usersTableModel.setObjects(rows);
 		tableEl.reloadData();
 
@@ -283,6 +304,10 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		flc.contextPut("toolCmpNames", toolCmpNames);
 	}
 	
+	private void updateModel(UserRequest ureq, Identity assessedIdentity) {
+		updateModel(ureq, null, null, null);
+	}
+	
 	private boolean accept(AssessmentEntry entry, SearchAssessedIdentityParams params) {
 		boolean ok = true;
 		
@@ -339,6 +364,21 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		super.event(ureq, source, event);
 	}
 
+	@Override
+	public void event(UserRequest ureq, Controller source, Event event) {
+		if(currentIdentityCtrl == source) {
+			if(event == Event.CHANGED_EVENT) {
+				updateModel(ureq, currentIdentityCtrl.getAssessedIdentity());
+			} else if(event == Event.DONE_EVENT) {
+				updateModel(ureq, currentIdentityCtrl.getAssessedIdentity());
+				stackPanel.popController(currentIdentityCtrl);
+			} else if(event == Event.CANCELLED_EVENT) {
+				stackPanel.popController(currentIdentityCtrl);
+			}
+		}
+		super.event(ureq, source, event);
+	}
+
 	@Override
 	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
 		if(tableEl == source) {
@@ -404,8 +444,13 @@ public class AssessmentIdentitiesCourseNodeController extends FormBasicControlle
 		Identity assessedIdentity = securityManager.loadIdentityByKey(row.getIdentityKey());
 		String fullName = userManager.getUserDisplayName(assessedIdentity);
 		
-		currentIdentityCtrl = new AssessmentIdentityCourseNodeController(ureq, getWindowControl(), stackPanel,
-				courseEntry, courseNode, assessedIdentity);
+		if(courseNode.getParent() == null) {
+			currentIdentityCtrl = new AssessmentIdentityCourseController(ureq, getWindowControl(), stackPanel,
+					courseEntry, assessedIdentity);
+		} else {
+			currentIdentityCtrl = new AssessmentIdentityCourseNodeController(ureq, getWindowControl(), stackPanel,
+					courseEntry, courseNode, assessedIdentity);
+		}
 		listenTo(currentIdentityCtrl);
 		stackPanel.pushController(fullName, currentIdentityCtrl);
 		
diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeTableModel.java b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java
similarity index 88%
rename from src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeTableModel.java
rename to src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java
index 9c9d85ff4bc..646b7f0b193 100644
--- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentitiesCourseNodeTableModel.java
+++ b/src/main/java/org/olat/course/assessment/ui/tool/IdentityListCourseNodeTableModel.java
@@ -21,6 +21,7 @@ package org.olat.course.assessment.ui.tool;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.ConcurrentMap;
 
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
@@ -30,6 +31,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable
 import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableModelDelegate;
 import org.olat.core.util.StringHelper;
+import org.olat.course.certificate.CertificateLight;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.STCourseNode;
 import org.olat.modules.assessment.model.AssessmentEntryStatus;
@@ -40,16 +42,21 @@ import org.olat.modules.assessment.model.AssessmentEntryStatus;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableDataModel<AssessedIdentityCourseElementRow>
+public class IdentityListCourseNodeTableModel extends DefaultFlexiTableDataModel<AssessedIdentityCourseElementRow>
 	implements SortableFlexiTableDataModel<AssessedIdentityCourseElementRow>, FilterableFlexiTableModel {
 
 	private final AssessableCourseNode courseNode;
 	private List<AssessedIdentityCourseElementRow> backups;
+	private ConcurrentMap<Long, CertificateLight> certificateMap;
 	
-	public AssessmentIdentitiesCourseNodeTableModel(FlexiTableColumnModel columnModel, AssessableCourseNode courseNode) {
+	public IdentityListCourseNodeTableModel(FlexiTableColumnModel columnModel, AssessableCourseNode courseNode) {
 		super(columnModel);
 		this.courseNode = courseNode;
 	}
+	
+	public void setCertificateMap(ConcurrentMap<Long, CertificateLight> certificateMap) {
+		this.certificateMap = certificateMap;
+	}
 
 	@Override
 	public void filter(String key) {
@@ -122,6 +129,7 @@ public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableD
 				case status: return "";
 				case passed: return row.getPassed();
 				case assessmentStatus: return row.getAssessmentStatus();
+				case certificate: return certificateMap.get(row.getIdentityKey());
 				case initialLaunchDate: return row.getCreationDate();
 				case lastScoreUpdate: return row.getLastModified();
 			}
@@ -132,7 +140,7 @@ public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableD
 
 	@Override
 	public DefaultFlexiTableDataModel<AssessedIdentityCourseElementRow> createCopyWithEmptyList() {
-		return new AssessmentIdentitiesCourseNodeTableModel(getTableColumnModel(), courseNode);
+		return new IdentityListCourseNodeTableModel(getTableColumnModel(), courseNode);
 	}
 	
 	public enum IdentityCourseElementCols implements FlexiSortableColumnDef {
@@ -144,6 +152,7 @@ public class AssessmentIdentitiesCourseNodeTableModel extends DefaultFlexiTableD
 		status("table.header.status"),
 		passed("table.header.passed"),
 		assessmentStatus("table.header.assessmentStatus"),
+		certificate("table.header.certificate"),
 		initialLaunchDate("table.header.initialLaunchDate"),
 		lastScoreUpdate("table.header.lastScoreDate");
 		
diff --git a/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java b/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java
index f61c916a8bd..cbc94574684 100644
--- a/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/CalculatedAssessableCourseNode.java
@@ -19,7 +19,9 @@
  */
 package org.olat.course.nodes;
 
+import org.olat.course.run.scoring.AssessmentEvaluation;
 import org.olat.course.run.scoring.ScoreCalculator;
+import org.olat.modules.assessment.AssessmentEntry;
 
 /**
  * 
@@ -30,5 +32,7 @@ import org.olat.course.run.scoring.ScoreCalculator;
 public interface CalculatedAssessableCourseNode extends AssessableCourseNode {
 	
 	public ScoreCalculator getScoreCalculator();
+	
+	public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry); 
 
 }
diff --git a/src/main/java/org/olat/course/nodes/STCourseNode.java b/src/main/java/org/olat/course/nodes/STCourseNode.java
index 7d022cf8bf1..9f2900d65a0 100644
--- a/src/main/java/org/olat/course/nodes/STCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/STCourseNode.java
@@ -77,6 +77,7 @@ import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.tree.CourseInternalLinkTreeModel;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.util.logging.activity.LoggingResourceable;
 
@@ -292,6 +293,11 @@ public class STCourseNode extends AbstractAccessableCourseNode implements Calcul
 		return new AssessmentEvaluation(score, passed);
 	}
 
+	@Override
+	public AssessmentEvaluation getUserScoreEvaluation(AssessmentEntry entry) {
+		return AssessmentEvaluation.toAssessmentEvalutation(entry, this);
+	}
+
 	/**
 	 * @see org.olat.course.nodes.CourseNode#isConfigValid()
 	 */
diff --git a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
index f9d77b19397..236c96e6212 100644
--- a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
+++ b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
@@ -82,6 +82,16 @@ final class PreviewAssessmentManager extends BasicManager implements AssessmentM
 		return Collections.emptyList();
 	}
 
+	@Override
+	public AssessmentEntry createAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, ScoreEvaluation scoreEvaluation) {
+		return null;
+	}
+
+	@Override
+	public AssessmentEntry updateAssessmentEntry(AssessmentEntry assessmentEntry) {
+		return assessmentEntry;
+	}
+
 	/**
 	 * @see org.olat.course.assessment.AssessmentManager#saveNodeAttempts(org.olat.course.nodes.CourseNode, org.olat.core.id.Identity, org.olat.core.id.Identity, java.lang.Integer)
 	 */
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 7f7c2e7360d..049c4020224 100644
--- a/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java
+++ b/src/main/java/org/olat/course/run/scoring/ScoreAccounting.java
@@ -25,6 +25,7 @@
 
 package org.olat.course.run.scoring;
 
+import java.math.BigDecimal;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -35,11 +36,14 @@ import org.olat.core.logging.Tracing;
 import org.olat.core.util.nodes.INode;
 import org.olat.core.util.tree.TreeVisitor;
 import org.olat.core.util.tree.Visitor;
+import org.olat.course.condition.interpreter.ConditionInterpreter;
 import org.olat.course.nodes.AssessableCourseNode;
+import org.olat.course.nodes.CalculatedAssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.PersistentAssessableCourseNode;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.modules.assessment.AssessmentEntry;
+import org.olat.modules.assessment.model.AssessmentEntryStatus;
 
 /**
  * Description:<BR/>
@@ -69,11 +73,15 @@ public class ScoreAccounting {
 	 * Retrieve all the score evaluations for all course nodes
 	 */
 	public void evaluateAll() {
+		evaluateAll(false);
+	}
+	
+	public boolean evaluateAll(boolean update) {
 		Identity identity = userCourseEnvironment.getIdentityEnvironment().getIdentity();
 		List<AssessmentEntry> entries = userCourseEnvironment.getCourseEnvironment()
 				.getAssessmentManager().getAssessmentEntries(identity);
 
-		AssessableTreeVisitor visitor = new AssessableTreeVisitor(userCourseEnvironment, entries);
+		AssessableTreeVisitor visitor = new AssessableTreeVisitor(entries, update);
 		// collect all assessable nodes and eval 'em
 		CourseNode root = userCourseEnvironment.getCourseEnvironment().getRunStructure().getRootNode();
 		// breadth first traversal gives an easier order of evaluation for debugging
@@ -81,21 +89,32 @@ public class ScoreAccounting {
 		// the score accoutings local cache hash map will never be used. this can slow down things like 
 		// crazy (course with 10 tests, 300 users and some crazy score and passed calculations will have
 		// 10 time performance differences) 
+
+		cachedScoreEvals.clear();
+		for(AssessmentEntry entry:entries) {
+			String nodeIdent = entry.getSubIdent();
+			CourseNode courseNode = userCourseEnvironment.getCourseEnvironment().getRunStructure().getNode(nodeIdent);
+			if(courseNode instanceof AssessableCourseNode) {
+				AssessableCourseNode acn = (AssessableCourseNode)courseNode;
+				AssessmentEvaluation se = AssessmentEvaluation.toAssessmentEvalutation(entry, acn);
+				cachedScoreEvals.put(acn, se);
+			}
+		}
+		
 		TreeVisitor tv = new TreeVisitor(visitor, root, true); // true=depth first
 		tv.visitAll();
-		cachedScoreEvals.clear();
-		cachedScoreEvals.putAll(visitor.nodeToScoreEvals);
+		return visitor.hasChanges();
 	}
 	
 	private class AssessableTreeVisitor implements Visitor {
 		
+		private final boolean update;
+		private boolean changes = false;
 		private int recursionLevel = 0;
-		private final UserCourseEnvironment userCourseEnv;
 		private final Map<String,AssessmentEntry> identToEntries = new HashMap<>();
-		private final Map<AssessableCourseNode, AssessmentEvaluation> nodeToScoreEvals = new HashMap<>();
 		
-		public AssessableTreeVisitor(UserCourseEnvironment userCourseEnv, List<AssessmentEntry> entries) {
-			this.userCourseEnv = userCourseEnv;
+		public AssessableTreeVisitor(List<AssessmentEntry> entries, boolean update) {
+			this.update = update;
 			for(AssessmentEntry entry:entries) {
 				String ident = entry.getSubIdent();
 				if(identToEntries.containsKey(ident)) {
@@ -108,6 +127,10 @@ public class ScoreAccounting {
 				}
 			}
 		}
+		
+		public boolean hasChanges() {
+			return changes;
+		}
 
 		@Override
 		public void visit(INode node) {
@@ -122,20 +145,91 @@ public class ScoreAccounting {
 			recursionLevel++;
 			AssessmentEvaluation se = null;
 			if (recursionLevel <= 15) {
-				se = nodeToScoreEvals.get(cn);
+				se = cachedScoreEvals.get(cn);
 				if (se == null) { // result of this node has not been calculated yet, do it
 					AssessmentEntry entry = identToEntries.get(cn.getIdent());
 					if(cn instanceof PersistentAssessableCourseNode) {
 						se = ((PersistentAssessableCourseNode)cn).getUserScoreEvaluation(entry);
+					} else if(cn instanceof CalculatedAssessableCourseNode) {
+						if(update) {
+							se = calculateScoreEvaluation(entry, (CalculatedAssessableCourseNode)cn);
+						} else {
+							se = ((CalculatedAssessableCourseNode)cn).getUserScoreEvaluation(entry);
+						}
 					} else {
-						se = cn.getUserScoreEvaluation(userCourseEnv);
+						se = cn.getUserScoreEvaluation(userCourseEnvironment);
 					}
-					nodeToScoreEvals.put(cn, se);
+					cachedScoreEvals.put(cn, se);
+				} else if(update && cn instanceof CalculatedAssessableCourseNode) {
+					AssessmentEntry entry = identToEntries.get(cn.getIdent());
+					se = calculateScoreEvaluation(entry, (CalculatedAssessableCourseNode)cn);
+					cachedScoreEvals.put(cn, se);
 				}
 			}
 			recursionLevel--;
 			return se;
 		}
+		
+		private AssessmentEvaluation calculateScoreEvaluation(AssessmentEntry entry, CalculatedAssessableCourseNode cNode) {
+			AssessmentEvaluation se;
+			if(cNode.hasScoreConfigured() || cNode.hasPassedConfigured()) {
+				ScoreCalculator scoreCalculator = cNode.getScoreCalculator();
+				String scoreExpressionStr = scoreCalculator.getScoreExpression();
+				String passedExpressionStr = scoreCalculator.getPassedExpression();
+
+				Float score = null;
+				Boolean passed = null;
+				AssessmentEntryStatus assessmentStatus = AssessmentEntryStatus.inProgress;
+				ConditionInterpreter ci = userCourseEnvironment.getConditionInterpreter();
+				if (scoreExpressionStr != null) {
+					score = new Float(ci.evaluateCalculation(scoreExpressionStr));
+				}
+				if (passedExpressionStr != null) {
+					boolean hasPassed = ci.evaluateCondition(passedExpressionStr);
+					if(hasPassed) {
+						passed = Boolean.TRUE;
+						assessmentStatus = AssessmentEntryStatus.done;
+					}
+					//some rules to set -> failed
+				}
+				se = new AssessmentEvaluation(score, passed, null, assessmentStatus, null, null, null, null);
+				
+				if(entry == null) {
+					Identity assessedIdentity = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+					userCourseEnvironment.getCourseEnvironment().getAssessmentManager()
+						.createAssessmentEntry(cNode, assessedIdentity, se);
+					changes = true;
+				} else if(!same(se, entry)) {
+					entry.setScore(new BigDecimal(score));
+					entry.setPassed(passed);
+					entry = userCourseEnvironment.getCourseEnvironment().getAssessmentManager().updateAssessmentEntry(entry);
+					identToEntries.put(cNode.getIdent(), entry);
+					changes = true;
+				}
+			} else {
+				se = AssessmentEvaluation.EMPTY_EVAL;
+			}
+			return se;
+		}
+		
+		private boolean same(AssessmentEvaluation se, AssessmentEntry entry) {
+			boolean same = true;
+			
+			if((se.getPassed() == null && entry.getPassed() != null)
+					|| (se.getPassed() != null && entry.getPassed() == null)
+					|| (se.getPassed() != null && !se.getPassed().equals(entry.getPassed()))) {
+				same &= false;
+			}
+			
+			if((se.getScore() == null && entry.getScore() != null)
+					|| (se.getScore() != null && entry.getScore() == null)
+					|| (se.getScore() != null && entry.getScore() != null
+							&& Math.abs(se.getScore().floatValue() - entry.getScore().floatValue()) > 0.00001)) {
+				same &= false;
+			}
+			
+			return same;
+		}
 	}
 
 	/**
diff --git a/src/main/java/org/olat/modules/assessment/AssessmentService.java b/src/main/java/org/olat/modules/assessment/AssessmentService.java
index e89be12007e..50d15294989 100644
--- a/src/main/java/org/olat/modules/assessment/AssessmentService.java
+++ b/src/main/java/org/olat/modules/assessment/AssessmentService.java
@@ -34,6 +34,19 @@ import org.olat.repository.RepositoryEntry;
  */
 public interface AssessmentService {
 	
+	/**
+	 * 
+	 * @param assessedIdentity
+	 * @param entry
+	 * @param subIdent
+	 * @param referenceEntry
+	 * @param score
+	 * @param passed
+	 * @return
+	 */
+	public AssessmentEntry createAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent,
+			RepositoryEntry referenceEntry, Float score, Boolean passed);
+	
 	/**
 	 * 
 	 * @param assessedIdentity
diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java
index c9d4ef85527..321db32cb7f 100644
--- a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java
+++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java
@@ -19,6 +19,7 @@
  */
 package org.olat.modules.assessment.manager;
 
+import java.math.BigDecimal;
 import java.util.Date;
 import java.util.List;
 
@@ -61,6 +62,23 @@ public class AssessmentEntryDAO {
 		return data;
 	}
 	
+	public AssessmentEntry createCourseNodeAssessment(Identity assessedIdentity,
+			RepositoryEntry entry, String subIdent, RepositoryEntry referenceEntry,
+			Float score, Boolean passed) {
+		
+		AssessmentEntryImpl data = new AssessmentEntryImpl();
+		data.setCreationDate(new Date());
+		data.setLastModified(data.getCreationDate());
+		data.setIdentity(assessedIdentity);
+		data.setRepositoryEntry(entry);
+		data.setSubIdent(subIdent);
+		data.setReferenceEntry(referenceEntry);
+		data.setScore(new BigDecimal(score));
+		data.setPassed(passed);
+		dbInstance.getCurrentEntityManager().persist(data);
+		return data;
+	}
+	
 	public AssessmentEntry loadAssessmentEntryById(Long id) {
 		List<AssessmentEntry> nodeAssessment = dbInstance.getCurrentEntityManager()
 				.createNamedQuery("loadAssessmentEntryById", AssessmentEntry.class)
diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java
index f27fb16770b..fa1b08af855 100644
--- a/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java
+++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java
@@ -45,6 +45,12 @@ public class AssessmentServiceImpl implements AssessmentService {
 	private DB dbInstance;
 	@Autowired
 	private AssessmentEntryDAO assessmentEntryDao;
+	
+	@Override
+	public AssessmentEntry createAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent,
+			RepositoryEntry referenceEntry, Float score, Boolean passed) {
+		return assessmentEntryDao.createCourseNodeAssessment(assessedIdentity, entry, subIdent, referenceEntry, score, passed);
+	}
 
 	@Override
 	public AssessmentEntry getOrCreateAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent,
diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java
index 8d8b0a05ea9..a4488196ba7 100644
--- a/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java
+++ b/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java
@@ -49,6 +49,7 @@ import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
+import org.olat.core.id.IdentityEnvironment;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.activity.LoggingObject;
@@ -63,6 +64,7 @@ import org.olat.core.util.tree.Visitor;
 import org.olat.course.CourseFactory;
 import org.olat.course.CourseModule;
 import org.olat.course.ICourse;
+import org.olat.course.assessment.model.UserEfficiencyStatementLight;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.BasicLTICourseNode;
 import org.olat.course.nodes.CourseNode;
@@ -81,6 +83,7 @@ import org.olat.course.nodes.ta.StatusForm;
 import org.olat.course.nodes.ta.StatusManager;
 import org.olat.course.nodes.ta.TaskController;
 import org.olat.course.run.scoring.ScoreCalculator;
+import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.course.tree.CourseEditorTreeNode;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupService;
@@ -105,6 +108,7 @@ import org.olat.portfolio.model.structel.PortfolioStructureMap;
 import org.olat.portfolio.model.structel.StructureStatusEnum;
 import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryEntryRef;
 import org.olat.repository.RepositoryManager;
 import org.olat.repository.RepositoryService;
 import org.olat.repository.model.SearchRepositoryEntryParameters;
@@ -191,6 +195,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 			do {
 				courses = repositoryManager.genericANDQueryWithRolesRestriction(params, counter, 50, true);
 				for(RepositoryEntry course:courses) {
+					convertUserEfficiencyStatemen(course);
 					allOk &= processCourseAssessmentData(course); 
 				}
 				counter += courses.size();
@@ -203,6 +208,44 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 		return allOk;
 	}
 	
+	private void convertUserEfficiencyStatemen(RepositoryEntry courseEntry) {
+		final ICourse course = CourseFactory.loadCourse(courseEntry);
+		CourseNode rootNode = course.getRunStructure().getRootNode();
+		Set<Long> identityKeys = new HashSet<>(loadIdentityKeyOfAssessmentEntries(courseEntry, rootNode.getIdent()));
+
+		int count = 0;
+		List<UserEfficiencyStatementLight> statements = getUserEfficiencyStatements(courseEntry);
+		for(UserEfficiencyStatementLight statement:statements) {
+			Identity identity = statement.getIdentity();
+			if(!identityKeys.contains(identity.getKey())) {
+				AssessmentEntry entry = createAssessmentEntry(identity, null, course, courseEntry, rootNode.getIdent());
+				if(statement.getScore() != null) {
+					entry.setScore(new BigDecimal(statement.getScore().floatValue()));
+				}
+				if(statement.getPassed() != null) {
+					entry.setPassed(statement.getPassed());
+				}
+				dbInstance.getCurrentEntityManager().persist(entry);
+				if(count++ % 25 == 0) {
+					dbInstance.commitAndCloseSession();
+				}
+			}
+		}
+		dbInstance.commitAndCloseSession();
+	}
+	
+	public List<UserEfficiencyStatementLight> getUserEfficiencyStatements(RepositoryEntryRef courseRepoEntry) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ")
+		  .append(" inner join fetch statement.identity as ident")
+		  .append(" where statement.courseRepoKey=:repoKey");
+
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), UserEfficiencyStatementLight.class)
+				.setParameter("repoKey", courseRepoEntry.getKey())
+				.getResultList();
+	}
+	
 	// select count(*) from o_property where name in ('SCORE','PASSED','ATTEMPTS','COMMENT','COACH_COMMENT','ASSESSMENT_ID','FULLY_ASSESSED');
 	private boolean processCourseAssessmentData(RepositoryEntry courseEntry) {
 		final Long courseResourceId = courseEntry.getOlatResource().getResourceableId();
@@ -211,46 +254,47 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 		//load all assessable identities
 		List<Identity> assessableIdentities = getAllAssessableIdentities(course, courseEntry);
 
-		//load already migrated data
-		List<AssessmentEntryImpl> currentNodeAssessmentList = loadAssessmentEntries(courseEntry);
 		Map<AssessmentDataKey,AssessmentEntryImpl> curentNodeAssessmentMap = new HashMap<>();
-		for(AssessmentEntryImpl currentNodeAssessment:currentNodeAssessmentList) {
-			AssessmentDataKey key = new AssessmentDataKey(currentNodeAssessment.getIdentity(), courseResourceId, currentNodeAssessment.getSubIdent());
-			curentNodeAssessmentMap.put(key, currentNodeAssessment);
+		{//load already migrated data
+			List<AssessmentEntryImpl> currentNodeAssessmentList = loadAssessmentEntries(courseEntry);
+			for(AssessmentEntryImpl currentNodeAssessment:currentNodeAssessmentList) {
+				AssessmentDataKey key = new AssessmentDataKey(currentNodeAssessment.getIdentity().getKey(), courseResourceId, currentNodeAssessment.getSubIdent());
+				curentNodeAssessmentMap.put(key, currentNodeAssessment);
+			}
 		}
 
 		Map<AssessmentDataKey,AssessmentEntryImpl> nodeAssessmentMap = new HashMap<>();
-		
-		//processed properties
-		List<Property> courseProperties = loadAssessmentProperties(courseEntry);
-		for(Property property:courseProperties) {
-			String propertyCategory = property.getCategory();
-			if(StringHelper.containsNonWhitespace(propertyCategory)) {
-				int nodeIdentIndex = propertyCategory.indexOf("::");
-				if(nodeIdentIndex > 0) {
-					String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2);
-					AssessmentDataKey key = new AssessmentDataKey(property.getIdentity(), property.getResourceTypeId(), nodeIdent);
-					if(curentNodeAssessmentMap.containsKey(key)) {
-						continue;
-					}
-					
-					AssessmentEntryImpl nodeAssessment;
-					if(nodeAssessmentMap.containsKey(key)) {
-						nodeAssessment = nodeAssessmentMap.get(key);
-						if(nodeAssessment.getCreationDate().after(property.getCreationDate())) {
-							nodeAssessment.setCreationDate(property.getCreationDate());
+		{//processed properties
+			List<Property> courseProperties = loadAssessmentProperties(courseEntry);
+			for(Property property:courseProperties) {
+				String propertyCategory = property.getCategory();
+				if(StringHelper.containsNonWhitespace(propertyCategory)) {
+					int nodeIdentIndex = propertyCategory.indexOf("::");
+					if(nodeIdentIndex > 0) {
+						String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2);
+						AssessmentDataKey key = new AssessmentDataKey(property.getIdentity(), property.getResourceTypeId(), nodeIdent);
+						if(curentNodeAssessmentMap.containsKey(key)) {
+							continue;
 						}
 						
-						if(nodeAssessment.getLastModified().before(property.getLastModified())) {
-							nodeAssessment.setLastModified(property.getLastModified());
+						AssessmentEntryImpl nodeAssessment;
+						if(nodeAssessmentMap.containsKey(key)) {
+							nodeAssessment = nodeAssessmentMap.get(key);
+							if(nodeAssessment.getCreationDate().after(property.getCreationDate())) {
+								nodeAssessment.setCreationDate(property.getCreationDate());
+							}
+							
+							if(nodeAssessment.getLastModified().before(property.getLastModified())) {
+								nodeAssessment.setLastModified(property.getLastModified());
+							}
+						} else {
+							nodeAssessment = createAssessmentEntry(property.getIdentity(), property, course, courseEntry, nodeIdent);
 						}
-					} else {
-						nodeAssessment = createAssessmentEntry(property.getIdentity(), property, course, courseEntry, nodeIdent);
+						copyAssessmentProperty(property, nodeAssessment, course);
+						nodeAssessmentMap.put(key, nodeAssessment);	
 					}
-					copyAssessmentProperty(property, nodeAssessment, course);
-					nodeAssessmentMap.put(key, nodeAssessment);	
-				}
-			}	
+				}	
+			}
 		}
 		
 		//check the transient qti ser
@@ -282,8 +326,13 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 		if(allOk) {
 			List<STCourseNode> nodes = hasAssessableSTCourseNode(course);
 			if(nodes.size() > 0) {
-				System.out.println("Has assessables ST nodes");
-				//processStructureNodes(assessableIdentities, course);
+				log.info("Has assessables ST nodes");
+				for(Identity identity:assessableIdentities) {
+					IdentityEnvironment identityEnv = new IdentityEnvironment(identity, null);
+					UserCourseEnvironmentImpl userCourseEnv = new UserCourseEnvironmentImpl(identityEnv, course.getCourseEnvironment());
+					userCourseEnv.getScoreAccounting().evaluateAll(true);
+					dbInstance.commit();
+				}
 			}
 		}
 
@@ -402,7 +451,9 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 	 */
 	private boolean verifyCourseAssessmentData(List<Identity> assessableIdentities, RepositoryEntry courseEntry) {
 		//load the cache and fill it with the same amount of datas as in assessment tool
-		ICourse course = CourseFactory.loadCourse(courseEntry);
+		final ICourse course = CourseFactory.loadCourse(courseEntry);
+		final Long courseResourceableId = course.getResourceableId();
+		
 		StaticCacheWrapper cache = new StaticCacheWrapper();
 		NewCachePersistingAssessmentManager assessmentManager = new NewCachePersistingAssessmentManager(course, cache);
 		assessmentManager.preloadCache(assessableIdentities);
@@ -440,7 +491,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 					String nodeIdent = key.substring(0, index);
 					String dataType = key.substring(index + 1);
 					
-					AssessmentDataKey assessmentDataKey = new AssessmentDataKey(identityKey, course.getResourceableId(), nodeIdent);
+					AssessmentDataKey assessmentDataKey = new AssessmentDataKey(identityKey, courseResourceableId, nodeIdent);
 					AssessmentEntryImpl nodeAssessment = nodeAssessmentMap.get(assessmentDataKey);
 					allOk &= compareProperty(dataType, data.getValue(), nodeAssessment, courseEntry, nodeIdent, identityKey);
 				}
@@ -511,7 +562,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 		}
 		
 		boolean allOk = true;
-		if(node != null) {
+		if(node instanceof AssessableCourseNode && !(node instanceof STCourseNode)) {
 			Identity assessedIdentity = entry.getIdentity();
 			
 			Integer attempts = assessmentManager.getNodeAttempts(node, assessedIdentity);
@@ -912,6 +963,15 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 				.getResultList();
 	}
 	
+	private List<Long> loadIdentityKeyOfAssessmentEntries(RepositoryEntry courseEntry, String subIdent) {
+		String sb = "select data.identity.key from assessmententry data where data.repositoryEntry.key=:courseEntryKey and data.subIdent=:subIdent";
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb, Long.class)
+				.setParameter("courseEntryKey", courseEntry.getKey())
+				.setParameter("subIdent", subIdent)
+				.getResultList();
+	}
+	
 	private List<Property> loadAssessmentProperties(RepositoryEntry course) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("from org.olat.properties.Property as p")
-- 
GitLab