From a279d5c18aeb224325cb438183b01a53b2e3236e Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 23 Jul 2015 17:13:34 +0200
Subject: [PATCH] OO-1593: link qti 2.1 session to assessment entry

---
 .../course/assessment/AssessmentManager.java  |  6 +-
 .../manager/CourseAssessmentManagerImpl.java  | 61 +++++++-------
 .../course/nodes/AssessableCourseNode.java    |  3 +
 .../olat/course/nodes/BasicLTICourseNode.java | 27 +++---
 .../course/nodes/CheckListCourseNode.java     | 29 +++++--
 .../org/olat/course/nodes/GTACourseNode.java  | 26 ++++--
 .../olat/course/nodes/IQTESTCourseNode.java   | 28 +++++--
 .../org/olat/course/nodes/MSCourseNode.java   | 38 +++++----
 .../course/nodes/PortfolioCourseNode.java     | 50 ++++++++---
 .../course/nodes/ProjectBrokerCourseNode.java | 35 ++++----
 .../org/olat/course/nodes/STCourseNode.java   | 13 ++-
 .../olat/course/nodes/ScormCourseNode.java    | 34 ++++++--
 .../org/olat/course/nodes/TACourseNode.java   | 31 ++++---
 .../course/nodes/cp/CPEditController.java     |  2 +-
 .../olat/course/nodes/iq/IQRunController.java | 29 +++----
 .../nodes/ta/ConvertToGTACourseNode.java      |  8 +-
 .../run/preview/PreviewAssessmentManager.java |  7 +-
 .../java/org/olat/ims/qti21/QTI21Service.java |  7 +-
 .../ims/qti21/manager/QTI21ServiceImpl.java   | 11 ++-
 .../olat/ims/qti21/manager/SessionDAO.java    | 40 +++++----
 .../qti21/model/jpa/UserTestSessionImpl.java  | 42 ++++++----
 .../handlers/QTI21AssessmentTestHandler.java  |  4 +-
 .../ui/AssessmentItemDisplayController.java   |  6 +-
 .../ui/AssessmentTestDisplayController.java   | 21 +++--
 .../qti21/ui/InMemoryOutcomesListener.java    | 12 +--
 .../AssessmentItemEditorController.java       |  8 +-
 .../modules/assessment/AssessmentService.java | 32 +++++++
 .../manager/AssessmentEntryDAO.java           | 60 +++++++++++--
 .../manager/AssessmentServiceImpl.java        | 84 +++++++++++++++++++
 .../assessment/model/AssessmentEntryImpl.java | 12 ++-
 .../database/mysql/alter_10_x_0_to_11_0_0.sql | 49 +++++------
 .../database/mysql/setupDatabase.sql          | 10 ++-
 .../postgresql/alter_10_x_0_to_11_0_0.sql     | 54 ++++++------
 .../database/postgresql/setupDatabase.sql     | 14 ++--
 .../ims/qti21/manager/SessionDAOTest.java     | 40 ++++++++-
 35 files changed, 653 insertions(+), 280 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java

diff --git a/src/main/java/org/olat/course/assessment/AssessmentManager.java b/src/main/java/org/olat/course/assessment/AssessmentManager.java
index 3a01ddc8098..5b90736b22a 100644
--- a/src/main/java/org/olat/course/assessment/AssessmentManager.java
+++ b/src/main/java/org/olat/course/assessment/AssessmentManager.java
@@ -210,6 +210,10 @@ public interface AssessmentManager {
 	 *         set to not fully assessed, null if no info is available
 	 */
 	public Boolean getNodeFullyAssessed(CourseNode courseNode, Identity identity);
+	
+
+	public AssessmentEntry getAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, String referenceSoftKey);
 
-	public List<AssessmentEntry> getAssessmentData(CourseNode courseNode);
+	public List<AssessmentEntry> getAssessmentEntries(CourseNode courseNode);
+	
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java
index 2dc48da9ec9..e8d6d50cd47 100644
--- a/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java
+++ b/src/main/java/org/olat/course/assessment/manager/CourseAssessmentManagerImpl.java
@@ -48,7 +48,7 @@ import org.olat.course.nodes.CourseNode;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.modules.assessment.AssessmentEntry;
-import org.olat.modules.assessment.manager.AssessmentEntryDAO;
+import org.olat.modules.assessment.AssessmentService;
 import org.olat.repository.RepositoryEntry;
 import org.olat.util.logging.activity.LoggingResourceable;
 
@@ -64,31 +64,29 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 	private static final Integer INTEGER_ZERO = new Integer(0);
 	
 	private final RepositoryEntry courseEntry;
+	private final AssessmentService assessmentService;
 	private final CertificatesManager certificatesManager;
-	private final AssessmentEntryDAO courseNodeAssessmentDao;
 	private final EfficiencyStatementManager efficiencyStatementManager;
 	
 	public CourseAssessmentManagerImpl(RepositoryEntry courseEntry) {
 		this.courseEntry = courseEntry;
+		assessmentService = CoreSpringFactory.getImpl(AssessmentService.class);
 		certificatesManager = CoreSpringFactory.getImpl(CertificatesManager.class);
-		courseNodeAssessmentDao = CoreSpringFactory.getImpl(AssessmentEntryDAO.class);
 		efficiencyStatementManager = CoreSpringFactory.getImpl(EfficiencyStatementManager.class);
 	}
 	
 	private AssessmentEntry getOrCreate(Identity assessedIdentity, CourseNode courseNode) {
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
-				.loadAssessmentEntry(assessedIdentity, courseEntry, courseNode.getIdent());
-		if(nodeAssessment == null) {
-			nodeAssessment = courseNodeAssessmentDao
-					.createCourseNodeAssessment(assessedIdentity, courseEntry,
-							courseNode.getIdent(), courseNode.getReferencedRepositoryEntry());
-		}
-		return nodeAssessment;
+		return assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, courseNode.getIdent(), courseNode.getReferencedRepositoryEntry());
 	}
 
 	@Override
-	public List<AssessmentEntry> getAssessmentData(CourseNode courseNode) {
-		return courseNodeAssessmentDao.loadAssessmentEntryBySubIdent(courseEntry, courseNode.getIdent());
+	public List<AssessmentEntry> getAssessmentEntries(CourseNode courseNode) {
+		return assessmentService.loadAssessmentEntriesBySubIdent(courseEntry, courseNode.getIdent());
+	}
+
+	@Override
+	public AssessmentEntry getAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, String referenceSoftKey) {
+		return assessmentService.loadAssessmentEntry(assessedIdentity, courseEntry, courseNode.getIdent(), referenceSoftKey);
 	}
 
 	@Override
@@ -97,7 +95,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 		
 		AssessmentEntry nodeAssessment = getOrCreate(assessedIdentity, courseNode);
 		nodeAssessment.setAttempts(attempts);
-		courseNodeAssessmentDao.updateCourseNodeAssessment(nodeAssessment);
+		assessmentService.updateAssessmentEntry(nodeAssessment);
 
 		//node log
 		UserNodeAuditManager am = course.getCourseEnvironment().getAuditManager();
@@ -120,7 +118,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 		
 		AssessmentEntry nodeAssessment = getOrCreate(assessedIdentity, courseNode);
 		nodeAssessment.setComment(comment);
-		courseNodeAssessmentDao.updateCourseNodeAssessment(nodeAssessment);
+		assessmentService.updateAssessmentEntry(nodeAssessment);
 		
 		// node log
 		UserNodeAuditManager am = course.getCourseEnvironment().getAuditManager();
@@ -143,7 +141,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 		
 		AssessmentEntry nodeAssessment = getOrCreate(assessedIdentity, courseNode);
 		nodeAssessment.setCoachComment(comment);
-		courseNodeAssessmentDao.updateCourseNodeAssessment(nodeAssessment);
+		assessmentService.updateAssessmentEntry(nodeAssessment);
 		
 		// notify about changes
 		AssessmentChangedEvent ace = new AssessmentChangedEvent(AssessmentChangedEvent.TYPE_COACH_COMMENT_CHANGED, assessedIdentity);
@@ -163,7 +161,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 		AssessmentEntry nodeAssessment = getOrCreate(assessedIdentity, courseNode);
 		int attempts = nodeAssessment.getAttempts() == null ? 1 :nodeAssessment.getAttempts().intValue() + 1;
 		nodeAssessment.setAttempts(attempts);
-		courseNodeAssessmentDao.updateCourseNodeAssessment(nodeAssessment);
+		assessmentService.updateAssessmentEntry(nodeAssessment);
 		if(courseNode instanceof AssessableCourseNode) {
 			// Update users efficiency statement
 			efficiencyStatementManager.updateUserEfficiencyStatement(userCourseEnv);
@@ -187,7 +185,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 		AssessmentEntry nodeAssessment = getOrCreate(assessedIdentity, courseNode);
 		int attempts = nodeAssessment.getAttempts() == null ? 1 :nodeAssessment.getAttempts().intValue() + 1;
 		nodeAssessment.setAttempts(attempts);
-		courseNodeAssessmentDao.updateCourseNodeAssessment(nodeAssessment);
+		assessmentService.updateAssessmentEntry(nodeAssessment);
 		if(courseNode instanceof AssessableCourseNode) {
 			// Update users efficiency statement
 			efficiencyStatementManager.updateUserEfficiencyStatement(userCourseEnv);
@@ -221,7 +219,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 			int attempts = nodeAssessment.getAttempts() == null ? 1 :nodeAssessment.getAttempts().intValue() + 1;
 			nodeAssessment.setAttempts(attempts);
 		}
-		nodeAssessment = courseNodeAssessmentDao.updateCourseNodeAssessment(nodeAssessment);
+		nodeAssessment = assessmentService.updateAssessmentEntry(nodeAssessment);
 		
 		
 		if(courseNode instanceof AssessableCourseNode) {
@@ -249,26 +247,25 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 			return FLOAT_ZERO; // return default value
 		}
 		
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
-				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
-		if(nodeAssessment != null && nodeAssessment.getScore() != null) {
-			return nodeAssessment.getScore().floatValue();
+		AssessmentEntry entry = assessmentService.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
+		if(entry != null && entry.getScore() != null) {
+			return entry.getScore().floatValue();
 		}
 		return FLOAT_ZERO;
 	}
 
 	@Override
 	public String getNodeComment(CourseNode courseNode, Identity identity) {
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry entry = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
-		return nodeAssessment == null ? null : nodeAssessment.getComment();
+		return entry == null ? null : entry.getComment();
 	}
 
 	@Override
 	public String getNodeCoachComment(CourseNode courseNode, Identity identity) {
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry entry = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
-		return nodeAssessment == null ? null : nodeAssessment.getCoachComment();
+		return entry == null ? null : entry.getCoachComment();
 	}
 
 	@Override
@@ -277,7 +274,7 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 			return Boolean.FALSE; // return default value
 		}
 		
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry nodeAssessment = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
 		return nodeAssessment == null ? null : nodeAssessment.getPassed();
 	}
@@ -286,14 +283,14 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 	public Integer getNodeAttempts(CourseNode courseNode, Identity identity) {
 		if(courseNode == null) return INTEGER_ZERO;
 		
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry nodeAssessment = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
 		return nodeAssessment == null ? INTEGER_ZERO : nodeAssessment.getAttempts();
 	}
 
 	@Override
 	public Long getAssessmentID(CourseNode courseNode, Identity identity) {
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry nodeAssessment = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
 		return nodeAssessment == null ? null : nodeAssessment.getAssessmentId();
 	}
@@ -301,14 +298,14 @@ public class CourseAssessmentManagerImpl implements AssessmentManager {
 	@Override
 	public Date getScoreLastModifiedDate(CourseNode courseNode, Identity identity) {
 		if(courseNode == null) return null;
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry nodeAssessment = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());
 		return nodeAssessment == null ? null : nodeAssessment.getLastModified();
 	}
 
 	@Override
 	public Boolean getNodeFullyAssessed(CourseNode courseNode, Identity identity) {
-		AssessmentEntry nodeAssessment = courseNodeAssessmentDao
+		AssessmentEntry nodeAssessment = assessmentService
 				.loadAssessmentEntry(identity, courseEntry, courseNode.getIdent());	
 		return nodeAssessment == null ? null : nodeAssessment.getFullyAssessed();
 	}
diff --git a/src/main/java/org/olat/course/nodes/AssessableCourseNode.java b/src/main/java/org/olat/course/nodes/AssessableCourseNode.java
index 9a9f8c4d317..be049f12b01 100644
--- a/src/main/java/org/olat/course/nodes/AssessableCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/AssessableCourseNode.java
@@ -36,6 +36,7 @@ import org.olat.core.id.Identity;
 import org.olat.course.run.environment.CourseEnvironment;
 import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
+import org.olat.modules.assessment.AssessmentEntry;
 
 
 /**
@@ -109,6 +110,8 @@ public interface AssessableCourseNode extends CourseNode {
 	 * @return null, if this node cannot deliver any useful scoring info (this is not the case for a test never tried or manual scoring: those have default values 0.0f / false for score/passed; currently only the STNode returns null if there are no scoring rules defined.)
 	 */
 	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv);
+	
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv);
 
 	/**
 	 * @param userCourseEnvironment
diff --git a/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java b/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java
index 127c1fba499..ba15d39c3d9 100644
--- a/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java
+++ b/src/main/java/org/olat/course/nodes/BasicLTICourseNode.java
@@ -51,6 +51,7 @@ import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.ims.lti.ui.LTIResultDetailsController;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
 
@@ -61,6 +62,7 @@ import org.olat.resource.OLATResource;
 public class BasicLTICourseNode extends AbstractAccessableCourseNode implements AssessableCourseNode {
 
 	private static final long serialVersionUID = 2210572148308757127L;
+	private static final String translatorStr = Util.getPackageName(LTIEditController.class);
 	private static final String TYPE = "lti";
 
 	public static final String CONFIG_KEY_AUTHORROLE = "authorRole";
@@ -144,8 +146,7 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements
 		if (!isValid) {
 			// FIXME: refine statusdescriptions
 			String[] params = new String[] { this.getShortTitle() };
-			String translPackage = Util.getPackageName(LTIConfigForm.class);
-			sd = new StatusDescription(StatusDescription.ERROR, NLS_ERROR_HOSTMISSING_SHORT, NLS_ERROR_HOSTMISSING_LONG, params, translPackage);
+			sd = new StatusDescription(StatusDescription.ERROR, NLS_ERROR_HOSTMISSING_SHORT, NLS_ERROR_HOSTMISSING_LONG, params, translatorStr);
 			sd.setDescriptionForUnit(getIdent());
 			// set which pane is affected by error
 			sd.setActivateableViewIdentifier(LTIEditController.PANE_TAB_LTCONFIG);
@@ -161,7 +162,6 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements
 		oneClickStatusCache = null;
 		// only here we know which translator to take for translating condition
 		// error messages
-		String translatorStr = Util.getPackageName(LTIEditController.class);
 		List<StatusDescription> sds =  isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions());
 		oneClickStatusCache = StatusDescriptionHelper.sort(sds);
 		return oneClickStatusCache;
@@ -289,6 +289,12 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements
 		Boolean score = config.getBooleanEntry(CONFIG_KEY_HAS_SCORE_FIELD);
 		return (score == null) ? false : score.booleanValue();
 	}
+	
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		return am.getAssessmentEntry(this, userCourseEnv.getIdentityEnvironment().getIdentity(), null);
+	}
 
 	@Override
 	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
@@ -301,24 +307,21 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements
 		if (hasPassedConfigured()) passed = am.getNodePassed(this, mySelf);
 		if (hasScoreConfigured()) score = am.getNodeScore(this, mySelf);
 
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+		return new ScoreEvaluation(score, passed);
 	}
 
 	@Override
 	public String getUserUserComment(UserCourseEnvironment userCourseEnvironment) {
 		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
 		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
-		String userCommentValue = am.getNodeComment(this, mySelf);
-		return userCommentValue;
+		return am.getNodeComment(this, mySelf);
 	}
 
 	@Override
 	public String getUserCoachComment(UserCourseEnvironment userCourseEnvironment) {
 		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
 		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
-		String coachCommentValue = am.getNodeCoachComment(this, mySelf);
-		return coachCommentValue;
+		return am.getNodeCoachComment(this, mySelf);
 	}
 
 	@Override
@@ -326,16 +329,14 @@ public class BasicLTICourseNode extends AbstractAccessableCourseNode implements
 		// having score defined means the node is assessable
  		UserNodeAuditManager am = userCourseEnvironment.getCourseEnvironment().getAuditManager();
 		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
-		String logValue = am.getUserNodeLog(this, mySelf);
-		return logValue;
+		return am.getUserNodeLog(this, mySelf);
 	}
 
 	@Override
 	public Integer getUserAttempts(UserCourseEnvironment userCourseEnvironment) {
 		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
 		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
-		Integer userAttemptsValue = am.getNodeAttempts(this, mySelf);
-		return userAttemptsValue;
+		return am.getNodeAttempts(this, mySelf);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/CheckListCourseNode.java b/src/main/java/org/olat/course/nodes/CheckListCourseNode.java
index a2818e861d3..189f23e1cb2 100644
--- a/src/main/java/org/olat/course/nodes/CheckListCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/CheckListCourseNode.java
@@ -74,6 +74,7 @@ import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.run.userview.UserCourseEnvironmentImpl;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 
 /**
@@ -170,18 +171,28 @@ public class CheckListCourseNode extends AbstractAccessableCourseNode implements
 	 * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment)
 	 */
 	@Override
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
-		// read score from properties
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		Boolean passed = null;
 		Float score = null;
-		// only db lookup if configured, else return null
-		if (hasPassedConfigured()) passed = am.getNodePassed(this, mySelf);
-		if (hasScoreConfigured()) score = am.getNodeScore(this, mySelf);
+		if(hasPassedConfigured() || hasScoreConfigured()) {
+			AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+			if(entry != null) {
+				if (hasPassedConfigured()) {
+					passed = entry.getPassed();
+				}
+				if (hasScoreConfigured() && entry.getScore() != null) {
+					score = entry.getScore().floatValue();
+				}
+			}
+		}
+		return new ScoreEvaluation(score, passed);
+	}
 
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		return am.getAssessmentEntry(this, mySelf, null);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/GTACourseNode.java b/src/main/java/org/olat/course/nodes/GTACourseNode.java
index b656f66768c..b4f8df8f94d 100644
--- a/src/main/java/org/olat/course/nodes/GTACourseNode.java
+++ b/src/main/java/org/olat/course/nodes/GTACourseNode.java
@@ -86,6 +86,7 @@ import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.group.BusinessGroup;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
 import org.olat.user.UserManager;
@@ -788,20 +789,29 @@ public class GTACourseNode extends AbstractAccessableCourseNode implements Asses
 
 	@Override
 	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
-		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
-		Identity assessedIdentity = userCourseEnv.getIdentityEnvironment().getIdentity();
 		Boolean passed = null;
 		Float score = null;
-		// only db lookup if configured, else return null
-		if (hasPassedConfigured()) {
-			passed = am.getNodePassed(this, assessedIdentity);
-		}
-		if (hasScoreConfigured()) {
-			score = am.getNodeScore(this, assessedIdentity);
+		if(hasPassedConfigured() || hasScoreConfigured()) {
+			AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+			if(entry != null) {
+				if (hasPassedConfigured()) {
+					passed = entry.getPassed();
+				}
+				if (hasScoreConfigured() && entry.getScore() != null) {
+					score = entry.getScore().floatValue();
+				}
+			}
 		}
 		return new ScoreEvaluation(score, passed);
 	}
 
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		return am.getAssessmentEntry(this, mySelf, null);
+	}
+
 	@Override
 	public String getUserUserComment(UserCourseEnvironment userCourseEnv) {
 		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
diff --git a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
index a0d0e98dc71..2caa3d0e3f0 100644
--- a/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/IQTESTCourseNode.java
@@ -86,6 +86,7 @@ import org.olat.ims.qti.statistics.QTIType;
 import org.olat.ims.qti.statistics.ui.QTI12PullTestsToolController;
 import org.olat.ims.qti.statistics.ui.QTI12StatisticsToolController;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.iq.IQSecurityCallback;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryImportExport;
@@ -107,6 +108,7 @@ import de.bps.onyx.plugin.run.OnyxRunController;
 public class IQTESTCourseNode extends AbstractAccessableCourseNode implements AssessableCourseNode, QTICourseNode {
 	private static final long serialVersionUID = 5806292895738005387L;
 	private static final OLog log = Tracing.createLoggerFor(IQTESTCourseNode.class);
+	private static final String translatorStr = Util.getPackageName(IQEditController.class);
 	private static final String TYPE = "iqtest";
 
 	private static final int CURRENT_CONFIG_VERSION = 2;
@@ -269,9 +271,8 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 		if (!isValid) {
 			String shortKey = "error.test.undefined.short";
 			String longKey = "error.test.undefined.long";
-			String[] params = new String[] { this.getShortTitle() };
-			String translPackage = Util.getPackageName(IQEditController.class);
-			sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, translPackage);
+			String[] params = new String[] { getShortTitle() };
+			sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, translatorStr);
 			sd.setDescriptionForUnit(getIdent());
 			// set which pane is affected by error
 			sd.setActivateableViewIdentifier(IQEditController.PANE_TAB_IQCONFIG_TEST);
@@ -287,7 +288,6 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 		oneClickStatusCache = null;
 		// only here we know which translator to take for translating condition
 		// error messages
-		String translatorStr = Util.getPackageName(IQEditController.class);
 		List<StatusDescription> sds = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions());
 		oneClickStatusCache = StatusDescriptionHelper.sort(sds);
 		return oneClickStatusCache;
@@ -297,10 +297,10 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 	 * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment)
 	 */
 	@Override
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		// read score from properties save score, passed and attempts information
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
 		Boolean passed = am.getNodePassed(this, mySelf);
 		Float score = am.getNodeScore(this, mySelf);		
 		Long assessmentID = am.getAssessmentID(this, mySelf);	
@@ -309,6 +309,16 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 		return se;
 	}
 
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		if(getRepositoryEntrySoftKey() != null) {
+			return am.getAssessmentEntry(this, mySelf, getRepositoryEntrySoftKey());
+		}
+		return null;
+	}
+
 	/**
 	 * @see org.olat.course.nodes.AssessableCourseNode#getCutValueConfiguration()
 	 */
@@ -464,6 +474,10 @@ public class IQTESTCourseNode extends AbstractAccessableCourseNode implements As
 		RepositoryEntry re = IQEditController.getIQReference(getModuleConfiguration(), false);
 		return re;
 	}
+	
+	private String getRepositoryEntrySoftKey() {
+		return (String)getModuleConfiguration().get(IQEditController.CONFIG_KEY_REPOSITORY_SOFTKEY);
+	}
 
 	/**
 	 * @see org.olat.course.nodes.CourseNode#needsReferenceToARepositoryEntry()
diff --git a/src/main/java/org/olat/course/nodes/MSCourseNode.java b/src/main/java/org/olat/course/nodes/MSCourseNode.java
index e106fde2c3a..529a9f20cb9 100644
--- a/src/main/java/org/olat/course/nodes/MSCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/MSCourseNode.java
@@ -62,6 +62,7 @@ import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
@@ -75,7 +76,6 @@ import org.olat.resource.OLATResource;
 public class MSCourseNode extends AbstractAccessableCourseNode implements AssessableCourseNode {
 	private static final long serialVersionUID = -7741172700015384397L;
 	private static final String PACKAGE_MS = Util.getPackageName(MSCourseNodeRunController.class);
-	private static final String PACKAGE = Util.getPackageName(MSCourseNode.class);
 
 	private static final String TYPE = "ms";
 	/** configuration: score can be set */
@@ -144,7 +144,7 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Assess
 		// Do not allow guests to have manual scoring
 		Roles roles = ureq.getUserSession().getRoles();
 		if (roles.isGuestOnly()) {
-			Translator trans = new PackageTranslator(PACKAGE, ureq.getLocale());
+			Translator trans = Util.createPackageTranslator(MSCourseNode.class, ureq.getLocale());
 			String title = trans.translate("guestnoaccess.title");
 			String message = trans.translate("guestnoaccess.message");
 			controller = MessageUIFactory.createInfoMessage(ureq, wControl, title, message);
@@ -187,8 +187,7 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Assess
 			String shortKey = "error.missingconfig.short";
 			String longKey = "error.missingconfig.long";
 			String[] params = new String[] { this.getShortTitle() };
-			String translPackage = Util.getPackageName(MSEditFormController.class);
-			sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, translPackage);
+			sd = new StatusDescription(StatusDescription.ERROR, shortKey, longKey, params, PACKAGE_MS);
 			sd.setDescriptionForUnit(getIdent());
 			// set which pane is affected by error
 			sd.setActivateableViewIdentifier(MSCourseNodeEditController.PANE_TAB_CONFIGURATION);
@@ -204,8 +203,7 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Assess
 		oneClickStatusCache = null;
 		// only here we know which translator to take for translating condition
 		// error messages
-		String translatorStr = Util.getPackageName(MSEditFormController.class);
-		List<StatusDescription> sds = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions());
+		List<StatusDescription> sds = isConfigValidWithTranslator(cev, PACKAGE_MS, getConditionExpressions());
 		oneClickStatusCache = StatusDescriptionHelper.sort(sds);
 		return oneClickStatusCache;
 	}
@@ -213,18 +211,28 @@ public class MSCourseNode extends AbstractAccessableCourseNode implements Assess
 	/**
 	 * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment)
 	 */
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
-		// read score from properties
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		Boolean passed = null;
 		Float score = null;
-		// only db lookup if configured, else return null
-		if (hasPassedConfigured()) passed = am.getNodePassed(this, mySelf);
-		if (hasScoreConfigured()) score = am.getNodeScore(this, mySelf);
+		if(hasPassedConfigured() || hasScoreConfigured()) {
+			AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+			if(entry != null) {
+				if (hasPassedConfigured()) {
+					passed = entry.getPassed();
+				}
+				if (hasScoreConfigured() && entry.getScore() != null) {
+					score = entry.getScore().floatValue();
+				}
+			}
+		}
+		return new ScoreEvaluation(score, passed);
+	}
 
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		return am.getAssessmentEntry(this, mySelf, null);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java b/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java
index 3aa6a46c30b..ab8b17ecf35 100644
--- a/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/PortfolioCourseNode.java
@@ -55,6 +55,7 @@ import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.portfolio.EPTemplateMapResource;
 import org.olat.portfolio.manager.EPFrontendManager;
 import org.olat.portfolio.manager.EPStructureManager;
@@ -197,6 +198,10 @@ public class PortfolioCourseNode extends AbstractAccessableCourseNode implements
 		return null;
 	}
 	
+	private String getReferencedRepositoryEntrySoftkey() {
+		return (String)getModuleConfiguration().get(PortfolioCourseNodeConfiguration.REPO_SOFT_KEY);
+	}
+	
 	@Override
 	public boolean needsReferenceToARepositoryEntry() {
 		return true;
@@ -305,18 +310,43 @@ public class PortfolioCourseNode extends AbstractAccessableCourseNode implements
 	}
 
 	@Override
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
-		// read score from properties
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		Boolean passed = null;
 		Float score = null;
-		// only db lookup if configured, else return null
-		if (hasPassedConfigured()) passed = am.getNodePassed(this, mySelf);
-		if (hasScoreConfigured()) score = am.getNodeScore(this, mySelf);
-
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+		if (hasPassedConfigured() || hasScoreConfigured()) {
+			AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+			if(entry != null) {
+				if (hasPassedConfigured()) {
+					passed = entry.getPassed();
+				}
+				if (hasScoreConfigured() && entry.getScore() != null) {
+					score = entry.getScore().floatValue();
+				}
+			}
+		}
+		return new ScoreEvaluation(score, passed);
+	}
+	
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		String referenceSoftkey = getReferencedRepositoryEntrySoftkey();
+		if(referenceSoftkey == null) {
+			Long mapKey = (Long)getModuleConfiguration().get(PortfolioCourseNodeConfiguration.MAP_KEY);
+			if(mapKey != null) {
+				RepositoryEntry re = CoreSpringFactory.getImpl(EPStructureManager.class)
+						.loadPortfolioRepositoryEntryByMapKey(mapKey);
+				if(re != null) {
+					referenceSoftkey = re.getSoftkey();
+				}
+			}
+		}
+		
+		if(referenceSoftkey != null) {
+			return am.getAssessmentEntry(this, mySelf, referenceSoftkey);
+		}
+		return null;
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java b/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java
index 62edaaebc93..3acb044bd09 100644
--- a/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/ProjectBrokerCourseNode.java
@@ -110,6 +110,7 @@ import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupService;
 import org.olat.group.model.BusinessGroupReference;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
@@ -466,24 +467,30 @@ public class ProjectBrokerCourseNode extends GenericCourseNode implements Assess
 		condition.setConditionId("projectbroker");
 		this.conditionProjectBroker = condition;
 	}
-	
-	// //////////// assessable interface implementation
 
-	/**
-	 * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment)
-	 */
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
-		// read score from properties
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+	@Override
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		Boolean passed = null;
 		Float score = null;
-		// only db lookup if configured, else return null
-		if (hasPassedConfigured()) passed = am.getNodePassed(this, mySelf);
-		if (hasScoreConfigured()) score = am.getNodeScore(this, mySelf);
+		if(hasPassedConfigured() || hasScoreConfigured()) {
+			AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+			if(entry != null) {
+				if (hasPassedConfigured()) {
+					passed = entry.getPassed();
+				}
+				if (hasScoreConfigured() && entry.getScore() != null) {
+					score = entry.getScore().floatValue();
+				}
+			}
+		}
+		return new ScoreEvaluation(score, passed);
+	}
 
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		return am.getAssessmentEntry(this, mySelf, null);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/STCourseNode.java b/src/main/java/org/olat/course/nodes/STCourseNode.java
index af2cd168fb9..a73a5fcd01c 100644
--- a/src/main/java/org/olat/course/nodes/STCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/STCourseNode.java
@@ -54,6 +54,7 @@ import org.olat.core.util.resource.OresHelper;
 import org.olat.course.CourseFactory;
 import org.olat.course.CourseModule;
 import org.olat.course.ICourse;
+import org.olat.course.assessment.AssessmentManager;
 import org.olat.course.condition.Condition;
 import org.olat.course.condition.interpreter.ConditionExpression;
 import org.olat.course.condition.interpreter.ConditionInterpreter;
@@ -75,6 +76,7 @@ import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.course.tree.CourseInternalLinkTreeModel;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.util.logging.activity.LoggingResourceable;
 
@@ -265,6 +267,7 @@ public class STCourseNode extends AbstractAccessableCourseNode implements Assess
 	 * 
 	 * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment)
 	 */
+	@Override
 	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		Float score = null;
 		Boolean passed = null;
@@ -284,8 +287,14 @@ public class STCourseNode extends AbstractAccessableCourseNode implements Assess
 		if (passedExpressionStr != null) {
 			passed = new Boolean(ci.evaluateCondition(passedExpressionStr));
 		}
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+		return new ScoreEvaluation(score, passed);
+	}
+
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		return am.getAssessmentEntry(this, mySelf, null);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/ScormCourseNode.java b/src/main/java/org/olat/course/nodes/ScormCourseNode.java
index 48ecf1a901d..a97d751d34e 100644
--- a/src/main/java/org/olat/course/nodes/ScormCourseNode.java
+++ b/src/main/java/org/olat/course/nodes/ScormCourseNode.java
@@ -63,6 +63,7 @@ import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.fileresource.types.ScormCPFileResource;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.scorm.ScormMainManager;
 import org.olat.modules.scorm.ScormPackageConfig;
 import org.olat.modules.scorm.archiver.ScormExportManager;
@@ -188,6 +189,10 @@ public class ScormCourseNode extends AbstractAccessableCourseNode implements Ass
 		RepositoryEntry entry = CPEditController.getCPReference(getModuleConfiguration(), false);
 		return entry;
 	}
+	
+	public String getReferencedRepositoryEntrySoftkey() {
+		return (String)getModuleConfiguration().get(CPEditController.CONFIG_KEY_REPOSITORY_SOFTKEY);
+	}
 
 	/**
 	 * @see org.olat.course.nodes.CourseNode#needsReferenceToARepositoryEntry()
@@ -371,14 +376,27 @@ public class ScormCourseNode extends AbstractAccessableCourseNode implements Ass
 	}
 
 	@Override
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
-		// read score from properties save score, passed and attempts information
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
-		Boolean passed = am.getNodePassed(this, mySelf);
-		Float score = am.getNodeScore(this, mySelf);
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
+		AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+		Boolean passed = null;
+		Float score = null;
+		if(entry != null) {
+			passed = entry.getPassed();
+			if(entry.getScore() != null) {
+				score = entry.getScore().floatValue();
+			}
+		}
+		return new ScoreEvaluation(score, passed);
+	}
+
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		if(getReferencedRepositoryEntrySoftkey() != null) {
+			return am.getAssessmentEntry(this, mySelf, getReferencedRepositoryEntrySoftkey());
+		}
+		return null;
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/TACourseNode.java b/src/main/java/org/olat/course/nodes/TACourseNode.java
index c13971347c8..322a2dacac3 100644
--- a/src/main/java/org/olat/course/nodes/TACourseNode.java
+++ b/src/main/java/org/olat/course/nodes/TACourseNode.java
@@ -94,6 +94,7 @@ import org.olat.course.run.scoring.ScoreEvaluation;
 import org.olat.course.run.userview.NodeEvaluation;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.modules.ModuleConfiguration;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.properties.Property;
 import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
@@ -480,24 +481,32 @@ public class TACourseNode extends GenericCourseNode implements AssessableCourseN
 		this.conditionSolution = conditionSolution;
 	}
 	
-	// //////////// assessable interface implementation
-
 	/**
 	 * @see org.olat.course.nodes.AssessableCourseNode#getUserScoreEvaluation(org.olat.course.run.userview.UserCourseEnvironment)
 	 */
 	@Override
-	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnvironment) {
-		// read score from properties
-		AssessmentManager am = userCourseEnvironment.getCourseEnvironment().getAssessmentManager();
-		Identity mySelf = userCourseEnvironment.getIdentityEnvironment().getIdentity();
+	public ScoreEvaluation getUserScoreEvaluation(UserCourseEnvironment userCourseEnv) {
 		Boolean passed = null;
 		Float score = null;
-		// only db lookup if configured, else return null
-		if (hasPassedConfigured()) passed = am.getNodePassed(this, mySelf);
-		if (hasScoreConfigured()) score = am.getNodeScore(this, mySelf);
+		if(hasPassedConfigured() || hasScoreConfigured()) {
+			AssessmentEntry entry = getUserAssessmentEntry(userCourseEnv);
+			if(entry != null) {
+				if (hasPassedConfigured()) {
+					passed = entry.getPassed();
+				}
+				if (hasScoreConfigured() && entry.getScore() != null) {
+					score = entry.getScore().floatValue();
+				}
+			}
+		}
+		return new ScoreEvaluation(score, passed);
+	}
 
-		ScoreEvaluation se = new ScoreEvaluation(score, passed);
-		return se;
+	@Override
+	public AssessmentEntry getUserAssessmentEntry(UserCourseEnvironment userCourseEnv) {
+		AssessmentManager am = userCourseEnv.getCourseEnvironment().getAssessmentManager();
+		Identity mySelf = userCourseEnv.getIdentityEnvironment().getIdentity();
+		return am.getAssessmentEntry(this, mySelf, null);
 	}
 
 	/**
diff --git a/src/main/java/org/olat/course/nodes/cp/CPEditController.java b/src/main/java/org/olat/course/nodes/cp/CPEditController.java
index 6580b4fc7c4..ee47bbe90d5 100644
--- a/src/main/java/org/olat/course/nodes/cp/CPEditController.java
+++ b/src/main/java/org/olat/course/nodes/cp/CPEditController.java
@@ -83,7 +83,7 @@ public class CPEditController extends ActivateableTabbableDefaultController impl
 	public static final String PANE_TAB_CPCONFIG = "pane.tab.cpconfig";
 	private static final String PANE_TAB_ACCESSIBILITY = "pane.tab.accessibility";
 	private static final String PANE_TAB_DELIVERYOPTIONS = "pane.tab.deliveryOptions";
-	private static final String CONFIG_KEY_REPOSITORY_SOFTKEY = "reporef";
+	public static final String CONFIG_KEY_REPOSITORY_SOFTKEY = "reporef";
 	private static final String VC_CHOSENCP = "chosencp";
 	public static final String CONFIG_DELIVERYOPTIONS = "deliveryOptions";
   
diff --git a/src/main/java/org/olat/course/nodes/iq/IQRunController.java b/src/main/java/org/olat/course/nodes/iq/IQRunController.java
index f9231f5baa9..755cd9ec860 100644
--- a/src/main/java/org/olat/course/nodes/iq/IQRunController.java
+++ b/src/main/java/org/olat/course/nodes/iq/IQRunController.java
@@ -555,7 +555,7 @@ public class IQRunController extends BasicController implements GenericEventList
 	}
 
 	private void exposeUserTestDataToVC(UserRequest ureq) {
-    // config : show score info
+		// config : show score info
 		Object enableScoreInfoObject = modConfig.get(IQEditController.CONFIG_KEY_ENABLESCOREINFO);
 		if (enableScoreInfoObject != null) {
 			myContent.contextPut("enableScoreInfo", enableScoreInfoObject );	
@@ -563,24 +563,25 @@ public class IQRunController extends BasicController implements GenericEventList
 			myContent.contextPut("enableScoreInfo", Boolean.TRUE );
 		}
    
-    // configuration data
-    myContent.contextPut("attemptsConfig", modConfig.get(IQEditController.CONFIG_KEY_ATTEMPTS));
-    // user data
-    if ( !(courseNode instanceof AssessableCourseNode))
-    	throw new AssertException("exposeUserTestDataToVC can only be called for test nodes, not for selftest or questionnaire");
+		// configuration data
+		myContent.contextPut("attemptsConfig", modConfig.get(IQEditController.CONFIG_KEY_ATTEMPTS));
+		// user data
+    	if ( !(courseNode instanceof AssessableCourseNode)) {
+    		throw new AssertException("exposeUserTestDataToVC can only be called for test nodes, not for selftest or questionnaire");
+    	}
 		AssessableCourseNode acn = (AssessableCourseNode)courseNode; // assessment nodes are assesable
 		ScoreEvaluation scoreEval = acn.getUserScoreEvaluation(userCourseEnv);
 		
 		//block if test passed (and config set to check it)
 		Boolean blockAfterSuccess = (Boolean)modConfig.get(IQEditController.CONFIG_KEY_BLOCK_AFTER_SUCCESS);
-    Boolean blocked = Boolean.FALSE;
-    if(blockAfterSuccess != null && blockAfterSuccess.booleanValue()) {
-    	Boolean passed = scoreEval.getPassed();
-    	if(passed != null && passed.booleanValue()) {
-    		blocked = Boolean.TRUE;
-    	}
-    }
-    myContent.contextPut("blockAfterSuccess", blocked );
+		Boolean blocked = Boolean.FALSE;
+		if(blockAfterSuccess != null && blockAfterSuccess.booleanValue()) {
+    		Boolean passed = scoreEval.getPassed();
+    		if(passed != null && passed.booleanValue()) {
+    			blocked = Boolean.TRUE;
+    		}
+		}
+		myContent.contextPut("blockAfterSuccess", blocked );
 		
 		Identity identity = userCourseEnv.getIdentityEnvironment().getIdentity();
 		myContent.contextPut("score", AssessmentHelper.getRoundedScore(scoreEval.getScore()));
diff --git a/src/main/java/org/olat/course/nodes/ta/ConvertToGTACourseNode.java b/src/main/java/org/olat/course/nodes/ta/ConvertToGTACourseNode.java
index cd73d218a1b..ccae86d1729 100644
--- a/src/main/java/org/olat/course/nodes/ta/ConvertToGTACourseNode.java
+++ b/src/main/java/org/olat/course/nodes/ta/ConvertToGTACourseNode.java
@@ -219,7 +219,7 @@ public class ConvertToGTACourseNode {
 		CoursePropertyManager propertyMgr = courseEnv.getCoursePropertyManager();
 		
 		Map<Long,AssessmentEntry> datas = new HashMap<>();
-		List<AssessmentEntry> properties = courseEnv.getAssessmentManager().getAssessmentData(sourceNode);
+		List<AssessmentEntry> properties = courseEnv.getAssessmentManager().getAssessmentEntries(sourceNode);
 
 		for(AssessmentEntry property:properties) {
 			Identity identity = property.getIdentity();
@@ -266,14 +266,14 @@ public class ConvertToGTACourseNode {
 		List<Property> logEntries = propertyMgr
 				.listCourseNodeProperties(sourceNode, null, null, UserNodeAuditManager.LOG_IDENTIFYER);
 		for(Property logEntry:logEntries) {
-			String log = logEntry.getTextValue();
+			String logText = logEntry.getTextValue();
 			Identity identity = securityManager.loadIdentityByKey(logEntry.getIdentity().getKey());
 			Property targetProp = propertyMgr.findCourseNodeProperty(gtaNode, identity, null, UserNodeAuditManager.LOG_IDENTIFYER);
 			if(targetProp == null) {
 				targetProp = propertyMgr
-					.createCourseNodePropertyInstance(gtaNode, identity, null, UserNodeAuditManager.LOG_IDENTIFYER, null, null, null, log);
+					.createCourseNodePropertyInstance(gtaNode, identity, null, UserNodeAuditManager.LOG_IDENTIFYER, null, null, null, logText);
 			} else {
-				targetProp.setTextValue(log);
+				targetProp.setTextValue(logText);
 			}
 			propertyMgr.saveProperty(targetProp);
 		}	
diff --git a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
index 242f9ece905..dc23b65b137 100644
--- a/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
+++ b/src/main/java/org/olat/course/run/preview/PreviewAssessmentManager.java
@@ -61,10 +61,15 @@ final class PreviewAssessmentManager extends BasicManager implements AssessmentM
 	}
 
 	@Override
-	public List<AssessmentEntry> getAssessmentData(CourseNode courseNode) {
+	public List<AssessmentEntry> getAssessmentEntries(CourseNode courseNode) {
 		return Collections.emptyList();
 	}
 
+	@Override
+	public AssessmentEntry getAssessmentEntry(CourseNode courseNode, Identity assessedIdentity, String referenceSoftKey) {
+		return null;
+	}
+
 	/**
 	 * @see org.olat.course.assessment.AssessmentManager#saveNodeAttempts(org.olat.course.nodes.CourseNode, org.olat.core.id.Identity, org.olat.core.id.Identity, java.lang.Integer)
 	 */
diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java
index 57135c1b9ca..276a673f928 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Service.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java
@@ -30,6 +30,7 @@ import org.olat.core.id.Identity;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
 
@@ -53,9 +54,11 @@ public interface QTI21Service {
 	public <E extends ResolvedAssessmentObject<?>> E loadAndResolveAssessmentObject(File resourceDirectory);
 	
 	
-	public UserTestSession createTestSession(RepositoryEntry testEntry, RepositoryEntry courseEntry, String subIdent, Identity identity);
+	public UserTestSession createTestSession(Identity identity, AssessmentEntry assessmentEntry,
+			RepositoryEntry entry, String subIdent, RepositoryEntry testEntry,
+			boolean authorMode);
 	
-	public UserTestSession getResumableTestSession(RepositoryEntry testEntry, RepositoryEntry courseEntry, String subIdent, Identity identity);
+	public UserTestSession getResumableTestSession(Identity identity, RepositoryEntry entry, String subIdent, RepositoryEntry testEntry);
 	
 	public UserTestSession updateTestSession(UserTestSession session);
 	
diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
index a8bfb7bbc94..52145ee18e7 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
@@ -53,6 +53,7 @@ import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.rendering.XmlUtilities;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -171,13 +172,15 @@ public class QTI21ServiceImpl implements QTI21Service {
 	}
 
 	@Override
-	public UserTestSession createTestSession(RepositoryEntry testEntry, RepositoryEntry courseEntry, String courseSubIdent, Identity identity) {
-		return testSessionDao.createTestSession(testEntry, courseEntry, courseSubIdent, identity);
+	public UserTestSession createTestSession(Identity identity, AssessmentEntry assessmentEntry,
+			RepositoryEntry entry, String subIdent, RepositoryEntry testEntry,
+			boolean authorMode) {
+		return testSessionDao.createTestSession(testEntry, entry, subIdent, assessmentEntry, identity, authorMode);
 	}
 
 	@Override
-	public UserTestSession getResumableTestSession(RepositoryEntry testEntry, RepositoryEntry courseEntry, String subIdent, Identity identity) {
-		UserTestSession session = testSessionDao.getLastTestSession(testEntry, courseEntry, subIdent, identity);
+	public UserTestSession getResumableTestSession(Identity identity, RepositoryEntry entry, String subIdent, RepositoryEntry testEntry) {
+		UserTestSession session = testSessionDao.getLastTestSession(testEntry, entry, subIdent, identity);
 		if(session == null || session.isExploded() || session.getTerminationTime() != null) {
 			session = null;
 		} else {
diff --git a/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java
index 08fbc8c41a6..60045e2337b 100644
--- a/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java
+++ b/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java
@@ -29,6 +29,7 @@ import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 import org.olat.ims.qti21.UserTestSession;
 import org.olat.ims.qti21.model.jpa.UserTestSessionImpl;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -50,16 +51,19 @@ public class SessionDAO {
 	
 
 	public UserTestSession createTestSession(RepositoryEntry testEntry,
-			RepositoryEntry courseEntry, String courseSubIdent, Identity identity) {
+			RepositoryEntry repositoryEntry, String subIdent,
+			AssessmentEntry assessmentEntry, Identity identity,
+			boolean authorMode) {
 		
 		UserTestSessionImpl testSession = new UserTestSessionImpl();
 		Date now = new Date();
 		testSession.setCreationDate(now);
 		testSession.setLastModified(now);
 		testSession.setTestEntry(testEntry);
-		testSession.setCourseEntry(courseEntry);
-		testSession.setCourseSubIdent(courseSubIdent);
-		testSession.setAuthorMode(false);
+		testSession.setRepositoryEntry(repositoryEntry);
+		testSession.setSubIdent(subIdent);
+		testSession.setAssessmentEntry(assessmentEntry);
+		testSession.setAuthorMode(authorMode);
 		testSession.setExploded(false);
 		testSession.setIdentity(identity);
 		testSession.setStorage(storage.getRelativeDir());
@@ -68,21 +72,21 @@ public class SessionDAO {
 	}
 	
 	public UserTestSession getLastTestSession(RepositoryEntryRef testEntry,
-			RepositoryEntryRef courseEntry, String courseSubIdent, IdentityRef identity) {
+			RepositoryEntryRef entry, String subIdent, IdentityRef identity) {
 		
 		StringBuilder sb = new StringBuilder();
 		sb.append("select session from qtiassessmentsession session ")
 		  .append("where session.testEntry.key=:testEntryKey and session.identity.key=:identityKey");
-		if(courseEntry != null) {
-			sb.append(" and session.courseEntry.key=:courseEntryKey");
+		if(entry != null) {
+			sb.append(" and session.repositoryEntry.key=:courseEntryKey");
 		} else {
-			sb.append(" and session.courseEntry.key is null");
+			sb.append(" and session.repositoryEntry.key is null");
 		}
 		
-		if(courseSubIdent != null) {
-			sb.append(" and session.courseSubIdent=:courseSubIdent");
+		if(subIdent != null) {
+			sb.append(" and session.subIdent=:courseSubIdent");
 		} else {
-			sb.append(" and session.courseSubIdent is null");
+			sb.append(" and session.subIdent is null");
 		}
 		sb.append(" order by session.creationDate desc");
 		
@@ -90,11 +94,11 @@ public class SessionDAO {
 				.createQuery(sb.toString(), UserTestSession.class)
 				.setParameter("testEntryKey", testEntry.getKey())
 				.setParameter("identityKey", identity.getKey());
-		if(courseEntry != null) {
-			query.setParameter("courseEntryKey", courseEntry.getKey());
+		if(entry != null) {
+			query.setParameter("courseEntryKey", entry.getKey());
 		}
-		if(courseSubIdent != null) {
-			query.setParameter("courseSubIdent", courseSubIdent);
+		if(subIdent != null) {
+			query.setParameter("courseSubIdent", subIdent);
 		}
 		
 		List<UserTestSession> lastSessions = query.setMaxResults(1).getResultList();
@@ -108,10 +112,10 @@ public class SessionDAO {
 	
 	public List<UserTestSession> getUserTestSessions(RepositoryEntryRef courseEntry, String courseSubIdent, IdentityRef identity) {
 		return dbInstance.getCurrentEntityManager()
-				.createNamedQuery("loadTestSessionsByUserAndCourse", UserTestSession.class)
-				.setParameter("courseEntryKey", courseEntry.getKey())
+				.createNamedQuery("loadTestSessionsByUserAndRepositoryEntryAndSubIdent", UserTestSession.class)
+				.setParameter("repositoryEntryKey", courseEntry.getKey())
 				.setParameter("identityKey", identity.getKey())
-				.setParameter("courseSubIdent", courseSubIdent)
+				.setParameter("subIdent", courseSubIdent)
 				.getResultList();
 	}
 
diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java
index 8f09d67ebb1..163d4011120 100644
--- a/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java
+++ b/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java
@@ -39,6 +39,8 @@ import org.olat.basesecurity.IdentityImpl;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Persistable;
 import org.olat.ims.qti21.UserTestSession;
+import org.olat.modules.assessment.AssessmentEntry;
+import org.olat.modules.assessment.model.AssessmentEntryImpl;
 import org.olat.repository.RepositoryEntry;
 
 /**
@@ -52,7 +54,7 @@ import org.olat.repository.RepositoryEntry;
 @Entity(name="qtiassessmentsession")
 @Table(name="o_qti_assessment_session")
 @NamedQueries({
-	@NamedQuery(name="loadTestSessionsByUserAndCourse", query="select session from qtiassessmentsession session where session.courseEntry.key=:courseEntryKey and session.identity.key=:identityKey and session.courseSubIdent=:courseSubIdent")
+	@NamedQuery(name="loadTestSessionsByUserAndRepositoryEntryAndSubIdent", query="select session from qtiassessmentsession session where session.repositoryEntry.key=:repositoryEntryKey and session.identity.key=:identityKey and session.subIdent=:subIdent")
 	
 })
 public class UserTestSessionImpl implements UserTestSession, Persistable {
@@ -70,17 +72,21 @@ public class UserTestSessionImpl implements UserTestSession, Persistable {
 	@Temporal(TemporalType.TIMESTAMP)
 	@Column(name="lastmodified", nullable=false, insertable=true, updatable=true)
 	private Date lastModified;
+	
+	@ManyToOne(targetEntity=AssessmentEntryImpl.class,fetch=FetchType.LAZY,optional=false)
+	@JoinColumn(name="fk_assessment_entry", nullable=false, insertable=true, updatable=false)
+    private AssessmentEntry assessmentEntry;
 
 	@ManyToOne(targetEntity=RepositoryEntry.class,fetch=FetchType.LAZY,optional=false)
-	@JoinColumn(name="fk_entry", nullable=false, insertable=true, updatable=false)
+	@JoinColumn(name="fk_reference_entry", nullable=false, insertable=true, updatable=false)
     private RepositoryEntry testEntry;
 	
 	@ManyToOne(targetEntity=RepositoryEntry.class,fetch=FetchType.LAZY,optional=true)
-	@JoinColumn(name="fk_course", nullable=true, insertable=true, updatable=false)
-    private RepositoryEntry courseEntry;
+	@JoinColumn(name="fk_entry", nullable=true, insertable=true, updatable=false)
+    private RepositoryEntry repositoryEntry;
 
-    @Column(name="q_course_subident", nullable=true, insertable=true, updatable=false)
-	private String courseSubIdent;
+    @Column(name="q_subident", nullable=true, insertable=true, updatable=false)
+	private String subIdent;
 
 	@ManyToOne(targetEntity=IdentityImpl.class,fetch=FetchType.LAZY,optional=false)
 	@JoinColumn(name="fk_identity", nullable=false, insertable=true, updatable=false)
@@ -162,6 +168,14 @@ public class UserTestSessionImpl implements UserTestSession, Persistable {
 		this.lastModified = lastModified;
 	}
 
+	public AssessmentEntry getAssessmentEntry() {
+		return assessmentEntry;
+	}
+
+	public void setAssessmentEntry(AssessmentEntry assessmentEntry) {
+		this.assessmentEntry = assessmentEntry;
+	}
+
 	public RepositoryEntry getTestEntry() {
 		return testEntry;
 	}
@@ -170,20 +184,20 @@ public class UserTestSessionImpl implements UserTestSession, Persistable {
 		this.testEntry = testEntry;
 	}
 
-	public RepositoryEntry getCourseEntry() {
-		return courseEntry;
+	public RepositoryEntry getRepositoryEntry() {
+		return repositoryEntry;
 	}
 
-	public void setCourseEntry(RepositoryEntry courseEntry) {
-		this.courseEntry = courseEntry;
+	public void setRepositoryEntry(RepositoryEntry repositoryEntry) {
+		this.repositoryEntry = repositoryEntry;
 	}
 
-	public String getCourseSubIdent() {
-		return courseSubIdent;
+	public String getSubIdent() {
+		return subIdent;
 	}
 
-	public void setCourseSubIdent(String courseSubIdent) {
-		this.courseSubIdent = courseSubIdent;
+	public void setSubIdent(String subIdent) {
+		this.subIdent = subIdent;
 	}
 
 	public Identity getIdentity() {
diff --git a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java
index 0515dbbcbfc..ddab0488267 100644
--- a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java
+++ b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java
@@ -299,9 +299,9 @@ public class QTI21AssessmentTestHandler extends FileHandler {
 				new RuntimeControllerCreator() {
 					@Override
 					public Controller create(UserRequest uureq, WindowControl wwControl, TooledStackedPanel toolbarPanel,
-							RepositoryEntry entry, RepositoryEntrySecurity reSecurity, AssessmentMode mode) {
+							RepositoryEntry entry, RepositoryEntrySecurity repoSecurity, AssessmentMode mode) {
 						InMemoryOutcomesListener listener = new InMemoryOutcomesListener();
-						return new AssessmentTestDisplayController(uureq, wwControl, listener, entry, null, null);
+						return new AssessmentTestDisplayController(uureq, wwControl, listener, entry, entry, null);
 					}
 				});
 	}
diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java
index 765d743ad80..204a1fb0670 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java
@@ -47,6 +47,7 @@ import org.olat.ims.qti21.UserTestSession;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.components.AssessmentItemFormItem;
+import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -96,7 +97,8 @@ public class AssessmentItemDisplayController extends BasicController implements
 	@Autowired
 	private JqtiExtensionManager jqtiExtensionManager;
 	
-	public AssessmentItemDisplayController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry,
+	public AssessmentItemDisplayController(UserRequest ureq, WindowControl wControl,
+			RepositoryEntry testEntry, AssessmentEntry assessmentEntry, boolean authorMode,
 			ResolvedAssessmentItem resolvedAssessmentItem, AssessmentItemRef itemRef, File fUnzippedDirRoot) {
 		super(ureq, wControl);
 		
@@ -104,7 +106,7 @@ public class AssessmentItemDisplayController extends BasicController implements
 		this.fUnzippedDirRoot = fUnzippedDirRoot;
 		this.resolvedAssessmentItem = resolvedAssessmentItem;
 		currentRequestTimestamp = ureq.getRequestTimestamp();
-		candidateSession = qtiService.createTestSession(testEntry, null, itemRef.getIdentifier().toString(), getIdentity());
+		candidateSession = qtiService.createTestSession(getIdentity(), assessmentEntry, testEntry, itemRef.getIdentifier().toString(), testEntry, authorMode);
 		mapperUri = registerCacheableMapper(null, UUID.randomUUID().toString(), new ResourcesMapper());
 		
 		itemSessionController = enterSession(ureq);
diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
index 43ecd8e5235..f5555b67d68 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
@@ -49,6 +49,8 @@ import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.components.AssessmentTestFormItem;
+import org.olat.modules.assessment.AssessmentEntry;
+import org.olat.modules.assessment.AssessmentService;
 import org.olat.repository.RepositoryEntry;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -102,6 +104,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 	private CandidateEvent lastEvent;
 	private Date currentRequestTimestamp;
 	private UserTestSession candidateSession;
+	private AssessmentEntry assessmentEntry;
 	
 	private OutcomesListener outcomesListener;
 
@@ -109,6 +112,8 @@ public class AssessmentTestDisplayController extends BasicController implements
 	@Autowired
 	private QTI21Service qtiService;
 	@Autowired
+	private AssessmentService assessmentService;
+	@Autowired
 	private JqtiExtensionManager jqtiExtensionManager;
 	
 	/**
@@ -116,25 +121,27 @@ public class AssessmentTestDisplayController extends BasicController implements
 	 * @param ureq
 	 * @param wControl
 	 * @param listener
-	 * @param entry
-	 * @param courseRe Course repository entry (optional)
+	 * @param testEntry
+	 * @param entry Course repository entry (optional)
 	 * @param subIdent The course node identifier (mandatory only if in a course is used)
 	 */
 	public AssessmentTestDisplayController(UserRequest ureq, WindowControl wControl, OutcomesListener listener,
-			RepositoryEntry entry, RepositoryEntry courseRe, String courseSubIdent) {
+			RepositoryEntry testEntry, RepositoryEntry entry, String subIdent) {
 		super(ureq, wControl);
 		
 		this.outcomesListener = listener;
 		
 		FileResourceManager frm = FileResourceManager.getInstance();
-		fUnzippedDirRoot = frm.unzipFileResource(entry.getOlatResource());
-		mapperUri = registerCacheableMapper(null, "QTI21Resources::" + entry.getKey(), new ResourcesMapper());
+		fUnzippedDirRoot = frm.unzipFileResource(testEntry.getOlatResource());
+		mapperUri = registerCacheableMapper(null, "QTI21Resources::" + testEntry.getKey(), new ResourcesMapper());
 		
 		currentRequestTimestamp = ureq.getRequestTimestamp();
 		
-		UserTestSession lastSession = qtiService.getResumableTestSession(entry, courseRe, courseSubIdent, getIdentity());
+		assessmentEntry = assessmentService.getOrCreateAssessmentEntry(getIdentity(), entry, subIdent, testEntry);
+		
+		UserTestSession lastSession = qtiService.getResumableTestSession(getIdentity(), entry, subIdent, testEntry);
 		if(lastSession == null) {
-			candidateSession = qtiService.createTestSession(entry, courseRe, courseSubIdent, getIdentity());
+			candidateSession = qtiService.createTestSession(getIdentity(), assessmentEntry, entry, subIdent, testEntry, false);
 			testSessionController = enterSession(ureq);
 		} else {
 			candidateSession = lastSession;
diff --git a/src/main/java/org/olat/ims/qti21/ui/InMemoryOutcomesListener.java b/src/main/java/org/olat/ims/qti21/ui/InMemoryOutcomesListener.java
index 3534fc7310f..1d6f447e4ce 100644
--- a/src/main/java/org/olat/ims/qti21/ui/InMemoryOutcomesListener.java
+++ b/src/main/java/org/olat/ims/qti21/ui/InMemoryOutcomesListener.java
@@ -43,14 +43,14 @@ public class InMemoryOutcomesListener implements OutcomesListener {
 	}
 
 	@Override
-	public void updateOutcomes(Float score, Boolean pass) {
-		this.score = score;
-		this.pass = pass;
+	public void updateOutcomes(Float updatedScore, Boolean updatedPassed) {
+		score = updatedScore;
+		pass = updatedPassed;
 	}
 
 	@Override
-	public void submit(Float score, Boolean pass) {
-		this.score = score;
-		this.pass = pass;
+	public void submit(Float submittedScore, Boolean submittedPass) {
+		score = submittedScore;
+		pass = submittedPass;
 	}
 }
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
index aceb0dd2972..94f5da88fa1 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
@@ -35,6 +35,8 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.ui.AssessmentItemDisplayController;
+import org.olat.modules.assessment.AssessmentEntry;
+import org.olat.modules.assessment.AssessmentService;
 import org.olat.repository.RepositoryEntry;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -63,6 +65,8 @@ public class AssessmentItemEditorController extends BasicController {
 	
 	@Autowired
 	private QtiSerializer qtiSerializer;
+	@Autowired
+	private AssessmentService assessmentService;
 	
 	public AssessmentItemEditorController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry,
 			ResolvedAssessmentItem resolvedAssessmentItem, AssessmentItemRef itemRef, File unzippedDirectory) {
@@ -76,7 +80,9 @@ public class AssessmentItemEditorController extends BasicController {
 
 		initItemEditor(ureq);
 		
-		displayCtrl = new AssessmentItemDisplayController(ureq, getWindowControl(), testEntry, resolvedAssessmentItem, itemRef, unzippedDirectory);
+		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(getIdentity(), testEntry, null, testEntry);
+		displayCtrl = new AssessmentItemDisplayController(ureq, getWindowControl(),
+				testEntry, assessmentEntry, true, resolvedAssessmentItem, itemRef, unzippedDirectory);
 		listenTo(displayCtrl);
 		tabbedPane.addTab("Preview", displayCtrl.getInitialComponent());
 		
diff --git a/src/main/java/org/olat/modules/assessment/AssessmentService.java b/src/main/java/org/olat/modules/assessment/AssessmentService.java
index 6715ccdec59..0263d575406 100644
--- a/src/main/java/org/olat/modules/assessment/AssessmentService.java
+++ b/src/main/java/org/olat/modules/assessment/AssessmentService.java
@@ -19,6 +19,11 @@
  */
 package org.olat.modules.assessment;
 
+import java.util.List;
+
+import org.olat.core.id.Identity;
+import org.olat.repository.RepositoryEntry;
+
 /**
  * 
  * Initial date: 22.07.2015<br>
@@ -26,5 +31,32 @@ package org.olat.modules.assessment;
  *
  */
 public interface AssessmentService {
+	
+	/**
+	 * 
+	 * @param assessedIdentity
+	 * @param entry The repository entry, the course
+	 * @param subIdent An additional reference for the cours element
+	 * @param referenceEntry The test repository entry 
+	 * @return
+	 */
+	public AssessmentEntry getOrCreateAssessmentEntry(Identity assessedIdentity,
+			RepositoryEntry entry, String subIdent, RepositoryEntry referenceEntry);
+	
+	public AssessmentEntry loadAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent);
+	
+	/**
+	 * The search by the reference soft key is set not is null if the value is null.
+	 * @param assessedIdentity
+	 * @param entry
+	 * @param subIdent
+	 * @param referenceSoftKey
+	 * @return
+	 */
+	public AssessmentEntry loadAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent, String referenceSoftKey);
+	
+	public AssessmentEntry updateAssessmentEntry(AssessmentEntry entry);
+	
+	public List<AssessmentEntry> loadAssessmentEntriesBySubIdent(RepositoryEntry entry, String subIdent);
 
 }
diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java
index 9bc23b903cf..ea26650e4e9 100644
--- a/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java
+++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentEntryDAO.java
@@ -22,6 +22,8 @@ package org.olat.modules.assessment.manager;
 import java.util.Date;
 import java.util.List;
 
+import javax.persistence.TypedQuery;
+
 import org.olat.basesecurity.IdentityRef;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
@@ -66,19 +68,59 @@ public class AssessmentEntryDAO {
 		return nodeAssessment.isEmpty() ? null : nodeAssessment.get(0);
 	}
 
-	public AssessmentEntry loadAssessmentEntry(IdentityRef assessedIdentity, RepositoryEntryRef entry,
-			String subIdent) {
+	public AssessmentEntry loadAssessmentEntry(IdentityRef assessedIdentity, RepositoryEntryRef entry, String subIdent) {
+		TypedQuery<AssessmentEntry> query;
+		if(subIdent == null) {
+			query = dbInstance.getCurrentEntityManager()
+				.createNamedQuery("loadAssessmentEntryByRepositoryEntryAndUserAndNullSubIdent", AssessmentEntry.class);
+		} else {
+			query = dbInstance.getCurrentEntityManager()
+				.createNamedQuery("loadAssessmentEntryByRepositoryEntryAndUserAndSubIdent", AssessmentEntry.class)
+				.setParameter("subIdent", subIdent);
+		}
+		List<AssessmentEntry> entries = query
+			.setParameter("repositoryEntryKey", entry.getKey())
+			.setParameter("identityKey", assessedIdentity.getKey())
+			.getResultList();
+		return entries.isEmpty() ? null : entries.get(0);
+	}
+	
+	public AssessmentEntry loadAssessmentEntry(IdentityRef assessedIdentity, RepositoryEntryRef entry, String subIdent, String referenceSoftKey) {
+		StringBuilder sb = new StringBuilder();
 		
-		List<AssessmentEntry> nodeAssessment = dbInstance.getCurrentEntityManager()
-				.createNamedQuery("loadAssessmentEntryByRepositoryEntryAndUser", AssessmentEntry.class)
+		sb.append("select data from assessmententry data");
+		if(referenceSoftKey != null) {
+			sb.append(" inner join data.referenceEntry referenceEntry");
+		}
+		
+		sb.append(" where data.repositoryEntry.key=:repositoryEntryKey and data.identity.key=:identityKey");
+		if(subIdent != null) {
+			sb.append(" and data.subIdent=:subIdent");
+		} else {
+			sb.append(" and data.subIdent is null");
+		}
+		
+		if(referenceSoftKey != null) {
+			sb.append(" and referenceEntry.softkey=:softkey");
+		} else {
+			sb.append(" and referenceEntry.softkey is null");
+		}
+
+		TypedQuery<AssessmentEntry> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), AssessmentEntry.class)
 				.setParameter("repositoryEntryKey", entry.getKey())
-				.setParameter("identityKey", assessedIdentity.getKey())
-				.setParameter("subIdent", subIdent)
-				.getResultList();
-		return nodeAssessment.isEmpty() ? null : nodeAssessment.get(0);
+				.setParameter("identityKey", assessedIdentity.getKey());
+		if(subIdent != null) {
+			query.setParameter("subIdent", subIdent);
+		}
+		if(referenceSoftKey != null) {
+			query.setParameter("softkey", referenceSoftKey);
+		}
+		List<AssessmentEntry> entries = query.getResultList();
+		return entries.isEmpty() ? null : entries.get(0);
 	}
 	
-	public AssessmentEntry updateCourseNodeAssessment(AssessmentEntry nodeAssessment) {
+	public AssessmentEntry updateAssessmentEntry(AssessmentEntry nodeAssessment) {
 		((AssessmentEntryImpl)nodeAssessment).setLastModified(new Date());
 		return dbInstance.getCurrentEntityManager().merge(nodeAssessment);
 	}
diff --git a/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java b/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java
new file mode 100644
index 00000000000..85103271194
--- /dev/null
+++ b/src/main/java/org/olat/modules/assessment/manager/AssessmentServiceImpl.java
@@ -0,0 +1,84 @@
+/**
+ * <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.modules.assessment.manager;
+
+import java.util.List;
+
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.modules.assessment.AssessmentEntry;
+import org.olat.modules.assessment.AssessmentService;
+import org.olat.repository.RepositoryEntry;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * Initial date: 23.07.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class AssessmentServiceImpl implements AssessmentService {
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private AssessmentEntryDAO assessmentEntryDao;
+
+	@Override
+	public AssessmentEntry getOrCreateAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent,
+			RepositoryEntry referenceEntry) {
+		
+		AssessmentEntry assessmentEntry = assessmentEntryDao.loadAssessmentEntry(assessedIdentity, entry, subIdent);
+		if(assessmentEntry == null) {
+			assessmentEntry = assessmentEntryDao.createCourseNodeAssessment(assessedIdentity, entry, subIdent, referenceEntry);
+			dbInstance.commit();
+		}
+		return assessmentEntry;
+	}
+
+	@Override
+	public AssessmentEntry loadAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent) {
+		if(assessedIdentity == null || entry == null) return null;
+		return assessmentEntryDao.loadAssessmentEntry(assessedIdentity, entry, subIdent);
+	}
+
+	@Override
+	public AssessmentEntry loadAssessmentEntry(Identity assessedIdentity, RepositoryEntry entry, String subIdent, String referenceSoftKey) {
+		if(assessedIdentity == null || entry == null) return null;
+		return assessmentEntryDao.loadAssessmentEntry(assessedIdentity, entry, subIdent, referenceSoftKey);
+	}
+
+	@Override
+	public AssessmentEntry updateAssessmentEntry(AssessmentEntry entry) {
+		return assessmentEntryDao.updateAssessmentEntry(entry);
+	}
+
+	@Override
+	public List<AssessmentEntry> loadAssessmentEntriesBySubIdent(RepositoryEntry entry, String subIdent) {
+		return assessmentEntryDao.loadAssessmentEntryBySubIdent(entry, subIdent);
+	}
+	
+	
+	
+	
+
+}
diff --git a/src/main/java/org/olat/modules/assessment/model/AssessmentEntryImpl.java b/src/main/java/org/olat/modules/assessment/model/AssessmentEntryImpl.java
index ca5d5c3da9d..ce35834cc4d 100644
--- a/src/main/java/org/olat/modules/assessment/model/AssessmentEntryImpl.java
+++ b/src/main/java/org/olat/modules/assessment/model/AssessmentEntryImpl.java
@@ -53,9 +53,15 @@ import org.olat.repository.RepositoryEntry;
 @Entity(name="assessmententry")
 @Table(name="o_as_entry")
 @NamedQueries({
-	@NamedQuery(name="loadAssessmentEntryById", query="select data from assessmententry data where data.key=:key"),
-	@NamedQuery(name="loadAssessmentEntryByRepositoryEntryAndUser", query="select data from assessmententry data where data.repositoryEntry.key=:repositoryEntryKey and data.identity.key=:identityKey and data.subIdent=:subIdent"),
-	@NamedQuery(name="loadAssessmentEntryByRepositoryEntryAndSubIdent", query="select data from assessmententry data where data.repositoryEntry.key=:repositoryEntryKey and data.subIdent=:subIdent")
+	@NamedQuery(name="loadAssessmentEntryById",
+		query="select data from assessmententry data where data.key=:key"),
+	@NamedQuery(name="loadAssessmentEntryByRepositoryEntryAndUserAndSubIdent",
+		query="select data from assessmententry data where data.repositoryEntry.key=:repositoryEntryKey and data.identity.key=:identityKey and data.subIdent=:subIdent"),
+	@NamedQuery(name="loadAssessmentEntryByRepositoryEntryAndUserAndNullSubIdent",
+		query="select data from assessmententry data where data.repositoryEntry.key=:repositoryEntryKey and data.identity.key=:identityKey and data.subIdent is null"),
+	@NamedQuery(name="loadAssessmentEntryByRepositoryEntryAndSubIdent",
+		query="select data from assessmententry data where data.repositoryEntry.key=:repositoryEntryKey and data.subIdent=:subIdent")
+	
 	
 	
 })
diff --git a/src/main/resources/database/mysql/alter_10_x_0_to_11_0_0.sql b/src/main/resources/database/mysql/alter_10_x_0_to_11_0_0.sql
index e4b4f1bbab4..e53734ee89a 100644
--- a/src/main/resources/database/mysql/alter_10_x_0_to_11_0_0.sql
+++ b/src/main/resources/database/mysql/alter_10_x_0_to_11_0_0.sql
@@ -1,26 +1,3 @@
-create table o_qti_assessment_session (
-   id bigint not null auto_increment,
-   creationdate datetime not null,
-   lastmodified datetime not null,
-   q_exploded bit not null default 0,
-   q_author_mode bit not null default 0,
-   q_finish_time datetime,
-   q_termination_time datetime,
-   q_storage varchar(32),
-   fk_identity bigint not null,
-   fk_entry bigint not null,
-   fk_course bigint,
-   q_course_subident varchar(64),
-   primary key (id)
-);
-alter table o_qti_assessment_session ENGINE = InnoDB;
-
-alter table o_qti_assessment_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
-alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_course) references o_repositoryentry (repositoryentry_id);
-alter table o_qti_assessment_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
-
-
-
 create table o_as_entry (
    id bigint not null auto_increment,
    creationdate datetime not null,
@@ -45,5 +22,29 @@ alter table o_as_entry ENGINE = InnoDB;
 alter table o_as_entry add constraint as_entry_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
 alter table o_as_entry add constraint as_entry_to_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
 alter table o_as_entry add constraint as_entry_to_refentry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
-
 create index idx_as_entry_to_id_idx on o_as_entry (a_assessment_id);
+
+
+
+create table o_qti_assessment_session (
+   id bigint not null auto_increment,
+   creationdate datetime not null,
+   lastmodified datetime not null,
+   q_exploded bit not null default 0,
+   q_author_mode bit not null default 0,
+   q_finish_time datetime,
+   q_termination_time datetime,
+   q_storage varchar(32),
+   fk_reference_entry bigint not null,
+   fk_entry bigint,
+   q_subident varchar(64),
+   fk_identity bigint not null,
+   fk_assessment_entry bigint not null,
+   primary key (id)
+);
+alter table o_qti_assessment_session ENGINE = InnoDB;
+
+alter table o_qti_assessment_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessment_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
+alter table o_qti_assessment_session add constraint qti_sess_to_as_entry_idx foreign key (fk_assessment_entry) references o_as_entry (id);
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index 1988ee14ff9..497e85e3bcf 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -1221,10 +1221,11 @@ create table o_qti_assessment_session (
    q_finish_time datetime,
    q_termination_time datetime,
    q_storage varchar(32),
+   fk_reference_entry bigint not null,
+   fk_entry bigint,
+   q_subident varchar(64),
    fk_identity bigint not null,
-   fk_entry bigint not null,
-   fk_course bigint,
-   q_course_subident varchar(64),
+   fk_assessment_entry bigint not null,
    primary key (id)
 );
 
@@ -2105,8 +2106,9 @@ create index o_mapper_uuid_idx on o_mapper (mapper_uuid);
 
 -- qti 2.1
 alter table o_qti_assessment_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
-alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_course) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
 alter table o_qti_assessment_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
+alter table o_qti_assessment_session add constraint qti_sess_to_as_entry_idx foreign key (fk_assessment_entry) references o_as_entry (id);
 
 -- question pool
 alter table o_qp_pool add constraint idx_qp_pool_owner_grp_id foreign key (fk_ownergroup) references o_bs_secgroup(id);
diff --git a/src/main/resources/database/postgresql/alter_10_x_0_to_11_0_0.sql b/src/main/resources/database/postgresql/alter_10_x_0_to_11_0_0.sql
index ec896e6017d..6e76bea7136 100644
--- a/src/main/resources/database/postgresql/alter_10_x_0_to_11_0_0.sql
+++ b/src/main/resources/database/postgresql/alter_10_x_0_to_11_0_0.sql
@@ -1,28 +1,3 @@
-create table o_qti_assessment_session (
-   id bigserial,
-   creationdate timestamp not null,
-   lastmodified timestamp not null,
-   q_exploded bool default false,
-   q_author_mode bool default false,
-   q_finish_time timestamp,
-   q_termination_time timestamp,
-   q_storage varchar(32),
-   fk_identity int8 not null,
-   fk_entry int8 not null,
-   fk_course int8,
-   q_course_subident varchar(64),
-   primary key (id)
-);
-
-alter table o_qti_assessment_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
-create index idx_testess_to_repo_entry_idx on o_qti_assessment_session (fk_entry);
-alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_course) references o_repositoryentry (repositoryentry_id);
-create index idx_qti_sess_to_course_entry_idx on o_qti_assessment_session (fk_course);
-alter table o_qti_assessment_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
-create index idx_qti_sess_to_identity_idx on o_qti_assessment_session (fk_identity);
-
-
-
 create table o_as_entry (
    id bigserial,
    creationdate timestamp not null,
@@ -49,7 +24,34 @@ alter table o_as_entry add constraint as_entry_to_entry_idx foreign key (fk_entr
 create index idx_as_entry_to_entry_idx on o_as_entry (fk_entry);
 alter table o_as_entry add constraint as_entry_to_refentry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
 create index idx_as_entry_to_refentry_idx on o_as_entry (fk_reference_entry);
-
 create index idx_as_entry_to_id_idx on o_as_entry (a_assessment_id);
 
 
+
+create table o_qti_assessment_session (
+   id bigserial,
+   creationdate timestamp not null,
+   lastmodified timestamp not null,
+   q_exploded bool default false,
+   q_author_mode bool default false,
+   q_finish_time timestamp,
+   q_termination_time timestamp,
+   q_storage varchar(32),
+   fk_reference_entry int8 not null,
+   fk_entry int8,
+   q_subident varchar(64),
+   fk_identity int8 not null,
+   fk_assessment_entry int8 not null,
+   primary key (id)
+);
+
+alter table o_qti_assessment_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
+create index idx_testess_to_repo_entry_idx on o_qti_assessment_session (fk_entry);
+alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
+create index idx_qti_sess_to_course_entry_idx on o_qti_assessment_session (fk_reference_entry);
+alter table o_qti_assessment_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
+create index idx_qti_sess_to_identity_idx on o_qti_assessment_session (fk_identity);
+alter table o_qti_assessment_session add constraint qti_sess_to_as_entry_idx foreign key (fk_assessment_entry) references o_as_entry (id);
+create index idx_qti_sess_to_as_entry_idx on o_qti_assessment_session (fk_assessment_entry);
+
+
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index 3cbe71a6ab2..edb770723b1 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -1222,10 +1222,11 @@ create table o_qti_assessment_session (
    q_finish_time timestamp,
    q_termination_time timestamp,
    q_storage varchar(32),
+   fk_reference_entry int8 not null,
+   fk_entry int8,
+   q_subident varchar(64),
    fk_identity int8 not null,
-   fk_entry int8 not null,
-   fk_course int8,
-   q_course_subident varchar(64),
+   fk_assessment_entry int8 not null,
    primary key (id)
 );
 
@@ -2108,10 +2109,13 @@ create index o_mapper_uuid_idx on o_mapper (mapper_uuid);
 -- qti 2.1
 alter table o_qti_assessment_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
 create index idx_testess_to_repo_entry_idx on o_qti_assessment_session (fk_entry);
-alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_course) references o_repositoryentry (repositoryentry_id);
-create index idx_qti_sess_to_course_entry_idx on o_qti_assessment_session (fk_course);
+alter table o_qti_assessment_session add constraint qti_sess_to_course_entry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
+create index idx_qti_sess_to_course_entry_idx on o_qti_assessment_session (fk_reference_entry);
 alter table o_qti_assessment_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
 create index idx_qti_sess_to_identity_idx on o_qti_assessment_session (fk_identity);
+alter table o_qti_assessment_session add constraint qti_sess_to_as_entry_idx foreign key (fk_assessment_entry) references o_as_entry (id);
+create index idx_qti_sess_to_as_entry_idx on o_qti_assessment_session (fk_assessment_entry);
+
 
 -- question pool
 alter table o_qp_pool add constraint idx_qp_pool_owner_grp_id foreign key (fk_ownergroup) references o_bs_secgroup(id);
diff --git a/src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java b/src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java
index dcb96d53c42..e6368471b17 100644
--- a/src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java
+++ b/src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java
@@ -27,6 +27,8 @@ import org.junit.Test;
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.id.Identity;
 import org.olat.ims.qti21.UserTestSession;
+import org.olat.modules.assessment.AssessmentEntry;
+import org.olat.modules.assessment.AssessmentService;
 import org.olat.repository.RepositoryEntry;
 import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
@@ -44,15 +46,18 @@ public class SessionDAOTest extends OlatTestCase {
 	private DB dbInstance;
 	@Autowired
 	private SessionDAO testSessionDao;
+	@Autowired
+	private AssessmentService assessmentService;
 	
 	@Test
 	public void createTestSession_repo() {
 		// prepare a test and a user
 		RepositoryEntry testEntry = JunitTestHelper.createAndPersistRepositoryEntry();
 		Identity assessedIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("session-1");
+		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, testEntry, "-", testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession = testSessionDao.createTestSession(testEntry, null, null, assessedIdentity);
+		UserTestSession testSession = testSessionDao.createTestSession(testEntry, testEntry, "-", assessmentEntry, assessedIdentity, true);
 		Assert.assertNotNull(testSession);
 		dbInstance.commit();
 	}
@@ -64,9 +69,10 @@ public class SessionDAOTest extends OlatTestCase {
 		RepositoryEntry courseEntry = JunitTestHelper.createAndPersistRepositoryEntry();
 		String subIdent = UUID.randomUUID().toString();
 		Identity assessedIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("session-2");
+		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, subIdent, testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessedIdentity);
+		UserTestSession testSession = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
 		Assert.assertNotNull(testSession);
 		dbInstance.commit();
 	}
@@ -78,9 +84,10 @@ public class SessionDAOTest extends OlatTestCase {
 		RepositoryEntry courseEntry = JunitTestHelper.createAndPersistRepositoryEntry();
 		String subIdent = UUID.randomUUID().toString();
 		Identity assessedIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("session-3");
+		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, subIdent, testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessedIdentity);
+		UserTestSession testSession = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, true);
 		Assert.assertNotNull(testSession);
 		dbInstance.commitAndCloseSession();
 		
@@ -89,5 +96,32 @@ public class SessionDAOTest extends OlatTestCase {
 		Assert.assertEquals(1, sessions.size());
 		Assert.assertEquals(testSession, sessions.get(0));
 	}
+	
+	@Test
+	public void getLastTestSession() {
+		// prepare a test and a user
+		RepositoryEntry testEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		RepositoryEntry courseEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		String subIdent = UUID.randomUUID().toString();
+		Identity assessedIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("session-3");
+		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, subIdent, testEntry);
+		dbInstance.commit();
+		
+		UserTestSession testSession1 = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
+		Assert.assertNotNull(testSession1);
+		dbInstance.commitAndCloseSession();
+		
+		//to have a time difference
+		sleep(1500);
+		
+		UserTestSession testSession2 = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
+		Assert.assertNotNull(testSession2);
+		dbInstance.commitAndCloseSession();
+		
+		//load the last session
+		UserTestSession lastTestSession = testSessionDao.getLastTestSession(testEntry, courseEntry, subIdent, assessedIdentity);
+		Assert.assertNotNull(lastTestSession);
+		Assert.assertEquals(testSession2, lastTestSession);
+	}
 
 }
-- 
GitLab