Skip to content
Snippets Groups Projects
Commit 3610c044 authored by srosse's avatar srosse
Browse files

OO-1593: add preview and editor for QTI 2.1 questions

parent 1852f8ce
No related branches found
No related tags found
No related merge requests found
Showing with 141 additions and 19 deletions
......@@ -19,14 +19,26 @@
*/
package org.olat.ims.qti21.pool;
import java.io.File;
import java.net.URI;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.ims.qti21.QTI21Service;
import org.olat.ims.qti21.ui.editor.AssessmentItemEditorController;
import org.olat.ims.qti21.ui.editor.events.AssessmentItemEvent;
import org.olat.modules.qpool.QPoolItemEditorController;
import org.olat.modules.qpool.QPoolService;
import org.olat.modules.qpool.QuestionItem;
import org.springframework.beans.factory.annotation.Autowired;
import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
/**
*
......@@ -37,13 +49,33 @@ import org.olat.modules.qpool.QuestionItem;
public class QTI21EditorController extends BasicController implements QPoolItemEditorController {
private final VelocityContainer mainVC;
private AssessmentItemEditorController editorCtrl;
private File resourceFile;
private QuestionItem questionItem;
@Autowired
private QPoolService qpoolService;
@Autowired
private QTI21Service qtiService;
public QTI21EditorController(UserRequest ureq, WindowControl wControl, QuestionItem questionItem) {
super(ureq, wControl);
this.questionItem = questionItem;
mainVC = createVelocityContainer("editor_wrapper");
mainVC = createVelocityContainer("pool_editor");
File resourceDirectory = qpoolService.getRootDirectory(questionItem);
resourceFile = qpoolService.getRootFile(questionItem);
URI assessmentItemUri = resourceFile.toURI();
ResolvedAssessmentItem resolvedAssessmentItem = qtiService
.loadAndResolveAssessmentItem(assessmentItemUri, resourceDirectory);
editorCtrl = new AssessmentItemEditorController(ureq, wControl,
resolvedAssessmentItem, resourceDirectory, resourceFile);
listenTo(editorCtrl);
mainVC.put("editor", editorCtrl.getInitialComponent());
putInitialPanel(mainVC);
}
......@@ -56,6 +88,17 @@ public class QTI21EditorController extends BasicController implements QPoolItemE
protected void doDispose() {
//
}
@Override
protected void event(UserRequest ureq, Controller source, Event event) {
if(source == editorCtrl) {
if(event instanceof AssessmentItemEvent) {
AssessmentItemEvent aie = (AssessmentItemEvent)event;
AssessmentItem assessmentItem = aie.getAssessmentItem();
qtiService.persistAssessmentObject(resourceFile, assessmentItem);
}
}
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
......
......@@ -120,6 +120,4 @@ public class QTI21ImportProcessor {
questionItemDao.persist(owner, poolItem);
return poolItem;
}
}
......@@ -63,10 +63,11 @@ public class QTI21PreviewController extends BasicController {
} else {
File resourceDirectory = qpoolService.getRootDirectory(qitem);
URI assessmentItemUri = file.toURI();
File itemFile = qpoolService.getRootFile(qitem);
ResolvedAssessmentItem resolvedAssessmentItem = qtiService
.loadAndResolveAssessmentItem(assessmentItemUri, resourceDirectory);
previewCtrl = new AssessmentItemDisplayController(ureq, wControl, true, resolvedAssessmentItem, resourceDirectory);
previewCtrl = new AssessmentItemDisplayController(ureq, wControl, resolvedAssessmentItem, resourceDirectory, itemFile);
listenTo(previewCtrl);
mainVC.put("preview", previewCtrl.getInitialComponent());
}
......
......@@ -46,12 +46,14 @@ import org.olat.ims.qti.fileresource.TestFileResource;
import org.olat.ims.qti21.QTI21Constants;
import org.olat.ims.qti21.QTI21Service;
import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
import org.olat.ims.qti21.model.xml.ManifestPackage;
import org.olat.ims.qti21.model.xml.interactions.EssayAssessmentItemBuilder;
import org.olat.ims.qti21.model.xml.interactions.KPrimAssessmentItemBuilder;
import org.olat.ims.qti21.model.xml.interactions.MultipleChoiceAssessmentItemBuilder;
import org.olat.ims.qti21.model.xml.interactions.SingleChoiceAssessmentItemBuilder;
import org.olat.ims.qti21.pool.QTI21AssessmentItemFactory.Type;
import org.olat.ims.resources.IMSEntityResolver;
import org.olat.imscp.xml.manifest.ManifestType;
import org.olat.modules.qpool.ExportFormatOptions;
import org.olat.modules.qpool.ExportFormatOptions.Outcome;
import org.olat.modules.qpool.QItemFactory;
......@@ -220,8 +222,9 @@ public class QTI21QPoolServiceProvider implements QPoolSPI {
}
@Override
public Controller getEditableController(UserRequest ureq, WindowControl wControl, QuestionItem item) {
return null;
public Controller getEditableController(UserRequest ureq, WindowControl wControl, QuestionItem qitem) {
Controller editorCtrl = new QTI21EditorController(ureq, wControl, qitem);
return editorCtrl;
}
public QuestionItem createItem(Identity identity, Type type, String title, Locale locale) {
......@@ -247,6 +250,12 @@ public class QTI21QPoolServiceProvider implements QPoolSPI {
VFSLeaf leaf = baseDir.createChildLeaf(qitem.getRootFilename());
File itemFile = ((LocalImpl)leaf).getBasefile();
qtiService.persistAssessmentObject(itemFile, assessmentItem);
//create imsmanifest
ManifestType manifestType = ManifestPackage.createEmptyManifest();
ManifestPackage.appendAssessmentItem(itemFile.getName(), manifestType);
ManifestPackage.write(manifestType, new File(itemFile.getParentFile(), "imsmanifest.xml"));
return qitem;
}
}
\ No newline at end of file
$r.render("editor")
\ No newline at end of file
$r.render("preview")
\ No newline at end of file
......@@ -86,7 +86,7 @@ public class AssessmentItemDisplayController extends BasicController implements
private final String mapperUri;
private final File fUnzippedDirRoot;
private final AssessmentItemRef itemRef;
private final File itemFileRef;
private final ResolvedAssessmentItem resolvedAssessmentItem;
private CandidateEvent lastEvent;
......@@ -96,28 +96,70 @@ public class AssessmentItemDisplayController extends BasicController implements
@Autowired
private QTI21Service qtiService;
public AssessmentItemDisplayController(UserRequest ureq, WindowControl wControl,
boolean authorMode, ResolvedAssessmentItem resolvedAssessmentItem, File fUnzippedDirRoot) {
this(ureq, wControl, null, null, authorMode, false, resolvedAssessmentItem, null, fUnzippedDirRoot);
/**
* OPen in memory session
* @param ureq
* @param wControl
* @param authorMode
* @param resolvedAssessmentItem
* @param fUnzippedDirRoot
* @param itemFileRef
*/
public AssessmentItemDisplayController(UserRequest ureq, WindowControl wControl, ResolvedAssessmentItem resolvedAssessmentItem,
File fUnzippedDirRoot, File itemFileRef) {
super(ureq, wControl);
this.itemFileRef = itemFileRef;
this.fUnzippedDirRoot = fUnzippedDirRoot;
this.resolvedAssessmentItem = resolvedAssessmentItem;
currentRequestTimestamp = ureq.getRequestTimestamp();
candidateSession = new InMemoryAssessmentTestSession();
mapperUri = registerCacheableMapper(null, UUID.randomUUID().toString(), new ResourcesMapper(itemFileRef.toURI()));
itemSessionController = enterSession(ureq);
if (itemSessionController.getItemSessionState().isEnded()) {
mainVC = createVelocityContainer("end");
} else {
mainVC = createVelocityContainer("run");
initQtiWorks(ureq);
}
putInitialPanel(mainVC);
}
public AssessmentItemDisplayController(UserRequest ureq, WindowControl wControl,
RepositoryEntry testEntry, AssessmentEntry assessmentEntry, boolean authorMode, boolean persistent,
ResolvedAssessmentItem resolvedAssessmentItem, AssessmentItemRef itemRef, File fUnzippedDirRoot) {
super(ureq, wControl);
this.itemRef = itemRef;
this.itemFileRef = new File(fUnzippedDirRoot, itemRef.getHref().toString());
this.fUnzippedDirRoot = fUnzippedDirRoot;
this.resolvedAssessmentItem = resolvedAssessmentItem;
currentRequestTimestamp = ureq.getRequestTimestamp();
if(persistent) {
candidateSession = qtiService.createAssessmentTestSession(getIdentity(), assessmentEntry, testEntry, itemRef.getIdentifier().toString(), testEntry, authorMode);
candidateSession = new InMemoryAssessmentTestSession();
mapperUri = registerCacheableMapper(null, UUID.randomUUID().toString(), new ResourcesMapper(itemFileRef.toURI()));
itemSessionController = enterSession(ureq);
if (itemSessionController.getItemSessionState().isEnded()) {
mainVC = createVelocityContainer("end");
} else {
candidateSession = new InMemoryAssessmentTestSession();
mainVC = createVelocityContainer("run");
initQtiWorks(ureq);
}
putInitialPanel(mainVC);
}
public AssessmentItemDisplayController(UserRequest ureq, WindowControl wControl,
RepositoryEntry testEntry, AssessmentEntry assessmentEntry, boolean authorMode,
ResolvedAssessmentItem resolvedAssessmentItem, AssessmentItemRef itemRef, File fUnzippedDirRoot) {
super(ureq, wControl);
File assessmentObjectUri = itemRef == null ? null : new File(fUnzippedDirRoot, itemRef.getHref().toString());
mapperUri = registerCacheableMapper(null, UUID.randomUUID().toString(), new ResourcesMapper(assessmentObjectUri.toURI()));
this.itemFileRef = new File(fUnzippedDirRoot, itemRef.getHref().toString());
this.fUnzippedDirRoot = fUnzippedDirRoot;
this.resolvedAssessmentItem = resolvedAssessmentItem;
currentRequestTimestamp = ureq.getRequestTimestamp();
candidateSession = qtiService.createAssessmentTestSession(getIdentity(), assessmentEntry, testEntry, itemRef.getIdentifier().toString(), testEntry, authorMode);
mapperUri = registerCacheableMapper(null, UUID.randomUUID().toString(), new ResourcesMapper(itemFileRef.toURI()));
itemSessionController = enterSession(ureq);
......@@ -131,7 +173,7 @@ public class AssessmentItemDisplayController extends BasicController implements
}
private void initQtiWorks(UserRequest ureq) {
String filename = itemRef.getHref().toString();
String filename = itemFileRef.getName();
qtiWorksCtrl = new QtiWorksController(ureq, getWindowControl(), filename);
listenTo(qtiWorksCtrl);
mainVC.put("qtirun", qtiWorksCtrl.getInitialComponent());
......
......@@ -83,6 +83,27 @@ public class AssessmentItemEditorController extends BasicController {
@Autowired
private AssessmentService assessmentService;
public AssessmentItemEditorController(UserRequest ureq, WindowControl wControl,
ResolvedAssessmentItem resolvedAssessmentItem, File unzippedDirectory, File itemFile) {
super(ureq, wControl);
this.itemRef = null;
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(), resolvedAssessmentItem, unzippedDirectory, itemFile);
listenTo(displayCtrl);
tabbedPane.addTab("Preview", displayCtrl.getInitialComponent());
putInitialPanel(mainVC);
}
public AssessmentItemEditorController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry,
ResolvedAssessmentItem resolvedAssessmentItem, AssessmentItemRef itemRef, File unzippedDirectory) {
super(ureq, wControl);
......@@ -98,7 +119,7 @@ public class AssessmentItemEditorController extends BasicController {
AssessmentEntry assessmentEntry = assessmentService.getOrCreateAssessmentEntry(getIdentity(), testEntry, null, testEntry);
displayCtrl = new AssessmentItemDisplayController(ureq, getWindowControl(),
testEntry, assessmentEntry, true, true, resolvedAssessmentItem, itemRef, unzippedDirectory);
testEntry, assessmentEntry, true, resolvedAssessmentItem, itemRef, unzippedDirectory);
listenTo(displayCtrl);
tabbedPane.addTab("Preview", displayCtrl.getInitialComponent());
......
......@@ -175,6 +175,12 @@ public class SingleChoiceEditorController extends FormBasicController {
allOk &= false;
}
String correctAnswer = ureq.getParameter("correct");
if(!StringHelper.containsNonWhitespace(correctAnswer)) {
allOk &= false;
textEl.setErrorKey("form.legende.mandatory", null);
}
return allOk & super.validateFormLogic(ureq);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment