From b1f31787084a0b9e6113a206c6fee18451cb65f2 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Mon, 12 Mar 2012 10:30:49 +0100
Subject: [PATCH] OO-170: refactoring of the property table, move efficiency
 statements and initial/recent launch dates to their own table

---
 .../assessment/AssessmentMainController.java  |  34 +-
 .../org/olat/basesecurity/BaseSecurity.java   |   2 +-
 .../basesecurity/BaseSecurityManager.java     |  17 +-
 .../olat/basesecurity/IdentityImpl.hbm.xml    |  18 +
 .../org/olat/basesecurity/IdentityShort.java  |  96 ++++-
 .../bc/version/DeletedFileListController.java |  17 +-
 .../bc/version/RevisionListController.java    |  17 +-
 .../_spring/databaseCorecontext.xml           |   2 +
 src/main/java/org/olat/course/ICourse.java    |  11 -
 .../archiver/ScoreAccountingHelper.java       |  26 +-
 .../course/assessment/AssessmentHelper.java   |  38 +-
 .../assessment/AssessmentMainController.java  |  31 +-
 .../course/assessment/AssessmentManager.java  |   5 -
 .../EfficiencyStatementController.java        | 178 +++++++--
 .../EfficiencyStatementManager.java           | 350 ++++++++++++++----
 .../EfficiencyStatementsListController.java   |  20 +-
 .../EfficiencyStatementsListModel.java        |  19 +-
 ...iciencyStatementsPortletRunController.java |  77 ++--
 .../IdentityAssessmentEditController.java     |  26 +-
 .../assessment/UserCourseInformations.java    |  27 ++
 .../assessment/UserEfficiencyStatement.java   |  55 +++
 .../_content/efficiencystatement.html         |  88 +++--
 .../assessment/_content/identityoverview.html |  42 ++-
 .../_i18n/LocalStrings_de.properties          |   5 +
 .../assessment/_spring/assessmentContext.xml  |  10 +-
 .../UserCourseInformationsManager.java        |  24 ++
 .../UserCourseInformationsManagerImpl.java    | 185 +++++++++
 .../model/UserCourseInfosImpl.hbm.xml         |  43 +++
 .../assessment/model/UserCourseInfosImpl.java | 103 ++++++
 .../model/UserEfficiencyStatementImpl.hbm.xml |  83 +++++
 .../model/UserEfficiencyStatementImpl.java    | 172 +++++++++
 .../model/UserEfficiencyStatementLight.java   | 153 ++++++++
 .../GetInitialCourseLaunchDateFunction.java   |  20 +-
 .../GetRecentCourseLaunchDateFunction.java    |  20 +-
 .../score/GetPassedWithCourseIdFunction.java  |   6 +-
 .../score/GetScoreWithCourseIdFunction.java   |   8 +-
 .../olat/course/run/RunMainController.java    |  28 +-
 .../org/olat/upgrade/OLATUpgrade_8_1_0.java   | 278 ++++++++++++++
 .../_spring/databaseUpgradeContext.xml        |   4 +
 .../olat/upgrade/_spring/upgradeContext.xml   |   1 +
 .../database/mysql/alter_8_0_x_to_8_1_0.sql   |  78 ++++
 .../database/mysql/setupDatabase.sql          |  79 ++++
 .../postgresql/alter_8_0_x_to_8_1_0.sql       |  74 ++++
 .../database/postgresql/setupDatabase.sql     |  76 ++++
 44 files changed, 2236 insertions(+), 410 deletions(-)
 create mode 100644 src/main/java/org/olat/course/assessment/UserCourseInformations.java
 create mode 100644 src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java
 create mode 100644 src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java
 create mode 100644 src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java
 create mode 100644 src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml
 create mode 100644 src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java
 create mode 100644 src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml
 create mode 100644 src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java
 create mode 100644 src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java
 create mode 100644 src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java
 create mode 100644 src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql
 create mode 100644 src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql

diff --git a/src/main/java/de/bps/course/assessment/AssessmentMainController.java b/src/main/java/de/bps/course/assessment/AssessmentMainController.java
index 81357d89fad..b22385e0bdd 100644
--- a/src/main/java/de/bps/course/assessment/AssessmentMainController.java
+++ b/src/main/java/de/bps/course/assessment/AssessmentMainController.java
@@ -27,7 +27,6 @@ package de.bps.course.assessment;
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -40,6 +39,7 @@ import org.olat.admin.user.UserTableDataModel;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.SecurityGroup;
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.commons.persistence.PersistenceHelper;
@@ -81,7 +81,6 @@ import org.olat.core.logging.OLATSecurityException;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.logging.activity.ActionType;
-import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.resource.OresHelper;
@@ -98,6 +97,7 @@ import org.olat.course.assessment.GroupAndContextTableModel;
 import org.olat.course.assessment.IAssessmentCallback;
 import org.olat.course.assessment.IdentityAssessmentEditController;
 import org.olat.course.assessment.IndentedNodeRenderer;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.condition.Condition;
 import org.olat.course.condition.interpreter.ConditionExpression;
 import org.olat.course.condition.interpreter.OnlyGroupConditionInterpreter;
@@ -107,19 +107,17 @@ import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.CourseNodeFactory;
 import org.olat.course.nodes.IQSURVCourseNode;
 import org.olat.course.nodes.STCourseNode;
-import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.group.BusinessGroup;
 import org.olat.group.ui.context.BGContextTableModel;
-import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserManager;
 
 import de.bps.onyx.plugin.OnyxModule;
-import de.bps.webservices.clients.onyxreporter.OnyxReporterException;
 import de.bps.webservices.clients.onyxreporter.OnyxReporterConnector;
+import de.bps.webservices.clients.onyxreporter.OnyxReporterException;
 
 /**
  * Initial Date:  Jun 18, 2004
@@ -346,9 +344,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 
 			// select user
 			assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
-
-			UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();
-			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true);
+			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, focusOnIdentity, course, true);
 			listenTo(identityAssessmentController);
 			setContent(identityAssessmentController.getInitialComponent());
 		}
@@ -582,9 +578,9 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 					// init edit controller for this identity and this course node
 					// or use identity assessment overview if no course node is defined
 					if (this.currentCourseNode == null) {
-						UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();
+						Identity assessedIdentity = assessedIdentityWrapper.getIdentity();
 						removeAsListenerAndDispose(identityAssessmentController);
-						identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true);
+						identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, assessedIdentity, course, true);
 						listenTo(identityAssessmentController);
 						setContent(identityAssessmentController.getInitialComponent());
 					} else {
@@ -753,7 +749,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 						if(initialLaunchDates.containsKey(identityKeyFromEvent)) {
 							initialLaunchDate = initialLaunchDates.get(identityKeyFromEvent);
 						} else {
-							initialLaunchDate = AssessmentHelper.getInitialLaunchDate(wrappedIdFromModel.getUserCourseEnvironment());
+							UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+							initialLaunchDate = userCourseInformationsManager.getInitialLaunchDate(ores.getResourceableId(),  wrappedIdFromModel.getIdentity());
 						}
 						wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), initialLaunchDate, currentCourseNode);
 						wrappers.add(wrappedIdFromModel);
@@ -1349,21 +1346,10 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			long start = 0;
 			boolean logDebug = log.isDebug();
 			if(logDebug) start = System.currentTimeMillis();
-			course.getCourseEnvironment().getAssessmentManager().preloadCache();
-			// 2) preload controller local user environment cache
-			start = System.currentTimeMillis();
 			List<Identity> identities = getAllIdentitisFromGroupmanagement();
 			
-			CourseNode node = course.getCourseEnvironment().getRunStructure().getRootNode();
-			CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager();
-			List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
-			Calendar cal = Calendar.getInstance();
-			for(Property property:firstTime) {
-				if (StringHelper.containsNonWhitespace(property.getStringValue()) && property.getIdentity() != null) {
-					cal.setTimeInMillis(Long.parseLong(property.getStringValue()));
-					initialLaunchDates.put(property.getIdentity().getKey(), cal.getTime());
-				}
-			}
+			UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+			initialLaunchDates.putAll(mgr.getInitialLaunchDates(course.getResourceableId(), identities));
 
 			for (Identity identity : identities) {
 				AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurity.java b/src/main/java/org/olat/basesecurity/BaseSecurity.java
index 5f46b5279df..3d124b646d2 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurity.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurity.java
@@ -157,7 +157,7 @@ public interface BaseSecurity {
 	 * @param identityNames
 	 * @return The identities
 	 */
-	public List<Identity> findIdentitiesByName(Collection<String> identityName);
+	public List<IdentityShort> findShortIdentitiesByName(Collection<String> identityName);
 
 	/**
 	 * find an identity by the key instead of the username. Prefer this method as
diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
index e0a54c752d2..e6149db50ec 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
@@ -891,12 +891,15 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 		if (secGroups == null || secGroups.isEmpty()) {
 			return Collections.emptyList();
 		}
-		
+
 		StringBuilder sb = new StringBuilder();
-		sb.append("select new org.olat.basesecurity.IdentityShort(sgmsi.identity.key, sgmsi.identity.name) from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ")
-			.append(" where sgmsi.securityGroup in (:secGroups)");
-		
-		DBQuery query = DBFactory.getInstance().createQuery(sb.toString());
+		sb.append("select id from ").append(IdentityShort.class.getName()).append(" as id ")
+			.append(" where id.key in (")
+			.append("   select sgmsi.identity.key from  ").append(SecurityGroupMembershipImpl.class.getName()).append(" as sgmsi ")
+			.append("   where sgmsi.securityGroup in (:secGroups)")
+			.append(" )");
+
+		DBQuery query = DBFactory.getInstance().createQuery(sb	.toString());
 		query.setParameterList("secGroups", secGroups);
 		List<IdentityShort> idents = query.list();
 		return idents;
@@ -1005,12 +1008,12 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity {
 	}
 
 	@Override
-	public List<Identity> findIdentitiesByName(Collection<String> identityNames) {
+	public List<IdentityShort> findShortIdentitiesByName(Collection<String> identityNames) {
 		if (identityNames == null || identityNames.isEmpty()) {
 			return Collections.emptyList();
 		}
 		StringBuilder sb = new StringBuilder();
-		sb.append("select ident from ").append(IdentityImpl.class.getName()).append(" as ident where ident.name in (:names)");
+		sb.append("select ident from ").append(IdentityShort.class.getName()).append(" as ident where ident.name in (:names)");
 		
 		DBQuery query = DBFactory.getInstance().createQuery(sb.toString());
 		query.setParameterList("names", identityNames);
diff --git a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml
index 209777fdd41..a11438afee1 100644
--- a/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml
+++ b/src/main/java/org/olat/basesecurity/IdentityImpl.hbm.xml
@@ -22,4 +22,22 @@
     <many-to-one name="user" column="fk_user_id" class="org.olat.user.UserImpl" fetch="join" unique="true"  cascade="none"/>
 
   </class>
+  
+  
+  <class name="org.olat.basesecurity.IdentityShort" table="o_bs_identity_short_v" mutable="false">
+  	<!-- the default columns -->
+	<composite-id>
+		<key-property name="key" column="id_id" type="long" />
+		<key-property name="userKey" column="us_id" type="long" />
+	</composite-id>
+	 
+ 	<property name="lastLogin" column="id_lastlogin" type="timestamp" />
+    <property name="name" column="id_name" type="string" />
+    <property name="status" column="id_status" type="integer" />
+
+    <property name="firstName" column="first_name" type="string" />
+    <property name="lastName" column="last_name" type="string" />
+    <property name="email" column="email" type="string" />
+
+  </class>
 </hibernate-mapping>
\ No newline at end of file
diff --git a/src/main/java/org/olat/basesecurity/IdentityShort.java b/src/main/java/org/olat/basesecurity/IdentityShort.java
index 20f340e7897..6897f3b155b 100644
--- a/src/main/java/org/olat/basesecurity/IdentityShort.java
+++ b/src/main/java/org/olat/basesecurity/IdentityShort.java
@@ -19,6 +19,10 @@
  */
 package org.olat.basesecurity;
 
+import java.util.Date;
+
+import org.olat.core.commons.persistence.PersistentObject;
+
 /**
  * 
  * Description:<br>
@@ -29,24 +33,98 @@ package org.olat.basesecurity;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 //fxdiff: FXOLAT-219 decrease the load for synching groups
-public class IdentityShort {
+public class IdentityShort extends PersistentObject {
+
+	private static final long serialVersionUID = -9039644291427632379L;
 	
-	private final Long key;
-	private final String name;
+	private Long userKey;
 	
-	public IdentityShort(Long key, String name) {
-		this.key = key;
-		this.name = name;
+	private String name;
+	private Date lastLogin;
+	private int status;
+	private String firstName;
+	private String lastName;
+	private String email;
+
+	public IdentityShort() {
+		//
+	}
+	
+	public Long getUserKey() {
+		return userKey;
 	}
 
-	public Long getKey() {
-		return key;
+	public void setUserKey(Long userKey) {
+		this.userKey = userKey;
 	}
 
 	public String getName() {
 		return name;
 	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Date getLastLogin() {
+		return lastLogin;
+	}
+
+	public void setLastLogin(Date lastLogin) {
+		this.lastLogin = lastLogin;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public String getFirstName() {
+		return firstName;
+	}
+
+	public void setFirstName(String firstName) {
+		this.firstName = firstName;
+	}
+
+	public String getLastName() {
+		return lastName;
+	}
+
+	public void setLastName(String lastName) {
+		this.lastName = lastName;
+	}
+
+	public String getEmail() {
+		return email;
+	}
+
+	public void setEmail(String email) {
+		this.email = email;
+	}
+
+	@Override
+	public String toString() {
+		return "IdentityShort[name=" + name + "], " + super.toString();
+	}
 	
+	@Override
+	public int hashCode() {
+		return getKey() == null ? 3482601 : getKey().hashCode();
+	}
 	
-
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof IdentityShort) {
+			IdentityShort id = (IdentityShort)obj;
+			return getKey() != null && getKey().equals(id.getKey());
+		}
+		return false;
+	}
 }
diff --git a/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java b/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java
index e78dd90b724..ae64337a405 100644
--- a/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java
+++ b/src/main/java/org/olat/core/commons/modules/bc/version/DeletedFileListController.java
@@ -31,6 +31,7 @@ import java.util.Locale;
 import java.util.Map;
 
 import org.olat.basesecurity.BaseSecurityManager;
+import org.olat.basesecurity.IdentityShort;
 import org.olat.core.commons.modules.bc.commands.FolderCommand;
 import org.olat.core.commons.modules.bc.commands.FolderCommandStatus;
 import org.olat.core.gui.UserRequest;
@@ -51,8 +52,6 @@ import org.olat.core.gui.control.controller.BasicController;
 import org.olat.core.gui.control.generic.modal.DialogBoxController;
 import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.gui.media.MediaResource;
-import org.olat.core.id.Identity;
-import org.olat.core.id.UserConstants;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSManager;
@@ -145,8 +144,8 @@ public class DeletedFileListController extends BasicController {
 			}
 		}
 		
-		Map<String, Identity> mappedIdentities = new HashMap<String, Identity>();
-		for(Identity identity :BaseSecurityManager.getInstance().findIdentitiesByName(names)) {
+		Map<String, IdentityShort> mappedIdentities = new HashMap<String, IdentityShort>();
+		for(IdentityShort identity :BaseSecurityManager.getInstance().findShortIdentitiesByName(names)) {
 			mappedIdentities.put(identity.getName(), identity);
 		}
 
@@ -276,9 +275,9 @@ public class DeletedFileListController extends BasicController {
 		private final DateFormat format;
 		private final List<Versions> versionList;
 		private final Calendar cal = Calendar.getInstance();
-		private final Map<String, Identity> mappedIdentities;
+		private final Map<String, IdentityShort> mappedIdentities;
 
-		public DeletedFileListDataModel(List<Versions> versionList, Map<String, Identity> mappedIdentities, Locale locale) {
+		public DeletedFileListDataModel(List<Versions> versionList, Map<String, IdentityShort> mappedIdentities, Locale locale) {
 			this.versionList = versionList;
 			this.mappedIdentities = mappedIdentities;
 			format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
@@ -320,15 +319,15 @@ public class DeletedFileListController extends BasicController {
 			if(!StringHelper.containsNonWhitespace(name)) {
 				return null;
 			}
-			Identity id = mappedIdentities.get(name);
+			IdentityShort id = mappedIdentities.get(name);
 			if(id == null) {
 				return null;
 			}
 			
 			StringBuilder sb = new StringBuilder();
-			sb.append(id.getUser().getProperty(UserConstants.FIRSTNAME, null))
+			sb.append(id.getFirstName())
 			  .append(" ")
-			  .append(id.getUser().getProperty(UserConstants.LASTNAME, null));
+			  .append(id.getLastName());
 			
 			if(isAdmin) {
 				sb.append(" (").append(name).append(")");
diff --git a/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java b/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java
index 3b4307175d0..ff5953609e9 100644
--- a/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java
+++ b/src/main/java/org/olat/core/commons/modules/bc/version/RevisionListController.java
@@ -32,6 +32,7 @@ import java.util.Locale;
 import java.util.Map;
 
 import org.olat.basesecurity.BaseSecurityManager;
+import org.olat.basesecurity.IdentityShort;
 import org.olat.core.commons.modules.bc.commands.FolderCommand;
 import org.olat.core.commons.modules.bc.commands.FolderCommandStatus;
 import org.olat.core.gui.UserRequest;
@@ -52,8 +53,6 @@ import org.olat.core.gui.control.controller.BasicController;
 import org.olat.core.gui.control.generic.modal.DialogBoxController;
 import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.gui.media.MediaResource;
-import org.olat.core.id.Identity;
-import org.olat.core.id.UserConstants;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSMediaResource;
@@ -175,8 +174,8 @@ public class RevisionListController extends BasicController {
 			}
 		}
 		
-		Map<String, Identity> mappedIdentities = new HashMap<String, Identity>();
-		for(Identity identity :BaseSecurityManager.getInstance().findIdentitiesByName(names)) {
+		Map<String, IdentityShort> mappedIdentities = new HashMap<String, IdentityShort>();
+		for(IdentityShort identity :BaseSecurityManager.getInstance().findShortIdentitiesByName(names)) {
 			mappedIdentities.put(identity.getName(), identity);
 		}
 
@@ -280,9 +279,9 @@ public class RevisionListController extends BasicController {
 		private final DateFormat format;
 		private final List<VFSRevision> versionList;
 		private final Calendar cal = Calendar.getInstance();
-		private final Map<String, Identity> mappedIdentities;
+		private final Map<String, IdentityShort> mappedIdentities;
 
-		public RevisionListDataModel(List<VFSRevision> versionList, Map<String, Identity> mappedIdentities, Locale locale) {
+		public RevisionListDataModel(List<VFSRevision> versionList, Map<String, IdentityShort> mappedIdentities, Locale locale) {
 			this.versionList = versionList;
 			this.mappedIdentities = mappedIdentities;
 			format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, locale);
@@ -324,15 +323,15 @@ public class RevisionListController extends BasicController {
 			if(!StringHelper.containsNonWhitespace(name)) {
 				return null;
 			}
-			Identity id = mappedIdentities.get(name);
+			IdentityShort id = mappedIdentities.get(name);
 			if(id == null) {
 				return null;
 			}
 			
 			StringBuilder sb = new StringBuilder();
-			sb.append(id.getUser().getProperty(UserConstants.FIRSTNAME, null))
+			sb.append(id.getFirstName())
 			  .append(" ")
-			  .append(id.getUser().getProperty(UserConstants.LASTNAME, null));
+			  .append(id.getLastName());
 			
 			if(isAdmin) {
 				sb.append(" (").append(name).append(")");
diff --git a/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml b/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml
index bdd87aa107e..7653c49a47f 100644
--- a/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml
+++ b/src/main/java/org/olat/core/commons/persistence/_spring/databaseCorecontext.xml
@@ -49,6 +49,8 @@
 				<value>org/olat/repository/MetaDataElement.hbm.xml</value>
 				<value>org/olat/course/nodes/projectbroker/datamodel/Project.hbm.xml</value>
 				<value>org/olat/course/nodes/projectbroker/datamodel/ProjectBroker.hbm.xml</value>
+				<value>org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml</value>
+				<value>org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml</value>
 				<value>org/olat/modules/fo/ForumImpl.hbm.xml</value>
 				<value>org/olat/modules/fo/MessageImpl.hbm.xml</value>
 				<value>org/olat/modules/fo/ReadMessage.hbm.xml</value>
diff --git a/src/main/java/org/olat/course/ICourse.java b/src/main/java/org/olat/course/ICourse.java
index 75b8170307a..519219c6d5e 100644
--- a/src/main/java/org/olat/course/ICourse.java
+++ b/src/main/java/org/olat/course/ICourse.java
@@ -47,17 +47,6 @@ public interface ICourse extends OLATResourceable {
 	 * Name of folder within course root directory where nodes export their data.
 	 */
 	public static final String EXPORTED_DATA_FOLDERNAME = "export";
-	
-	/**
-	 * property name for the initial launch date will be set only the first
-	 * time the users launch the course.
-	 */
-	public static final String PROPERTY_INITIAL_LAUNCH_DATE = "initialCourseLaunchDate";
-	/**
-	 * property name for the recent launch date will be changed every time the
-	 * user start the course.
-	 */
-	public static final String PROPERTY_RECENT_LAUNCH_DATE = "recentCourseLaunchDate";
 
 	/**
 	 * @return The course run structure
diff --git a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java
index 78cbb7be22d..d2d37f2d382 100644
--- a/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java
+++ b/src/main/java/org/olat/course/archiver/ScoreAccountingHelper.java
@@ -26,9 +26,7 @@
 package org.olat.course.archiver;
 
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -36,6 +34,7 @@ import java.util.Map;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.SecurityGroup;
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
 import org.olat.core.id.IdentityEnvironment;
@@ -48,17 +47,16 @@ import org.olat.course.CourseModule;
 import org.olat.course.ICourse;
 import org.olat.course.assessment.AssessmentHelper;
 import org.olat.course.assessment.AssessmentManager;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.groupsandrights.CourseGroupManager;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.STCourseNode;
-import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.group.BusinessGroup;
-import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserManager;
@@ -129,24 +127,12 @@ public class ScoreAccountingHelper {
 
 		// preload user properties cache
 		CourseEnvironment courseEnvironment = course.getCourseEnvironment();
-		AssessmentManager assessmentManager = courseEnvironment.getAssessmentManager();
-		assessmentManager.preloadCache();
 		
 		boolean firstIteration = true;
 		int rowNumber = 1;
 
-		CourseNode node = courseEnvironment.getRunStructure().getRootNode();
-		CoursePropertyManager pm = courseEnvironment.getCoursePropertyManager();
-		List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
-		Map<Identity,Date> firstTimes = new HashMap<Identity,Date>((identities.size() * 2) + 1);
-		Calendar cal = Calendar.getInstance();
-		for(Property property:firstTime) {
-			if (StringHelper.containsNonWhitespace(property.getStringValue())) {
-				cal.setTimeInMillis(Long.parseLong(property.getStringValue()));
-				firstTimes.put(property.getIdentity(), cal.getTime());
-			}
-		}
-		
+		UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+		Map<Long,Date> firstTimes = mgr.getInitialLaunchDates(courseEnvironment.getCourseResourceableId(), identities);
 		Formatter formatter = Formatter.getInstance(locale);
 
 		for (Identity identity:identities) {
@@ -158,8 +144,8 @@ public class ScoreAccountingHelper {
 			tableContent.append("\t");
 
 			String initialLaunchDate = "";
-			if(firstTimes.containsKey(identity)) {
-				initialLaunchDate = formatter.formatDateAndTime(firstTimes.get(identity));
+			if(firstTimes.containsKey(identity.getKey())) {
+				initialLaunchDate = formatter.formatDateAndTime(firstTimes.get(identity.getKey()));
 			}
 			tableContent.append(initialLaunchDate).append("\t");
 
diff --git a/src/main/java/org/olat/course/assessment/AssessmentHelper.java b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
index 499d3921e64..2a011bdc974 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentHelper.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentHelper.java
@@ -27,8 +27,8 @@ package org.olat.course.assessment;
 
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
+import java.text.ParseException;
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -50,14 +50,12 @@ import org.olat.course.nodes.ProjectBrokerCourseNode;
 import org.olat.course.nodes.STCourseNode;
 import org.olat.course.nodes.ScormCourseNode;
 import org.olat.course.nodes.iq.IQEditController;
-import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.course.tree.CourseEditorTreeModel;
 import org.olat.course.tree.CourseEditorTreeNode;
 import org.olat.modules.ModuleConfiguration;
-import org.olat.properties.Property;
 
 /**
  * Description:<br>
@@ -144,21 +142,6 @@ public class AssessmentHelper {
 		AssessedIdentityWrapper aiw = new AssessedIdentityWrapper(uce, attempts, details, initialLaunchDate, lastModified);
 		return aiw;
 	}
-	
-	public static Date getInitialLaunchDate(UserCourseEnvironment uce) {
-		CourseNode node = uce.getCourseEnvironment().getRunStructure().getRootNode();
-		CoursePropertyManager pm = uce.getCourseEnvironment().getCoursePropertyManager();
-		Identity identity = uce.getIdentityEnvironment().getIdentity();
-		Property firstTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
-		
-		Date initialLaunchDate = null;
-		if (firstTime != null && StringHelper.containsNonWhitespace(firstTime.getStringValue())) {
-			Calendar cal = Calendar.getInstance();
-			cal.setTimeInMillis(Long.parseLong(firstTime.getStringValue()));
-			initialLaunchDate = cal.getTime();
-		}
-		return initialLaunchDate;
-	}
 
 	/**
 	 * Create a user course environment for the given user and course. After
@@ -168,7 +151,7 @@ public class AssessmentHelper {
 	 * @param course
 	 * @return Initialized user course environment
 	 */
-	static UserCourseEnvironment createAndInitUserCourseEnvironment(Identity identity, ICourse course) {
+	public static UserCourseEnvironment createAndInitUserCourseEnvironment(Identity identity, ICourse course) {
 		// create an identenv with no roles, no attributes, no locale
 		IdentityEnvironment ienv = new IdentityEnvironment(); 
 		ienv.setIdentity(identity);
@@ -269,6 +252,20 @@ public class AssessmentHelper {
 		}
 		//return Formatter.roundToString(score.floatValue(), 3);
 	}
+	
+	public static Float getRoundedScore(String score) {
+		if (!StringHelper.containsNonWhitespace(score)) return null;
+
+		//cluster_OK the formatter is not multi-thread and costly to create
+		synchronized(scoreFormat) {
+			try {
+				return new Float(scoreFormat.parse(score).floatValue());
+			} catch (ParseException e) {
+				log.error("", e);
+				return null;
+			}
+		}
+	}
 
 	public static final String KEY_TYPE = "type";
 	public static final String KEY_IDENTIFYER = "identifyer";
@@ -285,6 +282,9 @@ public class AssessmentHelper {
 	//fxdiff VCRP-4: assessment overview with max score
 	public static final String KEY_MIN = "minScore";
 	public static final String KEY_MAX = "maxScore";
+	public static final String KEY_TOTAL_NODES = "totalNodes";
+	public static final String KEY_ATTEMPTED_NODES = "attemptedNodes";
+	public static final String KEY_PASSED_NODES = "attemptedNodes";
 	
 
 	
diff --git a/src/main/java/org/olat/course/assessment/AssessmentMainController.java b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
index 00244533921..984e8c3b533 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentMainController.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentMainController.java
@@ -26,7 +26,6 @@
 package org.olat.course.assessment;
 
 import java.util.ArrayList;
-import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -38,6 +37,7 @@ import org.olat.admin.user.UserTableDataModel;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.SecurityGroup;
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.gui.ShortName;
@@ -82,13 +82,13 @@ import org.olat.core.logging.OLATSecurityException;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.logging.activity.ActionType;
-import org.olat.core.util.StringHelper;
 import org.olat.core.util.Util;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.core.util.tree.TreeHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.condition.Condition;
 import org.olat.course.condition.interpreter.ConditionExpression;
 import org.olat.course.condition.interpreter.OnlyGroupConditionInterpreter;
@@ -103,7 +103,6 @@ import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.group.BusinessGroup;
 import org.olat.group.ui.context.BGContextTableModel;
-import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserManager;
@@ -302,8 +301,7 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			// select user
 			assessedIdentityWrapper = AssessmentHelper.wrapIdentity(focusOnIdentity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
 			
-			UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();		
-			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true);
+			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, assessedIdentityWrapper.getIdentity(), course, true);
 			listenTo(identityAssessmentController);
 			setContent(identityAssessmentController.getInitialComponent());
 		}
@@ -566,7 +564,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 						if(initialLaunchDates.containsKey(identityKeyFromEvent)) {
 							initialLaunchDate = initialLaunchDates.get(identityKeyFromEvent);
 						} else {
-							initialLaunchDate = AssessmentHelper.getInitialLaunchDate(wrappedIdFromModel.getUserCourseEnvironment());
+							UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+							initialLaunchDate = userCourseInformationsManager.getInitialLaunchDate(ores.getResourceableId(),  wrappedIdFromModel.getIdentity());
 						}
 						wrappedIdFromModel = AssessmentHelper.wrapIdentity(wrappedIdFromModel.getUserCourseEnvironment(), initialLaunchDate, currentCourseNode);
 						wrappers.add(wrappedIdFromModel);
@@ -644,9 +643,8 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 	 */
 	private void initIdentityEditController(UserRequest ureq, ICourse course) {
 		if (currentCourseNode == null) {
-			UserCourseEnvironment chooseUserCourseEnv = assessedIdentityWrapper.getUserCourseEnvironment();
 			removeAsListenerAndDispose(identityAssessmentController);
-			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, chooseUserCourseEnv, course, true);
+			identityAssessmentController = new IdentityAssessmentEditController(getWindowControl(),ureq, assessedIdentityWrapper.getIdentity(), course, true);
 			listenTo(identityAssessmentController);
 			setContent(identityAssessmentController.getInitialComponent());
 		} else {
@@ -1153,21 +1151,10 @@ AssessmentMainController(UserRequest ureq, WindowControl wControl, OLATResourcea
 			long start = 0;
 			boolean logDebug = log.isDebug();
 			if(logDebug) start = System.currentTimeMillis();
-			course.getCourseEnvironment().getAssessmentManager().preloadCache();
-			// 2) preload controller local user environment cache
-			start = System.currentTimeMillis();
 			List<Identity> identities = getAllAssessableIdentities();
-			
-			CourseNode node = course.getCourseEnvironment().getRunStructure().getRootNode();
-			CoursePropertyManager pm = course.getCourseEnvironment().getCoursePropertyManager();
-			List<Property> firstTime = pm.findCourseNodeProperties(node, identities, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
-			Calendar cal = Calendar.getInstance();
-			for(Property property:firstTime) {
-				if (StringHelper.containsNonWhitespace(property.getStringValue()) && property.getIdentity() != null) {
-					cal.setTimeInMillis(Long.parseLong(property.getStringValue()));
-					initialLaunchDates.put(property.getIdentity().getKey(), cal.getTime());
-				}
-			}
+
+			UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+			initialLaunchDates.putAll(mgr.getInitialLaunchDates(course.getResourceableId(), identities));
 			
 			for (Identity identity : identities) {
 				AssessmentHelper.wrapIdentity(identity, localUserCourseEnvironmentCache, initialLaunchDates, course, null);
diff --git a/src/main/java/org/olat/course/assessment/AssessmentManager.java b/src/main/java/org/olat/course/assessment/AssessmentManager.java
index 4f422eb77cb..ee5d1dfef54 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentManager.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentManager.java
@@ -57,11 +57,6 @@ public interface AssessmentManager {
 	public static final String COMMENT = "COMMENT";
 	public static final String COACH_COMMENT = "COACH_COMMENT";
 	public static final String ASSESSMENT_ID = "ASSESSMENT_ID";
-	
-	/**
-	 * Load all persisted assessment data of a course into a local cache if such a cache is available
-	 */
-	public void preloadCache();
 
 	/**
 	 * Load all persisted assessment data into a local cache if such a cache is available
diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java
index a6da07b86c4..6b3f25aae41 100644
--- a/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java
+++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementController.java
@@ -25,9 +25,11 @@
 
 package org.olat.course.assessment;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
+import org.olat.NewControllerFactory;
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.gui.UserRequest;
@@ -39,12 +41,22 @@ 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.controller.MainLayoutBasicController;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
 import org.olat.core.gui.control.generic.messages.MessageUIFactory;
 import org.olat.core.id.Identity;
+import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.Roles;
+import org.olat.core.id.context.BusinessControl;
+import org.olat.core.id.context.BusinessControlFactory;
+import org.olat.core.id.context.ContextEntry;
 import org.olat.core.util.StringHelper;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.ContactMessage;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.course.assessment.portfolio.EfficiencyStatementArtefact;
+import org.olat.group.BusinessGroup;
+import org.olat.modules.co.ContactFormController;
 import org.olat.portfolio.EPArtefactHandler;
 import org.olat.portfolio.PortfolioModule;
 import org.olat.portfolio.model.artefacts.AbstractArtefact;
@@ -65,14 +77,18 @@ public class EfficiencyStatementController extends MainLayoutBasicController {
 	private VelocityContainer userDataVC;
 	private static final String usageIdentifyer = EfficiencyStatementController.class.getCanonicalName();
 	
-	private EfficiencyStatement efficiencyStatement;
+	private final EfficiencyStatement efficiencyStatement;
+	private final Identity statementOwner;
+	private final Long businessGroupKey;
 	
 	//to collect the eff.Statement as artefact
-	private Link collectArtefactLink;
+	private Link collectArtefactLink, homeLink, courseLink, groupLink, contactLink;
 	private PortfolioModule portfolioModule;
 	// the collect-artefact-wizard
-	private Controller ePFCollCtrl; 
-
+	private Controller ePFCollCtrl;
+	//contact
+	private ContactFormController contactCtrl;
+	private CloseableModalController cmc;
 	/**
 	 * Constructor
 	 * @param wControl
@@ -84,32 +100,60 @@ public class EfficiencyStatementController extends MainLayoutBasicController {
 	}
 	
 	public EfficiencyStatementController(WindowControl wControl, UserRequest ureq, EfficiencyStatement efficiencyStatement) {
-		this(wControl, ureq, ureq.getIdentity(), efficiencyStatement);
+		this(wControl, ureq, ureq.getIdentity(), null, efficiencyStatement, true);
 	}
 	
-	public EfficiencyStatementController(WindowControl wControl, UserRequest ureq, Identity statementOwner, EfficiencyStatement efficiencyStatement) {
+	public EfficiencyStatementController(WindowControl wControl, UserRequest ureq, Identity statementOwner,
+			BusinessGroup businessGroup, EfficiencyStatement efficiencyStatement, boolean mainLayout) {
 		super(ureq, wControl);
-
-		//either the efficiency statement or the error message, that no data is available goes to the content area
-		final Component content;
-		if (efficiencyStatement != null) {
-			this.efficiencyStatement = efficiencyStatement;
-			
-			//extract efficiency statement data
-			//fallback translation for user properties 
-			setTranslator(UserManager.getInstance().getPropertyHandlerTranslator(getTranslator()));		
-			userDataVC = createVelocityContainer("efficiencystatement");
+		
+		this.businessGroupKey = businessGroup == null ? null : businessGroup.getKey();
+		this.statementOwner = statementOwner;
+		this.efficiencyStatement = efficiencyStatement;
+		init(ureq, statementOwner, businessGroup, true);
+	}
+		
+	private void init(UserRequest ureq, Identity statementOwner, BusinessGroup group, boolean mainLayout) { 
+		//extract efficiency statement data
+		//fallback translation for user properties 
+		setTranslator(UserManager.getInstance().getPropertyHandlerTranslator(getTranslator()));		
+		userDataVC = createVelocityContainer("efficiencystatement");
+		if(efficiencyStatement != null) {
 			userDataVC.contextPut("courseTitle", efficiencyStatement.getCourseTitle() + " (" + efficiencyStatement.getCourseRepoEntryKey().toString() + ")");
-			userDataVC.contextPut("user", statementOwner.getUser());			
-			userDataVC.contextPut("username", statementOwner.getName());
-			userDataVC.contextPut("date", StringHelper.formatLocaleDateTime(efficiencyStatement.getLastUpdated(), ureq.getLocale()));
 			
-			Roles roles = ureq.getUserSession().getRoles();
-			boolean isAdministrativeUser = (roles.isAuthor() || roles.isGroupManager() || roles.isUserManager() || roles.isOLATAdmin());	
-			List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor(usageIdentifyer, isAdministrativeUser);
-			userDataVC.contextPut("userPropertyHandlers", userPropertyHandlers);
+			courseLink = LinkFactory.createButton("course.link", userDataVC, this);
+			courseLink.setCustomEnabledLinkCSS("b_link_course");
+			userDataVC.put("course.link", courseLink);
+			userDataVC.contextPut("date", StringHelper.formatLocaleDateTime(efficiencyStatement.getLastUpdated(), ureq.getLocale()));
+		}
+		
+		userDataVC.contextPut("user", statementOwner.getUser());			
+		userDataVC.contextPut("username", statementOwner.getName());
+		
+		Roles roles = ureq.getUserSession().getRoles();
+		boolean isAdministrativeUser = (roles.isAuthor() || roles.isGroupManager() || roles.isUserManager() || roles.isOLATAdmin());	
+		List<UserPropertyHandler> userPropertyHandlers = UserManager.getInstance().getUserPropertyHandlersFor(usageIdentifyer, isAdministrativeUser);
+		userDataVC.contextPut("userPropertyHandlers", userPropertyHandlers);
+
+		if(!getIdentity().equals(statementOwner)) {
+			homeLink = LinkFactory.createButton("home.link", userDataVC, this);
+			homeLink.setCustomEnabledLinkCSS("b_link_to_home");
+			userDataVC.put("home.link", homeLink);
 			
-			Controller identityAssessmentCtr = new IdentityAssessmentOverviewController(ureq, wControl, efficiencyStatement.getAssessmentNodes());
+			contactLink = LinkFactory.createButton("contact.link", userDataVC, this);
+			contactLink.setCustomEnabledLinkCSS("b_link_mail");
+			userDataVC.put("contact.link", contactLink);
+		}
+
+		if(group != null) {
+			userDataVC.contextPut("groupName", group.getName());
+			groupLink = LinkFactory.createButton("group.link", userDataVC, this);
+			groupLink.setCustomEnabledLinkCSS("b_link_group");
+			userDataVC.put("group.link", groupLink);
+		}
+		
+		if (efficiencyStatement != null) {
+			Controller identityAssessmentCtr = new IdentityAssessmentOverviewController(ureq, getWindowControl(), efficiencyStatement.getAssessmentNodes());
 			listenTo(identityAssessmentCtr);//dispose it when this one is disposed
 			userDataVC.put("assessmentOverviewTable", identityAssessmentCtr.getInitialComponent());
 			
@@ -122,20 +166,24 @@ public class EfficiencyStatementController extends MainLayoutBasicController {
 					collectArtefactLink.setCustomEnabledLinkCSS("b_eportfolio_add_again");
 				}
 			}
-			
-			content = userDataVC;
 		} else {
 			//message, that no data is available. This may happen in the case the "open efficiency" link is available, while in the meantime an author
 			//disabled the efficiency statement.
 			String text = translate("efficiencystatement.nodata");
-			Controller messageCtr = MessageUIFactory.createErrorMessage(ureq, wControl, null, text);
+			Controller messageCtr = MessageUIFactory.createSimpleMessage(ureq, getWindowControl(), text);
 			listenTo(messageCtr);//gets disposed as this controller gets disposed.
-			content = messageCtr.getInitialComponent();
+			userDataVC.put("assessmentOverviewTable",  messageCtr.getInitialComponent());
 		}
+		
+		
 		//Content goes to a 3 cols layout without left and right column
-		LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), null, null, content, null);
-		listenTo(layoutCtr);
-		putInitialPanel(layoutCtr.getInitialComponent());
+		if(mainLayout) {
+			LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), null, null, userDataVC, null);
+			listenTo(layoutCtr);
+			putInitialPanel(layoutCtr.getInitialComponent());
+		} else {
+			putInitialPanel(userDataVC);
+		}	
 	}
 	
 	/**
@@ -145,8 +193,74 @@ public class EfficiencyStatementController extends MainLayoutBasicController {
 	public void event(UserRequest ureq, Component source, Event event) {
 		if(source.equals(collectArtefactLink)){
 			popupArtefactCollector(ureq);
+		} else if (source == homeLink) {
+			openHome(ureq);
+		} else if (source == courseLink) {
+			openCourse(ureq);
+		} else if (source == groupLink) {
+			openGroup(ureq);
+		} else if (source == contactLink) {
+			contact(ureq);
 		}
-		
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		 if (source == cmc) {
+				removeAsListenerAndDispose(cmc);
+				removeAsListenerAndDispose(contactCtrl);
+				cmc = null;
+				contactCtrl = null;
+			} else if (source == contactCtrl) {
+				cmc.deactivate();
+				removeAsListenerAndDispose(cmc);
+				removeAsListenerAndDispose(contactCtrl);
+				cmc = null;
+				contactCtrl = null;
+			}
+	}
+
+	private void contact(UserRequest ureq) {
+		removeAsListenerAndDispose(cmc);
+
+		ContactMessage cmsg = new ContactMessage(getIdentity());
+		ContactList contactList = new ContactList("to");
+		contactList.add(statementOwner);
+		cmsg.addEmailTo(contactList);
+		contactCtrl = new ContactFormController(ureq, getWindowControl(), true, true, false, false, cmsg);
+		listenTo(contactCtrl);
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), contactCtrl.getInitialComponent());
+		cmc.activate();
+		listenTo(cmc);
+	}
+	
+	private void openGroup(UserRequest ureq) {
+		List<ContextEntry> ces = new ArrayList<ContextEntry>(1);
+		OLATResourceable ores = OresHelper.createOLATResourceableInstance("BusinessGroup", businessGroupKey);
+		ces.add(BusinessControlFactory.getInstance().createContextEntry(ores));
+
+		BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces);
+	  WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
+		NewControllerFactory.getInstance().launch(ureq, bwControl);
+	}
+	
+	private void openCourse(UserRequest ureq) {
+		List<ContextEntry> ces = new ArrayList<ContextEntry>(1);
+		OLATResourceable ores = OresHelper.createOLATResourceableInstance("RepositoryEntry", efficiencyStatement.getCourseRepoEntryKey());
+		ces.add(BusinessControlFactory.getInstance().createContextEntry(ores));
+
+		BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces);
+	  WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
+		NewControllerFactory.getInstance().launch(ureq, bwControl);
+	}
+	
+	private void openHome(UserRequest ureq) {
+		List<ContextEntry> ces = new ArrayList<ContextEntry>(1);
+		ces.add(BusinessControlFactory.getInstance().createContextEntry(statementOwner));
+
+		BusinessControl bc = BusinessControlFactory.getInstance().createFromContextEntries(ces);
+	  WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl());
+		NewControllerFactory.getInstance().launch(ureq, bwControl);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java
index f5b3c614769..4e07607103e 100644
--- a/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java
+++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementManager.java
@@ -27,17 +27,20 @@ package org.olat.course.assessment;
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
 import org.olat.admin.user.delete.service.UserDeletionManager;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.persistence.DBFactory;
+import org.olat.core.commons.persistence.DBQuery;
 import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.User;
 import org.olat.core.id.UserConstants;
-import org.olat.core.logging.OLog;
-import org.olat.core.logging.Tracing;
 import org.olat.core.manager.BasicManager;
 import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.coordinate.SyncerExecutor;
@@ -46,15 +49,17 @@ import org.olat.core.util.xml.XStreamHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.CourseModule;
 import org.olat.course.ICourse;
+import org.olat.course.assessment.model.UserEfficiencyStatementImpl;
+import org.olat.course.assessment.model.UserEfficiencyStatementLight;
 import org.olat.course.config.CourseConfig;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.run.userview.UserCourseEnvironment;
-import org.olat.properties.Property;
-import org.olat.properties.PropertyManager;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.user.UserDataDeletable;
 
+import com.thoughtworks.xstream.XStream;
+
 /**
  * Description:<br>
  * Methods to update a users efficiency statement and to retrieve such statements
@@ -65,12 +70,17 @@ import org.olat.user.UserDataDeletable;
  * @author gnaegi
  */
 public class EfficiencyStatementManager extends BasicManager implements UserDataDeletable {
-	//TODO remove as already definded in basic manager
-	OLog log = Tracing.createLoggerFor(EfficiencyStatementManager.class);
+
 	public static final String KEY_ASSESSMENT_NODES = "assessmentNodes";
 	public static final String KEY_COURSE_TITLE = "courseTitle";
-	private static final String PROPERTY_CATEGORY = "efficiencyStatement";
+	public static final String PROPERTY_CATEGORY = "efficiencyStatement";
+	
 	private static EfficiencyStatementManager INSTANCE;
+
+	private DB dbInstance;
+	private RepositoryManager repositoryManager;
+	private final XStream xstream = XStreamHelper.createXStreamInstance();
+
 	
 	/**
 	 * Constructor
@@ -88,7 +98,22 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 		return INSTANCE;
 	}
 	
+	/**
+	 * [used by Spring]
+	 * @param dbInstance
+	 */
+	public void setDbInstance(DB dbInstance) {
+		this.dbInstance = dbInstance;
+	}
 	
+	/**
+	 * [used by Spring]
+	 * @param repositoryManager
+	 */
+	public void setRepositoryManager(RepositoryManager repositoryManager) {
+		this.repositoryManager = repositoryManager;
+	}
+
 	/**
 	 * Updates the users efficiency statement for this course. <p>
 	 * Called in AssessmentManager in a <code>doInSync</code> block, toghether with the saveScore.
@@ -96,7 +121,7 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 */
 	protected void updateUserEfficiencyStatement(UserCourseEnvironment userCourseEnv) {
 		Long courseResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); 
-		RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry(
+		RepositoryEntry re = repositoryManager.lookupRepositoryEntry(
 				OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId), false);
 		ICourse course = CourseFactory.loadCourse(userCourseEnv.getCourseEnvironment().getCourseResourceableId());
 		updateUserEfficiencyStatement(userCourseEnv, re.getKey(), course, true);
@@ -113,11 +138,8 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 		CourseConfig cc = userCourseEnv.getCourseEnvironment().getCourseConfig();
 		// write only when enabled for this course
 		if (cc.isEfficencyStatementEnabled()) {
-			final boolean logDebug = log.isDebug();
-			final Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity();
-			final PropertyManager pm = PropertyManager.getInstance();					
-			final String courseRepoEntryKey = getPropertyName(repoEntryKey);					
-				
+			Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity();				
+
 			CourseNode rootNode = userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode(); 
 			List<Map<String,Object>> assessmentNodes = AssessmentHelper.addAssessableNodeAndDataToList(0, rootNode, userCourseEnv, true, true);
 					
@@ -128,33 +150,42 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 			User user = identity.getUser();
 			efficiencyStatement.setDisplayableUserInfo(user.getProperty(UserConstants.FIRSTNAME, null) + " " + user.getProperty(UserConstants.LASTNAME, null) + " (" + identity.getName() + ")");
 			efficiencyStatement.setLastUpdated(System.currentTimeMillis());
-					
-			// save efficiency statement as xtream persisted list
-			final String efficiencyStatementX = XStreamHelper.toXML(efficiencyStatement);  					
-			Property efficiencyProperty = null;
+							
+			UserEfficiencyStatementImpl efficiencyProperty = null;
 			if (checkForExistingProperty) {
-				efficiencyProperty = pm.findUserProperty(identity, PROPERTY_CATEGORY, courseRepoEntryKey);
+				efficiencyProperty = getUserEfficiencyStatementFull(repoEntryKey, identity);
 			}
 			if (assessmentNodes != null) {				
 				if (efficiencyProperty == null) {
 					// create new
-					efficiencyProperty = pm.createUserPropertyInstance(identity, PROPERTY_CATEGORY, courseRepoEntryKey, null, null, null,	efficiencyStatementX);
-					pm.saveProperty(efficiencyProperty);
-					if (logDebug) log.debug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::"
-									+ identity.getName() + " repoEntry::" + courseRepoEntryKey);
+					efficiencyProperty = new UserEfficiencyStatementImpl();
+					efficiencyProperty.setIdentity(identity);
+					efficiencyProperty.setCourseRepoKey(repoEntryKey);
+					RepositoryEntry re = repositoryManager.lookupRepositoryEntry(repoEntryKey, false);
+					if(re != null) {
+						efficiencyProperty.setResource(re.getOlatResource());
+					}
+					
+					fillEfficiencyStatement(efficiencyStatement, efficiencyProperty);
+					dbInstance.saveObject(efficiencyProperty);
+					if (isLogDebugEnabled()) {
+						logDebug("creating new efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntryKey);
+					}				
 				} else {
 					// update existing
-					if (logDebug) log.debug("updatting efficiency statement property::" + efficiencyProperty.getKey() + " for id::"
-									+ identity.getName() + " repoEntry::" + courseRepoEntryKey);
-					efficiencyProperty.setTextValue(efficiencyStatementX);
-					pm.updateProperty(efficiencyProperty);
+					if (isLogDebugEnabled()) {
+						logDebug("updatting efficiency statement property::" + efficiencyProperty.getKey() + " for id::" + identity.getName() + " repoEntry::" + repoEntryKey);
+					}	
+					fillEfficiencyStatement(efficiencyStatement, efficiencyProperty);
+					dbInstance.updateObject(efficiencyProperty);
 				}
 			} else {
 				if (efficiencyProperty != null) {
 					// remove existing since now empty empty efficiency statements
-					if (logDebug) log.debug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::"
-									+ identity.getName() + " repoEntry::" + courseRepoEntryKey + " since empty");
-					pm.deleteProperty(efficiencyProperty);
+					if (isLogDebugEnabled()) {
+						logDebug("removing efficiency statement property::" + efficiencyProperty.getKey() + " for id::"	+ identity.getName() + " repoEntry::" + repoEntryKey + " since empty");
+					}
+					dbInstance.deleteObject(efficiencyProperty);
 				}
 				// else nothing to create and nothing to delete
 			}					
@@ -165,6 +196,44 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 		}
 	}
 	
+	public void fillEfficiencyStatement(EfficiencyStatement efficiencyStatement, UserEfficiencyStatementImpl efficiencyProperty) {
+		List<Map<String,Object>> nodeData = efficiencyStatement.getAssessmentNodes();
+		if(!nodeData.isEmpty()) {
+			Map<String,Object> rootNode = nodeData.get(0);
+			Object passed = rootNode.get(AssessmentHelper.KEY_PASSED);
+			if(passed instanceof Boolean) {
+				efficiencyProperty.setPassed((Boolean)passed);
+			}
+			
+			Object fscore = rootNode.get(AssessmentHelper.KEY_SCORE_F);
+			if(fscore instanceof Float) {
+				efficiencyProperty.setScore((Float)fscore);
+			}
+	
+			Object shortTitle = rootNode.get(AssessmentHelper.KEY_TITLE_SHORT);
+			if(shortTitle instanceof String) {
+				efficiencyProperty.setShortTitle((String)shortTitle);
+			}
+			
+			Object longTitle = rootNode.get(AssessmentHelper.KEY_TITLE_LONG);
+			if(longTitle instanceof String) {
+				efficiencyProperty.setTitle((String)longTitle);
+			}
+			
+			int totalNodes = getTotalNodes(nodeData);
+			efficiencyProperty.setTotalNodes(totalNodes);
+			
+			int attemptedNodes = getAttemptedNodes(nodeData);
+			efficiencyProperty.setAttemptedNodes(attemptedNodes);
+			
+			int passedNodes = getPassedNodes(nodeData);
+			efficiencyProperty.setPassedNodes(passedNodes);
+		}
+
+		efficiencyProperty.setLastModified(new Date());
+		efficiencyProperty.setStatementXml(xstream.toXML(efficiencyStatement));
+	}
+	
 	/**
 	 * LD: Debug method. 
 	 * @param efficiencyStatement
@@ -194,16 +263,73 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * keys defined in the AssessmentHelper and the title of the course
 	 */
 	public EfficiencyStatement getUserEfficiencyStatement(Long courseRepoEntryKey, Identity identity){
-		PropertyManager pm = PropertyManager.getInstance();
-		Property efficiencyProperty;
+		UserEfficiencyStatementImpl s = getUserEfficiencyStatementFull(courseRepoEntryKey, identity);
+		if(s == null || s.getStatementXml() == null) {
+			return null;
+		}
+		return (EfficiencyStatement)xstream.fromXML(s.getStatementXml());
+	}
+	
+
+	public UserEfficiencyStatementImpl getUserEfficiencyStatementFull(Long courseRepoEntryKey, Identity identity) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ")
+			  .append(" where statement.identity.key=:identityKey and statement.courseRepoKey=:repoKey");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("identityKey", identity.getKey());
+			query.setLong("repoKey", courseRepoEntryKey);
+			List<UserEfficiencyStatementImpl> statement = query.list();
+			if(statement.isEmpty()) {
+				return null;
+			}
+			return statement.get(0);
+		} catch (Exception e) {
+			logError("Cannot retrieve efficiency statement: " + courseRepoEntryKey + " from " + identity, e);
+			return null;
+		}
+	}
+	
+	public UserEfficiencyStatement getUserEfficiencyStatementLight(Long courseRepoEntryKey, Identity identity) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ")
+			  .append(" where statement.identity.key=:identityKey and statement.courseRepoKey=:repoKey");
 
-		efficiencyProperty = pm.findUserProperty(identity,PROPERTY_CATEGORY, getPropertyName(courseRepoEntryKey));
-		if (efficiencyProperty == null) {
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("identityKey", identity.getKey());
+			query.setLong("repoKey", courseRepoEntryKey);
+			List<UserEfficiencyStatement> statement = query.list();
+			if(statement.isEmpty()) {
+				return null;
+			}
+			return statement.get(0);
+		} catch (Exception e) {
+			logError("Cannot retrieve efficiency statement: " + courseRepoEntryKey + " from " + identity, e);
 			return null;
-		} else {
-			return (EfficiencyStatement) XStreamHelper.fromXML(efficiencyProperty.getTextValue());
 		}
 	}
+	
+	public EfficiencyStatement getUserEfficiencyStatementByKey(Long key) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ")
+			  .append(" where statement.key=:key");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("key", key);
+			List<UserEfficiencyStatementImpl> statement = query.list();
+			if(statement.isEmpty()) {
+				return null;
+			}
+			return (EfficiencyStatement)xstream.fromXML(statement.get(0).getStatementXml());
+		} catch (Exception e) {
+			logError("Cannot retrieve efficiency statement: " + key, e);
+			return null;
+		}
+	}
+	
 
 	/**
 	 * Get the passed value of a course node of a specific efficiency statment
@@ -224,6 +350,47 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 		}
 		return null;
 	}
+	
+	public int getTotalNodes(List<Map<String,Object>> assessmentNodes) {
+		int count = 0;
+		for (Iterator<Map<String,Object>> iter = assessmentNodes.iterator(); iter.hasNext(); ) {
+			Map<String,Object> nodeData = iter.next();
+			Boolean selectable = (Boolean)nodeData.get(AssessmentHelper.KEY_SELECTABLE);
+			if(selectable != null && selectable.booleanValue()) {
+				count++;
+			}
+		}
+		return count;
+	}
+	
+	public int getAttemptedNodes(List<Map<String,Object>> assessmentNodes) {
+		int count = 0;
+		for (Iterator<Map<String,Object>> iter = assessmentNodes.iterator(); iter.hasNext(); ) {
+			Map<String,Object> nodeData = iter.next();
+			Boolean selectable = (Boolean)nodeData.get(AssessmentHelper.KEY_SELECTABLE);
+			if(selectable != null && selectable.booleanValue()) {
+				if(nodeData.containsKey(AssessmentHelper.KEY_SCORE)) {
+					count++;
+				} else if (nodeData.containsKey(AssessmentHelper.KEY_PASSED)) {
+					count++;
+				}
+			}
+		}
+		return count;
+	}
+	
+	public int getPassedNodes(List<Map<String,Object>> assessmentNodes) {
+		int count = 0;
+		for (Iterator<Map<String,Object>> iter = assessmentNodes.iterator(); iter.hasNext(); ) {
+			Map<String,Object> nodeData = iter.next();
+			Boolean passed = (Boolean)nodeData.get(AssessmentHelper.KEY_PASSED);
+			Boolean selectable = (Boolean)nodeData.get(AssessmentHelper.KEY_SELECTABLE);
+			if(passed != null && passed.booleanValue() && selectable != null && selectable.booleanValue()) {
+				count++;
+			}
+		}
+		return count;
+	}
 
 	/**
 	 * Get the score value of a course node of a specific efficiency statment
@@ -253,26 +420,63 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * @return List of efficiency statements
 	 */
 	protected List<EfficiencyStatement> findEfficiencyStatements(Identity identity) {
-		PropertyManager pm = PropertyManager.getInstance();
-		List<Property> esProperties = pm.listProperties(identity, null, null, PROPERTY_CATEGORY, null);
 		List<EfficiencyStatement> efficiencyStatements = new ArrayList<EfficiencyStatement>();
-		Iterator<Property> iter = esProperties.iterator();
-		while (iter.hasNext()) {
-			Property efficiencyProperty = iter.next();
-			EfficiencyStatement efficiencyStatement = (EfficiencyStatement) XStreamHelper.fromXML(efficiencyProperty.getTextValue());
-			efficiencyStatements.add(efficiencyStatement);
+		try {
+			dbInstance = DBFactory.getInstance();
+
+			StringBuilder sb = new StringBuilder();
+			sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ")
+			  .append(" where statement.identity.key=:identityKey");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("identityKey", identity.getKey());
+			List<UserEfficiencyStatementImpl> statements = query.list();
+			for(UserEfficiencyStatementImpl statement:statements) {
+				EfficiencyStatement s = (EfficiencyStatement)xstream.fromXML(statement.getStatementXml());
+				efficiencyStatements.add(s);
+			}
+
+		} catch (Exception e) {
+			logError("findEfficiencyStatements: " + identity, e);
 		}
 		return efficiencyStatements;
 	}
 	
+	protected List<UserEfficiencyStatementLight> findEfficiencyStatementsLight(Identity identity) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select statement from ").append(UserEfficiencyStatementLight.class.getName()).append(" as statement ")
+			  .append(" where statement.identity.key=:identityKey");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("identityKey", identity.getKey());
+			List<UserEfficiencyStatementLight> statements = query.list();
+			return statements;
+		} catch (Exception e) {
+			logError("findEfficiencyStatements: " + identity, e);
+			return Collections.emptyList();
+		}
+	}
+	
 	/**
 	 * Find all identities who have an efficiency statement for this course repository entry
 	 * @param courseRepoEntryKey
 	 * @return List of identities
 	 */
 	protected List<Identity> findIdentitiesWithEfficiencyStatements(Long courseRepoEntryKey) {
-		PropertyManager pm = PropertyManager.getInstance();
-		return pm.findIdentitiesWithProperty(null, PROPERTY_CATEGORY, getPropertyName(courseRepoEntryKey), false);
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select distinct(statement.identity) from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ")
+			  .append(" where statement.courseRepoKey=:repoKey");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("repoKey", courseRepoEntryKey);
+			List<Identity> identities = query.list();
+			return identities;
+		} catch (Exception e) {
+			logError("findIdentitiesWithEfficiencyStatements: " + courseRepoEntryKey, e);
+			return Collections.emptyList();
+		}
 	}
 	
 	/**
@@ -281,8 +485,20 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * @return int number of deleted efficiency statements
 	 */
 	public void deleteEfficiencyStatementsFromCourse(Long courseRepoEntryKey) {
-		PropertyManager pm = PropertyManager.getInstance();
-		pm.deleteProperties(null, null, null, PROPERTY_CATEGORY, getPropertyName(courseRepoEntryKey));
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select statement from ").append(UserEfficiencyStatementImpl.class.getName()).append(" as statement ")
+			  .append(" where statement.courseRepoKey=:repoKey");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("repoKey", courseRepoEntryKey);
+			List<UserEfficiencyStatementImpl> statements = query.list();
+			for(UserEfficiencyStatementImpl statement:statements) {
+				dbInstance.deleteObject(statement);
+			}
+		} catch (Exception e) {
+			logError("deleteEfficiencyStatementsFromCourse: " + courseRepoEntryKey, e);
+		}
 	}
 
 	/**
@@ -291,20 +507,19 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * @param efficiencyStatement
 	 */
 	protected void deleteEfficiencyStatement(Identity identity, EfficiencyStatement efficiencyStatement) {
-		PropertyManager pm = PropertyManager.getInstance();
-		String crourseRepoEntryKey = getPropertyName(efficiencyStatement.getCourseRepoEntryKey());
-		pm.deleteProperties(identity, null, null, PROPERTY_CATEGORY, crourseRepoEntryKey);
+		UserEfficiencyStatement s = getUserEfficiencyStatementLight(efficiencyStatement.getCourseRepoEntryKey(), identity);
+		if(s != null) {
+			dbInstance.deleteObject(s);
+		}
 	}
 	
-	
 	/**
-	 * Internal helper: convert the course repository entry key to a value that is used
-	 * in the property name field
-	 * @param courseRepoEntryKey
-	 * @return String converted course id
+	 * Delete the given efficiency statement for this person
+	 * @param identity
+	 * @param efficiencyStatement
 	 */
-	private String getPropertyName(Long courseRepoEntryKey) {
-		return courseRepoEntryKey.toString();
+	protected void deleteEfficiencyStatement(UserEfficiencyStatementLight efficiencyStatement) {
+		dbInstance.deleteObject(efficiencyStatement);
 	}
 
 	/**
@@ -319,16 +534,14 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	public void updateEfficiencyStatements(OLATResourceable ores, List<Identity> identities, final boolean checkForExistingProperty) {
 		if (identities.size() > 0) {
 			final ICourse course = CourseFactory.loadCourse(ores);
-			log.audit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU");
+			logAudit("Updating efficiency statements for course::" + course.getResourceableId() + ", this might produce temporary heavy load on the CPU");
 			Long courseResId = course.getCourseEnvironment().getCourseResourceableId(); 
-			final RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry(
+			final RepositoryEntry re = repositoryManager.lookupRepositoryEntry(
 					OresHelper.createOLATResourceableInstance(CourseModule.class, courseResId), false);
 
 			// preload cache to speed up things
-			AssessmentManager am = course.getCourseEnvironment().getAssessmentManager();
 			long start = System.currentTimeMillis();
-			am.preloadCache();
-			long between = System.currentTimeMillis();
+			AssessmentManager am = course.getCourseEnvironment().getAssessmentManager();
 
 			Iterator<Identity> iter = identities.iterator();			
 			while (iter.hasNext()) {
@@ -345,15 +558,16 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 				if (Thread.interrupted()) break;
 			}
 			//}
-			long end = System.currentTimeMillis();
-			if (log.isDebug()) 
-				log.debug("Updated efficiency statements for course::" + course.getResourceableId() 
-						+ ". Prepare cache: " + (between-start) + "ms; Updating statements: " + (end-between) + "ms; Users: " + identities.size());
+			if (isLogDebugEnabled()) {
+				long end = System.currentTimeMillis();
+				logDebug("Updated efficiency statements for course::" + course.getResourceableId() 
+					 + "ms; Updating statements: " + (end-start) + "ms; Users: " + identities.size());
+			}
 		}
 	}
 
 	public void archiveUserData(Identity identity, File archiveDir) {
-		List<EfficiencyStatement> efficiencyStatements = this.findEfficiencyStatements(identity);
+		List<EfficiencyStatement> efficiencyStatements = findEfficiencyStatements(identity);
 		EfficiencyStatementArchiver.getInstance().archive(efficiencyStatements, identity, archiveDir);
 	}
 	
@@ -362,11 +576,11 @@ public class EfficiencyStatementManager extends BasicManager implements UserData
 	 * @param identity  Delete data for this identity.
 	 */
 	public void deleteUserData(Identity identity, String newDeletedUserName) {
-		List<EfficiencyStatement> efficiencyStatements = this.findEfficiencyStatements(identity);
+		List<EfficiencyStatement> efficiencyStatements = findEfficiencyStatements(identity);
 		for (Iterator<EfficiencyStatement> iter = efficiencyStatements.iterator(); iter.hasNext();) {
 			deleteEfficiencyStatement(identity, iter.next());
 		}
-		log.debug("All efficiency statements deleted for identity=" + identity);
+		logDebug("All efficiency statements deleted for identity=" + identity);
 	}
 	
 }
diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java
index 9a32d2dc82b..d0abd1191a2 100644
--- a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java
+++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListController.java
@@ -59,6 +59,7 @@ import org.olat.core.gui.render.Renderer;
 import org.olat.core.gui.render.StringOutput;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.util.vfs.VFSContainer;
+import org.olat.course.assessment.model.UserEfficiencyStatementLight;
 import org.olat.course.assessment.portfolio.EfficiencyStatementArtefact;
 import org.olat.portfolio.EPArtefactHandler;
 import org.olat.portfolio.PortfolioModule;
@@ -83,7 +84,7 @@ public class EfficiencyStatementsListController extends BasicController {
 	private TableController tableCtr;
 	private EfficiencyStatementsListModel efficiencyStatementsListModel;
 	private DialogBoxController confirmDeleteCtr;
-	private EfficiencyStatement efficiencyStatement;
+	private UserEfficiencyStatementLight efficiencyStatement;
 	private Controller ePFCollCtrl;
 	private PortfolioModule portfolioModule;
 	
@@ -115,7 +116,7 @@ public class EfficiencyStatementsListController extends BasicController {
 		listenTo(tableCtr);
 		
 		EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
-		List<EfficiencyStatement> efficiencyStatementsList = esm.findEfficiencyStatements(ureq.getIdentity());
+		List<UserEfficiencyStatementLight> efficiencyStatementsList = esm.findEfficiencyStatementsLight(ureq.getIdentity());
 		efficiencyStatementsListModel = new EfficiencyStatementsListModel(efficiencyStatementsList);
 		tableCtr.setTableDataModel(efficiencyStatementsListModel);
 
@@ -143,7 +144,7 @@ public class EfficiencyStatementsListController extends BasicController {
 					// will not be disposed on course run dispose, popus up as new browserwindow
 					ControllerCreator ctrlCreator = new ControllerCreator() {
 						public Controller createController(UserRequest lureq, WindowControl lwControl) {
-							return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoEntryKey());
+							return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoKey());
 						}					
 					};
 					//wrap the content controller into a full header layout
@@ -154,7 +155,7 @@ public class EfficiencyStatementsListController extends BasicController {
 					//
 				} else if (actionid.equals(CMD_LAUNCH_COURSE)) {
 					RepositoryManager rm = RepositoryManager.getInstance();
-					RepositoryEntry re = rm.lookupRepositoryEntry(efficiencyStatement.getCourseRepoEntryKey());
+					RepositoryEntry re = rm.lookupRepositoryEntry(efficiencyStatement.getCourseRepoKey(), false);
 					if (re == null) {
 						showWarning("efficiencyStatements.course.noexists");
 					} else if (!rm.isAllowedToLaunch(ureq, re)) {
@@ -167,7 +168,7 @@ public class EfficiencyStatementsListController extends BasicController {
 						if (dt == null) {
 							// does not yet exist -> create and add
 							//fxdiff BAKS-7 Resume function
-							dt = dts.createDTab(ores, re, efficiencyStatement.getCourseTitle());
+							dt = dts.createDTab(ores, re, efficiencyStatement.getShortTitle());
 							if (dt == null) return;
 							Controller launchController = ControllerFactory.createLaunchController(ores, null, ureq, dt.getWindowControl(), true);
 							dt.setController(launchController);
@@ -178,7 +179,7 @@ public class EfficiencyStatementsListController extends BasicController {
 					}
 				} else if (actionid.equals(CMD_DELETE)) {					
 					// show confirmation dialog
-					confirmDeleteCtr = activateYesNoDialog(ureq, null, translate("efficiencyStatements.delete.confirm", efficiencyStatement.getCourseTitle()), confirmDeleteCtr);
+					confirmDeleteCtr = activateYesNoDialog(ureq, null, translate("efficiencyStatements.delete.confirm", efficiencyStatement.getShortTitle()), confirmDeleteCtr);
 					return;
 				} else if (actionid.equals(CMD_ARTEFACT)) {
 					popupArtefactCollector(ureq);
@@ -188,7 +189,7 @@ public class EfficiencyStatementsListController extends BasicController {
 			if (DialogBoxUIFactory.isYesEvent(event)) {
 				// delete efficiency statement manager
 				EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
-				esm.deleteEfficiencyStatement(ureq.getIdentity(), efficiencyStatement);
+				esm.deleteEfficiencyStatement(efficiencyStatement);
 				efficiencyStatementsListModel.getObjects().remove(efficiencyStatement);
 				efficiencyStatement = null;
 				tableCtr.modelChanged();
@@ -204,8 +205,9 @@ public class EfficiencyStatementsListController extends BasicController {
 			artefact.setAuthor(getIdentity());//only author can create artefact
 			//no business path becouse we cannot launch an efficiency statement
 			artefact.setCollectionDate(new Date());
-			artefact.setTitle(translate("artefact.title", new String[]{efficiencyStatement.getCourseTitle()}));
-			artHandler.prefillArtefactAccordingToSource(artefact, efficiencyStatement);
+			artefact.setTitle(translate("artefact.title", new String[]{efficiencyStatement.getShortTitle()}));
+			EfficiencyStatement fullStatement = EfficiencyStatementManager.getInstance().getUserEfficiencyStatementByKey(efficiencyStatement.getKey());
+			artHandler.prefillArtefactAccordingToSource(artefact, fullStatement);
 			ePFCollCtrl = new ArtefactWizzardStepsController(ureq, getWindowControl(), artefact, (VFSContainer)null);
 			listenTo(ePFCollCtrl);
 		}
diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java
index d4df23428a8..326c2ec1d2a 100644
--- a/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java
+++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementsListModel.java
@@ -26,10 +26,10 @@
 package org.olat.course.assessment;
 
 import java.util.List;
-import java.util.Map;
 
 import org.apache.commons.lang.StringEscapeUtils;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
+import org.olat.course.assessment.model.UserEfficiencyStatementLight;
 
 /**
  * Description:<br>
@@ -44,7 +44,7 @@ public class EfficiencyStatementsListModel extends DefaultTableDataModel {
 	/**
 	 * @param list of efficiencyStatements
 	 */
-	public EfficiencyStatementsListModel(List efficiencyStatements) {
+	public EfficiencyStatementsListModel(List<UserEfficiencyStatementLight> efficiencyStatements) {
 		super(efficiencyStatements);
 	}
 
@@ -59,16 +59,15 @@ public class EfficiencyStatementsListModel extends DefaultTableDataModel {
 	 * @see org.olat.core.gui.components.table.TableDataModel#getValueAt(int, int)
 	 */
 	public Object getValueAt(int row, int col) {
-		EfficiencyStatement efficiencyStatement = getEfficiencyStatementAt(row);
-		List nodeData = efficiencyStatement.getAssessmentNodes();
-		Map rootNode = (Map) nodeData.get(0);
+		UserEfficiencyStatementLight efficiencyStatement = getEfficiencyStatementAt(row);
 		switch (col) {
 			case 0:
-				return StringEscapeUtils.escapeHtml(efficiencyStatement.getCourseTitle());
+				return StringEscapeUtils.escapeHtml(efficiencyStatement.getShortTitle());
 			case 1:
-				return rootNode.get(AssessmentHelper.KEY_SCORE);
+				Float score = efficiencyStatement.getScore();
+				return AssessmentHelper.getRoundedScore(score);
 			case 2:
-				return rootNode.get(AssessmentHelper.KEY_PASSED);
+				return efficiencyStatement.getPassed();
 			default:
 				return "ERROR";
 		}
@@ -78,7 +77,7 @@ public class EfficiencyStatementsListModel extends DefaultTableDataModel {
 	 * @param row
 	 * @return the efficiencyStatement at the given row
 	 */
-	public EfficiencyStatement getEfficiencyStatementAt(int row) {
-		return (EfficiencyStatement) objects.get(row);
+	public UserEfficiencyStatementLight getEfficiencyStatementAt(int row) {
+		return (UserEfficiencyStatementLight) objects.get(row);
 	}
 }
diff --git a/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java b/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java
index c0e0d91cd3a..3e47875d595 100644
--- a/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java
+++ b/src/main/java/org/olat/course/assessment/EfficiencyStatementsPortletRunController.java
@@ -27,11 +27,7 @@ package org.olat.course.assessment;
 
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.Date;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Locale;
-import java.util.Map;
 
 import org.apache.commons.lang.StringEscapeUtils;
 import org.olat.NewControllerFactory;
@@ -61,10 +57,9 @@ import org.olat.core.gui.translator.Translator;
 import org.olat.core.id.Identity;
 import org.olat.core.id.context.BusinessControl;
 import org.olat.core.id.context.BusinessControlFactory;
-import org.olat.core.logging.OLog;
-import org.olat.core.logging.Tracing;
 import org.olat.core.util.event.GenericEventListener;
 import org.olat.course.CourseModule;
+import org.olat.course.assessment.model.UserEfficiencyStatementLight;
 
 
 /**
@@ -79,13 +74,11 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
 	private static final String CMD_LAUNCH = "cmd.launch";
 
 	private TableController tableCtr;
-	//private EfficiencyStatementsListModel efficiencyStatementsListModel;
 	private EfficiencyStatementsTableDataModel efficiencyStatementsListModel;
 	private VelocityContainer efficiencyStatementsVC;
 	private boolean needReloadModel;
 	private Identity cOwner;
 	private Link showAllLink;
-	private OLog log = Tracing.createLoggerFor(EfficiencyStatementsPortletRunController.class);
 
 	/**
 	 * Constructor
@@ -135,8 +128,8 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
 	 * @return the PortletEntry list.
 	 */
 	private List<PortletEntry> getAllPortletEntries() {
-		List efficiencyStatementsList = EfficiencyStatementManager.getInstance().findEfficiencyStatements(identity);	
-		List<PortletEntry> portletEntryList = this.convertEfficiencyStatementToPortletEntryList(efficiencyStatementsList);
+		List<UserEfficiencyStatementLight> efficiencyStatementsList = EfficiencyStatementManager.getInstance().findEfficiencyStatementsLight(identity);	
+		List<PortletEntry> portletEntryList = convertEfficiencyStatementToPortletEntryList(efficiencyStatementsList);
 		return portletEntryList;
 	}
 	
@@ -145,11 +138,10 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
    * @param items
    * @return
    */
-  private List<PortletEntry> convertEfficiencyStatementToPortletEntryList(List<EfficiencyStatement> items) {
+  private List<PortletEntry> convertEfficiencyStatementToPortletEntryList(List<UserEfficiencyStatementLight> items) {
 		List<PortletEntry> convertedList = new ArrayList<PortletEntry>();
-		Iterator<EfficiencyStatement> listIterator = items.iterator();
-		while(listIterator.hasNext()) {
-			convertedList.add(new EfficiencyStatementPortletEntry(listIterator.next()));
+		for(UserEfficiencyStatementLight item:items) {
+			convertedList.add(new EfficiencyStatementPortletEntry(item));
 		}
 		return convertedList;
 	}
@@ -161,7 +153,7 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
   protected void reloadModel(SortingCriteria sortingCriteria) {
   	if (sortingCriteria.getSortingType() == SortingCriteria.AUTO_SORTING) {
   		EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
-  		List efficiencyStatementsList = esm.findEfficiencyStatements(identity);
+  		List<UserEfficiencyStatementLight> efficiencyStatementsList = esm.findEfficiencyStatementsLight(identity);
 
   		efficiencyStatementsList = getSortedList(efficiencyStatementsList, sortingCriteria);  		
   		List<PortletEntry> entries = convertEfficiencyStatementToPortletEntryList(efficiencyStatementsList);
@@ -209,11 +201,11 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
 				String actionid = te.getActionId();
 				if (actionid.equals(CMD_LAUNCH)) {
 					int rowid = te.getRowId();
-					final EfficiencyStatement efficiencyStatement = efficiencyStatementsListModel.getEfficiencyStatementAt(rowid);
+					final UserEfficiencyStatementLight efficiencyStatement = efficiencyStatementsListModel.getEfficiencyStatementAt(rowid);
 					// will not be disposed on course run dispose, popus up as new browserwindow
 					ControllerCreator ctrlCreator = new ControllerCreator() {
 						public Controller createController(UserRequest lureq, WindowControl lwControl) {
-							return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoEntryKey());
+							return new EfficiencyStatementController(lwControl, lureq, efficiencyStatement.getCourseRepoKey());
 						}					
 					};
 					//wrap the content controller into a full header layout
@@ -261,7 +253,7 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
 		if(portletToolsController==null) {			
 			
 			List<PortletEntry> portletEntryList = getAllPortletEntries();		
-			PortletDefaultTableDataModel tableDataModel = new EfficiencyStatementsManualSortingTableDataModel(portletEntryList, 2, ureq.getLocale());
+			PortletDefaultTableDataModel tableDataModel = new EfficiencyStatementsManualSortingTableDataModel(portletEntryList, 2);
 			List<PortletEntry> sortedItems = getPersistentManuallySortedItems(); 
 			
 			portletToolsController = new PortletToolSortingControllerImpl(ureq, wControl, getTranslator(), sortingCriteria, tableDataModel, sortedItems);
@@ -289,16 +281,14 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
 	 * @param sortingCriteria
 	 * @return a Comparator for the input sortingCriteria
 	 */
-  protected Comparator getComparator(final SortingCriteria sortingCriteria) {
-		return new Comparator(){			
-			public int compare(final Object o1, final Object o2) {
-				EfficiencyStatement statement1 = (EfficiencyStatement)o1;
-				EfficiencyStatement statement2 = (EfficiencyStatement)o2;		
+  protected Comparator<UserEfficiencyStatementLight> getComparator(final SortingCriteria sortingCriteria) {
+		return new Comparator<UserEfficiencyStatementLight>(){			
+			public int compare(final UserEfficiencyStatementLight s1, final UserEfficiencyStatementLight s2) {	
 				int comparisonResult = 0;
 			  if(sortingCriteria.getSortingTerm()==SortingCriteria.ALPHABETICAL_SORTING) {			  	
-			  	comparisonResult = collator.compare(statement1.getCourseTitle(), statement2.getCourseTitle());			  		  	
+			  	comparisonResult = collator.compare(s1.getShortTitle(), s2.getShortTitle());			  		  	
 			  } else if(sortingCriteria.getSortingTerm()==SortingCriteria.DATE_SORTING) {
-			  	comparisonResult = Long.valueOf(statement1.getLastUpdated()).compareTo(Long.valueOf(statement2.getLastUpdated()));
+			  	comparisonResult = s1.getLastModified().compareTo(s2.getLastModified());
 			  } 
 			  if(!sortingCriteria.isAscending()) {
 			  	//if not isAscending return (-comparisonResult)			  	
@@ -323,23 +313,22 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
   	}
   	  	
   	public Object getValueAt(int row, int col) {
-  		EfficiencyStatement efficiencyStatement = (EfficiencyStatement)this.getObject(row).getValue();
-  		List nodeData = efficiencyStatement.getAssessmentNodes();
-  		Map rootNode = (Map) nodeData.get(0);
+  		UserEfficiencyStatementLight efficiencyStatement = getEfficiencyStatementAt(row);
   		switch (col) {
   			case 0:
-  				return StringEscapeUtils.escapeHtml(efficiencyStatement.getCourseTitle());
+  				return StringEscapeUtils.escapeHtml(efficiencyStatement.getShortTitle());
   			case 1:
-  				return rootNode.get(AssessmentHelper.KEY_SCORE);
+  				Float score = efficiencyStatement.getScore();
+  				return AssessmentHelper.getRoundedScore(score);
   			case 2:
-  				return rootNode.get(AssessmentHelper.KEY_PASSED);
+  				return efficiencyStatement.getPassed();
   			default:
   				return "ERROR";
   		}
   	}
   	
-  	public EfficiencyStatement getEfficiencyStatementAt(int row) {
-  		return (EfficiencyStatement)this.getObject(row).getValue();
+  	public UserEfficiencyStatementLight getEfficiencyStatementAt(int row) {
+  		return (UserEfficiencyStatementLight)getObject(row).getValue();
   	}
   }
   
@@ -352,14 +341,12 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
    * @author Lavinia Dumitrescu
    */
   private class EfficiencyStatementsManualSortingTableDataModel extends PortletDefaultTableDataModel  {		
-		private Locale locale;
 		/**
 		 * @param objects
 		 * @param locale
 		 */
-		public EfficiencyStatementsManualSortingTableDataModel(List<PortletEntry> objects, int numCols, Locale locale) {
+		public EfficiencyStatementsManualSortingTableDataModel(List<PortletEntry> objects, int numCols) {
 			super(objects, numCols);
-			this.locale = locale;
 		}
 
 		/**
@@ -367,14 +354,12 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
 		 */
 		public final Object getValueAt(int row, int col) {			
 			PortletEntry entry = getObject(row);
-			EfficiencyStatement statement = (EfficiencyStatement)entry.getValue();
+			UserEfficiencyStatementLight statement = (UserEfficiencyStatementLight)entry.getValue();
 			switch (col) {
 				case 0:					
-					return statement.getCourseTitle();
+					return statement.getShortTitle();
 				case 1:									
-					Date lastUpdate = new Date(statement.getLastUpdated());
-					//return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getTranslator().getLocale()).format(lastUpdate);
-					return lastUpdate;
+					return statement.getLastModified();
 				default:
 					return "error";
 			}
@@ -389,20 +374,20 @@ public class EfficiencyStatementsPortletRunController extends AbstractPortletRun
    * Initial Date:  07.12.2007 <br>
    * @author Lavinia Dumitrescu
    */
-  private class EfficiencyStatementPortletEntry implements PortletEntry {
-  	private EfficiencyStatement value;
+  private class EfficiencyStatementPortletEntry implements PortletEntry<UserEfficiencyStatementLight> {
+  	private UserEfficiencyStatementLight value;
   	private Long key;
   	
-  	public EfficiencyStatementPortletEntry(EfficiencyStatement efficiencyStatement) {
+  	public EfficiencyStatementPortletEntry(UserEfficiencyStatementLight efficiencyStatement) {
   		value = efficiencyStatement;
-  		key = efficiencyStatement.getCourseRepoEntryKey();
+  		key = efficiencyStatement.getCourseRepoKey();
   	}
   	
   	public Long getKey() {
   		return key;
   	}
   	
-  	public EfficiencyStatement getValue() {
+  	public UserEfficiencyStatementLight getValue() {
   		return value;
   	}
   }
diff --git a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
index f7be1e5e1a9..069e7af85c1 100644
--- a/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
+++ b/src/main/java/org/olat/course/assessment/IdentityAssessmentEditController.java
@@ -27,6 +27,7 @@ package org.olat.course.assessment;
 
 import java.util.Date;
 
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
@@ -46,6 +47,7 @@ import org.olat.core.util.resource.OresHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.ICourse;
 import org.olat.course.Structure;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.nodes.AssessableCourseNode;
 import org.olat.course.nodes.CourseNode;
 import org.olat.course.run.userview.UserCourseEnvironment;
@@ -74,6 +76,7 @@ public class IdentityAssessmentEditController extends BasicController {
 	private UserCourseEnvironment assessedUserCourseEnvironment;
 	private Link backLink;
 	private OLATResourceable ores;
+	private final boolean headers;
 
 	/**
 	 * Constructor for the identity assessment overview controller
@@ -84,13 +87,19 @@ public class IdentityAssessmentEditController extends BasicController {
 	 * @param mayEdit true: user may edit the assessment, false: readonly view (user view)
 	 */
 	public IdentityAssessmentEditController(WindowControl wControl, UserRequest ureq, 
-			UserCourseEnvironment assessedUserCourseEnvironment, OLATResourceable ores, boolean mayEdit) {
+			Identity assessedIdentity, ICourse course, boolean mayEdit) {
+		this(wControl, ureq, assessedIdentity, course, mayEdit, false); 
+	}
+		
+	public IdentityAssessmentEditController(WindowControl wControl, UserRequest ureq, 
+			Identity assessedIdentity, ICourse course, boolean mayEdit, boolean headers) {
 		
 		super(ureq, wControl);
 		this.mayEdit = mayEdit;
 		this.main = new Panel("main");
-		this.assessedUserCourseEnvironment = assessedUserCourseEnvironment;
-		this.ores = ores;
+		this.assessedUserCourseEnvironment = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course);
+		this.ores = course;
+		this.headers = headers;
 		doIdentityAssessmentOverview(ureq, true);		
 		putInitialPanel(main);
 		
@@ -140,10 +149,12 @@ public class IdentityAssessmentEditController extends BasicController {
 	private void doIdentityAssessmentOverview(UserRequest ureq, boolean initTable) {
 		if (identityOverviewVC == null) {
 			identityOverviewVC = createVelocityContainer("identityoverview");
-			backLink = LinkFactory.createLinkBack(identityOverviewVC, this);
 			
-			Identity assessedIdentity = assessedUserCourseEnvironment.getIdentityEnvironment().getIdentity();
-			identityOverviewVC.contextPut("user", assessedIdentity.getUser());
+			if(headers) {
+				backLink = LinkFactory.createLinkBack(identityOverviewVC, this);
+				Identity assessedIdentity = assessedUserCourseEnvironment.getIdentityEnvironment().getIdentity();
+				identityOverviewVC.contextPut("user", assessedIdentity.getUser());
+			}
 		}
 		if (initTable) {
 			assessmentOverviewCtr = new IdentityAssessmentOverviewController(ureq, getWindowControl(), 
@@ -158,7 +169,8 @@ public class IdentityAssessmentEditController extends BasicController {
 	private void doEditNodeAssessment(UserRequest ureq, AssessableCourseNode courseNode){
 		if (mayEdit) {
 			ICourse course = CourseFactory.loadCourse(ores);
-			Date initialLaunchDate = AssessmentHelper.getInitialLaunchDate(assessedUserCourseEnvironment);
+			UserCourseInformationsManager userCourseInformationsManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+			Date initialLaunchDate = userCourseInformationsManager.getInitialLaunchDate(ores.getResourceableId(),  assessedUserCourseEnvironment.getIdentityEnvironment().getIdentity());
 			AssessedIdentityWrapper assessedIdentityWrapper = AssessmentHelper.wrapIdentity(assessedUserCourseEnvironment, initialLaunchDate, courseNode);
 			assessmentEditCtr = new AssessmentEditController(ureq, getWindowControl(), course, courseNode, assessedIdentityWrapper);			
 			listenTo(assessmentEditCtr);
diff --git a/src/main/java/org/olat/course/assessment/UserCourseInformations.java b/src/main/java/org/olat/course/assessment/UserCourseInformations.java
new file mode 100644
index 00000000000..0112e6355ca
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/UserCourseInformations.java
@@ -0,0 +1,27 @@
+package org.olat.course.assessment;
+
+import java.util.Date;
+
+import org.olat.core.id.Identity;
+
+/**
+ * Some statistical datas about a user visiting a course
+ * 
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public interface UserCourseInformations {
+	
+	public Long getKey();
+	
+	public Date getInitialLaunch();
+	
+	public Date getRecentLaunch();
+	
+	public int getVisit();
+	
+	public long getTimeSpend();
+	
+	public Identity getIdentity();
+
+}
diff --git a/src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java b/src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java
new file mode 100644
index 00000000000..3bc60791e91
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/UserEfficiencyStatement.java
@@ -0,0 +1,55 @@
+/**
+* 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;
+
+import java.util.Date;
+
+import org.olat.core.id.Identity;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public interface UserEfficiencyStatement {
+
+	public Identity getIdentity();
+	
+	public Date  getLastModified();
+	
+	public Long getCourseRepoKey();
+
+	public String getShortTitle();
+	
+	public Float getScore();
+	
+	public Boolean getPassed();
+	
+	public Integer getTotalNodes();
+
+	public Integer getAttemptedNodes();
+	
+	public Integer getPassedNodes();
+}
diff --git a/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html b/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html
index c3f88f0cdba..3a13a3b8008 100644
--- a/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html
+++ b/src/main/java/org/olat/course/assessment/_content/efficiencystatement.html
@@ -15,33 +15,67 @@ Ext.onReady(function(){
 	#end
 	<h3 class="b_with_small_icon_left o_efficiencystatement_icon">$r.translate("efficiencystatement.title")</h3>
 	
-	<table class="b_table">
-		<tr>
-			<td><label>$r.translate("course")</label></td>
-			<td>$courseTitle</td>
-		</tr>		
-		<tr>
-			<td><label>$r.translate("username")</label></td>
-			<td>$username</td>
-		</tr>
-		
-		#foreach( $propertyHandler in $userPropertyHandlers )		
-		    #if ($propertyHandler.getUserPropertyAsHTML($user, $locale) && $propertyHandler.getUserPropertyAsHTML($user, $locale) != "")		
-			<tr>
-				<td>
-					<label>$r.translate($propertyHandler.i18nFormElementLabelKey())</label>
-				</td>
-				<td>$propertyHandler.getUserPropertyAsHTML($user, $locale)</td>
-			</tr>				
-			#end
-		#end		
-					
-		<tr>
-			<td><label>$r.translate("date")</label></td>
-			<td>$date</td>
-		</tr>
-	</table>
-	
+	<div class="b_clearfix">
+		<div class="b_c50l">
+			<table class="b_table">
+				<tr>
+					<td><label>$r.translate("username")</label></td>
+					<td>$username</td>
+					<td>
+						#if($r.available("home.link"))
+							$r.render("home.link")
+						#end
+						
+						#if($r.available("contact.link"))
+							$r.render("contact.link")
+						#end
+					</td>
+				</tr>
+				#foreach( $propertyHandler in $userPropertyHandlers )		
+				    #if ($propertyHandler.getUserPropertyAsHTML($user, $locale) && $propertyHandler.getUserPropertyAsHTML($user, $locale) != "")		
+					<tr>
+						<td>
+							<label>$r.translate($propertyHandler.i18nFormElementLabelKey())</label>
+						</td>
+						<td>
+							#if($propertyHandler.name == "email")
+								$propertyHandler.getUserProperty($user, $locale)
+							#else
+								$propertyHandler.getUserPropertyAsHTML($user, $locale)
+							#end
+						</td>
+					</tr>				
+					#end
+				#end
+			</table>
+		</div>
+		<div class="b_c50r">
+			<table class="b_table">
+				#if($groupName)
+					<tr>
+						<td><label>$r.translate("group")</label></td>
+						<td>$groupName</td>
+						#if($r.available("group.link"))
+							<td>$r.render("group.link")</td>
+						#end
+					</tr>
+				#end
+				#if($courseTitle)
+					<tr>
+						<td><label>$r.translate("course")</label></td>
+						<td>$courseTitle</td>
+						#if($r.available("course.link"))
+							<td>$r.render("course.link")</td>
+						#end
+					</tr>
+					<tr>
+						<td><label>$r.translate("date")</label></td>
+						<td>$date</td>
+					</tr>
+				#end
+			</table>
+		</div>
+	</div>
 	<p>
 		$r.render("assessmentOverviewTable")
 	</p>
diff --git a/src/main/java/org/olat/course/assessment/_content/identityoverview.html b/src/main/java/org/olat/course/assessment/_content/identityoverview.html
index 22ec4f3946f..558c8da7b41 100644
--- a/src/main/java/org/olat/course/assessment/_content/identityoverview.html
+++ b/src/main/java/org/olat/course/assessment/_content/identityoverview.html
@@ -1,23 +1,27 @@
-<h5>$r.translate("identityoverview.title")</h5>
-$r.render("backLink")
-<br><br>
-<span class="b_with_small_icon_left b_user_icon">
-	$user.getProperty("firstName", $locale) $user.getProperty("lastName", $locale)
-</span>
-<br />
-<a href="mailto:$user.getProperty("email", $locale)" class="b_link_mailto">$user.getProperty("email", $locale)</a>
-#if ($user.getProperty("institutionalEmail", $locale) && $user.getProperty("institutionalEmail", $locale) != "" && $user.getProperty("email", $locale) != $user.getProperty("institutionalEmail", $locale))
-	<br />
-	<a href="mailto:$user.getProperty("institutionalEmail", $locale)" class="b_link_mailto">$user.getProperty("institutionalEmail", $locale)</a>
+#if($r.available("backLink"))
+	<h5>$r.translate("identityoverview.title")</h5>
+	$r.render("backLink")
+	<br><br>
 #end
-#if ($user.getProperty("institutionalName", $locale))
-	<br />
-	<span class="b_with_small_icon_left b_institution_icon">
-		$user.getProperty("institutionalName", $locale)
+
+#if($user)
+	<span class="b_with_small_icon_left b_user_icon">
+		$user.getProperty("firstName", $locale) $user.getProperty("lastName", $locale)
 	</span>
+	<br />
+	<a href="mailto:$user.getProperty("email", $locale)" class="b_link_mailto">$user.getProperty("email", $locale)</a>
+	#if ($user.getProperty("institutionalEmail", $locale) && $user.getProperty("institutionalEmail", $locale) != "" && $user.getProperty("email", $locale) != $user.getProperty("institutionalEmail", $locale))
+		<br />
+		<a href="mailto:$user.getProperty("institutionalEmail", $locale)" class="b_link_mailto">$user.getProperty("institutionalEmail", $locale)</a>
+	#end
+	#if ($user.getProperty("institutionalName", $locale))
+		<br />
+		<span class="b_with_small_icon_left b_institution_icon">
+			$user.getProperty("institutionalName", $locale)
+		</span>
+	#end
+	#if ($user.getProperty("institutionalUserIdentifier", $locale))
+		($user.getProperty("institutionalUserIdentifier", $locale))
+	#end
 #end
-#if ($user.getProperty("institutionalUserIdentifier", $locale))
-	($user.getProperty("institutionalUserIdentifier", $locale))
-#end
-
 $r.render("assessmentOverviewTable")
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties
index 14c06179a80..84be500eed1 100644
--- a/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/assessment/_i18n/LocalStrings_de.properties
@@ -32,11 +32,14 @@ cmd.filterGroups=Von mir betreute Gruppen mit Zugriff auf gew\u00E4hlten Kursbau
 cmd.showAllCourseNodes=Alle bewertbaren Kursbausteine anzeigen 
 cmd.showAllGroups=Alle von mir betreuten Gruppen anzeigen 
 column=Spalte {0}
+contact.link=Kontakt
 command.closeassessment=Schliessen
 command.hidelog=\u00C4nderungsverlauf ausblenden
 command.showlog=\u00C4nderungsverlauf anzeigen
 command.start.bulkwizard=Massenbewertung starten
 course=Kurs\:
+group=Gruppe\:
+group.link=Gruppe
 date=Datum\:
 detailview.title=Detailansicht der Bewertung
 efficiencyStatements.course.noaccess=Dieser Kurs wurde gesperrt, Sie haben keinen Zugang mehr 
@@ -95,6 +98,8 @@ institutionalname=Institution\:
 institutionaluseridentifier=Institutionsnummer (Matrikelnummer)\:
 lastname=Name\:
 log.title=\u00C4nderungsverlauf
+home.link=Visitkarte
+course.link=Kurs
 menu.bulkfocus=Massenbewertung
 menu.bulkfocus.alt=Massenbewertung von Bewertungs- und Aufgabekursbausteinen.
 menu.groupfocus=Nach Gruppen
diff --git a/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml b/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml
index 8f46e6f1fce..4ae1eaae9fc 100644
--- a/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml
+++ b/src/main/java/org/olat/course/assessment/_spring/assessmentContext.xml
@@ -1,9 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+	xmlns:context="http://www.springframework.org/schema/context" 
 	xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
-  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
+  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+  http://www.springframework.org/schema/context 
+  http://www.springframework.org/schema/context/spring-context-3.0.xsd">
+  
+  <context:annotation-config />
+	<context:component-scan base-package="org.olat.course.assessment.manager" />
 
 <bean id="assessmentModule" class="org.olat.course.assessment.AssessmentModule" init-method="init" destroy-method="destroy" >
 	<constructor-arg index="0" ref="courseModule" />
@@ -17,6 +23,8 @@
 
 <bean id="org.olat.course.assessment.EfficiencyStatementManager" class="org.olat.course.assessment.EfficiencyStatementManager">
 	<constructor-arg index="0" ref="userDeletionManager" />
+	<property name="dbInstance" ref="database"/>
+	<property name="repositoryManager" ref="repositoryManager"/>
 </bean>
 
 
diff --git a/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java
new file mode 100644
index 00000000000..ec5b0850629
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManager.java
@@ -0,0 +1,24 @@
+package org.olat.course.assessment.manager;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.olat.core.id.Identity;
+import org.olat.course.assessment.UserCourseInformations;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public interface UserCourseInformationsManager {
+	
+	UserCourseInformations getUserCourseInformations(Long courseResourceId, Identity identity);
+	
+	public UserCourseInformations updateUserCourseInformations(Long courseResId, Identity identity);
+	
+	public Date getInitialLaunchDate(Long courseResourceId, Identity identity);
+	
+	public Map<Long,Date> getInitialLaunchDates(Long courseResourceId, List<Identity> identities);
+
+}
diff --git a/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java
new file mode 100644
index 00000000000..5c3f2359356
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/manager/UserCourseInformationsManagerImpl.java
@@ -0,0 +1,185 @@
+/**
+* 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.manager;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.persistence.DBQuery;
+import org.olat.core.id.Identity;
+import org.olat.core.manager.BasicManager;
+import org.olat.course.assessment.UserCourseInformations;
+import org.olat.course.assessment.model.UserCourseInfosImpl;
+import org.olat.resource.OLATResource;
+import org.olat.resource.OLATResourceManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * Manager for infos as initial launch date...
+ * 
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+@Service
+public class UserCourseInformationsManagerImpl extends BasicManager implements UserCourseInformationsManager {
+
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private OLATResourceManager resourceManager;
+
+	private UserCourseInfosImpl createUserCourseInformations(Identity identity, OLATResource courseResource) {
+		UserCourseInfosImpl infos = new UserCourseInfosImpl();
+		infos.setIdentity(identity);
+		infos.setInitialLaunch(new Date());
+		infos.setLastModified(new Date());
+		infos.setRecentLaunch(new Date());
+		infos.setVisit(1);
+		infos.setResource(courseResource);
+		dbInstance.saveObject(infos);
+		return infos;
+	}
+
+	@Override
+	public UserCourseInfosImpl getUserCourseInformations(Long courseResourceId, Identity identity) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select infos from ").append(UserCourseInfosImpl.class.getName()).append(" as infos ")
+				.append(" inner join infos.resource as resource")
+			  .append(" where infos.identity.key=:identityKey and resource.resId=:resId and resource.resName='CourseModule'");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			query.setLong("identityKey", identity.getKey());
+			query.setLong("resId", courseResourceId);
+			@SuppressWarnings("unchecked")
+			List<UserCourseInfosImpl> infoList = query.list();
+			if(infoList.isEmpty()) {
+				return null;
+			}
+			return infoList.get(0);
+		} catch (Exception e) {
+			logError("Cannot retrieve course informations for: " + identity + " from " + identity, e);
+			return null;
+		}
+	}
+	
+	/**
+	 * Update (or create if not exists) the course informations for a user
+	 * @param userCourseEnv
+	 * @return
+	 */
+	@Override
+	public UserCourseInformations updateUserCourseInformations(Long courseResourceableId, Identity identity) {
+		try {
+			UserCourseInfosImpl infos = getUserCourseInformations(courseResourceableId, identity);
+			if(infos == null) {
+				OLATResource courseResource = resourceManager.findResourceable(courseResourceableId, "CourseModule");
+				infos = createUserCourseInformations(identity, courseResource);
+			} else {
+				infos.setVisit(infos.getVisit() + 1);
+				infos.setRecentLaunch(new Date());
+				infos.setLastModified(new Date());
+				dbInstance.updateObject(infos);
+			}
+			return infos;
+		} catch (Exception e) {
+			logError("Cannot update course informations for: " + identity + " from " + identity, e);
+			return null;
+		}
+	}
+	
+
+	
+	@Override
+	public Date getInitialLaunchDate(Long courseResourceId, Identity identity) {
+		return getInitialLaunchDate(courseResourceId, identity.getKey());
+	}
+	
+	public Date getInitialLaunchDate(Long courseResourceId, Long identityKey) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select infos.initialLaunch from ").append(UserCourseInfosImpl.class.getName()).append(" as infos ")
+			  .append(" inner join infos.resource as resource")
+			  .append(" where infos.identity.key=:identityKey and resource.resId=:resId and resource.resName='CourseModule'");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+
+			query.setLong("identityKey", identityKey);
+			query.setLong("resId", courseResourceId);
+			@SuppressWarnings("unchecked")
+			List<Date> infoList = query.list();
+			if(infoList.isEmpty()) {
+				return null;
+			}
+			return infoList.get(0);
+		} catch (Exception e) {
+			logError("Cannot retrieve course informations for: " + courseResourceId, e);
+			return null;
+		}
+	}
+
+	/**
+	 * Return a map of identity keys to initial launch date.
+	 * @param courseEnv
+	 * @param identities
+	 * @return
+	 */
+	@Override
+	public Map<Long,Date> getInitialLaunchDates(Long courseResourceId, List<Identity> identities) {
+		try {
+			StringBuilder sb = new StringBuilder();
+			sb.append("select infos.identity.key, infos.initialLaunch from ").append(UserCourseInfosImpl.class.getName()).append(" as infos ")
+			  .append(" inner join infos.resource as resource")
+			  .append(" where infos.identity.key in (:identityKeys) and resource.resId=:resId and resource.resName='CourseModule'");
+
+			DBQuery query = dbInstance.createQuery(sb.toString());
+			List<Long> identityKeys = new ArrayList<Long>();
+			for(Identity identity:identities) {
+				identityKeys.add(identity.getKey());
+			}
+			query.setParameterList("identityKeys", identityKeys);
+			query.setLong("resourceKey", courseResourceId);
+			@SuppressWarnings("unchecked")
+			List<Object[]> infoList = query.list();
+			Map<Long,Date> dateMap = new HashMap<Long,Date>();
+			for(Object[] infos:infoList) {
+				Long identityKey = (Long)infos[0];
+				Date initialLaunch = (Date)infos[1];
+				dateMap.put(identityKey, initialLaunch);
+			}
+			return dateMap;
+		} catch (Exception e) {
+			logError("Cannot retrieve course informations for: " + courseResourceId, e);
+			return null;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml
new file mode 100644
index 00000000000..c8374f7e5a0
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.hbm.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+	"-//Hibernate/Hibernate Mapping DTD//EN"
+	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+  
+  <class name="org.olat.course.assessment.model.UserCourseInfosImpl" table="o_as_user_course_infos">	
+	<cache usage="read-write" />
+
+	<id name="key" type="long" column="id"	unsaved-value="null">
+		<generator class="hilo"/>
+	</id>
+
+	<version name="version" access="field" column="version" type="int"/>
+	<property name="creationDate" column="creationdate" type="timestamp" />
+	<property name="lastModified" column="lastmodified" type="timestamp" />
+	
+	
+	<property name="initialLaunch" column="initiallaunchdate" type="timestamp" />
+	<property name="recentLaunch" column="recentlaunchdate" type="timestamp" />
+
+	<property name="visit" column="visit" type="int" />
+	<property name="timeSpend" column="timespend" type="long" />
+	
+	<many-to-one name="resource"
+                 column="fk_resource_id"
+                 foreign-key="none"
+                 class="org.olat.resource.OLATResourceImpl"
+                 outer-join="true"
+                 unique="false"
+                 not-found="ignore"
+                 cascade="none"/>
+                 
+	<many-to-one name="identity"
+                 column="fk_identity"
+                 foreign-key="cx_eff_statement_to_identity"
+                 class="org.olat.basesecurity.IdentityImpl"
+                 outer-join="true"
+                 unique="false"
+                 cascade="none"/>
+	
+  </class>
+</hibernate-mapping>
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java
new file mode 100644
index 00000000000..924eb7065bf
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/model/UserCourseInfosImpl.java
@@ -0,0 +1,103 @@
+package org.olat.course.assessment.model;
+
+import java.util.Date;
+
+import org.olat.core.commons.persistence.PersistentObject;
+import org.olat.core.id.Identity;
+import org.olat.core.id.ModifiedInfo;
+import org.olat.course.assessment.UserCourseInformations;
+import org.olat.resource.OLATResource;
+
+public class UserCourseInfosImpl extends PersistentObject implements UserCourseInformations, ModifiedInfo {
+
+	private static final long serialVersionUID = -6933599547069673655L;
+	
+	private Date lastModified;
+	private Date initialLaunch;
+	private Date recentLaunch;
+	private int visit;
+	private long timeSpend;
+	
+	private Identity identity;
+	private OLATResource resource;
+
+	@Override
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	@Override
+	public void setLastModified(Date lastModified) {
+		this.lastModified = lastModified;
+	}
+
+	@Override
+	public Date getInitialLaunch() {
+		return initialLaunch;
+	}
+
+	public void setInitialLaunch(Date initialLaunch) {
+		this.initialLaunch = initialLaunch;
+	}
+
+	@Override
+	public Date getRecentLaunch() {
+		return recentLaunch;
+	}
+
+	public void setRecentLaunch(Date recentLaunch) {
+		this.recentLaunch = recentLaunch;
+	}
+
+	@Override
+	public int getVisit() {
+		return visit;
+	}
+
+	public void setVisit(int visit) {
+		this.visit = visit;
+	}
+
+	@Override
+	public long getTimeSpend() {
+		return timeSpend;
+	}
+
+	public void setTimeSpend(long timeSpend) {
+		this.timeSpend = timeSpend;
+	}
+
+	@Override
+	public Identity getIdentity() {
+		return identity;
+	}
+
+	public void setIdentity(Identity identity) {
+		this.identity = identity;
+	}
+	
+	public OLATResource getResource() {
+		return resource;
+	}
+
+	public void setResource(OLATResource resource) {
+		this.resource = resource;
+	}
+
+	@Override
+	public int hashCode() {
+		return getKey() == null ? 9271 : getKey().hashCode();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof UserCourseInfosImpl) {
+			UserCourseInfosImpl prop = (UserCourseInfosImpl)obj;
+			return getKey() != null && getKey().equals(prop.getKey());	
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml
new file mode 100644
index 00000000000..d3f10f24324
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.hbm.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+	"-//Hibernate/Hibernate Mapping DTD//EN"
+	"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+<hibernate-mapping default-lazy="false">
+  
+  <class name="org.olat.course.assessment.model.UserEfficiencyStatementImpl" table="o_as_eff_statement">	
+	<cache usage="read-write" />
+
+	<id name="key" type="long" column="id"	unsaved-value="null">
+		<generator class="hilo"/>
+	</id>
+
+	<version name="version" access="field" column="version" type="int"/>
+	<property name="creationDate" column="creationdate" type="timestamp" />
+	<property name="lastModified" column="lastmodified" type="timestamp" />
+
+	<property name="score" column="score" type="float" />
+	<property name="passed" column="passed" type="boolean" />
+	<property name="totalNodes" column="total_nodes" type="int" />
+	<property name="attemptedNodes" column="attempted_nodes" type="int" />
+	<property name="passedNodes" column="passed_nodes" type="int" />
+
+	<property name="title" column="course_title" length="255"  type="string" />
+	<property name="shortTitle" column="course_short_title" length="128"  type="string" />
+	<property name="courseRepoKey" column="course_repo_key" type="long" />
+	<property name="statementXml" column="statement_xml" type="string" length="16777210" />
+	
+	<many-to-one name="resource"
+                 column="fk_resource_id"
+                 foreign-key="none"
+                 class="org.olat.resource.OLATResourceImpl"
+                 outer-join="true"
+                 unique="false"
+                 not-found="ignore"
+                 cascade="none"/>
+                 
+	<many-to-one name="identity"
+                 column="fk_identity"
+                 foreign-key="cx_eff_statement_to_identity"
+                 class="org.olat.basesecurity.IdentityImpl"
+                 outer-join="true"
+                 unique="false"
+                 cascade="none"/>
+  </class>
+  
+  <class name="org.olat.course.assessment.model.UserEfficiencyStatementLight" table="o_as_eff_statement" mutable="false">
+	<id name="key" type="long" column="id"	unsaved-value="null">
+		<generator class="hilo"/>
+	</id>
+
+	<version name="version" access="field" column="version" type="int"/>
+	<property name="creationDate" column="creationdate" type="timestamp" />
+	<property name="lastModified" column="lastmodified" type="timestamp" />
+
+	<property name="score" column="score" type="float" />
+	<property name="passed" column="passed" type="boolean" />
+	<property name="totalNodes" column="total_nodes" type="int" />
+	<property name="attemptedNodes" column="attempted_nodes" type="int" />
+	<property name="passedNodes" column="passed_nodes" type="int" />
+	
+	<property name="shortTitle" column="course_short_title" length="128"  type="string" />
+	<property name="courseRepoKey" column="course_repo_key" type="long" />
+	
+	<many-to-one name="resource"
+                 column="fk_resource_id"
+                 foreign-key="none"
+                 class="org.olat.resource.OLATResourceImpl"
+                 outer-join="true"
+                 unique="false"
+                 not-found="ignore"
+                 cascade="none"/>
+                 
+	<many-to-one name="identity"
+                 column="fk_identity"
+                 foreign-key="cx_eff_statement_to_identity"
+                 class="org.olat.basesecurity.IdentityImpl"
+                 outer-join="true"
+                 unique="false"
+                 cascade="none"/>
+  </class>
+  
+</hibernate-mapping>
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java
new file mode 100644
index 00000000000..ff13944b932
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementImpl.java
@@ -0,0 +1,172 @@
+/**
+ * <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.model;
+
+import java.util.Date;
+
+import org.olat.core.commons.persistence.PersistentObject;
+import org.olat.core.id.Identity;
+import org.olat.core.id.ModifiedInfo;
+import org.olat.course.assessment.UserEfficiencyStatement;
+import org.olat.resource.OLATResource;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public class UserEfficiencyStatementImpl extends PersistentObject implements UserEfficiencyStatement, ModifiedInfo {
+
+	private static final long serialVersionUID = 2996458434418813284L;
+	
+	private Float score;
+	private Boolean passed;
+	private Integer totalNodes;
+	private Integer attemptedNodes;
+	private Integer passedNodes;
+	
+	private Identity identity;
+	private OLATResource resource;
+	
+	private String title;
+	private String shortTitle;
+	private Long courseRepoKey;
+	
+	private String statementXml;
+	
+	private Date lastModified;
+		
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	public void setLastModified(Date lastModified) {
+		this.lastModified = lastModified;
+	}
+
+	public Float getScore() {
+		return score;
+	}
+		
+	public void setScore(Float score) {
+		this.score = score;
+	}
+		
+	public Boolean getPassed() {
+		return passed;
+	}
+		
+	public void setPassed(Boolean passed) {
+		this.passed = passed;
+	}
+	
+	public Integer getTotalNodes() {
+		return totalNodes;
+	}
+
+	public void setTotalNodes(Integer totalNodes) {
+		this.totalNodes = totalNodes;
+	}
+
+	public Integer getAttemptedNodes() {
+		return attemptedNodes;
+	}
+
+	public void setAttemptedNodes(Integer attemptedNodes) {
+		this.attemptedNodes = attemptedNodes;
+	}
+
+	public Integer getPassedNodes() {
+		return passedNodes;
+	}
+
+	public void setPassedNodes(Integer passedNodes) {
+		this.passedNodes = passedNodes;
+	}
+
+	public String getStatementXml() {
+		return statementXml;
+	}
+
+	public void setStatementXml(String statementXml) {
+		this.statementXml = statementXml;
+	}
+
+	public Identity getIdentity() {
+		return identity;
+	}
+	
+	public void setIdentity(Identity identity) {
+		this.identity = identity;
+	}
+		
+	public OLATResource getResource() {
+		return resource;
+	}
+		
+	public void setResource(OLATResource resource) {
+		this.resource = resource;
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+
+	public void setTitle(String title) {
+		this.title = title;
+	}
+
+	public String getShortTitle() {
+		return shortTitle;
+	}
+
+	public void setShortTitle(String shortTitle) {
+		this.shortTitle = shortTitle;
+	}
+
+	public Long getCourseRepoKey() {
+		return courseRepoKey;
+	}
+
+	public void setCourseRepoKey(Long courseRepoKey) {
+		this.courseRepoKey = courseRepoKey;
+	}
+
+	@Override
+	public String toString() {
+		return super.toString();
+	}
+	
+	@Override
+	public int hashCode() {
+		return getKey() == null ? -82654 : getKey().hashCode();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof UserEfficiencyStatementImpl) {
+			UserEfficiencyStatementImpl statement = (UserEfficiencyStatementImpl)obj;
+			return getKey() != null && getKey().equals(statement.getKey());
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java
new file mode 100644
index 00000000000..f3b8f8d027b
--- /dev/null
+++ b/src/main/java/org/olat/course/assessment/model/UserEfficiencyStatementLight.java
@@ -0,0 +1,153 @@
+/**
+ * <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.model;
+
+import java.util.Date;
+
+import org.olat.core.commons.persistence.PersistentObject;
+import org.olat.core.id.Identity;
+import org.olat.core.id.ModifiedInfo;
+import org.olat.course.assessment.UserEfficiencyStatement;
+import org.olat.resource.OLATResource;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public class UserEfficiencyStatementLight extends PersistentObject implements UserEfficiencyStatement, ModifiedInfo {
+
+	private static final long serialVersionUID = 2996458434418813284L;
+	
+	private Float score;
+	private Boolean passed;
+	private Integer totalNodes;
+	private Integer attemptedNodes;
+	private Integer passedNodes;
+	
+	private Identity identity;
+	private OLATResource resource;
+
+	private String shortTitle;
+	private Long courseRepoKey;
+	
+	private Date lastModified;
+		
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	public void setLastModified(Date lastModified) {
+		this.lastModified = lastModified;
+	}
+
+	public Float getScore() {
+		return score;
+	}
+		
+	public void setScore(Float score) {
+		this.score = score;
+	}
+		
+	public Boolean getPassed() {
+		return passed;
+	}
+		
+	public void setPassed(Boolean passed) {
+		this.passed = passed;
+	}
+	
+	public Integer getTotalNodes() {
+		return totalNodes;
+	}
+
+	public void setTotalNodes(Integer totalNodes) {
+		this.totalNodes = totalNodes;
+	}
+
+	public Integer getAttemptedNodes() {
+		return attemptedNodes;
+	}
+
+	public void setAttemptedNodes(Integer attemptedNodes) {
+		this.attemptedNodes = attemptedNodes;
+	}
+
+	public Integer getPassedNodes() {
+		return passedNodes;
+	}
+
+	public void setPassedNodes(Integer passedNodes) {
+		this.passedNodes = passedNodes;
+	}
+
+	public Identity getIdentity() {
+		return identity;
+	}
+	
+	public void setIdentity(Identity identity) {
+		this.identity = identity;
+	}
+		
+	public OLATResource getResource() {
+		return resource;
+	}
+		
+	public void setResource(OLATResource resource) {
+		this.resource = resource;
+	}
+
+	public String getShortTitle() {
+		return shortTitle;
+	}
+
+	public void setShortTitle(String shortTitle) {
+		this.shortTitle = shortTitle;
+	}
+
+	public Long getCourseRepoKey() {
+		return courseRepoKey;
+	}
+
+	public void setCourseRepoKey(Long courseRepoKey) {
+		this.courseRepoKey = courseRepoKey;
+	}
+
+	@Override
+	public String toString() {
+		return super.toString();
+	}
+	
+	@Override
+	public int hashCode() {
+		return getKey() == null ? -82654 : getKey().hashCode();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof UserEfficiencyStatementLight) {
+			UserEfficiencyStatementLight statement = (UserEfficiencyStatementLight)obj;
+			return getKey() != null && getKey().equals(statement.getKey());
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java b/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java
index 7f8f01e66e9..b06c8f35972 100644
--- a/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java
+++ b/src/main/java/org/olat/course/condition/interpreter/GetInitialCourseLaunchDateFunction.java
@@ -20,13 +20,11 @@
 
 package org.olat.course.condition.interpreter;
 
-import org.olat.core.id.Identity;
-import org.olat.course.ICourse;
+import org.olat.core.CoreSpringFactory;
+import org.olat.course.assessment.UserCourseInformations;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.editor.CourseEditorEnv;
-import org.olat.course.nodes.CourseNode;
-import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.userview.UserCourseEnvironment;
-import org.olat.properties.Property;
 
 /**
  * 
@@ -61,14 +59,10 @@ public class GetInitialCourseLaunchDateFunction extends AbstractFunction {
 		}
 
 		//the real function evaluation which is used during run time
-		CourseNode node = getUserCourseEnv().getCourseEnvironment().getRunStructure().getRootNode();
-		CoursePropertyManager pm = getUserCourseEnv().getCourseEnvironment().getCoursePropertyManager();
-		Identity identity = getUserCourseEnv().getIdentityEnvironment().getIdentity();
-
-		Property firstTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
-		if (firstTime != null) {
-			String firstTimeMillis = firstTime.getStringValue();
-			return Double.valueOf(firstTimeMillis);
+		UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+		UserCourseInformations infos = mgr.getUserCourseInformations(getUserCourseEnv().getCourseEnvironment().getCourseResourceableId(), getUserCourseEnv().getIdentityEnvironment().getIdentity());
+		if (infos != null) {
+			return Double.valueOf(infos.getInitialLaunch().getTime());
 		} else {
 			// what to do in case of no date available??? -> return date in the future
 			return new Double(Double.POSITIVE_INFINITY);
diff --git a/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java b/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java
index 31e5fc51c62..5f98ae78c85 100644
--- a/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java
+++ b/src/main/java/org/olat/course/condition/interpreter/GetRecentCourseLaunchDateFunction.java
@@ -20,13 +20,11 @@
 
 package org.olat.course.condition.interpreter;
 
-import org.olat.core.id.Identity;
-import org.olat.course.ICourse;
+import org.olat.core.CoreSpringFactory;
+import org.olat.course.assessment.UserCourseInformations;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.editor.CourseEditorEnv;
-import org.olat.course.nodes.CourseNode;
-import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.userview.UserCourseEnvironment;
-import org.olat.properties.Property;
 
 /**
  * 
@@ -59,14 +57,10 @@ public class GetRecentCourseLaunchDateFunction extends AbstractFunction {
 			return defaultValue();
 		}
 
-		CourseNode node = getUserCourseEnv().getCourseEnvironment().getRunStructure().getRootNode();
-		CoursePropertyManager pm = getUserCourseEnv().getCourseEnvironment().getCoursePropertyManager();
-		Identity identity = getUserCourseEnv().getIdentityEnvironment().getIdentity();
-		Property recentTime = pm.findCourseNodeProperty(node, identity, null, ICourse.PROPERTY_RECENT_LAUNCH_DATE);
-
-		if (recentTime != null) {
-			String firstTimeMillis = recentTime.getStringValue();
-			return Double.valueOf(firstTimeMillis);
+		UserCourseInformationsManager mgr = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+		UserCourseInformations infos = mgr.getUserCourseInformations(getUserCourseEnv().getCourseEnvironment().getCourseResourceableId(), getUserCourseEnv().getIdentityEnvironment().getIdentity());
+		if (infos != null) {
+			return Double.valueOf(infos.getRecentLaunch().getTime());
 		} else {
 			// what to do in case of no date available??? -> return date in the future
 			return new Double(Double.POSITIVE_INFINITY);
diff --git a/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java b/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java
index c18bc90bb6c..c428f245482 100644
--- a/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java
+++ b/src/main/java/org/olat/course/condition/interpreter/score/GetPassedWithCourseIdFunction.java
@@ -25,8 +25,8 @@
 
 package org.olat.course.condition.interpreter.score;
 
-import org.olat.course.assessment.EfficiencyStatement;
 import org.olat.course.assessment.EfficiencyStatementManager;
+import org.olat.course.assessment.UserEfficiencyStatement;
 import org.olat.course.condition.interpreter.AbstractFunction;
 import org.olat.course.condition.interpreter.ArgumentParseException;
 import org.olat.course.condition.interpreter.ConditionInterpreter;
@@ -89,9 +89,9 @@ public class GetPassedWithCourseIdFunction extends AbstractFunction {
 		 * the real function evaluation which is used during run time
 		 */
 		EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
-		EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity());
+		UserEfficiencyStatement es = esm.getUserEfficiencyStatementLight(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity());
 		if (es == null) return defaultValue();
-		Boolean passed = esm.getPassed(childId, es);
+		Boolean passed = es.getPassed();
 		if (passed == null) return defaultValue();
 		// finally check existing value
 		return (passed.booleanValue() ? ConditionInterpreter.INT_TRUE : ConditionInterpreter.INT_FALSE);
diff --git a/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java b/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java
index 25a4d52af42..8f42f3af450 100644
--- a/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java
+++ b/src/main/java/org/olat/course/condition/interpreter/score/GetScoreWithCourseIdFunction.java
@@ -25,8 +25,8 @@
 
 package org.olat.course.condition.interpreter.score;
 
-import org.olat.course.assessment.EfficiencyStatement;
 import org.olat.course.assessment.EfficiencyStatementManager;
+import org.olat.course.assessment.UserEfficiencyStatement;
 import org.olat.course.condition.interpreter.AbstractFunction;
 import org.olat.course.condition.interpreter.ArgumentParseException;
 import org.olat.course.editor.CourseEditorEnv;
@@ -88,12 +88,12 @@ public class GetScoreWithCourseIdFunction extends AbstractFunction {
 		 */
 
 		EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
-		EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity());
+		UserEfficiencyStatement es = esm.getUserEfficiencyStatementLight(courseRepoEntryKey, getUserCourseEnv().getIdentityEnvironment().getIdentity());
 		if (es == null) return defaultValue();
-		Double score = esm.getScore(childId, es);
+		Float score = es.getScore();
 		if (score == null) return defaultValue();
 		// finally check existing value
-		return score;
+		return new Double(score.doubleValue());
 		
 	}
 
diff --git a/src/main/java/org/olat/course/run/RunMainController.java b/src/main/java/org/olat/course/run/RunMainController.java
index e83aa118cab..e6fc0f8d458 100644
--- a/src/main/java/org/olat/course/run/RunMainController.java
+++ b/src/main/java/org/olat/course/run/RunMainController.java
@@ -35,6 +35,7 @@ import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.bookmark.AddAndEditBookmarkController;
 import org.olat.bookmark.BookmarkManager;
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory;
 import org.olat.core.commons.persistence.PersistenceHelper;
@@ -93,10 +94,11 @@ import org.olat.course.archiver.IArchiverCallback;
 import org.olat.course.assessment.AssessmentChangedEvent;
 import org.olat.course.assessment.AssessmentUIFactory;
 import org.olat.course.assessment.CoachingGroupAccessAssessmentCallback;
-import org.olat.course.assessment.EfficiencyStatement;
 import org.olat.course.assessment.EfficiencyStatementController;
 import org.olat.course.assessment.EfficiencyStatementManager;
 import org.olat.course.assessment.FullAccessAssessmentCallback;
+import org.olat.course.assessment.UserEfficiencyStatement;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
 import org.olat.course.config.CourseConfig;
 import org.olat.course.config.CourseConfigEvent;
 import org.olat.course.editor.PublishEvent;
@@ -104,7 +106,6 @@ import org.olat.course.groupsandrights.CourseGroupManager;
 import org.olat.course.groupsandrights.CourseRights;
 import org.olat.course.groupsandrights.ui.CourseGroupManagementMainController;
 import org.olat.course.nodes.CourseNode;
-import org.olat.course.properties.CoursePropertyManager;
 import org.olat.course.run.calendar.CourseCalendarController;
 import org.olat.course.run.glossary.CourseGlossaryFactory;
 import org.olat.course.run.glossary.CourseGlossaryToolLinkController;
@@ -122,7 +123,6 @@ import org.olat.instantMessaging.InstantMessagingModule;
 import org.olat.instantMessaging.groupchat.GroupChatManagerController;
 import org.olat.modules.cp.TreeNodeEvent;
 import org.olat.note.NoteController;
-import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryStatus;
 import org.olat.repository.RepositoryManager;
@@ -446,24 +446,8 @@ public class RunMainController extends MainLayoutBasicController implements Gene
 	private void setLaunchDates(final Identity identity) {
 		CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(createOLATResourceableForLocking(identity), new SyncerExecutor(){
 			public void execute() {
-				CourseNode rootNode = course.getRunStructure().getRootNode();
-				//log launch date
-				String nowString = Long.toString(course.getCourseEnvironment().getCurrentTimeMillis());
-				CoursePropertyManager cpm = course.getCourseEnvironment().getCoursePropertyManager();
-				Property initialLaunchProperty = cpm.findCourseNodeProperty(rootNode, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE);
-				if(initialLaunchProperty == null) {
-					initialLaunchProperty = cpm.createCourseNodePropertyInstance(rootNode, identity, null, ICourse.PROPERTY_INITIAL_LAUNCH_DATE, null, null, nowString, null);
-					cpm.saveProperty(initialLaunchProperty);
-				}
-				
-				Property recentLaunchProperty = cpm.findCourseNodeProperty(rootNode, identity, null, ICourse.PROPERTY_RECENT_LAUNCH_DATE);
-				if (recentLaunchProperty == null) {
-					recentLaunchProperty = cpm.createCourseNodePropertyInstance(rootNode, identity, null, ICourse.PROPERTY_RECENT_LAUNCH_DATE, null, null, nowString, null);
-					cpm.saveProperty(recentLaunchProperty);
-				} else {
-					recentLaunchProperty.setStringValue(nowString);
-					cpm.updateProperty(recentLaunchProperty);
-				}
+				UserCourseInformationsManager efficiencyStatementManager = CoreSpringFactory.getImpl(UserCourseInformationsManager.class);
+				efficiencyStatementManager.updateUserCourseInformations(uce.getCourseEnvironment().getCourseResourceableId(), getIdentity());
 			}
 		});
 	}
@@ -1245,7 +1229,7 @@ public class RunMainController extends MainLayoutBasicController implements Gene
 			myTool.addPopUpLink("efficiencystatement", translate("command.efficiencystatement"), "command.efficiencystatement", null,
 					"750", "800", false);
 			EfficiencyStatementManager esm = EfficiencyStatementManager.getInstance();
-			EfficiencyStatement es = esm.getUserEfficiencyStatement(courseRepositoryEntry.getKey(), identity);
+			UserEfficiencyStatement es = esm.getUserEfficiencyStatementLight(courseRepositoryEntry.getKey(), identity);
 			if (es == null) {
 				myTool.setEnabled("command.efficiencystatement", false);
 			}
diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java
new file mode 100644
index 00000000000..12df950aaf4
--- /dev/null
+++ b/src/main/java/org/olat/upgrade/OLATUpgrade_8_1_0.java
@@ -0,0 +1,278 @@
+/**
+ * <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.upgrade;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.persistence.DBQuery;
+import org.olat.core.id.Identity;
+import org.olat.core.util.xml.XStreamHelper;
+import org.olat.course.assessment.EfficiencyStatement;
+import org.olat.course.assessment.EfficiencyStatementManager;
+import org.olat.course.assessment.manager.UserCourseInformationsManager;
+import org.olat.course.assessment.manager.UserCourseInformationsManagerImpl;
+import org.olat.course.assessment.model.UserCourseInfosImpl;
+import org.olat.course.assessment.model.UserEfficiencyStatementImpl;
+import org.olat.properties.Property;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryManager;
+import org.olat.resource.OLATResource;
+import org.olat.resource.OLATResourceManager;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Description:<br>
+ * upgrade code for OLAT 7.1.0 -> OLAT 7.1.1
+ * - fixing invalid structures being built by synchronisation, see OLAT-6316 and OLAT-6306
+ * - merges all yet found data to last valid node 
+ * 
+ * <P>
+ * Initial Date: 24.03.2011 <br>
+ * 
+ * @author Roman Haag, roman.haag@frentix.com, www.frentix.com
+ */
+public class OLATUpgrade_8_1_0 extends OLATUpgrade {
+
+	private static final String TASK_EFFICIENCY_STATEMENT = "Upgrade efficiency statement";
+	private static final String TASK_LAUNCH_DATES = "Upgrade launch dates";
+	private static final int REPO_ENTRIES_BATCH_SIZE = 20;
+	private static final String VERSION = "OLAT_8.1.0";
+	
+	private static final String PROPERTY_INITIAL_LAUNCH_DATE = "initialCourseLaunchDate";
+	private static final String PROPERTY_RECENT_LAUNCH_DATE = "recentCourseLaunchDate";
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private RepositoryManager repositoryManager;
+	@Autowired
+	private EfficiencyStatementManager efficiencyStatementManager;
+	@Autowired
+	private UserCourseInformationsManager userCourseInformationsManager;
+	@Autowired
+	private OLATResourceManager resourceManager;
+	@Autowired
+	private BaseSecurity securityManager;
+
+	public OLATUpgrade_8_1_0() {
+		super();
+	}
+
+	@Override
+	public boolean doPreSystemInitUpgrade(UpgradeManager upgradeManager) {
+		return false;
+	}
+
+	@Override
+	public boolean doPostSystemInitUpgrade(UpgradeManager upgradeManager) {
+		UpgradeHistoryData uhd = upgradeManager.getUpgradesHistory(VERSION);
+		if (uhd == null) {
+			// has never been called, initialize
+			uhd = new UpgradeHistoryData();
+		} else {
+			if (uhd.isInstallationComplete()) {
+				return false;
+			}
+		}
+		
+		upgradeEfficiencyStatements(upgradeManager, uhd);
+		upgradeLaunchDates(upgradeManager, uhd);
+
+		uhd.setInstallationComplete(true);
+		upgradeManager.setUpgradesHistory(uhd, VERSION);
+		log.audit("Finished OLATUpgrade_8_1_0 successfully!");
+		return true;
+	}
+	
+	private void upgradeEfficiencyStatements(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
+		if (!uhd.getBooleanDataValue(TASK_EFFICIENCY_STATEMENT)) {
+
+			int counter = 0;
+			List<Property> properties;
+			do {
+				properties = getEfficiencyStatement(counter);
+				for(Property property:properties) {
+					createStatement(property);
+				}
+				counter += properties.size();
+				log.audit("Processed efficiency statement: " + properties.size());
+			} while(properties.size() == REPO_ENTRIES_BATCH_SIZE);
+
+			uhd.setBooleanDataValue(TASK_EFFICIENCY_STATEMENT, true);
+			upgradeManager.setUpgradesHistory(uhd, VERSION);
+		}
+	}
+	
+	private void createStatement(Property property) {
+		String repoKeyStr = property.getName();
+		Long repoKey = new Long(repoKeyStr);
+		RepositoryEntry re = repositoryManager.lookupRepositoryEntry(repoKey, false);
+		UserEfficiencyStatementImpl impl = efficiencyStatementManager.getUserEfficiencyStatementFull(repoKey, property.getIdentity());
+		if(impl != null) {
+			return;
+		}
+
+		UserEfficiencyStatementImpl statement = new UserEfficiencyStatementImpl();
+		statement.setIdentity(property.getIdentity());
+		statement.setStatementXml(property.getTextValue());
+		if(re != null) {
+			statement.setResource(re.getOlatResource());
+		}
+
+		EfficiencyStatement s = (EfficiencyStatement)XStreamHelper.createXStreamInstance().fromXML(property.getTextValue());
+		efficiencyStatementManager.fillEfficiencyStatement(s, statement);
+		statement.setLastModified(property.getLastModified());
+		
+		dbInstance.saveObject(statement);
+		dbInstance.commitAndCloseSession();
+	}
+	
+	private List<Property> getEfficiencyStatement(int firstResult) {
+		StringBuilder query = new StringBuilder();
+		query.append("select p from ").append(Property.class.getName()).append(" as p ");
+		query.append(" where p.category='efficiencyStatement' order by p.key");
+		
+		DBQuery dbQuery = dbInstance.createQuery(query.toString());
+		dbQuery.setFirstResult(firstResult);
+		dbQuery.setMaxResults(REPO_ENTRIES_BATCH_SIZE);
+		@SuppressWarnings("unchecked")
+		List<Property> props = dbQuery.list();
+		return props;
+	}
+	
+	private void upgradeLaunchDates(UpgradeManager upgradeManager, UpgradeHistoryData uhd) {
+		if (!uhd.getBooleanDataValue(TASK_LAUNCH_DATES)) {
+			int count = 0;
+			Set<SimpleProp> props = getLaunchProperties();
+			if(props == null) {
+				//problems
+				uhd.setBooleanDataValue(TASK_LAUNCH_DATES, false);	
+			} else {
+				for(SimpleProp prop:props) {
+					Date d = ((UserCourseInformationsManagerImpl)userCourseInformationsManager).getInitialLaunchDate(prop.resourceId, prop.identityKey);
+					if(d == null) {
+						createUserCourseInformation(prop);
+					}
+					
+					if(count % 25 == 0) {
+						dbInstance.commitAndCloseSession();
+						log.info("Convert lanch dates property: " + count);
+					}
+				}
+				uhd.setBooleanDataValue(TASK_LAUNCH_DATES, true);
+			}
+			upgradeManager.setUpgradesHistory(uhd, VERSION);
+		}
+	}
+	
+	private void createUserCourseInformation(SimpleProp prop) {
+		Identity identity = securityManager.loadIdentityByKey(prop.identityKey);
+		OLATResource resource = resourceManager.findResourceable(prop.resourceId, "CourseModule");
+
+		UserCourseInfosImpl infos = new UserCourseInfosImpl();
+		infos.setIdentity(identity);
+		infos.setLastModified(new Date());
+		infos.setInitialLaunch(prop.initialLaunch);
+		infos.setRecentLaunch(prop.recentLaunch);
+		infos.setVisit(1);
+		infos.setResource(resource);
+		dbInstance.saveObject(infos);
+	}
+	
+	private Set<SimpleProp> getLaunchProperties() {
+
+		try {
+			StringBuilder query = new StringBuilder();
+			query.append("select p.resourceTypeId, p.identity.key, p.name, p.stringValue from ").append(Property.class.getName()).append(" as p ");
+			query.append(" where p.resourceTypeName='CourseModule' and p.name in ('initialCourseLaunchDate','recentCourseLaunchDate')");
+			
+			DBQuery dbQuery = dbInstance.createQuery(query.toString());
+			@SuppressWarnings("unchecked")
+			List<Object[]> props = dbQuery.list();
+			
+			Calendar cal = Calendar.getInstance();
+			Map<SimpleProp, SimpleProp> simpleProps = new HashMap<SimpleProp, SimpleProp>((2 * props.size()) + 1);
+			for(Object[] prop:props) {
+				SimpleProp simpleProp = new SimpleProp();
+				simpleProp.resourceId = (Long)prop[0];
+				simpleProp.identityKey = (Long)prop[1];
+				if(simpleProps.containsKey(simpleProp)) {
+					simpleProp = simpleProps.get(simpleProp);
+				}
+
+				String name = (String)prop[2];
+				try {
+					long time = Long.valueOf((String)prop[3]);
+					cal.setTimeInMillis(time);
+					if(PROPERTY_INITIAL_LAUNCH_DATE.equals(name)) {
+						simpleProp.initialLaunch = cal.getTime();
+					}else if(PROPERTY_RECENT_LAUNCH_DATE.equals(name)) {
+						simpleProp.recentLaunch = cal.getTime();
+					}
+					simpleProps.put(simpleProp, simpleProp);
+				} catch(Exception e) {
+					log.error("", e);
+				}
+			}
+			return simpleProps.keySet();
+		} catch (Exception e) {
+			log.error("", e);
+			return null;
+		}
+	}
+
+	@Override
+	public String getVersion() {
+		return VERSION;
+	}
+	
+	private class SimpleProp {
+		private Long resourceId;
+		private Long identityKey;
+		private Date initialLaunch;
+		private Date recentLaunch;
+		
+		@Override
+		public int hashCode() {
+			return (resourceId == null ? -1 : resourceId.hashCode())
+					+ (identityKey == null ? -1 : identityKey.hashCode());
+		}
+		
+		@Override
+		public boolean equals(Object obj) {
+			if(this == obj) {
+				return true;
+			}
+			if(obj instanceof SimpleProp) {
+				SimpleProp prop = (SimpleProp)obj;
+				return resourceId != null && resourceId.equals(prop.resourceId)
+						&& identityKey != null && identityKey.equals(prop.identityKey);
+			}
+			return false;
+		}
+	}
+}
diff --git a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
index dd5776dd50a..2384349a336 100644
--- a/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
+++ b/src/main/java/org/olat/upgrade/_spring/databaseUpgradeContext.xml
@@ -35,6 +35,10 @@
 					<constructor-arg index="0" value="OLAT_8.0.2" />
 					<property name="alterDbStatements" value="alter_8_0_x_to_8_0_2.sql" />
 				</bean>
+				<bean id="database_upgrade_8_1_0" class="org.olat.upgrade.DatabaseUpgrade">
+					<constructor-arg index="0" value="OLAT_8.1.0" />
+					<property name="alterDbStatements" value="alter_8_0_x_to_8_1_0.sql" />
+				</bean>
 			</list>
 		</property>
 	</bean>
diff --git a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml
index ddeecd14f9a..baf1d2385f0 100644
--- a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml
+++ b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml
@@ -37,6 +37,7 @@
 					<constructor-arg index="0" ref="portfolioModule" />
 					<property name="portfolioCourseNodeEnabled" value="${course.node.portfolio.enabled}"/>
 				</bean>
+				<bean id="upgrade_8_1_0" class="org.olat.upgrade.OLATUpgrade_8_1_0"/>
 			</list>
 		</property>
 	</bean>
diff --git a/src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql b/src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql
new file mode 100644
index 00000000000..8add8d747ab
--- /dev/null
+++ b/src/main/resources/database/mysql/alter_8_0_x_to_8_1_0.sql
@@ -0,0 +1,78 @@
+-- user view
+create view o_bs_identity_short_v as (
+   select
+      ident.id as id_id,
+      ident.name as id_name,
+      ident.lastlogin as id_lastlogin,
+      ident.status as id_status,
+      us.user_id as us_id,
+      p_firstname.propvalue as first_name,
+      p_lastname.propvalue as last_name,
+      p_email.propvalue as email
+   from o_bs_identity as ident
+   inner join o_user as us on (ident.fk_user_id = us.user_id)
+   left join o_userproperty as p_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName')
+   left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName')
+   left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email')
+);
+
+-- assessment tables
+-- efficiency statments
+create table if not exists o_as_eff_statement (
+   id bigint not null,
+   version mediumint unsigned not null,
+   lastmodified datetime,
+   creationdate datetime,
+   passed bit(0),
+   score bigint,
+   total_nodes bigint,
+   attempted_nodes bigint,
+   passed_nodes bigint,
+   course_title varchar(255),
+   course_short_title varchar(128),
+   course_repo_key bigint,
+   statement_xml longtext,
+   fk_identity bigint,
+   fk_resource_id bigint,
+   primary key (id)
+);
+alter table o_as_eff_statement add index eff_statement_id_cstr (fk_identity), add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key);
+create index eff_statement_resource_idx on o_as_eff_statement (fk_resource_id);
+
+-- user to course informations (was property initial and recent launch dates)
+create table o_as_user_course_infos (
+   id bigint not null,
+   version mediumint unsigned not null,
+   creationdate datetime,
+   lastmodified datetime,
+   initiallaunchdate datetime,
+   recentlaunchdate datetime,
+   visit bigint,
+   timespend bigint,
+   fk_identity bigint,
+   fk_resource_id bigint,
+   primary key (id)
+);
+alter table o_as_user_course_infos add index user_course_infos_id_cstr (fk_identity), add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+alter table o_as_user_course_infos add index user_course_infos_res_cstr (fk_resource_id), add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id);
+
+-- assessment results
+-- help view
+drop view if exists o_gp_contextresource_2_group_v;
+create view o_gp_contextresource_2_group_v as (
+   select
+      cg_bg2resource.groupcontextresource_id as groupcontextresource_id,
+      cg_bgcontext.groupcontext_id as groupcontext_id,
+      cg_bgroup.group_id as group_id,
+      cg_bg2resource.oresource_id as oresource_id,
+      cg_bgcontext.grouptype as grouptype,
+      cg_bgcontext.defaultcontext as defaultcontext,
+      cg_bgroup.groupname as groupname,
+      cg_bgroup.fk_ownergroup as fk_ownergroup,
+      cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup,
+      cg_bgroup.fk_waitinggroup as fk_waitinggroup
+   from o_gp_bgcontextresource_rel as cg_bg2resource
+   inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id)
+   inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk)
+);
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index c65eff85a4d..8cb40cc21bf 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -907,6 +907,77 @@ create table if not exists o_ac_transaction (
 	primary key (transaction_id)
 );
 
+-- assessment tables
+-- efficiency statments
+create table if not exists o_as_eff_statement (
+   id bigint not null,
+   version mediumint unsigned not null,
+   lastmodified datetime,
+   creationdate datetime,
+   passed bit(0),
+   score bigint,
+   total_nodes bigint,
+   attempted_nodes bigint,
+   passed_nodes bigint,
+   course_title varchar(255),
+   course_short_title varchar(128),
+   course_repo_key bigint,
+   statement_xml longtext,
+   fk_identity bigint,
+   fk_resource_id bigint,
+   primary key (id)
+);
+
+-- user to course informations (was property initial and recent launch dates)
+create table o_as_user_course_infos (
+   id bigint not null,
+   version mediumint unsigned not null,
+   creationdate datetime,
+   lastmodified datetime,
+   initiallaunchdate datetime,
+   recentlaunchdate datetime,
+   visit bigint,
+   timespend bigint,
+   fk_identity bigint,
+   fk_resource_id bigint,
+   primary key (id)
+);
+
+-- user view
+create view o_bs_identity_short_v as (
+   select
+      ident.id as id_id,
+      ident.name as id_name,
+      ident.lastlogin as id_lastlogin,
+      ident.status as id_status,
+      us.user_id as us_id,
+      p_firstname.propvalue as first_name,
+      p_lastname.propvalue as last_name,
+      p_email.propvalue as email
+   from o_bs_identity as ident
+   inner join o_user as us on (ident.fk_user_id = us.user_id)
+   left join o_userproperty as p_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName')
+   left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName')
+   left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email')
+);
+
+create view o_gp_contextresource_2_group_v as (
+   select
+      cg_bg2resource.groupcontextresource_id as groupcontextresource_id,
+      cg_bgcontext.groupcontext_id as groupcontext_id,
+      cg_bgroup.group_id as group_id,
+      cg_bg2resource.oresource_id as oresource_id,
+      cg_bgcontext.grouptype as grouptype,
+      cg_bgcontext.defaultcontext as defaultcontext,
+      cg_bgroup.groupname as groupname,
+      cg_bgroup.fk_ownergroup as fk_ownergroup,
+      cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup,
+      cg_bgroup.fk_waitinggroup as fk_waitinggroup
+   from o_gp_bgcontextresource_rel as cg_bg2resource
+   inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id)
+   inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk)
+);
+
 create index  ocl_asset_idx on oc_lock (asset);
 alter table oc_lock add index FK9E30F4B66115906D (identity_fk), add constraint FK9E30F4B66115906D foreign key (identity_fk) references o_bs_identity (id);
 
@@ -1122,6 +1193,14 @@ alter table o_tag add constraint FK6491FCA5A4FA5DC foreign key (fk_author_id) re
 
 alter table o_bs_invitation add constraint FKF26C8375236F27X foreign key (fk_secgroup) references o_bs_secgroup (id);
 
+alter table o_as_eff_statement add index eff_statement_id_cstr (fk_identity), add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key);
+create index eff_statement_resource_idx on o_as_eff_statement (fk_resource_id);
+
+alter table o_as_user_course_infos add index user_course_infos_id_cstr (fk_identity), add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+alter table o_as_user_course_infos add index user_course_infos_res_cstr (fk_resource_id), add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id);
+
+
 insert into hibernate_unique_key values ( 0 );
 
 -- the following are redundant indexes
diff --git a/src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql b/src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql
new file mode 100644
index 00000000000..d12ae4317a0
--- /dev/null
+++ b/src/main/resources/database/postgresql/alter_8_0_x_to_8_1_0.sql
@@ -0,0 +1,74 @@
+-- user view
+create view o_bs_identity_short_v as (
+   select
+      ident.id as id_id,
+      ident.name as id_name,
+      ident.lastlogin as id_lastlogin,
+      ident.status as id_status,
+      us.user_id as us_id,
+      p_firstname.propvalue as first_name,
+      p_lastname.propvalue as last_name,
+      p_email.propvalue as email
+   from o_bs_identity as ident
+   inner join o_user as us on (ident.fk_user_id = us.user_id)
+   left join o_userproperty as P_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName')
+   left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName')
+   left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email')
+);
+
+-- efficiency statments
+create table o_as_eff_statement (
+   id int8 not null,
+   version int4 not null,
+   lastmodified timestamp,
+   creationdate timestamp,
+   passed boolean,
+   score decimal,
+   course_title varchar(255),
+   course_short_title varchar(128),
+   course_repo_key int8,
+   statement_xml text,
+   fk_identity int8,
+   fk_resource_id int8,
+   primary key (id)
+);
+alter table o_as_eff_statement add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key);
+create index eff_statement_ident_key_idx on o_as_eff_statement (fk_resource_id);
+
+-- user to course informations (was property initial and recent launch dates)
+create table o_as_user_course_infos (
+   id bigint not null,
+   version int4 not null,
+   creationdate timestamp,
+   lastmodified timestamp,
+   initiallaunchdate timestamp,
+   recentlaunchdate timestamp,
+   visit int4,
+   timespend int8,
+   fk_identity int8,
+   fk_resource_id int8,
+   primary key (id)
+);
+alter table o_as_user_course_infos add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+alter table o_as_user_course_infos add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id);
+
+-- assessment results
+-- help view
+create view o_gp_contextresource_2_group_v as (
+   select
+      cg_bg2resource.groupcontextresource_id as groupcontextresource_id,
+      cg_bgcontext.groupcontext_id as groupcontext_id,
+      cg_bgroup.group_id as group_id,
+      cg_bg2resource.oresource_id as oresource_id,
+      cg_bgcontext.grouptype as grouptype,
+      cg_bgcontext.defaultcontext as defaultcontext,
+      cg_bgroup.groupname as groupname,
+      cg_bgroup.fk_ownergroup as fk_ownergroup,
+      cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup,
+      cg_bgroup.fk_waitinggroup as fk_waitinggroup
+   from o_gp_bgcontextresource_rel as cg_bg2resource
+   inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id)
+   inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk)
+);
+
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index 9f201646dbe..f7e3ce84440 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -904,6 +904,75 @@ create table o_mark (
   primary key (mark_id)
 );
 
+-- efficiency statments
+create table o_as_eff_statement (
+   id int8 not null,
+   version int4 not null,
+   lastmodified timestamp,
+   creationdate timestamp,
+   passed boolean,
+   score decimal,
+   course_title varchar(255),
+   course_short_title varchar(128),
+   course_repo_key int8,
+   statement_xml text,
+   fk_identity int8,
+   fk_resource_id int8,
+   primary key (id)
+);
+
+-- user to course informations (was property initial and recent launch dates)
+create table o_as_user_course_infos (
+   id bigint not null,
+   version int4 not null,
+   creationdate timestamp,
+   lastmodified timestamp,
+   initiallaunchdate timestamp,
+   recentlaunchdate timestamp,
+   visit int4,
+   timespend int8,
+   fk_identity int8,
+   fk_resource_id int8,
+   primary key (id)
+);
+
+-- user view
+create view o_bs_identity_short_v as (
+   select
+      ident.id as id_id,
+      ident.name as id_name,
+      ident.lastlogin as id_lastlogin,
+      ident.status as id_status,
+      us.user_id as us_id,
+      p_firstname.propvalue as first_name,
+      p_lastname.propvalue as last_name,
+      p_email.propvalue as email
+   from o_bs_identity as ident
+   inner join o_user as us on (ident.fk_user_id = us.user_id)
+   left join o_userproperty as P_firstname on (us.user_id = p_firstname.fk_user_id and p_firstname.propName = 'firstName')
+   left join o_userproperty as p_lastname on (us.user_id = p_lastname.fk_user_id and p_lastname.propName = 'lastName')
+   left join o_userproperty as p_email on (us.user_id = p_email.fk_user_id and p_email.propName = 'email')
+);
+
+-- assessment results
+-- help view
+create view o_gp_contextresource_2_group_v as (
+   select
+      cg_bg2resource.groupcontextresource_id as groupcontextresource_id,
+      cg_bgcontext.groupcontext_id as groupcontext_id,
+      cg_bgroup.group_id as group_id,
+      cg_bg2resource.oresource_id as oresource_id,
+      cg_bgcontext.grouptype as grouptype,
+      cg_bgcontext.defaultcontext as defaultcontext,
+      cg_bgroup.groupname as groupname,
+      cg_bgroup.fk_ownergroup as fk_ownergroup,
+      cg_bgroup.fk_partipiciantgroup as fk_partipiciantgroup,
+      cg_bgroup.fk_waitinggroup as fk_waitinggroup
+   from o_gp_bgcontextresource_rel as cg_bg2resource
+   inner join o_gp_bgcontext as cg_bgcontext on (cg_bg2resource.groupcontext_fk = cg_bgcontext.groupcontext_id)
+   inner join o_gp_business as cg_bgroup on (cg_bg2resource.groupcontext_fk = cg_bgroup.groupcontext_fk)
+);
+
 create index userrating_id_idx on o_userrating (resid);
 create index userrating_name_idx on o_userrating (resname);
 create index userrating_subpath_idx on o_userrating (ressubpath);
@@ -1058,5 +1127,12 @@ alter table o_ac_transaction add constraint trans_ord_ctx foreign key (fk_order_
 alter table o_ac_transaction add constraint trans_ord_part_ctx foreign key (fk_order_part_id) references o_ac_order_part (order_part_id);
 alter table o_ac_transaction add constraint trans_method_ctx foreign key (fk_method_id) references o_ac_method (method_id);
 
+alter table o_as_eff_statement add constraint eff_statement_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+create index eff_statement_repo_key_idx on o_as_eff_statement (course_repo_key);
+create index eff_statement_ident_key_idx on o_as_eff_statement (fk_resource_id);
+
+alter table o_as_user_course_infos add constraint user_course_infos_id_cstr foreign key (fk_identity) references o_bs_identity (id);
+alter table o_as_user_course_infos add constraint user_course_infos_res_cstr foreign key (fk_resource_id) references o_olatresource (resource_id);
+
 
 insert into hibernate_unique_key values ( 0 );
-- 
GitLab