From e44b959b31e04e16e0a1eac63b657e8429f47892 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Tue, 2 Feb 2016 17:10:27 +0100
Subject: [PATCH] OO-1593: work on QTI 2.1 database model

---
 .../core/logging/OLATRuntimeException.java    |   4 +
 .../iq/QTI21AssessmentDetailsController.java  |   8 +-
 .../nodes/iq/QTI21TestSessionTableModel.java  |   8 +-
 .../olat/ims/qti21/AssessmentItemSession.java |  32 ++++
 .../olat/ims/qti21/AssessmentResponse.java}   |  28 ++-
 ...ession.java => AssessmentTestSession.java} |   2 +-
 .../java/org/olat/ims/qti21/QTI21Service.java |  44 +++--
 .../manager/AssessmentItemSessionDAO.java     |  68 +++++++
 .../qti21/manager/AssessmentResponseDAO.java  |  85 +++++++++
 ...DAO.java => AssessmentTestSessionDAO.java} |  30 +--
 .../org/olat/ims/qti21/manager/EventDAO.java  |   6 +-
 .../ims/qti21/manager/QTI21ServiceImpl.java   | 101 +++++++---
 .../manager/QTI21StatisticsManagerImpl.java   |   7 +-
 .../qti21/model/AssessmentFileSubmission.java |  30 +++
 .../ims/qti21/model/ResponseLegality.java     |  48 +++++
 .../model/jpa/AssessmentItemSessionImpl.java  | 169 +++++++++++++++++
 .../model/jpa/AssessmentResponseImpl.java     | 177 ++++++++++++++++++
 ...pl.java => AssessmentTestSessionImpl.java} |  14 +-
 .../ims/qti21/model/jpa/CandidateEvent.java   |   8 +-
 .../xml/items/EssayAssessmentItemBuilder.java |  19 ++
 .../ui/AssessmentItemDisplayController.java   |  14 +-
 .../ui/AssessmentTestDisplayController.java   |  92 ++++++---
 .../ims/qti21/ui/CandidateSessionContext.java |   4 +-
 .../AssessmentItemComponentRenderer.java      |   4 +-
 .../AssessmentTestComponentRenderer.java      |   4 +-
 .../editor/items/EssayEditorController.java   |  19 ++
 src/main/resources/META-INF/persistence.xml   |   4 +-
 .../database/mysql/alter_10_x_0_to_11_0_0.sql |  49 ++++-
 .../database/mysql/setupDatabase.sql          |  47 ++++-
 .../oracle/alter_10_x_0_to_11_0_0.sql         |  14 +-
 .../database/oracle/setupDatabase.sql         |  12 +-
 .../postgresql/alter_10_x_0_to_11_0_0.sql     |  56 +++++-
 .../database/postgresql/setupDatabase.sql     |  54 +++++-
 .../manager/AssessmentResponseDAOTest.java    |  82 ++++++++
 ...java => AssessmentTestSessionDAOTest.java} |  24 +--
 .../java/org/olat/test/AllTestsJunit4.java    |   4 +-
 36 files changed, 1170 insertions(+), 201 deletions(-)
 create mode 100644 src/main/java/org/olat/ims/qti21/AssessmentItemSession.java
 rename src/{test/java/org/olat/ims/qti21/manager/EventDAOTest.java => main/java/org/olat/ims/qti21/AssessmentResponse.java} (67%)
 rename src/main/java/org/olat/ims/qti21/{UserTestSession.java => AssessmentTestSession.java} (95%)
 create mode 100644 src/main/java/org/olat/ims/qti21/manager/AssessmentItemSessionDAO.java
 create mode 100644 src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java
 rename src/main/java/org/olat/ims/qti21/manager/{SessionDAO.java => AssessmentTestSessionDAO.java} (77%)
 create mode 100644 src/main/java/org/olat/ims/qti21/model/AssessmentFileSubmission.java
 create mode 100644 src/main/java/org/olat/ims/qti21/model/ResponseLegality.java
 create mode 100644 src/main/java/org/olat/ims/qti21/model/jpa/AssessmentItemSessionImpl.java
 create mode 100644 src/main/java/org/olat/ims/qti21/model/jpa/AssessmentResponseImpl.java
 rename src/main/java/org/olat/ims/qti21/model/jpa/{UserTestSessionImpl.java => AssessmentTestSessionImpl.java} (94%)
 create mode 100644 src/test/java/org/olat/ims/qti21/manager/AssessmentResponseDAOTest.java
 rename src/test/java/org/olat/ims/qti21/manager/{SessionDAOTest.java => AssessmentTestSessionDAOTest.java} (77%)

diff --git a/src/main/java/org/olat/core/logging/OLATRuntimeException.java b/src/main/java/org/olat/core/logging/OLATRuntimeException.java
index 4ec9ea47562..143e1b1ff49 100644
--- a/src/main/java/org/olat/core/logging/OLATRuntimeException.java
+++ b/src/main/java/org/olat/core/logging/OLATRuntimeException.java
@@ -86,6 +86,10 @@ public class OLATRuntimeException extends RuntimeException {
 	public OLATRuntimeException(String logMsg, Throwable cause) {
 		this (OLATRuntimeException.class, null, null, null, logMsg, cause);
 	}
+	
+	public OLATRuntimeException(String logMsg) {
+		this (OLATRuntimeException.class, null, null, null, logMsg, null);
+	}
 
 	/**
 	 * Format throwable as HTML fragment.
diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java
index 6642523c2b9..6693f149ad8 100644
--- a/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java
+++ b/src/main/java/org/olat/course/nodes/iq/QTI21AssessmentDetailsController.java
@@ -42,7 +42,7 @@ import org.olat.course.nodes.IQTESTCourseNode;
 import org.olat.course.nodes.iq.QTI21TestSessionTableModel.TSCols;
 import org.olat.course.run.userview.UserCourseEnvironment;
 import org.olat.ims.qti21.QTI21Service;
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.ui.AssessmentResultController;
 import org.olat.repository.RepositoryEntry;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -97,7 +97,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController {
 	}
 	
 	private void updateModel() {
-		List<UserTestSession> sessions = qtiService.getUserTestSessions(courseEntry, courseNode.getIdent(), assessedIdentity);
+		List<AssessmentTestSession> sessions = qtiService.getAssessmentTestSessions(courseEntry, courseNode.getIdent(), assessedIdentity);
 		tableModel.setObjects(sessions);
 		tableEl.reset();
 	}
@@ -124,7 +124,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController {
 			if(event instanceof SelectionEvent) {
 				SelectionEvent se = (SelectionEvent)event;
 				String cmd = se.getCommand();
-				UserTestSession row = tableModel.getObject(se.getIndex());
+				AssessmentTestSession row = tableModel.getObject(se.getIndex());
 				if("result".equals(cmd)) {
 					doOpenResult(ureq, row);
 				}
@@ -138,7 +138,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController {
 		//
 	}
 
-	private void doOpenResult(UserRequest ureq, UserTestSession row) {
+	private void doOpenResult(UserRequest ureq, AssessmentTestSession row) {
 		if(resultCtrl != null) return;
 		
 		resultCtrl = new AssessmentResultController(ureq, getWindowControl());
diff --git a/src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java b/src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java
index 5f833c95f84..126070d2834 100644
--- a/src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java
+++ b/src/main/java/org/olat/course/nodes/iq/QTI21TestSessionTableModel.java
@@ -23,7 +23,7 @@ import java.util.Date;
 
 import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 
 /**
  * 
@@ -31,20 +31,20 @@ import org.olat.ims.qti21.UserTestSession;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QTI21TestSessionTableModel extends DefaultFlexiTableDataModel<UserTestSession> {
+public class QTI21TestSessionTableModel extends DefaultFlexiTableDataModel<AssessmentTestSession> {
 	
 	public QTI21TestSessionTableModel(FlexiTableColumnModel columnModel) {
 		super(columnModel);
 	}
 
 	@Override
-	public DefaultFlexiTableDataModel<UserTestSession> createCopyWithEmptyList() {
+	public DefaultFlexiTableDataModel<AssessmentTestSession> createCopyWithEmptyList() {
 		return new QTI21TestSessionTableModel(getTableColumnModel());
 	}
 
 	@Override
 	public Object getValueAt(int row, int col) {
-		UserTestSession session = getObject(row);
+		AssessmentTestSession session = getObject(row);
 		switch(TSCols.values()[col]) {
 			case lastModified: return session.getLastModified();
 			case results: {
diff --git a/src/main/java/org/olat/ims/qti21/AssessmentItemSession.java b/src/main/java/org/olat/ims/qti21/AssessmentItemSession.java
new file mode 100644
index 00000000000..ff83280e7c0
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/AssessmentItemSession.java
@@ -0,0 +1,32 @@
+/**
+ * <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.ims.qti21;
+
+/**
+ * 
+ * Initial date: 02.02.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface AssessmentItemSession {
+	
+	public Long getKey();
+
+}
diff --git a/src/test/java/org/olat/ims/qti21/manager/EventDAOTest.java b/src/main/java/org/olat/ims/qti21/AssessmentResponse.java
similarity index 67%
rename from src/test/java/org/olat/ims/qti21/manager/EventDAOTest.java
rename to src/main/java/org/olat/ims/qti21/AssessmentResponse.java
index 266e3797491..06cf98e4a0e 100644
--- a/src/test/java/org/olat/ims/qti21/manager/EventDAOTest.java
+++ b/src/main/java/org/olat/ims/qti21/AssessmentResponse.java
@@ -17,29 +17,25 @@
  * frentix GmbH, http://www.frentix.com
  * <p>
  */
-package org.olat.ims.qti21.manager;
+package org.olat.ims.qti21;
 
-import org.junit.Assert;
-import org.junit.Test;
-import org.olat.test.OlatTestCase;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.olat.core.id.CreateInfo;
+import org.olat.core.id.ModifiedInfo;
 
 /**
  * 
- * Initial date: 20.05.2015<br>
+ * Initial date: 29.01.2016<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class EventDAOTest extends OlatTestCase {
+public interface AssessmentResponse extends CreateInfo, ModifiedInfo {
+
+	public Long getKey();
 	
-	@Autowired
-	private EventDAO eventDao;
+	public String getResponseIdentifier();
+	
+	public String getStringuifiedResponse();
+	
+	public void setStringuifiedResponse(String response);
 	
-	@Test
-	public void createEvent() {
-		Assert.assertNotNull(eventDao);
-		//nothing 
-		//eventDao.create(textEventType, itemEventType, itemKey);
-	}
-
 }
diff --git a/src/main/java/org/olat/ims/qti21/UserTestSession.java b/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java
similarity index 95%
rename from src/main/java/org/olat/ims/qti21/UserTestSession.java
rename to src/main/java/org/olat/ims/qti21/AssessmentTestSession.java
index a82103d51db..a30f67cf6cd 100644
--- a/src/main/java/org/olat/ims/qti21/UserTestSession.java
+++ b/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java
@@ -31,7 +31,7 @@ import org.olat.core.id.ModifiedInfo;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public interface UserTestSession extends CreateInfo, ModifiedInfo {
+public interface AssessmentTestSession extends CreateInfo, ModifiedInfo {
 	
 	public Long getKey();
 	
diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java
index 59c87a87a7e..4fb2249b96f 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Service.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java
@@ -21,14 +21,17 @@ package org.olat.ims.qti21;
 
 import java.io.File;
 import java.net.URI;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import org.olat.basesecurity.IdentityRef;
 import org.olat.core.gui.components.form.flexible.impl.MultipartFileInfos;
 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.ResponseLegality;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
@@ -44,6 +47,8 @@ import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
 import uk.ac.ed.ph.jqtiplus.state.ItemSessionState;
 import uk.ac.ed.ph.jqtiplus.state.TestPlanNodeKey;
 import uk.ac.ed.ph.jqtiplus.state.TestSessionState;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
+import uk.ac.ed.ph.jqtiplus.types.ResponseData.ResponseDataType;
 import uk.ac.ed.ph.jqtiplus.xmlutils.xslt.XsltStylesheetCache;
 import uk.ac.ed.ph.jqtiplus.xmlutils.xslt.XsltStylesheetManager;
 
@@ -101,15 +106,15 @@ public interface QTI21Service {
 	public void setDeliveryOptions(RepositoryEntry testEntry, QTI21DeliveryOptions options);
 	
 	
-	public UserTestSession createTestSession(Identity identity, AssessmentEntry assessmentEntry,
+	public AssessmentTestSession createAssessmentTestSession(Identity identity, AssessmentEntry assessmentEntry,
 			RepositoryEntry entry, String subIdent, RepositoryEntry testEntry,
 			boolean authorMode);
 	
-	public UserTestSession getResumableTestSession(Identity identity, RepositoryEntry entry, String subIdent, RepositoryEntry testEntry);
+	public AssessmentTestSession getResumableAssessmentTestSession(Identity identity, RepositoryEntry entry, String subIdent, RepositoryEntry testEntry);
 	
-	public UserTestSession updateTestSession(UserTestSession session);
+	public AssessmentTestSession updateAssessmentTestSession(AssessmentTestSession session);
 	
-	public TestSessionState loadTestSessionState(UserTestSession session);
+	public TestSessionState loadTestSessionState(AssessmentTestSession session);
 	
 	/**
 	 * Retrieve the sessions of a user.
@@ -119,35 +124,44 @@ public interface QTI21Service {
 	 * @param identity
 	 * @return
 	 */
-	public List<UserTestSession> getUserTestSessions(RepositoryEntryRef courseEntry, String subIdent, IdentityRef identity);
+	public List<AssessmentTestSession> getAssessmentTestSessions(RepositoryEntryRef courseEntry, String subIdent, IdentityRef identity);
 	
-	public UserTestSession recordTestAssessmentResult(UserTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResult);
+	public AssessmentItemSession getOrCreateAssessmentItemSession(AssessmentTestSession candidateSession, String assessmentItemIdentifier);
 	
-	public UserTestSession finishTestSession(UserTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResul, Date timestamp);
+	public AssessmentResponse createAssessmentResponse(AssessmentTestSession candidateSession, AssessmentItemSession assessmentItemSession,
+			String responseIdentifier, ResponseLegality legality, ResponseDataType type);
 	
-	public CandidateEvent recordCandidateTestEvent(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public Map<Identifier, AssessmentResponse> getAssessmentResponses(AssessmentItemSession assessmentItemSession);
+	
+	public void recordTestAssessmentResponses(Collection<AssessmentResponse> responses);
+	
+	public AssessmentTestSession recordTestAssessmentResult(AssessmentTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResult);
+	
+	public AssessmentTestSession finishTestSession(AssessmentTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResul, Date timestamp);
+	
+	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			TestSessionState testSessionState, NotificationRecorder notificationRecorder);
 
-	public CandidateEvent recordCandidateTestEvent(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			CandidateItemEventType itemEventType, TestSessionState testSessionState, NotificationRecorder notificationRecorder);
 
-	public CandidateEvent recordCandidateTestEvent(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			CandidateItemEventType itemEventType, TestPlanNodeKey itemKey, TestSessionState testSessionState, NotificationRecorder notificationRecorder);
 	
 	
 	
 
-	public UserTestSession finishItemSession(UserTestSession candidateSession, AssessmentResult assessmentResul, Date timestamp);
+	public AssessmentTestSession finishItemSession(AssessmentTestSession candidateSession, AssessmentResult assessmentResul, Date timestamp);
 	
 
-	public void recordItemAssessmentResult(UserTestSession candidateSession, AssessmentResult assessmentResult);
+	public void recordItemAssessmentResult(AssessmentTestSession candidateSession, AssessmentResult assessmentResult);
 	
-	public CandidateEvent recordCandidateItemEvent(UserTestSession candidateSession, CandidateItemEventType itemEventType,
+	public CandidateEvent recordCandidateItemEvent(AssessmentTestSession candidateSession, CandidateItemEventType itemEventType,
 			ItemSessionState itemSessionState, NotificationRecorder notificationRecorder);
 	
-	public CandidateEvent recordCandidateItemEvent(UserTestSession candidateSession,
+	public CandidateEvent recordCandidateItemEvent(AssessmentTestSession candidateSession,
             CandidateItemEventType itemEventType, ItemSessionState itemSessionState);
 	
-	public String importFileSubmission(UserTestSession candidateSession, MultipartFileInfos multipartFile);
+	public String importFileSubmission(AssessmentTestSession candidateSession, MultipartFileInfos multipartFile);
 
 }
diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentItemSessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentItemSessionDAO.java
new file mode 100644
index 00000000000..cf696d02148
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentItemSessionDAO.java
@@ -0,0 +1,68 @@
+/**
+ * <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.ims.qti21.manager;
+
+import java.util.Date;
+import java.util.List;
+
+import org.olat.core.commons.persistence.DB;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentTestSession;
+import org.olat.ims.qti21.model.jpa.AssessmentItemSessionImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * Initial date: 02.02.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class AssessmentItemSessionDAO {
+	
+	@Autowired
+	private DB dbInstance;
+	
+	public AssessmentItemSession createAndPersistAssessmentItemSession(AssessmentTestSession assessmentTestSession, String assessmentItemIdentifier) {
+		AssessmentItemSessionImpl itemSession = new AssessmentItemSessionImpl();
+		Date now = new Date();
+		itemSession.setCreationDate(now);
+		itemSession.setLastModified(now);
+		itemSession.setAssessmentItemIdentifier(assessmentItemIdentifier);
+		itemSession.setAssessmentTestSession(assessmentTestSession);
+		dbInstance.getCurrentEntityManager().persist(itemSession);
+		return itemSession;
+	}
+	
+	public AssessmentItemSession getAssessmentItemSession(AssessmentTestSession assessmentTestSession, String assessmentItemIdentifier) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select itemSession from qtiassessmentitemsession itemSession")
+		  .append(" where itemSession.assessmentItemIdentifier=:assessmentItemIdentifier")
+		  .append(" and itemSession.assessmentTestSession.key=:assessmentTestSessionKey");
+		
+		List<AssessmentItemSession> itemSessions = dbInstance.getCurrentEntityManager()
+			.createQuery(sb.toString(), AssessmentItemSession.class)
+			.setParameter("assessmentItemIdentifier", assessmentItemIdentifier)
+			.setParameter("assessmentTestSessionKey", assessmentTestSession.getKey())
+			.getResultList();
+		return itemSessions == null || itemSessions.isEmpty() ? null : itemSessions.get(0);
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java
new file mode 100644
index 00000000000..caac2da16ff
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentResponseDAO.java
@@ -0,0 +1,85 @@
+/**
+ * <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.ims.qti21.manager;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.olat.core.commons.persistence.DB;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentResponse;
+import org.olat.ims.qti21.AssessmentTestSession;
+import org.olat.ims.qti21.model.ResponseLegality;
+import org.olat.ims.qti21.model.jpa.AssessmentResponseImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import uk.ac.ed.ph.jqtiplus.types.ResponseData.ResponseDataType;
+
+/**
+ * 
+ * Initial date: 29.01.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class AssessmentResponseDAO {
+
+	@Autowired
+	private DB dbInstance;
+	
+	public AssessmentResponse createAssessmentResponse(AssessmentTestSession assessmentTestSession, AssessmentItemSession assessmentItemSession,
+			String responseIdentifier, ResponseLegality legality, ResponseDataType type) {
+		AssessmentResponseImpl response = new AssessmentResponseImpl();
+		Date now = new Date();
+		response.setCreationDate(now);
+		response.setLastModified(now);
+		response.setResponseDataType(type.name());
+		response.setResponseLegality(legality.name());
+		response.setAssessmentItemSession(assessmentItemSession);
+		response.setAssessmentTestSession(assessmentTestSession);
+		response.setResponseIdentifier(responseIdentifier);
+		return response;
+	}
+	
+	public List<AssessmentResponse> getResponses(AssessmentItemSession assessmentItemSession) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select response from qtiassessmentresponse response where")
+		  .append(" response.assessmentItemSession.key=:assessmentItemSessionKey");
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), AssessmentResponse.class)
+				.setParameter("assessmentItemSessionKey", assessmentItemSession.getKey())
+				.getResultList();
+	}
+	
+	public void save(Collection<AssessmentResponse> responses) {
+		if(responses != null && responses.isEmpty()) return;
+		
+		for(AssessmentResponse response:responses) {
+			if(response.getKey() != null) {
+				dbInstance.getCurrentEntityManager().merge(response);
+			} else {
+				dbInstance.getCurrentEntityManager().persist(response);
+			}
+		}
+	}
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java
similarity index 77%
rename from src/main/java/org/olat/ims/qti21/manager/SessionDAO.java
rename to src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java
index 60045e2337b..9803a9bcd5e 100644
--- a/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java
+++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java
@@ -27,8 +27,8 @@ import javax.persistence.TypedQuery;
 import org.olat.basesecurity.IdentityRef;
 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.ims.qti21.AssessmentTestSession;
+import org.olat.ims.qti21.model.jpa.AssessmentTestSessionImpl;
 import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryEntryRef;
@@ -42,7 +42,7 @@ import org.springframework.stereotype.Service;
  *
  */
 @Service
-public class SessionDAO {
+public class AssessmentTestSessionDAO {
 	
 	@Autowired
 	private DB dbInstance;
@@ -50,12 +50,12 @@ public class SessionDAO {
 	private QTI21Storage storage;
 	
 
-	public UserTestSession createTestSession(RepositoryEntry testEntry,
+	public AssessmentTestSession createAndPersistTestSession(RepositoryEntry testEntry,
 			RepositoryEntry repositoryEntry, String subIdent,
 			AssessmentEntry assessmentEntry, Identity identity,
 			boolean authorMode) {
 		
-		UserTestSessionImpl testSession = new UserTestSessionImpl();
+		AssessmentTestSessionImpl testSession = new AssessmentTestSessionImpl();
 		Date now = new Date();
 		testSession.setCreationDate(now);
 		testSession.setLastModified(now);
@@ -71,11 +71,11 @@ public class SessionDAO {
 		return testSession;
 	}
 	
-	public UserTestSession getLastTestSession(RepositoryEntryRef testEntry,
+	public AssessmentTestSession getLastTestSession(RepositoryEntryRef testEntry,
 			RepositoryEntryRef entry, String subIdent, IdentityRef identity) {
 		
 		StringBuilder sb = new StringBuilder();
-		sb.append("select session from qtiassessmentsession session ")
+		sb.append("select session from qtiassessmenttestsession session ")
 		  .append("where session.testEntry.key=:testEntryKey and session.identity.key=:identityKey");
 		if(entry != null) {
 			sb.append(" and session.repositoryEntry.key=:courseEntryKey");
@@ -90,8 +90,8 @@ public class SessionDAO {
 		}
 		sb.append(" order by session.creationDate desc");
 		
-		TypedQuery<UserTestSession> query = dbInstance.getCurrentEntityManager()
-				.createQuery(sb.toString(), UserTestSession.class)
+		TypedQuery<AssessmentTestSession> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), AssessmentTestSession.class)
 				.setParameter("testEntryKey", testEntry.getKey())
 				.setParameter("identityKey", identity.getKey());
 		if(entry != null) {
@@ -101,22 +101,24 @@ public class SessionDAO {
 			query.setParameter("courseSubIdent", subIdent);
 		}
 		
-		List<UserTestSession> lastSessions = query.setMaxResults(1).getResultList();
+		List<AssessmentTestSession> lastSessions = query.setMaxResults(1).getResultList();
 		return lastSessions == null || lastSessions.isEmpty() ? null : lastSessions.get(0);
 	}
 	
-	public UserTestSession update(UserTestSession testSession) {
-		((UserTestSessionImpl)testSession).setLastModified(new Date());
+	public AssessmentTestSession update(AssessmentTestSession testSession) {
+		((AssessmentTestSessionImpl)testSession).setLastModified(new Date());
 		return dbInstance.getCurrentEntityManager().merge(testSession);
 	}
 	
-	public List<UserTestSession> getUserTestSessions(RepositoryEntryRef courseEntry, String courseSubIdent, IdentityRef identity) {
+	public List<AssessmentTestSession> getUserTestSessions(RepositoryEntryRef courseEntry, String courseSubIdent, IdentityRef identity) {
 		return dbInstance.getCurrentEntityManager()
-				.createNamedQuery("loadTestSessionsByUserAndRepositoryEntryAndSubIdent", UserTestSession.class)
+				.createNamedQuery("loadTestSessionsByUserAndRepositoryEntryAndSubIdent", AssessmentTestSession.class)
 				.setParameter("repositoryEntryKey", courseEntry.getKey())
 				.setParameter("identityKey", identity.getKey())
 				.setParameter("subIdent", courseSubIdent)
 				.getResultList();
 	}
+	
+	
 
 }
diff --git a/src/main/java/org/olat/ims/qti21/manager/EventDAO.java b/src/main/java/org/olat/ims/qti21/manager/EventDAO.java
index a1e251fdf6d..a24eb8b9fa4 100644
--- a/src/main/java/org/olat/ims/qti21/manager/EventDAO.java
+++ b/src/main/java/org/olat/ims/qti21/manager/EventDAO.java
@@ -19,7 +19,7 @@
  */
 package org.olat.ims.qti21.manager;
 
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
@@ -36,7 +36,7 @@ import uk.ac.ed.ph.jqtiplus.state.TestPlanNodeKey;
 @Service
 public class EventDAO {
 	
-	public CandidateEvent create(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public CandidateEvent create(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			CandidateItemEventType itemEventType, TestPlanNodeKey itemKey) {
 		
 		CandidateEvent event = new CandidateEvent();
@@ -49,7 +49,7 @@ public class EventDAO {
 		return event;
 	}
 
-	public CandidateEvent create(UserTestSession candidateSession, CandidateItemEventType itemEventType) {
+	public CandidateEvent create(AssessmentTestSession candidateSession, CandidateItemEventType itemEventType) {
 		
 		CandidateEvent event = new CandidateEvent();
         event.setCandidateSession(candidateSession);
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 15c96b92ba6..5af3f8f5dce 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
@@ -28,8 +28,11 @@ import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.transform.Transformer;
@@ -50,14 +53,17 @@ import org.olat.core.util.xml.XStreamHelper;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.fileresource.types.ImsQTI21Resource;
 import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentResponse;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.QTI21ContentPackage;
 import org.olat.ims.qti21.QTI21DeliveryOptions;
 import org.olat.ims.qti21.QTI21Module;
 import org.olat.ims.qti21.QTI21Service;
-import org.olat.ims.qti21.UserTestSession;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
+import org.olat.ims.qti21.model.ResponseLegality;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.rendering.XmlUtilities;
 import org.olat.modules.assessment.AssessmentEntry;
@@ -95,6 +101,7 @@ import uk.ac.ed.ph.jqtiplus.state.TestSessionState;
 import uk.ac.ed.ph.jqtiplus.state.marshalling.ItemSessionStateXmlMarshaller;
 import uk.ac.ed.ph.jqtiplus.state.marshalling.TestSessionStateXmlMarshaller;
 import uk.ac.ed.ph.jqtiplus.types.Identifier;
+import uk.ac.ed.ph.jqtiplus.types.ResponseData.ResponseDataType;
 import uk.ac.ed.ph.jqtiplus.value.BooleanValue;
 import uk.ac.ed.ph.jqtiplus.value.NumberValue;
 import uk.ac.ed.ph.jqtiplus.value.RecordValue;
@@ -128,7 +135,11 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	@Autowired
 	private EventDAO eventDao;
 	@Autowired
-	private SessionDAO testSessionDao;
+	private AssessmentTestSessionDAO testSessionDao;
+	@Autowired
+	private AssessmentItemSessionDAO itemSessionDao;
+	@Autowired
+	private AssessmentResponseDAO testResponseDao;
 	@Autowired
 	private QTI21Storage storage;
 	@Autowired
@@ -305,15 +316,14 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	}
 
 	@Override
-	public UserTestSession createTestSession(Identity identity, AssessmentEntry assessmentEntry,
-			RepositoryEntry entry, String subIdent, RepositoryEntry testEntry,
-			boolean authorMode) {
-		return testSessionDao.createTestSession(testEntry, entry, subIdent, assessmentEntry, identity, authorMode);
+	public AssessmentTestSession createAssessmentTestSession(Identity identity, AssessmentEntry assessmentEntry,
+			RepositoryEntry entry, String subIdent, RepositoryEntry testEntry, boolean authorMode) {
+		return testSessionDao.createAndPersistTestSession(testEntry, entry, subIdent, assessmentEntry, identity, authorMode);
 	}
 
 	@Override
-	public UserTestSession getResumableTestSession(Identity identity, RepositoryEntry entry, String subIdent, RepositoryEntry testEntry) {
-		UserTestSession session = testSessionDao.getLastTestSession(testEntry, entry, subIdent, identity);
+	public AssessmentTestSession getResumableAssessmentTestSession(Identity identity, RepositoryEntry entry, String subIdent, RepositoryEntry testEntry) {
+		AssessmentTestSession session = testSessionDao.getLastTestSession(testEntry, entry, subIdent, identity);
 		if(session == null || session.isExploded() || session.getTerminationTime() != null) {
 			session = null;
 		} else {
@@ -324,14 +334,24 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 		}
 		return session;
 	}
+	
+	@Override
+	public AssessmentTestSession updateAssessmentTestSession(AssessmentTestSession session) {
+		return testSessionDao.update(session);
+	}
+
+	@Override
+	public List<AssessmentTestSession> getAssessmentTestSessions(RepositoryEntryRef courseEntry, String courseSubIdent, IdentityRef identity) {
+		return testSessionDao.getUserTestSessions(courseEntry, courseSubIdent, identity);
+	}
 
 	@Override
-	public TestSessionState loadTestSessionState(UserTestSession candidateSession) {
+	public TestSessionState loadTestSessionState(AssessmentTestSession candidateSession) {
         Document document = loadStateDocument(candidateSession);
         return document == null ? null: TestSessionStateXmlMarshaller.unmarshal(document.getDocumentElement());
     }
 	
-    private Document loadStateDocument(UserTestSession candidateSession) {
+    private Document loadStateDocument(AssessmentTestSession candidateSession) {
         File sessionFile = getTestSessionStateFile(candidateSession);
         if(sessionFile.exists()) {
 	        DocumentBuilder documentBuilder = XmlUtilities.createNsAwareDocumentBuilder();
@@ -345,17 +365,38 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
     }
 
 	@Override
-	public UserTestSession updateTestSession(UserTestSession session) {
-		return testSessionDao.update(session);
+	public AssessmentItemSession getOrCreateAssessmentItemSession(AssessmentTestSession assessmentTestSession, String assessmentItemIdentifier) {
+		AssessmentItemSession itemSession = itemSessionDao.getAssessmentItemSession(assessmentTestSession, assessmentItemIdentifier);
+		if(itemSession == null) {
+			itemSession = itemSessionDao.createAndPersistAssessmentItemSession(assessmentTestSession, assessmentItemIdentifier);
+		}
+		return itemSession;
 	}
 
 	@Override
-	public List<UserTestSession> getUserTestSessions(RepositoryEntryRef courseEntry, String courseSubIdent, IdentityRef identity) {
-		return testSessionDao.getUserTestSessions(courseEntry, courseSubIdent, identity);
+	public AssessmentResponse createAssessmentResponse(AssessmentTestSession assessmentTestSession, AssessmentItemSession assessmentItemSession, String responseIdentifier,
+			ResponseLegality legality, ResponseDataType type) {
+		return testResponseDao.createAssessmentResponse(assessmentTestSession, assessmentItemSession,
+				responseIdentifier, legality, type);
+	}
+	
+	@Override
+	public Map<Identifier, AssessmentResponse> getAssessmentResponses(AssessmentItemSession assessmentItemSession) {
+		List<AssessmentResponse> responses = testResponseDao.getResponses(assessmentItemSession);
+		Map<Identifier, AssessmentResponse> responseMap = new HashMap<>();
+		for(AssessmentResponse response:responses) {
+			responseMap.put(Identifier.assumedLegal(response.getResponseIdentifier()), response);
+		}
+		return responseMap;
+	}
+
+	@Override
+	public void recordTestAssessmentResponses(Collection<AssessmentResponse> responses) {
+		testResponseDao.save(responses);
 	}
 
 	@Override
-	public UserTestSession recordTestAssessmentResult(UserTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResult) {
+	public AssessmentTestSession recordTestAssessmentResult(AssessmentTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResult) {
 		// First record full result XML to filesystem
         storeAssessmentResultFile(candidateSession, assessmentResult);
         // Then record test outcome variables to DB
@@ -366,7 +407,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	}
 
 	@Override
-	public UserTestSession finishTestSession(UserTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResul, Date timestamp) {
+	public AssessmentTestSession finishTestSession(AssessmentTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResul, Date timestamp) {
 		/* Mark session as finished */
         candidateSession.setFinishTime(timestamp);
         // Set duration
@@ -382,7 +423,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
         return candidateSession;
 	}
 	
-    private void recordOutcomeVariables(UserTestSession candidateSession, AbstractResult resultNode) {
+    private void recordOutcomeVariables(AssessmentTestSession candidateSession, AbstractResult resultNode) {
         for (final ItemVariable itemVariable : resultNode.getItemVariables()) {
             if (itemVariable instanceof OutcomeVariable) {
                 
@@ -426,7 +467,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
         return value.toQtiString();
     }
     
-    private void storeAssessmentResultFile(final UserTestSession candidateSession, final QtiNode resultNode) {
+    private void storeAssessmentResultFile(final AssessmentTestSession candidateSession, final QtiNode resultNode) {
         final File resultFile = getAssessmentResultFile(candidateSession);
         try(OutputStream resultStream = FileUtils.getBos(resultFile);) {
             qtiSerializer().serializeJqtiObject(resultNode, resultStream);
@@ -435,19 +476,19 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
         }
     }
     
-    private File getAssessmentResultFile(final UserTestSession candidateSession) {
+    private File getAssessmentResultFile(final AssessmentTestSession candidateSession) {
     	File myStore = storage.getDirectory(candidateSession.getStorage());
         return new File(myStore, "assessmentResult.xml");
     }
 
 	@Override
-	public CandidateEvent recordCandidateTestEvent(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			TestSessionState testSessionState, NotificationRecorder notificationRecorder) {
 		return recordCandidateTestEvent(candidateSession, textEventType, null, null, testSessionState, notificationRecorder);
 	}
 
 	@Override
-	public CandidateEvent recordCandidateTestEvent(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			CandidateItemEventType itemEventType, TestSessionState testSessionState, NotificationRecorder notificationRecorder) {
 		CandidateEvent event = new CandidateEvent();
 		event.setCandidateSession(candidateSession);
@@ -456,7 +497,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	}
 
 	@Override
-	public CandidateEvent recordCandidateTestEvent(UserTestSession candidateSession, CandidateTestEventType textEventType,
+	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			CandidateItemEventType itemEventType, TestPlanNodeKey itemKey, TestSessionState testSessionState, NotificationRecorder notificationRecorder) {
 		CandidateEvent event = eventDao.create(candidateSession, textEventType, itemEventType, itemKey);
 		storeTestSessionState(event, testSessionState);
@@ -470,23 +511,23 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	}
 
     private File getTestSessionStateFile(CandidateEvent candidateEvent) {
-    	UserTestSession candidateSession = candidateEvent.getCandidateSession();
+    	AssessmentTestSession candidateSession = candidateEvent.getCandidateSession();
     	return getTestSessionStateFile(candidateSession);
     }
     
-    private File getTestSessionStateFile(UserTestSession candidateSession) {
+    private File getTestSessionStateFile(AssessmentTestSession candidateSession) {
     	File myStore = storage.getDirectory(candidateSession.getStorage());
         return new File(myStore, "testSessionState.xml");
     }
 	
     @Override
-	public CandidateEvent recordCandidateItemEvent(UserTestSession candidateSession, CandidateItemEventType itemEventType,
+	public CandidateEvent recordCandidateItemEvent(AssessmentTestSession candidateSession, CandidateItemEventType itemEventType,
 			ItemSessionState itemSessionState) {
 		return recordCandidateItemEvent(candidateSession, itemEventType, itemSessionState, null);
 	}
 		
 	@Override
-    public CandidateEvent recordCandidateItemEvent(UserTestSession candidateSession, CandidateItemEventType itemEventType,
+    public CandidateEvent recordCandidateItemEvent(AssessmentTestSession candidateSession, CandidateItemEventType itemEventType,
     		ItemSessionState itemSessionState, NotificationRecorder notificationRecorder) {
     	return eventDao.create(candidateSession, itemEventType);
     }
@@ -498,7 +539,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
     }
     
     private File getItemSessionStateFile(CandidateEvent candidateEvent) {
-    	UserTestSession candidateSession = candidateEvent.getCandidateSession();
+    	AssessmentTestSession candidateSession = candidateEvent.getCandidateSession();
     	File myStore = storage.getDirectory(candidateSession.getStorage());
         return new File(myStore, "itemSessionState.xml");
     }
@@ -517,7 +558,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
     }
 
 	@Override
-	public UserTestSession finishItemSession(UserTestSession candidateSession, AssessmentResult assessmentResult, Date timestamp) {
+	public AssessmentTestSession finishItemSession(AssessmentTestSession candidateSession, AssessmentResult assessmentResult, Date timestamp) {
 		/* Mark session as finished */
         candidateSession.setFinishTime(timestamp);
 
@@ -532,7 +573,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	}
 
 	@Override
-	public void recordItemAssessmentResult(UserTestSession candidateSession, AssessmentResult assessmentResult) {
+	public void recordItemAssessmentResult(AssessmentTestSession candidateSession, AssessmentResult assessmentResult) {
 		//do nothing for the mmoment
 		List<ItemResult> itemResults = assessmentResult.getItemResults();
 		for(ItemResult itemResult:itemResults) {
@@ -558,7 +599,7 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
 	
 
 	@Override
-	public String importFileSubmission(UserTestSession candidateSession, MultipartFileInfos multipartFile) {
+	public String importFileSubmission(AssessmentTestSession candidateSession, MultipartFileInfos multipartFile) {
 		File myStore = storage.getDirectory(candidateSession.getStorage());
         File submissionDir = new File(myStore, "submissions");
         if(!submissionDir.exists()) {
diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java
index cf016bec360..8e617784806 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21StatisticsManagerImpl.java
@@ -50,8 +50,9 @@ public class QTI21StatisticsManagerImpl implements QTI21StatisticsManager {
 		if(searchParams.getCourseEntry() != null) {
 			sb.append(" and asession.repositoryEntry.key=:repositoryEntryKey and asession.subIdent=:subIdent");
 		}
-		sb.append(" and asession.lastModified = (select max(a2session.lastModified) from qtiassessmentsession a2session")
-		  .append("   where a2session.identity=asession.identity and a2session.repositoryEntry=asession.repositoryEntry")
+		sb.append(" and asession.lastModified = (select max(a2session.lastModified) from qtiassessmenttestsession a2session")
+		  .append("   where a2session.identity.key=asession.identity.key and a2session.repositoryEntry.key=asession.repositoryEntry.key")
+		  .append("   and a2session.subIdent=asession.subIdent")
 		  .append(" )");
 		
 		if(searchParams.getLimitToGroups() != null && searchParams.getLimitToGroups().size() > 0) {
@@ -82,7 +83,7 @@ public class QTI21StatisticsManagerImpl implements QTI21StatisticsManager {
 	@Override
 	public StatisticAssessment getAssessmentStatistics(QTI21StatisticSearchParams searchParams) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select asession.score, asession.passed, asession.duration from qtiassessmentsession asession ");
+		sb.append("select asession.score, asession.passed, asession.duration from qtiassessmenttestsession asession ");
 		decorateRSet(sb, searchParams);
 		sb.append(" order by asession.key asc");
 
diff --git a/src/main/java/org/olat/ims/qti21/model/AssessmentFileSubmission.java b/src/main/java/org/olat/ims/qti21/model/AssessmentFileSubmission.java
new file mode 100644
index 00000000000..e71d93e290b
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/AssessmentFileSubmission.java
@@ -0,0 +1,30 @@
+/**
+ * <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.ims.qti21.model;
+
+/**
+ * 
+ * Initial date: 29.01.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentFileSubmission {
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/ResponseLegality.java b/src/main/java/org/olat/ims/qti21/model/ResponseLegality.java
new file mode 100644
index 00000000000..955d67e8430
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/ResponseLegality.java
@@ -0,0 +1,48 @@
+/* Copyright (c) 2012-2013, University of Edinburgh.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ * * Neither the name of the University of Edinburgh nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * This software is derived from (and contains code from) QTItools and MathAssessEngine.
+ * QTItools is (c) 2008, University of Southampton.
+ * MathAssessEngine is (c) 2010, University of Edinburgh.
+ */
+package org.olat.ims.qti21.model;
+
+/**
+ * Encapsulates the legality of a {@link CandidateResponse}
+ *
+ * @author David McKain
+ */
+public enum ResponseLegality {
+
+    VALID,
+    INVALID,
+    BAD
+    ;
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentItemSessionImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentItemSessionImpl.java
new file mode 100644
index 00000000000..93c411c7fc2
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentItemSessionImpl.java
@@ -0,0 +1,169 @@
+/**
+ * <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.ims.qti21.model.jpa;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.olat.core.id.Persistable;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentTestSession;
+
+/**
+ * 
+ * Initial date: 02.02.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Entity(name="qtiassessmentitemsession")
+@Table(name="o_qti_assessmentitem_session")
+public class AssessmentItemSessionImpl implements AssessmentItemSession, Persistable {
+
+	private static final long serialVersionUID = 404608933232435117L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="lastmodified", nullable=false, insertable=true, updatable=true)
+	private Date lastModified;
+	
+    @Column(name="q_itemidentifier", nullable=false, insertable=true, updatable=false)
+    private String assessmentItemIdentifier;
+    
+    @Column(name="q_duration", nullable=true, insertable=true, updatable=true)
+    private Long duration;
+    @Column(name="q_passed", nullable=true, insertable=true, updatable=true)
+    private Boolean passed;
+    @Column(name="q_score", nullable=true, insertable=true, updatable=true)
+    private BigDecimal score;
+    
+	@ManyToOne(targetEntity=AssessmentTestSessionImpl.class,fetch=FetchType.LAZY,optional=false)
+	@JoinColumn(name="fk_assessmenttest_session", nullable=false, insertable=true, updatable=false)
+	private AssessmentTestSession assessmentTestSession;
+    
+
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	public void setKey(Long key) {
+		this.key = key;
+	}
+
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	public void setLastModified(Date lastModified) {
+		this.lastModified = lastModified;
+	}
+
+	public Long getDuration() {
+		return duration;
+	}
+
+	public void setDuration(Long duration) {
+		this.duration = duration;
+	}
+
+	public Boolean getPassed() {
+		return passed;
+	}
+
+	public void setPassed(Boolean passed) {
+		this.passed = passed;
+	}
+
+	public BigDecimal getScore() {
+		return score;
+	}
+
+	public void setScore(BigDecimal score) {
+		this.score = score;
+	}
+
+	public String getAssessmentItemIdentifier() {
+		return assessmentItemIdentifier;
+	}
+
+	public void setAssessmentItemIdentifier(String assessmentItemIdentifier) {
+		this.assessmentItemIdentifier = assessmentItemIdentifier;
+	}
+
+	public AssessmentTestSession getAssessmentTestSession() {
+		return assessmentTestSession;
+	}
+
+	public void setAssessmentTestSession(AssessmentTestSession assessmentTestSession) {
+		this.assessmentTestSession = assessmentTestSession;
+	}
+	
+	
+	
+	@Override
+	public int hashCode() {
+		return key == null ? -86534687 : key.hashCode();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof AssessmentItemSessionImpl) {
+			AssessmentItemSessionImpl session = (AssessmentItemSessionImpl)obj;
+			return getKey() != null && getKey().equals(session.getKey());
+		}
+		return false;
+	}
+	
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+	
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentResponseImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentResponseImpl.java
new file mode 100644
index 00000000000..60daf231cac
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentResponseImpl.java
@@ -0,0 +1,177 @@
+/**
+ * <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.ims.qti21.model.jpa;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.olat.core.id.Persistable;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentResponse;
+import org.olat.ims.qti21.AssessmentTestSession;
+
+/**
+ * 
+ * Initial date: 29.01.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Entity(name="qtiassessmentresponse")
+@Table(name="o_qti_assessment_response")
+public class AssessmentResponseImpl implements AssessmentResponse, Persistable {
+
+	private static final long serialVersionUID = 7341596483676802054L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="lastmodified", nullable=false, insertable=true, updatable=true)
+	private Date lastModified;
+
+	@Column(name="q_responseidentifier", nullable=false, insertable=true, updatable=false)
+	private String responseIdentifier;
+	@Column(name="q_responsedatatype", nullable=false, insertable=true, updatable=false)
+	private String responseDataType;
+	@Column(name="q_responselegality", nullable=false, insertable=true, updatable=true)
+	private String responseLegality;
+	@Column(name="q_stringuifiedresponse", nullable=false, insertable=true, updatable=true)
+	private String stringuifiedResponse;
+	
+	@ManyToOne(targetEntity=AssessmentItemSessionImpl.class,fetch=FetchType.LAZY,optional=false)
+	@JoinColumn(name="fk_assessmentitem_session", nullable=false, insertable=true, updatable=false)
+	private AssessmentItemSession assessmentItemSession;
+	
+	@ManyToOne(targetEntity=AssessmentTestSessionImpl.class,fetch=FetchType.LAZY,optional=true)
+	@JoinColumn(name="fk_assessmenttest_session", nullable=true, insertable=true, updatable=false)
+	private AssessmentTestSession assessmentTestSession;
+
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	public void setKey(Long key) {
+		this.key = key;
+	}
+
+	@Override
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	public void setLastModified(Date lastModified) {
+		this.lastModified = lastModified;
+	}
+
+	public String getResponseIdentifier() {
+		return responseIdentifier;
+	}
+
+	public void setResponseIdentifier(String responseIdentifier) {
+		this.responseIdentifier = responseIdentifier;
+	}
+
+	public String getResponseDataType() {
+		return responseDataType;
+	}
+
+	public void setResponseDataType(String responseDataType) {
+		this.responseDataType = responseDataType;
+	}
+
+	public String getResponseLegality() {
+		return responseLegality;
+	}
+
+	public void setResponseLegality(String responseLegality) {
+		this.responseLegality = responseLegality;
+	}
+
+	public String getStringuifiedResponse() {
+		return stringuifiedResponse;
+	}
+
+	public void setStringuifiedResponse(String stringuifiedResponse) {
+		this.stringuifiedResponse = stringuifiedResponse;
+	}
+
+	public AssessmentItemSession getAssessmentItemSession() {
+		return assessmentItemSession;
+	}
+
+	public void setAssessmentItemSession(AssessmentItemSession assessmentItemSession) {
+		this.assessmentItemSession = assessmentItemSession;
+	}
+
+	public AssessmentTestSession getAssessmentTestSession() {
+		return assessmentTestSession;
+	}
+
+	public void setAssessmentTestSession(AssessmentTestSession assessmentTestSession) {
+		this.assessmentTestSession = assessmentTestSession;
+	}
+
+	@Override
+	public int hashCode() {
+		return key == null ? -86534687 : key.hashCode();
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		}
+		if(obj instanceof AssessmentResponseImpl) {
+			AssessmentResponseImpl response = (AssessmentResponseImpl)obj;
+			return getKey() != null && getKey().equals(response.getKey());
+		}
+		return false;
+	}
+	
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java
similarity index 94%
rename from src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java
rename to src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java
index aacfb64d437..f23fed4ecc2 100644
--- a/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java
+++ b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java
@@ -39,7 +39,7 @@ import javax.persistence.TemporalType;
 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.ims.qti21.AssessmentTestSession;
 import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.assessment.model.AssessmentEntryImpl;
 import org.olat.repository.RepositoryEntry;
@@ -52,13 +52,13 @@ import org.olat.repository.RepositoryEntry;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-@Entity(name="qtiassessmentsession")
-@Table(name="o_qti_assessment_session")
+@Entity(name="qtiassessmenttestsession")
+@Table(name="o_qti_assessmenttest_session")
 @NamedQueries({
-	@NamedQuery(name="loadTestSessionsByUserAndRepositoryEntryAndSubIdent", query="select session from qtiassessmentsession session where session.repositoryEntry.key=:repositoryEntryKey and session.identity.key=:identityKey and session.subIdent=:subIdent")
+	@NamedQuery(name="loadTestSessionsByUserAndRepositoryEntryAndSubIdent", query="select session from qtiassessmenttestsession session where session.repositoryEntry.key=:repositoryEntryKey and session.identity.key=:identityKey and session.subIdent=:subIdent")
 	
 })
-public class UserTestSessionImpl implements UserTestSession, Persistable {
+public class AssessmentTestSessionImpl implements AssessmentTestSession, Persistable {
 
 	private static final long serialVersionUID = -6069133323360142500L;
 
@@ -304,8 +304,8 @@ public class UserTestSessionImpl implements UserTestSession, Persistable {
 		if(this == obj) {
 			return true;
 		}
-		if(obj instanceof UserTestSessionImpl) {
-			UserTestSessionImpl session = (UserTestSessionImpl)obj;
+		if(obj instanceof AssessmentTestSessionImpl) {
+			AssessmentTestSessionImpl session = (AssessmentTestSessionImpl)obj;
 			return getKey() != null && getKey().equals(session.getKey());
 		}
 		return false;
diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java b/src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java
index f985f33f93f..cedb9e3b2b4 100644
--- a/src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java
+++ b/src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java
@@ -21,7 +21,7 @@ package org.olat.ims.qti21.model.jpa;
 
 import java.util.Date;
 
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
 
@@ -36,7 +36,7 @@ public class CandidateEvent {
 	private Date timestamp;
     private String testItemKey;
 
-	private UserTestSession candidateSession;
+	private AssessmentTestSession candidateSession;
 	
 	private CandidateTestEventType testEventType;
 	private CandidateItemEventType itemEventType;
@@ -77,11 +77,11 @@ public class CandidateEvent {
 		this.testItemKey = testItemKey;
 	}
 
-	public UserTestSession getCandidateSession() {
+	public AssessmentTestSession getCandidateSession() {
 		return candidateSession;
 	}
 
-	public void setCandidateSession(UserTestSession candidateSession) {
+	public void setCandidateSession(AssessmentTestSession candidateSession) {
 		this.candidateSession = candidateSession;
 	}
 	
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/items/EssayAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/items/EssayAssessmentItemBuilder.java
index 0d68f723dc9..5cb303a70bf 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/items/EssayAssessmentItemBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/items/EssayAssessmentItemBuilder.java
@@ -1,3 +1,22 @@
+/**
+ * <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.ims.qti21.model.xml.items;
 
 import static org.olat.ims.qti21.model.xml.AssessmentItemFactory.appendDefaultItemBody;
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 15d18d08205..6fb4c274467 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java
@@ -44,7 +44,7 @@ import org.olat.core.gui.control.controller.BasicController;
 import org.olat.fileresource.types.ImsQTI21Resource;
 import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
 import org.olat.ims.qti21.QTI21Service;
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.components.AssessmentItemFormItem;
@@ -90,7 +90,7 @@ public class AssessmentItemDisplayController extends BasicController implements
 	
 	private CandidateEvent lastEvent;
 	private Date currentRequestTimestamp;
-	private UserTestSession candidateSession;
+	private AssessmentTestSession candidateSession;
 
 	@Autowired
 	private QTI21Service qtiService;
@@ -104,7 +104,7 @@ public class AssessmentItemDisplayController extends BasicController implements
 		this.fUnzippedDirRoot = fUnzippedDirRoot;
 		this.resolvedAssessmentItem = resolvedAssessmentItem;
 		currentRequestTimestamp = ureq.getRequestTimestamp();
-		candidateSession = qtiService.createTestSession(getIdentity(), assessmentEntry, testEntry, itemRef.getIdentifier().toString(), testEntry, authorMode);
+		candidateSession = qtiService.createAssessmentTestSession(getIdentity(), assessmentEntry, testEntry, itemRef.getIdentifier().toString(), testEntry, authorMode);
 		
 		//TODO qti beautify
 		URI assessmentObjectUri = new File(fUnzippedDirRoot, itemRef.getHref().toString()).toURI();
@@ -139,7 +139,7 @@ public class AssessmentItemDisplayController extends BasicController implements
 	}
 
 	@Override
-	public UserTestSession getCandidateSession() {
+	public AssessmentTestSession getCandidateSession() {
 		return candidateSession;
 	}
 
@@ -405,7 +405,7 @@ public class AssessmentItemDisplayController extends BasicController implements
 		updateSessionFinishedStatus(ureq);
 	}
 	
-    private UserTestSession updateSessionFinishedStatus(UserRequest ureq) {
+    private AssessmentTestSession updateSessionFinishedStatus(UserRequest ureq) {
         /* Record current result state and maybe close session */
         final ItemSessionState itemSessionState = itemSessionController.getItemSessionState();
         final AssessmentResult assessmentResult = computeAndRecordItemAssessmentResult(ureq);
@@ -416,7 +416,7 @@ public class AssessmentItemDisplayController extends BasicController implements
             if (candidateSession != null && candidateSession.getFinishTime() != null) {
                 /* (Session is being reopened) */
                 candidateSession.setFinishTime(null);
-                candidateSession = qtiService.updateTestSession(candidateSession);
+                candidateSession = qtiService.updateAssessmentTestSession(candidateSession);
             }
         }
         return candidateSession;
@@ -563,7 +563,7 @@ public class AssessmentItemDisplayController extends BasicController implements
 
 	        /* Update session entity */
 	        candidateSession.setTerminationTime(currentTimestamp);
-	        candidateSession = qtiService.updateTestSession(candidateSession);
+	        candidateSession = qtiService.updateAssessmentTestSession(candidateSession);
 
 	        /* Record and log event */
 	        final CandidateEvent candidateEvent = qtiService.recordCandidateItemEvent(candidateSession,
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 06ad5bb731c..13240f51128 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
@@ -45,17 +45,22 @@ import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory;
 import org.olat.core.id.OLATResourceable;
 import org.olat.core.id.context.BusinessControlFactory;
 import org.olat.core.id.context.ContextEntry;
+import org.olat.core.logging.OLATRuntimeException;
 import org.olat.core.util.resource.OresHelper;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.fileresource.types.ImsQTI21Resource;
 import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentResponse;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.OutcomesListener;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.QTI21DeliveryOptions;
 import org.olat.ims.qti21.QTI21Service;
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.model.AssessmentFileSubmission;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.CandidateTestEventType;
+import org.olat.ims.qti21.model.ResponseLegality;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.components.AssessmentTestFormItem;
 import org.olat.modules.assessment.AssessmentEntry;
@@ -111,7 +116,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 	
 	private CandidateEvent lastEvent;
 	private Date currentRequestTimestamp;
-	private UserTestSession candidateSession;
+	private AssessmentTestSession candidateSession;
 	private AssessmentEntry assessmentEntry;
 	private ResolvedAssessmentTest resolvedAssessmentTest;
 	
@@ -152,12 +157,12 @@ public class AssessmentTestDisplayController extends BasicController implements
 		boolean allowResume = deliveryOptions != null && deliveryOptions.getEnableSuspend() != null
 				&& deliveryOptions.getEnableSuspend().booleanValue();
 		
-		UserTestSession lastSession = null;
+		AssessmentTestSession lastSession = null;
 		if(allowResume) {
-			lastSession = qtiService.getResumableTestSession(getIdentity(), entry, subIdent, testEntry);
+			lastSession = qtiService.getResumableAssessmentTestSession(getIdentity(), entry, subIdent, testEntry);
 		}
 		if(lastSession == null) {
-			candidateSession = qtiService.createTestSession(getIdentity(), assessmentEntry, entry, subIdent, testEntry, false);
+			candidateSession = qtiService.createAssessmentTestSession(getIdentity(), assessmentEntry, entry, subIdent, testEntry, false);
 			testSessionController = enterSession(ureq);
 		} else {
 			candidateSession = lastSession;
@@ -198,7 +203,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 	}
 
 	@Override
-	public UserTestSession getCandidateSession() {
+	public AssessmentTestSession getCandidateSession() {
 		return candidateSession;
 	}
 	
@@ -422,33 +427,70 @@ public class AssessmentTestDisplayController extends BasicController implements
         //assertSessionNotTerminated(candidateSession);
 
 		NotificationRecorder notificationRecorder = new NotificationRecorder(NotificationLevel.INFO);
-        TestSessionState testSessionState = testSessionController.getTestSessionState();
+		TestSessionState testSessionState = testSessionController.getTestSessionState();
 		
 		final Map<Identifier, ResponseData> responseDataMap = new HashMap<Identifier, ResponseData>();
-        if (stringResponseMap != null) {
-            for (final Entry<Identifier, StringResponseData> stringResponseEntry : stringResponseMap.entrySet()) {
-                final Identifier identifier = stringResponseEntry.getKey();
-                final StringResponseData stringResponseData = stringResponseEntry.getValue();
-                responseDataMap.put(identifier, stringResponseData);
+		if (stringResponseMap != null) {
+			for (final Entry<Identifier, StringResponseData> stringResponseEntry : stringResponseMap.entrySet()) {
+				final Identifier identifier = stringResponseEntry.getKey();
+				final StringResponseData stringResponseData = stringResponseEntry.getValue();
+				responseDataMap.put(identifier, stringResponseData);
             }
-        }
+		}
         
-       // final Map<Identifier, CandidateFileSubmission> fileSubmissionMap = new HashMap<Identifier, CandidateFileSubmission>();
+		String assessmentItemIdentifier = testSessionState.getCurrentItemKey().getIdentifier().toString();
+		AssessmentItemSession itemSession = qtiService.getOrCreateAssessmentItemSession(candidateSession, assessmentItemIdentifier);
+        
+        Map<Identifier, AssessmentFileSubmission> fileSubmissionMap = new HashMap<Identifier, AssessmentFileSubmission>();
         if (fileResponseMap!=null) {
-            for (final Entry<Identifier, MultipartFileInfos> fileResponseEntry : fileResponseMap.entrySet()) {
-                final Identifier identifier = fileResponseEntry.getKey();
-                final MultipartFileInfos multipartFile = fileResponseEntry.getValue();
+            for (Entry<Identifier, MultipartFileInfos> fileResponseEntry : fileResponseMap.entrySet()) {
+                Identifier identifier = fileResponseEntry.getKey();
+                MultipartFileInfos multipartFile = fileResponseEntry.getValue();
                 if (!multipartFile.isEmpty()) {
-                    //final CandidateFileSubmission fileSubmission = candidateUploadService.importFileSubmission(candidateSession, multipartFile);
                 	String storedFilePath = qtiService.importFileSubmission(candidateSession, multipartFile);
                 	File storedFile = new File(storedFilePath);
                 	final FileResponseData fileResponseData = new FileResponseData(storedFile, multipartFile.getContentType(), multipartFile.getFileName());
                     responseDataMap.put(identifier, fileResponseData);
+                    //final CandidateFileSubmission fileSubmission = candidateUploadService.importFileSubmission(candidateSession, multipartFile);
                     //fileSubmissionMap.put(identifier, fileSubmission);
                 }
             }
         }
         
+        Map<Identifier, AssessmentResponse> candidateResponseMap = qtiService.getAssessmentResponses(itemSession);
+        for (Entry<Identifier, ResponseData> responseEntry : responseDataMap.entrySet()) {
+            Identifier responseIdentifier = responseEntry.getKey();
+            ResponseData responseData = responseEntry.getValue();
+            AssessmentResponse candidateItemResponse;
+            if(candidateResponseMap.containsKey(responseIdentifier)) {
+            	candidateItemResponse = candidateResponseMap.get(responseIdentifier);
+            } else {
+            	candidateItemResponse = qtiService
+            		.createAssessmentResponse(candidateSession, itemSession, responseIdentifier.toString(), ResponseLegality.VALID, responseData.getType());
+            }
+		
+            switch (responseData.getType()) {
+                case STRING:
+                	List<String> data = ((StringResponseData) responseData).getResponseData();
+                	StringBuilder sb = new StringBuilder();
+                	for(String str:data) {
+                		sb.append(str);
+                	}
+                	
+                    //(((StringResponseData) responseData).getResponseData());
+                    candidateItemResponse.setStringuifiedResponse(sb.toString());
+                    break;
+
+                case FILE:
+                    //candidateItemResponse.setFileSubmission(fileSubmissionMap.get(responseIdentifier));
+                    //break;
+
+                default:
+                    throw new OLATRuntimeException("Unexpected switch case: " + responseData.getType());
+            }
+            candidateResponseMap.put(responseIdentifier, candidateItemResponse);
+        }
+        
         boolean allResponsesValid = true;
         boolean allResponsesBound = true;
 		
@@ -479,17 +521,13 @@ public class AssessmentTestDisplayController extends BasicController implements
         this.lastEvent = candidateEvent;
 
         /* Persist CandidateResponse entities */
-        /*for (final CandidateResponse candidateResponse : candidateResponseMap.values()) {
-            candidateResponse.setCandidateEvent(candidateEvent);
-            candidateResponseDao.persist(candidateResponse);
-        }*/
-        
+        qtiService.recordTestAssessmentResponses(candidateResponseMap.values());
         
         /* Record current result state */
         computeAndRecordTestAssessmentResult(ureq, testSessionState, false);
 
         /* Save any change to session state */
-        candidateSession = qtiService.updateTestSession(candidateSession);
+        candidateSession = qtiService.updateAssessmentTestSession(candidateSession);
 	}
 
 	//public CandidateSession endCurrentTestPart(final CandidateSessionContext candidateSessionContext)
@@ -540,7 +578,7 @@ public class AssessmentTestDisplayController extends BasicController implements
                 eventType = CandidateTestEventType.EXIT_TEST;
                 testSessionController.exitTest(currentTimestamp);
                 candidateSession.setTerminationTime(currentTimestamp);
-                candidateSession = qtiService.updateTestSession(candidateSession);
+                candidateSession = qtiService.updateAssessmentTestSession(candidateSession);
             }
             else {
                 eventType = CandidateTestEventType.ADVANCE_TEST_PART;
@@ -604,7 +642,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 
         /* Update CandidateSession as appropriate */
         candidateSession.setTerminationTime(currentTimestamp);
-        candidateSession = qtiService.updateTestSession(candidateSession);
+        candidateSession = qtiService.updateAssessmentTestSession(candidateSession);
 
         /* Record current result state (final) */
         computeAndRecordTestAssessmentResult(ureq, testSessionState, true);
@@ -764,7 +802,7 @@ public class AssessmentTestDisplayController extends BasicController implements
         }
     }
 	
-    private AssessmentResult computeTestAssessmentResult(UserRequest ureq, final UserTestSession testSession) {
+    private AssessmentResult computeTestAssessmentResult(UserRequest ureq, final AssessmentTestSession testSession) {
     	List<ContextEntry> entries = getWindowControl().getBusinessControl().getEntries();
     	OLATResourceable testSessionOres = OresHelper.createOLATResourceableInstance("TestSession", testSession.getKey());
     	entries.add(BusinessControlFactory.getInstance().createContextEntry(testSessionOres));
diff --git a/src/main/java/org/olat/ims/qti21/ui/CandidateSessionContext.java b/src/main/java/org/olat/ims/qti21/ui/CandidateSessionContext.java
index 08680ecea36..4c9780efa35 100644
--- a/src/main/java/org/olat/ims/qti21/ui/CandidateSessionContext.java
+++ b/src/main/java/org/olat/ims/qti21/ui/CandidateSessionContext.java
@@ -21,14 +21,14 @@ package org.olat.ims.qti21.ui;
 
 import java.util.Date;
 
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 
 public interface CandidateSessionContext {
 	
 	public boolean isTerminated();
 
-	public UserTestSession getCandidateSession();
+	public AssessmentTestSession getCandidateSession();
 	
 	public CandidateEvent getLastEvent();
 	
diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java
index c800624327f..9e6c916b442 100644
--- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java
+++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentItemComponentRenderer.java
@@ -30,7 +30,7 @@ import org.olat.core.gui.translator.Translator;
 import org.olat.core.logging.OLATRuntimeException;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.model.CandidateItemEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.CandidateSessionContext;
@@ -67,7 +67,7 @@ public class AssessmentItemComponentRenderer extends AssessmentObjectComponentRe
 		CandidateSessionContext candidateSessionContext = cmp.getCandidateSessionContext();
 
         /* Create appropriate options that link back to this controller */
-		final UserTestSession candidateSession = candidateSessionContext.getCandidateSession();
+		final AssessmentTestSession candidateSession = candidateSessionContext.getCandidateSession();
         if (candidateSession != null && candidateSession.isExploded()) {
             renderExploded(sb);
         } else if (candidateSessionContext.isTerminated()) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java
index d778278ff9f..76bdc5e3529 100644
--- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java
+++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentTestComponentRenderer.java
@@ -49,7 +49,7 @@ import org.olat.core.gui.translator.Translator;
 import org.olat.core.logging.OLATRuntimeException;
 import org.olat.core.util.StringHelper;
 import org.olat.ims.qti21.QTI21Service;
-import org.olat.ims.qti21.UserTestSession;
+import org.olat.ims.qti21.AssessmentTestSession;
 import org.olat.ims.qti21.model.CandidateTestEventType;
 import org.olat.ims.qti21.model.jpa.CandidateEvent;
 import org.olat.ims.qti21.ui.CandidateSessionContext;
@@ -105,7 +105,7 @@ public class AssessmentTestComponentRenderer extends AssessmentObjectComponentRe
 	        /* Create appropriate options that link back to this controller */
 	        TestSessionState testSessionState = testSessionController.getTestSessionState();
 			CandidateSessionContext candidateSessionContext = cmp.getCandidateSessionContext();
-			final UserTestSession candidateSession = candidateSessionContext.getCandidateSession();
+			final AssessmentTestSession candidateSession = candidateSessionContext.getCandidateSession();
 			
 	        if (candidateSession.isExploded()) {
 	            renderExploded(sb);
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/items/EssayEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/items/EssayEditorController.java
index 7cba5b2a9b9..674508b45a8 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/items/EssayEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/items/EssayEditorController.java
@@ -1,3 +1,22 @@
+/**
+ * <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.ims.qti21.ui.editor.items;
 
 import org.olat.core.gui.UserRequest;
diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml
index 6f6ff74a41f..256abeb1a58 100644
--- a/src/main/resources/META-INF/persistence.xml
+++ b/src/main/resources/META-INF/persistence.xml
@@ -136,7 +136,9 @@
 		<class>org.olat.instantMessaging.model.InstantMessageNotificationImpl</class>
 		<class>org.olat.ims.qti.statistics.model.QTIStatisticResult</class>
 		<class>org.olat.ims.qti.statistics.model.QTIStatisticResultSet</class>
-		<class>org.olat.ims.qti21.model.jpa.UserTestSessionImpl</class>
+		<class>org.olat.ims.qti21.model.jpa.AssessmentResponseImpl</class>
+		<class>org.olat.ims.qti21.model.jpa.AssessmentItemSessionImpl</class>
+		<class>org.olat.ims.qti21.model.jpa.AssessmentTestSessionImpl</class>
 		<class>org.olat.modules.assessment.model.AssessmentEntryImpl</class>
 		<class>org.olat.modules.fo.model.ForumImpl</class>
 		<class>org.olat.modules.fo.model.MessageImpl</class>
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 9c86e4726f5..c60e09203b7 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
@@ -26,9 +26,7 @@ alter table o_as_entry add constraint as_entry_to_entry_idx foreign key (fk_entr
 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 (
+create table o_qti_assessmenttest_session (
    id bigint not null auto_increment,
    creationdate datetime not null,
    lastmodified datetime not null,
@@ -47,9 +45,44 @@ create table o_qti_assessment_session (
    fk_assessment_entry bigint not null,
    primary key (id)
 );
-alter table o_qti_assessment_session ENGINE = InnoDB;
+alter table o_qti_assessmenttest_session ENGINE = InnoDB;
+
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_course_entry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_as_entry_idx foreign key (fk_assessment_entry) references o_as_entry (id);
+
+create table o_qti_assessmentitem_session (
+   id bigint not null auto_increment,
+   creationdate datetime not null,
+   lastmodified datetime not null,
+   q_itemidentifier varchar(64) not null,
+   q_duration bigint,
+   q_score decimal default null,
+   q_passed bit default null,
+   q_storage varchar(32),
+   fk_assessmenttest_session bigint not null,
+   primary key (id)
+);
+alter table o_qti_assessmentitem_session ENGINE = InnoDB;
+
+alter table o_qti_assessmentitem_session add constraint qti_itemsess_to_testsess_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+create index idx_item_identifier_idx on o_qti_assessmentitem_session (q_itemidentifier);
+
+create table o_qti_assessment_response (
+   id bigint not null auto_increment,
+   creationdate datetime not null,
+   lastmodified datetime not null,
+   q_responseidentifier varchar(64) not null,
+   q_responsedatatype varchar(16) not null,
+   q_responselegality varchar(16) not null,
+   q_stringuifiedresponse mediumtext,
+   fk_assessmentitem_session bigint not null,
+   fk_assessmenttest_session bigint not null,
+   primary key (id)
+);
+alter table o_qti_assessment_response 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);
+alter table o_qti_assessment_response add constraint qti_resp_to_testsession_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+alter table o_qti_assessment_response add constraint qti_resp_to_itemsession_idx foreign key (fk_assessmentitem_session) references o_qti_assessmentitem_session (id);
+create index idx_response_identifier_idx on o_qti_assessment_response (q_responseidentifier);
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index 839d445c2ad..765cc5f0fcd 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -1260,7 +1260,7 @@ create table o_mapper (
 );
 
 -- qti 2.1
-create table o_qti_assessment_session (
+create table o_qti_assessmenttest_session (
    id bigint not null auto_increment,
    creationdate datetime not null,
    lastmodified datetime not null,
@@ -1280,6 +1280,32 @@ create table o_qti_assessment_session (
    primary key (id)
 );
 
+create table o_qti_assessmentitem_session (
+   id bigint not null auto_increment,
+   creationdate datetime not null,
+   lastmodified datetime not null,
+   q_itemidentifier varchar(64) not null,
+   q_duration bigint,
+   q_score decimal default null,
+   q_passed bit default null,
+   q_storage varchar(32),
+   fk_assessmenttest_session bigint not null,
+   primary key (id)
+);
+
+create table o_qti_assessment_response (
+   id bigint not null auto_increment,
+   creationdate datetime not null,
+   lastmodified datetime not null,
+   q_responseidentifier varchar(64) not null,
+   q_responsedatatype varchar(16) not null,
+   q_responselegality varchar(16) not null,
+   q_stringuifiedresponse mediumtext,
+   fk_assessmentitem_session bigint not null,
+   fk_assessmenttest_session bigint not null,
+   primary key (id)
+);
+
 -- question item
 create table o_qp_pool (
    id bigint not null,
@@ -1788,7 +1814,9 @@ alter table o_cal_use_config ENGINE = InnoDB;
 alter table o_cal_import ENGINE = InnoDB;
 alter table o_cal_import_to ENGINE = InnoDB;
 alter table o_mapper ENGINE = InnoDB;
-alter table o_qti_assessment_session ENGINE = InnoDB;
+alter table o_qti_assessmenttest_session ENGINE = InnoDB;
+alter table o_qti_assessmentitem_session ENGINE = InnoDB;
+alter table o_qti_assessment_response ENGINE = InnoDB;
 alter table o_qp_pool ENGINE = InnoDB;
 alter table o_qp_taxonomy_level ENGINE = InnoDB;
 alter table o_qp_item ENGINE = InnoDB;
@@ -2131,10 +2159,17 @@ create index idx_cal_imp_to_cal_type_idx on o_cal_import_to (c_to_calendar_type)
 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_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);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_repo_entry_idx foreign key (fk_entry) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_course_entry_idx foreign key (fk_reference_entry) references o_repositoryentry (repositoryentry_id);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_identity_idx foreign key (fk_identity) references o_bs_identity (id);
+alter table o_qti_assessmenttest_session add constraint qti_sess_to_as_entry_idx foreign key (fk_assessment_entry) references o_as_entry (id);
+
+alter table o_qti_assessmentitem_session add constraint qti_itemsess_to_testsess_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+create index idx_item_identifier_idx on o_qti_assessmentitem_session (q_itemidentifier);
+
+alter table o_qti_assessment_response add constraint qti_resp_to_testsession_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+alter table o_qti_assessment_response add constraint qti_resp_to_itemsession_idx foreign key (fk_assessmentitem_session) references o_qti_assessmentitem_session (id);
+create index idx_response_identifier_idx on o_qti_assessment_response (q_responseidentifier);
 
 -- 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/oracle/alter_10_x_0_to_11_0_0.sql b/src/main/resources/database/oracle/alter_10_x_0_to_11_0_0.sql
index e5cac0d03af..515f65b5a62 100644
--- a/src/main/resources/database/oracle/alter_10_x_0_to_11_0_0.sql
+++ b/src/main/resources/database/oracle/alter_10_x_0_to_11_0_0.sql
@@ -1,4 +1,4 @@
-create table o_qti_assessment_session (
+create table o_qti_assessmenttest_session (
    id NUMBER(20) GENERATED ALWAYS AS IDENTITY,
    creationdate date not null,
    lastmodified date not null,
@@ -15,9 +15,9 @@ create table o_qti_assessment_session (
    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);
\ No newline at end of file
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_entry);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_course);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_identity);
\ No newline at end of file
diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql
index 7525a11fcf2..20fc2230286 100644
--- a/src/main/resources/database/oracle/setupDatabase.sql
+++ b/src/main/resources/database/oracle/setupDatabase.sql
@@ -1267,7 +1267,7 @@ create table o_mapper (
    primary key (id)
 );
 
-create table o_qti_assessment_session (
+create table o_qti_assessmenttest_session (
    id NUMBER(20) GENERATED ALWAYS AS IDENTITY,
    creationdate date not null,
    lastmodified date not null,
@@ -2248,12 +2248,12 @@ create index idx_ucourseinfos_rsrc_idx on o_as_user_course_infos (fk_resource_id
 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);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_entry);
+alter table o_qti_assessmenttest_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);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_identity);
 
 -- 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 e1b968bd14d..51ab2187af5 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
@@ -30,7 +30,7 @@ create index idx_as_entry_to_id_idx on o_as_entry (a_assessment_id);
 
 
 
-create table o_qti_assessment_session (
+create table o_qti_assessmenttest_session (
    id bigserial,
    creationdate timestamp not null,
    lastmodified timestamp not null,
@@ -50,13 +50,51 @@ create table o_qti_assessment_session (
    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);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_entry);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_reference_entry);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_identity);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_assessment_entry);
+
+create table o_qti_assessmentitem_session (
+   id bigserial,
+   creationdate timestamp not null,
+   lastmodified timestamp not null,
+   q_itemidentifier varchar(64) not null,
+   q_duration int8,
+   q_score decimal default null,
+   q_passed bool default null,
+   q_storage varchar(32),
+   fk_assessmenttest_session int8 not null,
+   primary key (id)
+);
+
+alter table o_qti_assessmentitem_session add constraint qti_itemsess_to_testsess_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+create index idx_itemsess_to_testsess_idx on o_qti_assessmentitem_session (fk_assessmenttest_session);
+create index idx_item_identifier_idx on o_qti_assessmentitem_session (q_itemidentifier);
+
+create table o_qti_assessment_response (
+   id bigserial,
+   creationdate timestamp not null,
+   lastmodified timestamp not null,
+   q_responseidentifier varchar(64) not null,
+   q_responsedatatype varchar(16) not null,
+   q_responselegality varchar(16) not null,
+   q_stringuifiedresponse text,
+   fk_assessmentitem_session int8 not null,
+   fk_assessmenttest_session int8 not null,
+   primary key (id)
+);
+
+alter table o_qti_assessment_response add constraint qti_resp_to_testsession_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+create index idx_resp_to_testsession_idx on o_qti_assessment_response (fk_assessmenttest_session);
+alter table o_qti_assessment_response add constraint qti_resp_to_itemsession_idx foreign key (fk_assessmentitem_session) references o_qti_assessmentitem_session (id);
+create index idx_resp_to_itemsession_idx on o_qti_assessment_response (fk_assessmentitem_session);
+create index idx_response_identifier_idx on o_qti_assessment_response (q_responseidentifier);
+
+
 
 
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index 87bec804548..1511b836e2c 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -1258,7 +1258,7 @@ create table o_mapper (
 );
 
 -- qti 2.1
-create table o_qti_assessment_session (
+create table o_qti_assessmenttest_session (
    id bigserial,
    creationdate timestamp not null,
    lastmodified timestamp not null,
@@ -1278,6 +1278,32 @@ create table o_qti_assessment_session (
    primary key (id)
 );
 
+create table o_qti_assessmentitem_session (
+   id bigserial,
+   creationdate timestamp not null,
+   lastmodified timestamp not null,
+   q_itemidentifier varchar(64) not null,
+   q_duration int8,
+   q_score decimal default null,
+   q_passed bool default null,
+   q_storage varchar(32),
+   fk_assessmenttest_session int8 not null,
+   primary key (id)
+);
+
+create table o_qti_assessment_response (
+   id bigserial,
+   creationdate timestamp not null,
+   lastmodified timestamp not null,
+   q_responseidentifier varchar(64) not null,
+   q_responsedatatype varchar(16) not null,
+   q_responselegality varchar(16) not null,
+   q_stringuifiedresponse text,
+   fk_assessmentitem_session int8 not null,
+   fk_assessmenttest_session int8 not null,
+   primary key (id)
+);
+
 -- question item
 create table o_qp_pool (
    id int8 not null,
@@ -2129,14 +2155,24 @@ create index idx_cal_imp_to_cal_type_idx on o_cal_import_to (c_to_calendar_type)
 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_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);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_entry);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_reference_entry);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_identity);
+alter table o_qti_assessmenttest_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_assessmenttest_session (fk_assessment_entry);
+
+alter table o_qti_assessmentitem_session add constraint qti_itemsess_to_testsess_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+create index idx_itemsess_to_testsess_idx on o_qti_assessmentitem_session (fk_assessmenttest_session);
+create index idx_item_identifier_idx on o_qti_assessmentitem_session (q_itemidentifier);
+
+alter table o_qti_assessment_response add constraint qti_resp_to_testsession_idx foreign key (fk_assessmenttest_session) references o_qti_assessmenttest_session (id);
+create index idx_resp_to_testsession_idx on o_qti_assessment_response (fk_assessmenttest_session);
+alter table o_qti_assessment_response add constraint qti_resp_to_itemsession_idx foreign key (fk_assessmentitem_session) references o_qti_assessmentitem_session (id);
+create index idx_resp_to_itemsession_idx on o_qti_assessment_response (fk_assessmentitem_session);
+create index idx_response_identifier_idx on o_qti_assessment_response (q_responseidentifier);
 
 -- 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/AssessmentResponseDAOTest.java b/src/test/java/org/olat/ims/qti21/manager/AssessmentResponseDAOTest.java
new file mode 100644
index 00000000000..7523438287b
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/manager/AssessmentResponseDAOTest.java
@@ -0,0 +1,82 @@
+/**
+ * <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.ims.qti21.manager;
+
+import java.util.UUID;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.ims.qti21.AssessmentItemSession;
+import org.olat.ims.qti21.AssessmentResponse;
+import org.olat.ims.qti21.AssessmentTestSession;
+import org.olat.ims.qti21.model.ResponseLegality;
+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;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import uk.ac.ed.ph.jqtiplus.types.ResponseData.ResponseDataType;
+
+/**
+ * 
+ * Initial date: 29.01.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentResponseDAOTest extends OlatTestCase {
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private AssessmentResponseDAO responseDao;
+	@Autowired
+	private AssessmentItemSessionDAO itemSessionDao;
+	@Autowired
+	private AssessmentTestSessionDAO testSessionDao;
+	@Autowired
+	private AssessmentService assessmentService;
+	
+	@Test
+	public void createResponse() {
+		// prepare a test and a user
+		RepositoryEntry testEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity assessedIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("response-session-1");
+		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, testEntry, "-", testEntry);
+		dbInstance.commit();
+		
+		String itemIdentifier = UUID.randomUUID().toString();
+		String responseIdentifier = UUID.randomUUID().toString();
+		
+		//make test, item and response
+		AssessmentTestSession testSession = testSessionDao.createAndPersistTestSession(testEntry, testEntry, "_", assessmentEntry, assessedIdentity, true);
+		Assert.assertNotNull(testSession);
+		AssessmentItemSession itemSession = itemSessionDao.createAndPersistAssessmentItemSession(testSession, itemIdentifier);
+		Assert.assertNotNull(itemSession);
+		AssessmentResponse response = responseDao.createAssessmentResponse(testSession, itemSession, responseIdentifier, ResponseLegality.VALID, ResponseDataType.FILE);
+		Assert.assertNotNull(response);
+		dbInstance.commit();
+	}
+	
+
+}
diff --git a/src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java b/src/test/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAOTest.java
similarity index 77%
rename from src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java
rename to src/test/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAOTest.java
index e6368471b17..abc9d872f5b 100644
--- a/src/test/java/org/olat/ims/qti21/manager/SessionDAOTest.java
+++ b/src/test/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAOTest.java
@@ -26,7 +26,7 @@ import org.junit.Assert;
 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.ims.qti21.AssessmentTestSession;
 import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.assessment.AssessmentService;
 import org.olat.repository.RepositoryEntry;
@@ -36,19 +36,19 @@ import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * 
- * Initial date: 12.05.2015<br>
+ * Initial date: 02.02.2016<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class SessionDAOTest extends OlatTestCase {
+public class AssessmentTestSessionDAOTest extends OlatTestCase {
 	
 	@Autowired
 	private DB dbInstance;
 	@Autowired
-	private SessionDAO testSessionDao;
+	private AssessmentTestSessionDAO testSessionDao;
 	@Autowired
 	private AssessmentService assessmentService;
-	
+
 	@Test
 	public void createTestSession_repo() {
 		// prepare a test and a user
@@ -57,7 +57,7 @@ public class SessionDAOTest extends OlatTestCase {
 		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, testEntry, "-", testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession = testSessionDao.createTestSession(testEntry, testEntry, "-", assessmentEntry, assessedIdentity, true);
+		AssessmentTestSession testSession = testSessionDao.createAndPersistTestSession(testEntry, testEntry, "-", assessmentEntry, assessedIdentity, true);
 		Assert.assertNotNull(testSession);
 		dbInstance.commit();
 	}
@@ -72,7 +72,7 @@ public class SessionDAOTest extends OlatTestCase {
 		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, subIdent, testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
+		AssessmentTestSession testSession = testSessionDao.createAndPersistTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
 		Assert.assertNotNull(testSession);
 		dbInstance.commit();
 	}
@@ -87,11 +87,11 @@ public class SessionDAOTest extends OlatTestCase {
 		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, subIdent, testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, true);
+		AssessmentTestSession testSession = testSessionDao.createAndPersistTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, true);
 		Assert.assertNotNull(testSession);
 		dbInstance.commitAndCloseSession();
 		
-		List<UserTestSession> sessions = testSessionDao.getUserTestSessions(courseEntry, subIdent, assessedIdentity);
+		List<AssessmentTestSession> sessions = testSessionDao.getUserTestSessions(courseEntry, subIdent, assessedIdentity);
 		Assert.assertNotNull(sessions);
 		Assert.assertEquals(1, sessions.size());
 		Assert.assertEquals(testSession, sessions.get(0));
@@ -107,19 +107,19 @@ public class SessionDAOTest extends OlatTestCase {
 		AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(assessedIdentity, courseEntry, subIdent, testEntry);
 		dbInstance.commit();
 		
-		UserTestSession testSession1 = testSessionDao.createTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
+		AssessmentTestSession testSession1 = testSessionDao.createAndPersistTestSession(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);
+		AssessmentTestSession testSession2 = testSessionDao.createAndPersistTestSession(testEntry, courseEntry, subIdent, assessmentEntry, assessedIdentity, false);
 		Assert.assertNotNull(testSession2);
 		dbInstance.commitAndCloseSession();
 		
 		//load the last session
-		UserTestSession lastTestSession = testSessionDao.getLastTestSession(testEntry, courseEntry, subIdent, assessedIdentity);
+		AssessmentTestSession lastTestSession = testSessionDao.getLastTestSession(testEntry, courseEntry, subIdent, assessedIdentity);
 		Assert.assertNotNull(lastTestSession);
 		Assert.assertEquals(testSession2, lastTestSession);
 	}
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index d6dba3eab8b..1ada3a84e13 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -167,8 +167,8 @@ import org.junit.runners.Suite;
 	org.olat.ims.qti.statistics.manager.QTIStatisticsManagerLargeTest.class,
 	org.olat.ims.qti.statistics.manager.QTIStatisticsManagerTest.class,
 	org.olat.ims.qti.statistics.manager.StatisticsTest.class,
-	org.olat.ims.qti21.manager.EventDAOTest.class,
-	org.olat.ims.qti21.manager.SessionDAOTest.class,
+	org.olat.ims.qti21.manager.AssessmentTestSessionDAOTest.class,
+	org.olat.ims.qti21.manager.AssessmentResponseDAOTest.class,
 	org.olat.ims.lti.LTIManagerTest.class,
 	org.olat.modules.webFeed.FeedManagerImplTest.class,
 	org.olat.modules.qpool.manager.MetadataConverterHelperTest.class,
-- 
GitLab