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