From f4c771dcb471d8d19b725d76dea64b2699a78f75 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Fri, 29 May 2015 12:07:40 +0200 Subject: [PATCH] no-jira: tests to generate imsmanifest, assessment-item and assessment-test programmatically --- pom.xml | 5 + .../persistence/_spring/core_persistence.xml | 2 +- .../org/olat/ims/qti21/QTI21Constants.java | 4 + .../java/org/olat/ims/qti21/QTI21Service.java | 2 +- .../org/olat/ims/qti21/manager/EventDAO.java | 2 +- .../ims/qti21/manager/QTI21ServiceImpl.java | 2 +- .../olat/ims/qti21/manager/SessionDAO.java | 2 +- .../qti21/model/{ => jpa}/CandidateEvent.java | 5 +- .../model/{ => jpa}/UserTestSessionImpl.java | 2 +- .../model/xml/AssessmentItemPackage.java | 14 + .../model/xml/AssessmentTestPackage.java | 5 + .../ims/qti21/model/xml/ManifestPackage.java | 87 +++++ .../handlers/QTI21AssessmentTestHandler.java | 53 ++- .../ui/AssessmentItemDisplayController.java | 2 +- .../ui/AssessmentTestDisplayController.java | 2 +- .../ims/qti21/ui/CandidateSessionContext.java | 2 +- .../AssessmentItemComponentRenderer.java | 2 +- .../AssessmentTestComponentRenderer.java | 2 +- .../AssessmentItemEditorController.java | 56 ++- .../editor/SingleChoiceEditorController.java | 38 +++ .../ui/editor/UnkownItemEditorController.java | 38 +++ .../_content/assessment_item_editor.html | 4 +- .../editor/_i18n/LocalStrings_de.properties | 3 + .../editor/_i18n/LocalStrings_en.properties | 3 + .../model/xml/AssessmentItemPackageTest.java | 319 ++++++++++++++++++ .../model/xml/AssessmentTestPackageTest.java | 151 +++++++++ .../qti21/model/xml/ManifestPackageTest.java | 46 +++ .../xml/assessment-item-single-choice.xml | 67 ++++ .../QTI21AssessmentTestHandlerTest.java | 15 + 29 files changed, 914 insertions(+), 21 deletions(-) rename src/main/java/org/olat/ims/qti21/model/{ => jpa}/CandidateEvent.java (92%) rename src/main/java/org/olat/ims/qti21/model/{ => jpa}/UserTestSessionImpl.java (99%) create mode 100644 src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java create mode 100644 src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java create mode 100644 src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java create mode 100644 src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java create mode 100644 src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java create mode 100644 src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties create mode 100644 src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties create mode 100644 src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemPackageTest.java create mode 100644 src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java create mode 100644 src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java create mode 100644 src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice.xml create mode 100644 src/test/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandlerTest.java diff --git a/pom.xml b/pom.xml index 133905bc713..db18db2d939 100644 --- a/pom.xml +++ b/pom.xml @@ -1872,6 +1872,11 @@ --> </exclusions> </dependency> + <dependency> + <groupId>org.openolat.imscp</groupId> + <artifactId>manifest</artifactId> + <version>1.1-SNAPSHOT</version> + </dependency> <dependency> <groupId>rome</groupId> <artifactId>rome</artifactId> diff --git a/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml b/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml index b36eaa7ebfc..e4f0bb7550b 100644 --- a/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml +++ b/src/main/java/org/olat/core/commons/persistence/_spring/core_persistence.xml @@ -130,7 +130,7 @@ <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.UserTestSessionImpl</class> + <class>org.olat.ims.qti21.model.jpa.UserTestSessionImpl</class> <class>org.olat.modules.qpool.model.PoolImpl</class> <class>org.olat.modules.qpool.model.PoolToItem</class> <class>org.olat.modules.qpool.model.PoolItemShortView</class> diff --git a/src/main/java/org/olat/ims/qti21/QTI21Constants.java b/src/main/java/org/olat/ims/qti21/QTI21Constants.java index 42081da4fe8..5c90dddbe1e 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Constants.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Constants.java @@ -29,6 +29,10 @@ import uk.ac.ed.ph.jqtiplus.types.Identifier; */ public class QTI21Constants { + public static final String TOOLNAME = "OpenOLAT"; + + public static final String TOOLVERSION = "v1.0"; + public static final String SCORE = "SCORE"; public static final Identifier SCORE_IDENTIFIER = Identifier.assumedLegal(SCORE); diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java index 9b4d0917c36..22850a07ef9 100644 --- a/src/main/java/org/olat/ims/qti21/QTI21Service.java +++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java @@ -26,9 +26,9 @@ import java.util.List; import org.olat.basesecurity.IdentityRef; import org.olat.core.id.Identity; -import org.olat.ims.qti21.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateItemEventType; import org.olat.ims.qti21.model.CandidateTestEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; 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 579f969649f..45d473469e3 100644 --- a/src/main/java/org/olat/ims/qti21/manager/EventDAO.java +++ b/src/main/java/org/olat/ims/qti21/manager/EventDAO.java @@ -19,9 +19,9 @@ */ package org.olat.ims.qti21.manager; -import org.olat.ims.qti21.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateItemEventType; import org.olat.ims.qti21.model.CandidateTestEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.springframework.stereotype.Service; import org.w3c.dom.Document; 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 b073af080b5..8c52fe80606 100644 --- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java +++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java @@ -41,9 +41,9 @@ import org.olat.ims.qti21.QTI21ContentPackage; import org.olat.ims.qti21.QTI21Module; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.UserTestSession; -import org.olat.ims.qti21.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateItemEventType; import org.olat.ims.qti21.model.CandidateTestEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java index 2f4b76e6026..1de61e3171f 100644 --- a/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java +++ b/src/main/java/org/olat/ims/qti21/manager/SessionDAO.java @@ -26,7 +26,7 @@ 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.UserTestSessionImpl; +import org.olat.ims.qti21.model.jpa.UserTestSessionImpl; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryRef; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/main/java/org/olat/ims/qti21/model/CandidateEvent.java b/src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java similarity index 92% rename from src/main/java/org/olat/ims/qti21/model/CandidateEvent.java rename to src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java index 954d07768e5..4420515701a 100644 --- a/src/main/java/org/olat/ims/qti21/model/CandidateEvent.java +++ b/src/main/java/org/olat/ims/qti21/model/jpa/CandidateEvent.java @@ -17,10 +17,13 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.ims.qti21.model; +package org.olat.ims.qti21.model.jpa; import java.util.Date; +import org.olat.ims.qti21.model.CandidateItemEventType; +import org.olat.ims.qti21.model.CandidateTestEventType; + /** * * Initial date: 19.05.2015<br> diff --git a/src/main/java/org/olat/ims/qti21/model/UserTestSessionImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java similarity index 99% rename from src/main/java/org/olat/ims/qti21/model/UserTestSessionImpl.java rename to src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java index 82232b9c418..9e9ddfd7222 100644 --- a/src/main/java/org/olat/ims/qti21/model/UserTestSessionImpl.java +++ b/src/main/java/org/olat/ims/qti21/model/jpa/UserTestSessionImpl.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.ims.qti21.model; +package org.olat.ims.qti21.model.jpa; import java.util.Date; diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java new file mode 100644 index 00000000000..3f68c1793b9 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemPackage.java @@ -0,0 +1,14 @@ +package org.olat.ims.qti21.model.xml; + +import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem; + +public class AssessmentItemPackage { + + public ResolvedAssessmentItem createSingleChoice() { + + ResolvedAssessmentItem item = new ResolvedAssessmentItem(null, null); + return item; + + } + +} diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java new file mode 100644 index 00000000000..94c8d780521 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestPackage.java @@ -0,0 +1,5 @@ +package org.olat.ims.qti21.model.xml; + +public class AssessmentTestPackage { + +} diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java b/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java new file mode 100644 index 00000000000..b23b5066ab0 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/model/xml/ManifestPackage.java @@ -0,0 +1,87 @@ +package org.olat.ims.qti21.model.xml; + +import java.io.OutputStream; +import java.util.UUID; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.imscp.xml.manifest.FileType; +import org.olat.imscp.xml.manifest.ManifestMetadataType; +import org.olat.imscp.xml.manifest.ManifestType; +import org.olat.imscp.xml.manifest.ObjectFactory; +import org.olat.imscp.xml.manifest.OrganizationsType; +import org.olat.imscp.xml.manifest.ResourceType; +import org.olat.imscp.xml.manifest.ResourcesType; + +public class ManifestPackage { + + private static final OLog log = Tracing.createLoggerFor(ManifestPackage.class); + private static final ObjectFactory objectFactory = new ObjectFactory(); + + public static ManifestType createEmptyManifest() { + ManifestType manifestType = objectFactory.createManifestType(); + ManifestMetadataType metadataType = objectFactory.createManifestMetadataType(); + metadataType.setSchema("QTIv2.1 Package"); + metadataType.setSchemaversion("1.0.0"); + manifestType.setMetadata(metadataType); + + OrganizationsType organisationsType = objectFactory.createOrganizationsType(); + manifestType.setOrganizations(organisationsType); + + ResourcesType resourcesType = objectFactory.createResourcesType(); + manifestType.setResources(resourcesType); + return manifestType; + } + + public static String appendAssessmentTest(ManifestType manifest) { + String testId = "id" + UUID.randomUUID().toString(); + String testFileName = testId + ".xml"; + + ResourceType testResourceType = objectFactory.createResourceType(); + testResourceType.setIdentifier(testId); + testResourceType.setType("imsqti_test_xmlv2p1"); + testResourceType.setHref(testFileName); + manifest.getResources().getResource().add(testResourceType); + + appendFile(testResourceType, testFileName); + return testFileName; + } + + public static String appendAssessmentItem(ManifestType manifest) { + String itemId = "id" + UUID.randomUUID().toString(); + String itemFileName = itemId + ".xml"; + + ResourceType itemResourceType = objectFactory.createResourceType(); + itemResourceType.setIdentifier(itemId); + itemResourceType.setType("imsqti_item_xmlv2p1"); + itemResourceType.setHref(itemFileName); + manifest.getResources().getResource().add(itemResourceType); + + appendFile(itemResourceType, itemFileName); + return itemFileName; + } + + + public static void appendFile(ResourceType resource, String href) { + FileType itemFileType = objectFactory.createFileType(); + itemFileType.setHref(href); + resource.getFile().add(itemFileType); + } + + public static void write(ManifestType manifest, OutputStream out) { + try { + JAXBContext context = JAXBContext.newInstance("org.olat.imscp.xml.manifest"); + Marshaller marshaller = context.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.imsglobal.org/xsd/imscp_v1p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/qtiv2p1_imscpv1p2_v1p0.xsd"); + + marshaller.marshal(objectFactory.createManifest(manifest), out); + } catch (JAXBException e) { + log.error("", e); + } + } +} diff --git a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java index 826c9ed2f86..2f0aefba518 100644 --- a/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java +++ b/src/main/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandler.java @@ -21,6 +21,11 @@ package org.olat.ims.qti21.repository.handlers; import java.io.File; import java.util.Locale; +import java.util.UUID; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; @@ -33,15 +38,25 @@ import org.olat.core.gui.control.generic.wizard.StepsMainRunController; import org.olat.core.gui.media.MediaResource; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; import org.olat.core.util.coordinate.LockResult; import org.olat.course.assessment.AssessmentMode; import org.olat.fileresource.FileResourceManager; import org.olat.fileresource.types.FileResource; import org.olat.fileresource.types.ImsQTI21Resource; import org.olat.fileresource.types.ResourceEvaluation; +import org.olat.ims.qti21.model.xml.ManifestPackage; import org.olat.ims.qti21.ui.AssessmentTestDisplayController; import org.olat.ims.qti21.ui.InMemoryOutcomesListener; import org.olat.ims.qti21.ui.editor.AssessmentTestComposerController; +import org.olat.imscp.xml.manifest.FileType; +import org.olat.imscp.xml.manifest.ManifestMetadataType; +import org.olat.imscp.xml.manifest.ManifestType; +import org.olat.imscp.xml.manifest.ObjectFactory; +import org.olat.imscp.xml.manifest.OrganizationsType; +import org.olat.imscp.xml.manifest.ResourceType; +import org.olat.imscp.xml.manifest.ResourcesType; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryService; import org.olat.repository.handlers.EditionSupport; @@ -61,6 +76,8 @@ import org.springframework.stereotype.Service; */ @Service public class QTI21AssessmentTestHandler extends FileHandler { + + private static final OLog log = Tracing.createLoggerFor(QTI21AssessmentTestHandler.class); @Override public String getSupportedType() { @@ -69,7 +86,7 @@ public class QTI21AssessmentTestHandler extends FileHandler { @Override public boolean isCreate() { - return false; + return true; } @Override @@ -78,10 +95,38 @@ public class QTI21AssessmentTestHandler extends FileHandler { } @Override - public RepositoryEntry createResource(Identity initialAuthor, String displayname, String description, - Object createObject, Locale locale) { - return null; + public RepositoryEntry createResource(Identity initialAuthor, String displayname, String description, Object createObject, Locale locale) { + ImsQTI21Resource ores = new ImsQTI21Resource(); + + RepositoryService repositoryService = CoreSpringFactory.getImpl(RepositoryService.class); + OLATResource resource = OLATResourceManager.getInstance().findOrPersistResourceable(ores); + RepositoryEntry re = repositoryService.create(initialAuthor, null, "", displayname, description, resource, RepositoryEntry.ACC_OWNERS); + DBFactory.getInstance().commit(); + + File repositoryDir = new File(FileResourceManager.getInstance().getFileResourceRoot(re.getOlatResource()), FileResourceManager.ZIPDIR); + if(!repositoryDir.exists()) { + + } + return re; + } + + public void createMinimalAssessmentTest() { + ManifestType manifestType = ManifestPackage.createEmptyManifest(); + String testFilename = ManifestPackage.appendAssessmentTest(manifestType); + String itemFilename = ManifestPackage.appendAssessmentItem(manifestType); + ManifestPackage.write(manifestType, System.out); + + //create basic assessment test + + + //create single choice + + + + } + + @Override public boolean isPostCreateWizardAvailable() { 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 b4c7fa8477f..4cea09589ff 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentItemDisplayController.java @@ -43,8 +43,8 @@ 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.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateItemEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.olat.ims.qti21.ui.components.AssessmentItemFormItem; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; 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 a0d72b5ff6f..e11d1472548 100644 --- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java +++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java @@ -44,9 +44,9 @@ import org.olat.ims.qti21.OutcomesListener; import org.olat.ims.qti21.QTI21Constants; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.UserTestSession; -import org.olat.ims.qti21.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateItemEventType; import org.olat.ims.qti21.model.CandidateTestEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.olat.ims.qti21.ui.components.AssessmentTestFormItem; import org.olat.repository.RepositoryEntry; import org.springframework.beans.factory.annotation.Autowired; 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 b92414af7d2..08680ecea36 100644 --- a/src/main/java/org/olat/ims/qti21/ui/CandidateSessionContext.java +++ b/src/main/java/org/olat/ims/qti21/ui/CandidateSessionContext.java @@ -22,7 +22,7 @@ package org.olat.ims.qti21.ui; import java.util.Date; import org.olat.ims.qti21.UserTestSession; -import org.olat.ims.qti21.model.CandidateEvent; +import org.olat.ims.qti21.model.jpa.CandidateEvent; public interface CandidateSessionContext { 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 f60046bfbd0..52a37e09c59 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 @@ -36,8 +36,8 @@ import org.olat.core.gui.render.URLBuilder; import org.olat.core.gui.translator.Translator; import org.olat.core.logging.OLATRuntimeException; import org.olat.ims.qti21.UserTestSession; -import org.olat.ims.qti21.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateItemEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.olat.ims.qti21.ui.CandidateSessionContext; import org.olat.ims.qti21.ui.rendering.AbstractRenderingOptions; import org.olat.ims.qti21.ui.rendering.AbstractRenderingRequest; 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 377cdd74d05..0ede59aa9c4 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 @@ -36,8 +36,8 @@ import org.olat.core.gui.render.URLBuilder; import org.olat.core.gui.translator.Translator; import org.olat.core.logging.OLATRuntimeException; import org.olat.ims.qti21.UserTestSession; -import org.olat.ims.qti21.model.CandidateEvent; import org.olat.ims.qti21.model.CandidateTestEventType; +import org.olat.ims.qti21.model.jpa.CandidateEvent; import org.olat.ims.qti21.ui.CandidateSessionContext; import org.olat.ims.qti21.ui.rendering.AbstractRenderingOptions; import org.olat.ims.qti21.ui.rendering.AbstractRenderingRequest; diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java index 5683498d144..035f0308a8b 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java @@ -20,16 +20,23 @@ package org.olat.ims.qti21.ui.editor; import java.io.File; +import java.util.List; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.tabbedpane.TabbedPane; import org.olat.core.gui.components.velocity.VelocityContainer; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; +import org.olat.ims.qti21.QTI21Constants; import org.olat.ims.qti21.ui.AssessmentItemDisplayController; import org.olat.repository.RepositoryEntry; +import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.Interaction; import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef; import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem; @@ -42,8 +49,11 @@ import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem; public class AssessmentItemEditorController extends BasicController { private final ResolvedAssessmentItem resolvedAssessmentItem; + + private final TabbedPane tabbedPane; private final VelocityContainer mainVC; + private FormBasicController itemEditor; private AssessmentItemDisplayController displayCtrl; public AssessmentItemEditorController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry, @@ -52,13 +62,55 @@ public class AssessmentItemEditorController extends BasicController { this.resolvedAssessmentItem = resolvedAssessmentItem; mainVC = createVelocityContainer("assessment_item_editor"); + tabbedPane = new TabbedPane("itemTabs", getLocale()); + tabbedPane.addListener(this); + mainVC.put("tabbedpane", tabbedPane); + + initItemEditor(ureq); displayCtrl = new AssessmentItemDisplayController(ureq, getWindowControl(), testEntry, resolvedAssessmentItem, itemRef, unzippedDirectory); listenTo(displayCtrl); - mainVC.put("display", displayCtrl.getInitialComponent()); + tabbedPane.addTab("Preview", displayCtrl.getInitialComponent()); putInitialPanel(mainVC); - + } + + private void initItemEditor(UserRequest ureq) { + + AssessmentItem item = resolvedAssessmentItem.getItemLookup().getRootNodeHolder().getRootNode(); + if(QTI21Constants.TOOLNAME.equals(item.getToolName())) { + //we have create this one + List<Interaction> interactions = item.getItemBody().findInteractions(); + + boolean choice = false; + boolean unkown = false; + + if(interactions != null && interactions.size() > 0) { + for(Interaction interaction: interactions) { + if(interaction instanceof ChoiceInteraction) { + choice = true; + } else { + unkown = true; + } + } + } + + if(choice && !unkown) { + itemEditor = new SingleChoiceEditorController(ureq, getWindowControl()); + listenTo(itemEditor); + tabbedPane.addTab("Choice", itemEditor.getInitialComponent()); + } else if(unkown) { + initItemCreatedByUnkownEditor(ureq); + } + } else { + initItemCreatedByUnkownEditor(ureq); + } + } + + private void initItemCreatedByUnkownEditor(UserRequest ureq) { + itemEditor = new UnkownItemEditorController(ureq, getWindowControl()); + listenTo(itemEditor); + tabbedPane.addTab("Unkown", itemEditor.getInitialComponent()); } @Override diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java new file mode 100644 index 00000000000..6448b1ffd14 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java @@ -0,0 +1,38 @@ +package org.olat.ims.qti21.ui.editor; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; + +/** + * + * Initial date: 26.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class SingleChoiceEditorController extends FormBasicController { + + public SingleChoiceEditorController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl); + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + setFormTitle("editor.sc.title"); + // + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + // + } +} diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java new file mode 100644 index 00000000000..f593a1eedf7 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java @@ -0,0 +1,38 @@ +package org.olat.ims.qti21.ui.editor; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; + +/** + * + * Initial date: 26.05.2015<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class UnkownItemEditorController extends FormBasicController { + + public UnkownItemEditorController(UserRequest ureq, WindowControl wControl) { + super(ureq, wControl); + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + setFormTitle("editor.unkown.title"); + // + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + // + } +} diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_content/assessment_item_editor.html b/src/main/java/org/olat/ims/qti21/ui/editor/_content/assessment_item_editor.html index c3e3485de69..23b14550deb 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/_content/assessment_item_editor.html +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_content/assessment_item_editor.html @@ -1,3 +1 @@ -[Assessment item editor] - -$r.render("display") \ No newline at end of file +$r.render("tabbedpane") \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties new file mode 100644 index 00000000000..fc8aa54fd70 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties @@ -0,0 +1,3 @@ +#Mon Mar 02 09:54:04 CET 2009 +editor.sc.title=Single choice +editor.unkown.title=Unkown interaction \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties new file mode 100644 index 00000000000..d186f30e906 --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties @@ -0,0 +1,3 @@ +#Sat Jan 22 17:01:28 CET 2011 +editor.sc.title=Single choice +editor.unkown.title=Unkown interaction \ No newline at end of file diff --git a/src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemPackageTest.java b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemPackageTest.java new file mode 100644 index 00000000000..e60183f47c8 --- /dev/null +++ b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemPackageTest.java @@ -0,0 +1,319 @@ +package org.olat.ims.qti21.model.xml; + +import java.io.File; +import java.io.FileOutputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator; + +import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager; +import uk.ac.ed.ph.jqtiplus.group.NodeGroupList; +import uk.ac.ed.ph.jqtiplus.group.item.ItemBodyGroup; +import uk.ac.ed.ph.jqtiplus.group.item.interaction.PromptGroup; +import uk.ac.ed.ph.jqtiplus.group.item.interaction.choice.SimpleChoiceGroup; +import uk.ac.ed.ph.jqtiplus.group.item.response.declaration.ResponseDeclarationGroup; +import uk.ac.ed.ph.jqtiplus.group.item.response.processing.ResponseProcessingGroup; +import uk.ac.ed.ph.jqtiplus.group.outcome.declaration.OutcomeDeclarationGroup; +import uk.ac.ed.ph.jqtiplus.node.QtiNode; +import uk.ac.ed.ph.jqtiplus.node.content.ItemBody; +import uk.ac.ed.ph.jqtiplus.node.content.basic.TextRun; +import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P; +import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue; +import uk.ac.ed.ph.jqtiplus.node.expression.general.Correct; +import uk.ac.ed.ph.jqtiplus.node.expression.general.Variable; +import uk.ac.ed.ph.jqtiplus.node.expression.operator.IsNull; +import uk.ac.ed.ph.jqtiplus.node.expression.operator.Match; +import uk.ac.ed.ph.jqtiplus.node.expression.operator.Sum; +import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem; +import uk.ac.ed.ph.jqtiplus.node.item.CorrectResponse; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction; +import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleChoice; +import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseCondition; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseElse; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseElseIf; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseIf; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseProcessing; +import uk.ac.ed.ph.jqtiplus.node.item.response.processing.SetOutcomeValue; +import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration; +import uk.ac.ed.ph.jqtiplus.node.shared.FieldValue; +import uk.ac.ed.ph.jqtiplus.node.shared.declaration.DefaultValue; +import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader; +import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader; +import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem; +import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer; +import uk.ac.ed.ph.jqtiplus.types.ComplexReferenceIdentifier; +import uk.ac.ed.ph.jqtiplus.types.Identifier; +import uk.ac.ed.ph.jqtiplus.validation.ItemValidationResult; +import uk.ac.ed.ph.jqtiplus.value.BaseType; +import uk.ac.ed.ph.jqtiplus.value.Cardinality; +import uk.ac.ed.ph.jqtiplus.value.FloatValue; +import uk.ac.ed.ph.jqtiplus.value.IdentifierValue; +import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator; + +public class AssessmentItemPackageTest { + + private static final OLog log = Tracing.createLoggerFor(AssessmentItemPackageTest.class); + + @Test + public void loadAssessmentItem() throws URISyntaxException { + QtiXmlReader qtiXmlReader = new QtiXmlReader(new JqtiExtensionManager()); + QtiSerializer qtiSerializer = new QtiSerializer(new JqtiExtensionManager()); + + + URL testUrl = AssessmentItemPackageTest.class.getResource("assessment-item-single-choice.xml"); + ResourceLocator fileResourceLocator = new PathResourceLocator(Paths.get(testUrl.toURI())); + AssessmentObjectXmlLoader assessmentObjectXmlLoader = new AssessmentObjectXmlLoader(qtiXmlReader, fileResourceLocator); + + ResolvedAssessmentItem item = assessmentObjectXmlLoader.loadAndResolveAssessmentItem(testUrl.toURI()); + Assert.assertNotNull(item); + + AssessmentItem assessmentItem = item.getItemLookup().getRootNodeHolder().getRootNode(); + Assert.assertNotNull(assessmentItem); + + qtiSerializer.serializeJqtiObject(assessmentItem, System.out); + } + + @Test + public void buildAssessmentItem() throws URISyntaxException { + + QtiSerializer qtiSerializer = new QtiSerializer(new JqtiExtensionManager()); + + AssessmentItem assessmentItem = new AssessmentItem(); + assessmentItem.setIdentifier("id" + UUID.randomUUID()); + assessmentItem.setTitle("Physicists"); + assessmentItem.setAdaptive(Boolean.FALSE); + assessmentItem.setTimeDependent(Boolean.FALSE); + NodeGroupList nodeGroups = assessmentItem.getNodeGroups(); + + //single choice + Identifier deBroglieId = Identifier.parseString("id" + UUID.randomUUID().toString()); + Identifier maxPlanckId = Identifier.parseString("id" + UUID.randomUUID().toString()); + + + //define correct answer + ResponseDeclarationGroup responseDeclarations = nodeGroups.getResponseDeclarationGroup(); + ResponseDeclaration responseDeclaration = new ResponseDeclaration(assessmentItem); + responseDeclaration.setIdentifier(Identifier.parseString("RESPONSE_1")); + responseDeclaration.setCardinality(Cardinality.SINGLE); + responseDeclaration.setBaseType(BaseType.IDENTIFIER); + responseDeclarations.getResponseDeclarations().add(responseDeclaration); + + CorrectResponse correctResponse = new CorrectResponse(responseDeclaration); + responseDeclaration.setCorrectResponse(correctResponse); + + FieldValue fieldValue = new FieldValue(correctResponse); + IdentifierValue identifierValue = new IdentifierValue(maxPlanckId); + fieldValue.setSingleValue(identifierValue); + correctResponse.getFieldValues().add(fieldValue); + + //outcomes + OutcomeDeclarationGroup outcomeDeclarations = nodeGroups.getOutcomeDeclarationGroup(); + + // outcome score + OutcomeDeclaration scoreOutcomeDeclaration = new OutcomeDeclaration(assessmentItem); + scoreOutcomeDeclaration.setIdentifier(Identifier.parseString("SCORE")); + scoreOutcomeDeclaration.setCardinality(Cardinality.SINGLE); + scoreOutcomeDeclaration.setBaseType(BaseType.FLOAT); + outcomeDeclarations.getOutcomeDeclarations().add(scoreOutcomeDeclaration); + + DefaultValue scoreDefaultVal = new DefaultValue(scoreOutcomeDeclaration); + scoreOutcomeDeclaration.setDefaultValue(scoreDefaultVal); + + FieldValue scoreDefaultFieldVal = new FieldValue(scoreDefaultVal, FloatValue.ZERO); + scoreDefaultVal.getFieldValues().add(scoreDefaultFieldVal); + + // outcome max score + OutcomeDeclaration maxScoreOutcomeDeclaration = new OutcomeDeclaration(assessmentItem); + maxScoreOutcomeDeclaration.setIdentifier(Identifier.parseString("MAXSCORE")); + maxScoreOutcomeDeclaration.setCardinality(Cardinality.SINGLE); + maxScoreOutcomeDeclaration.setBaseType(BaseType.FLOAT); + outcomeDeclarations.getOutcomeDeclarations().add(maxScoreOutcomeDeclaration); + + DefaultValue maxScoreDefaultVal = new DefaultValue(maxScoreOutcomeDeclaration); + maxScoreOutcomeDeclaration.setDefaultValue(maxScoreDefaultVal); + + FieldValue maxScoreDefaultFieldVal = new FieldValue(maxScoreDefaultVal, new FloatValue(1.0f)); + maxScoreDefaultVal.getFieldValues().add(maxScoreDefaultFieldVal); + + // outcome feedback + OutcomeDeclaration feedbackOutcomeDeclaration = new OutcomeDeclaration(assessmentItem); + feedbackOutcomeDeclaration.setIdentifier(Identifier.parseString("FEEDBACKBASIC")); + feedbackOutcomeDeclaration.setCardinality(Cardinality.SINGLE); + feedbackOutcomeDeclaration.setBaseType(BaseType.IDENTIFIER); + outcomeDeclarations.getOutcomeDeclarations().add(feedbackOutcomeDeclaration); + + DefaultValue feedbackDefaultVal = new DefaultValue(feedbackOutcomeDeclaration); + feedbackOutcomeDeclaration.setDefaultValue(feedbackDefaultVal); + + FieldValue feedbackDefaultFieldVal = new FieldValue(feedbackDefaultVal, new IdentifierValue("empty")); + feedbackDefaultVal.getFieldValues().add(feedbackDefaultFieldVal); + + + //the interaction + ItemBodyGroup itemBodyGroup = nodeGroups.getItemBodyGroup(); + itemBodyGroup.setItemBody(new ItemBody(assessmentItem)); + + ItemBody itemBody = itemBodyGroup.getItemBody(); + + P question = getParagraph(itemBody, "Wo is the greatest physicist of all time?"); + itemBodyGroup.getItemBody().getBlocks().add(question); + + ChoiceInteraction choiceInteraction = new ChoiceInteraction(itemBody); + choiceInteraction.setMaxChoices(1); + choiceInteraction.setShuffle(true); + choiceInteraction.setResponseIdentifier(Identifier.parseString("RESPONSE_1")); + itemBodyGroup.getItemBody().getBlocks().add(choiceInteraction); + + PromptGroup prompts = new PromptGroup(choiceInteraction); + choiceInteraction.getNodeGroups().add(prompts); + + SimpleChoiceGroup singleChoices = new SimpleChoiceGroup(choiceInteraction); + choiceInteraction.getNodeGroups().add(singleChoices); + + SimpleChoice firstChoice = new SimpleChoice(choiceInteraction); + firstChoice.setIdentifier(deBroglieId); + P firstChoiceText = getParagraph(firstChoice, "Louis de Broglie"); + firstChoice.getFlowStatics().add(firstChoiceText); + singleChoices.getSimpleChoices().add(firstChoice); + + SimpleChoice secondChoice = new SimpleChoice(choiceInteraction); + secondChoice.setIdentifier(maxPlanckId); + P secondChoiceText = getParagraph(secondChoice, "Max Planck"); + secondChoice.getFlowStatics().add(secondChoiceText); + singleChoices.getSimpleChoices().add(secondChoice); + + //response processing + ResponseProcessingGroup responsesProcessing = nodeGroups.getResponseProcessingGroup(); + ResponseProcessing responseProcessing = new ResponseProcessing(assessmentItem); + responsesProcessing.setResponseProcessing(responseProcessing); + + ResponseCondition rule = new ResponseCondition(responseProcessing); + + //if no response + ResponseIf responseIf = new ResponseIf(rule); + rule.setResponseIf(responseIf); + + IsNull isNull = new IsNull(responseIf); + responseIf.getExpressions().add(isNull); + + Variable variable = new Variable(isNull); + variable.setIdentifier(ComplexReferenceIdentifier.parseString("RESPONSE_1")); + isNull.getExpressions().add(variable); + + { + SetOutcomeValue feedbackVar = new SetOutcomeValue(responseIf); + feedbackVar.setIdentifier(Identifier.parseString("FEEDBACKBASIC")); + BaseValue feedbackVal = new BaseValue(feedbackVar); + feedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER); + feedbackVal.setSingleValue(new IdentifierValue("empty")); + feedbackVar.setExpression(feedbackVal); + responseIf.getResponseRules().add(feedbackVar); + } + + //else if correct response + ResponseElseIf responseElseIf = new ResponseElseIf(rule); + rule.getResponseElseIfs().add(responseElseIf); + + //match + { + Match match = new Match(responseElseIf); + responseElseIf.getExpressions().add(match); + + Variable responseVar = new Variable(match); + responseVar.setIdentifier(ComplexReferenceIdentifier.parseString("RESPONSE_1")); + match.getExpressions().add(responseVar); + + Correct correct = new Correct(match); + correct.setIdentifier(ComplexReferenceIdentifier.parseString("RESPONSE_1")); + match.getExpressions().add(correct); + } + + // outcome score + { + SetOutcomeValue scoreOutcomeVar = new SetOutcomeValue(responseIf); + scoreOutcomeVar.setIdentifier(Identifier.parseString("SCORE")); + responseElseIf.getResponseRules().add(scoreOutcomeVar); + + Sum sum = new Sum(scoreOutcomeVar); + scoreOutcomeVar.getExpressions().add(sum); + + Variable scoreVar = new Variable(sum); + scoreVar.setIdentifier(ComplexReferenceIdentifier.parseString("SCORE")); + sum.getExpressions().add(scoreVar); + + Variable maxScoreVar = new Variable(sum); + maxScoreVar.setIdentifier(ComplexReferenceIdentifier.parseString("MAXSCORE")); + sum.getExpressions().add(maxScoreVar); + } + + // outcome feedback + { + SetOutcomeValue correctFeedbackVar = new SetOutcomeValue(responseIf); + correctFeedbackVar.setIdentifier(Identifier.parseString("FEEDBACKBASIC")); + BaseValue correctFeedbackVal = new BaseValue(correctFeedbackVar); + correctFeedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER); + correctFeedbackVal.setSingleValue(new IdentifierValue("correct")); + correctFeedbackVar.setExpression(correctFeedbackVal); + responseElseIf.getResponseRules().add(correctFeedbackVar); + } + + + // else failed + ResponseElse responseElse = new ResponseElse(rule); + rule.setResponseElse(responseElse); + {// feedback incorrect + SetOutcomeValue incorrectFeedbackVar = new SetOutcomeValue(responseIf); + incorrectFeedbackVar.setIdentifier(Identifier.parseString("FEEDBACKBASIC")); + BaseValue incorrectFeedbackVal = new BaseValue(incorrectFeedbackVar); + incorrectFeedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER); + incorrectFeedbackVal.setSingleValue(new IdentifierValue("incorrect")); + incorrectFeedbackVar.setExpression(incorrectFeedbackVal); + responseElse.getResponseRules().add(incorrectFeedbackVar); + } + + + + responseProcessing.getResponseRules().add(rule); + + + + + qtiSerializer.serializeJqtiObject(assessmentItem, System.out); + System.out.println("\n-------------"); + + File outputFile = new File("/Users/srosse/Desktop/generated_item.xml"); + if(outputFile.exists()) { + outputFile.delete(); + outputFile = new File("/Users/srosse/Desktop/generated_item.xml"); + } + try(FileOutputStream out = new FileOutputStream(outputFile)) { + qtiSerializer.serializeJqtiObject(assessmentItem, out); + } catch(Exception e) { + log.error("", e); + } + + QtiXmlReader qtiXmlReader = new QtiXmlReader(new JqtiExtensionManager()); + ResourceLocator fileResourceLocator = new PathResourceLocator(outputFile.toPath()); + AssessmentObjectXmlLoader assessmentObjectXmlLoader = new AssessmentObjectXmlLoader(qtiXmlReader, fileResourceLocator); + ItemValidationResult item = assessmentObjectXmlLoader.loadResolveAndValidateItem(outputFile.toURI()); + System.out.println("Has errors: " + item.hasErrors()); + } + + private P getParagraph(QtiNode parent, String content) { + P paragraph = new P(parent); + TextRun text = new TextRun(paragraph, content); + paragraph.getInlines().add(text); + return paragraph; + } + + + +} diff --git a/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java new file mode 100644 index 00000000000..616dcdfe4db --- /dev/null +++ b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentTestPackageTest.java @@ -0,0 +1,151 @@ +package org.olat.ims.qti21.model.xml; + +import java.io.File; +import java.io.FileOutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator; + +import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager; +import uk.ac.ed.ph.jqtiplus.node.content.variable.RubricBlock; +import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration; +import uk.ac.ed.ph.jqtiplus.node.test.AssessmentItemRef; +import uk.ac.ed.ph.jqtiplus.node.test.AssessmentSection; +import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest; +import uk.ac.ed.ph.jqtiplus.node.test.ItemSessionControl; +import uk.ac.ed.ph.jqtiplus.node.test.NavigationMode; +import uk.ac.ed.ph.jqtiplus.node.test.Ordering; +import uk.ac.ed.ph.jqtiplus.node.test.SubmissionMode; +import uk.ac.ed.ph.jqtiplus.node.test.TestPart; +import uk.ac.ed.ph.jqtiplus.node.test.TimeLimits; +import uk.ac.ed.ph.jqtiplus.node.test.View; +import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeProcessing; +import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.SetOutcomeValue; +import uk.ac.ed.ph.jqtiplus.notification.Notification; +import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader; +import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader; +import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer; +import uk.ac.ed.ph.jqtiplus.types.Identifier; +import uk.ac.ed.ph.jqtiplus.validation.TestValidationResult; +import uk.ac.ed.ph.jqtiplus.value.BaseType; +import uk.ac.ed.ph.jqtiplus.value.Cardinality; +import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator; + +public class AssessmentTestPackageTest { + + private static final OLog log = Tracing.createLoggerFor(AssessmentTestPackageTest.class); + + @Test + public void buildAssessmentTest() throws URISyntaxException { + + QtiSerializer qtiSerializer = new QtiSerializer(new JqtiExtensionManager()); + + AssessmentTest assessmentTest = new AssessmentTest(); + assessmentTest.setIdentifier("id" + UUID.randomUUID()); + assessmentTest.setTitle("My test"); + assessmentTest.setToolName("OpenOLAT"); + assessmentTest.setToolVersion("11.0"); + + //outcome declarations + OutcomeDeclaration scoreOutcomeDeclaration = new OutcomeDeclaration(assessmentTest); + scoreOutcomeDeclaration.setIdentifier(Identifier.parseString("SCORE")); + scoreOutcomeDeclaration.setCardinality(Cardinality.SINGLE); + scoreOutcomeDeclaration.setBaseType(BaseType.FLOAT); + assessmentTest.getOutcomeDeclarations().add(scoreOutcomeDeclaration); + + + //time limits + TimeLimits timeLimits = new TimeLimits(assessmentTest); + timeLimits.setAllowLateSubmission(Boolean.FALSE); + timeLimits.setMinimum(0.0d); + timeLimits.setMaximum(1800.0); + assessmentTest.setTimeLimits(timeLimits); + + //test parts + TestPart part = new TestPart(assessmentTest); + part.setIdentifier(Identifier.parseString("id" + UUID.randomUUID())); + part.setNavigationMode(NavigationMode.NONLINEAR); + part.setSubmissionMode(SubmissionMode.INDIVIDUAL); + assessmentTest.getTestParts().add(part); + + //part -> item session control + ItemSessionControl itemSessionControl = new ItemSessionControl(part); + itemSessionControl.setAllowComment(Boolean.TRUE); + itemSessionControl.setAllowReview(Boolean.TRUE); + itemSessionControl.setAllowSkipping(Boolean.TRUE); + itemSessionControl.setMaxAttempts(12); + itemSessionControl.setShowFeedback(Boolean.TRUE); + itemSessionControl.setShowSolution(Boolean.TRUE); + part.setItemSessionControl(itemSessionControl); + + // section + AssessmentSection section = new AssessmentSection(part); + section.setFixed(Boolean.TRUE); + section.setVisible(Boolean.TRUE); + section.setTitle("My section"); + section.setIdentifier(Identifier.parseString("id" + UUID.randomUUID())); + part.getAssessmentSections().add(section); + + Ordering ordering = new Ordering(section); + ordering.setShuffle(false); + section.setOrdering(ordering); + + RubricBlock rubrickBlock = new RubricBlock(section); + rubrickBlock.setViews(Collections.singletonList(View.CANDIDATE)); + section.getRubricBlocks().add(rubrickBlock); + + AssessmentItemRef item = new AssessmentItemRef(section); + item.setIdentifier(Identifier.parseString("id" + UUID.randomUUID())); + item.setHref(new URI("test-item.xml")); + section.getSectionParts().add(item); + + + + + + //outcome processing + OutcomeProcessing outcomeProcessing = new OutcomeProcessing(assessmentTest); + assessmentTest.setOutcomeProcessing(outcomeProcessing); + + SetOutcomeValue outcomeRule = new SetOutcomeValue(outcomeProcessing); + + + outcomeProcessing.getOutcomeRules().add(outcomeRule); + + //feedbacks + assessmentTest.getTestFeedbacks(); + + + qtiSerializer.serializeJqtiObject(assessmentTest, System.out); + System.out.println("\n-------------"); + + File outputFile = new File("/Users/srosse/Desktop/QTI 2.1/generated_test.xml"); + if(outputFile.exists()) { + outputFile.delete(); + outputFile = new File("/Users/srosse/Desktop/QTI 2.1/generated_test.xml"); + } + try(FileOutputStream out = new FileOutputStream(outputFile)) { + qtiSerializer.serializeJqtiObject(assessmentTest, out); + } catch(Exception e) { + log.error("", e); + } + + QtiXmlReader qtiXmlReader = new QtiXmlReader(new JqtiExtensionManager()); + ResourceLocator fileResourceLocator = new PathResourceLocator(outputFile.toPath()); + AssessmentObjectXmlLoader assessmentObjectXmlLoader = new AssessmentObjectXmlLoader(qtiXmlReader, fileResourceLocator); + TestValidationResult test = assessmentObjectXmlLoader.loadResolveAndValidateTest(outputFile.toURI()); + + System.out.println("Has errors: " + test.hasErrors()); + for(Notification notification: test.getErrors()) { + System.out.println(notification.getQtiNode() + " : " + notification.getMessage()); + } + Assert.assertFalse(test.hasErrors()); + } +} \ No newline at end of file diff --git a/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java b/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java new file mode 100644 index 00000000000..e931a16ef48 --- /dev/null +++ b/src/test/java/org/olat/ims/qti21/model/xml/ManifestPackageTest.java @@ -0,0 +1,46 @@ +package org.olat.ims.qti21.model.xml; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.imscp.xml.manifest.ManifestType; + +import uk.ac.ed.ph.jqtiplus.utils.contentpackaging.ContentPackageResource; +import uk.ac.ed.ph.jqtiplus.utils.contentpackaging.ImsManifestException; +import uk.ac.ed.ph.jqtiplus.utils.contentpackaging.QtiContentPackageExtractor; +import uk.ac.ed.ph.jqtiplus.utils.contentpackaging.QtiContentPackageSummary; +import uk.ac.ed.ph.jqtiplus.xmlutils.XmlResourceNotFoundException; + +public class ManifestPackageTest { + + @Test + public void makeManifest() throws XmlResourceNotFoundException, ImsManifestException, IOException { + ManifestType manifestType = ManifestPackage.createEmptyManifest(); + String testFilename = ManifestPackage.appendAssessmentTest(manifestType); + String itemFilename = ManifestPackage.appendAssessmentItem(manifestType); + Assert.assertNotNull(testFilename); + Assert.assertNotNull(itemFilename); + + File tmpDir = new File("/HotCoffee/tmp/imsmanifest/"); + if(!tmpDir.exists()) { + tmpDir.mkdirs(); + } + + + FileOutputStream out = new FileOutputStream(new File(tmpDir, "imsmanifest.xml")); + ManifestPackage.write(manifestType, out); + out.flush(); + out.close(); + + QtiContentPackageExtractor extractor = new QtiContentPackageExtractor(tmpDir); + QtiContentPackageSummary summary = extractor.parse(); + List<ContentPackageResource> items = summary.getItemResources(); + List<ContentPackageResource> tests = summary.getTestResources(); + Assert.assertEquals(1, items.size()); + Assert.assertEquals(1, tests.size()); + } +} diff --git a/src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice.xml b/src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice.xml new file mode 100644 index 00000000000..75292ef68e0 --- /dev/null +++ b/src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8"?> +<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1p1.xsd http://www.w3.org/1998/Math/MathML http://www.w3.org/Math/XMLSchema/mathml2/mathml2.xsd" identifier="id1399ad03-4981-4240-8a39-dfd9857bf0e8_1" title="Single choice" adaptive="false" timeDependent="false"> + <responseDeclaration identifier="RESPONSE_1" cardinality="single" baseType="identifier"> + <correctResponse> + <value>id2c67a95f-608b-4d29-b0dc-1aba7f2f8b76_1</value> + </correctResponse> + </responseDeclaration> + <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"> + <defaultValue> + <value>0</value> + </defaultValue> + </outcomeDeclaration> + <outcomeDeclaration identifier="MAXSCORE" cardinality="single" baseType="float"> + <defaultValue> + <value>1</value> + </defaultValue> + </outcomeDeclaration> + <outcomeDeclaration identifier="FEEDBACKBASIC" cardinality="single" baseType="identifier"> + <defaultValue> + <value>empty</value> + </defaultValue> + </outcomeDeclaration> + <itemBody> + <p>Wo is a <strong>physicist</strong>?</p> + <choiceInteraction responseIdentifier="RESPONSE_1" shuffle="true" maxChoices="1"> + <simpleChoice identifier="id2c67a95f-608b-4d29-b0dc-1aba7f2f8b76_1"> + <p>Masamune Shirow</p> + </simpleChoice> + <simpleChoice identifier="id9c2a3e00-7759-4d14-8194-4b8fef39bf31"> + <p>Alfred Bogart</p> + </simpleChoice> + <simpleChoice identifier="id2df244d9-6c2e-4856-9e18-48ad452d03ea"> + <p>Louis de Broglie</p> + </simpleChoice> + </choiceInteraction> + </itemBody> + <responseProcessing> + <responseCondition> + <responseIf> + <isNull> + <variable identifier="RESPONSE_1" /> + </isNull> + <setOutcomeValue identifier="FEEDBACKBASIC"> + <baseValue baseType="identifier">empty</baseValue> + </setOutcomeValue> + </responseIf> + <responseElseIf> + <match> + <variable identifier="RESPONSE_1" /><correct identifier="RESPONSE_1" /> + </match> + <setOutcomeValue identifier="SCORE"> + <sum> + <variable identifier="SCORE" /><variable identifier="MAXSCORE" /> + </sum> + </setOutcomeValue> + <setOutcomeValue identifier="FEEDBACKBASIC"> + <baseValue baseType="identifier">correct</baseValue> + </setOutcomeValue> + </responseElseIf> + <responseElse> + <setOutcomeValue identifier="FEEDBACKBASIC"> + <baseValue baseType="identifier">incorrect</baseValue> + </setOutcomeValue> + </responseElse> + </responseCondition> + </responseProcessing> +</assessmentItem> \ No newline at end of file diff --git a/src/test/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandlerTest.java b/src/test/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandlerTest.java new file mode 100644 index 00000000000..6c4cb5b658b --- /dev/null +++ b/src/test/java/org/olat/ims/qti21/repository/handlers/QTI21AssessmentTestHandlerTest.java @@ -0,0 +1,15 @@ +package org.olat.ims.qti21.repository.handlers; + +import org.junit.Test; + +public class QTI21AssessmentTestHandlerTest { + + @Test + public void createImsManfest() { + + QTI21AssessmentTestHandler handler = new QTI21AssessmentTestHandler(); + handler.createMinimalAssessmentTest(); + + } + +} -- GitLab