Skip to content
Snippets Groups Projects
Commit 702f9b61 authored by srosse's avatar srosse
Browse files

no-jira: add a converter on import of fix the issues QtiWorks model has with Onyx files

parent e9f50f14
No related branches found
No related tags found
No related merge requests found
/**
* <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;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;
/**
* It converts Onyx final to qtiWorks. It fix:<br>
* <ul>
* <li>imsmanifest: type from "imsqti_assessment_xmlv2p1" to "imsqti_test_xmlv2p1"</li>
* <li>assessmentTest: surround rubricBlock's text only content with &lt;p&gt;</li>
* <li>assesementItem: surround itemBody's text only with &lt;p&gt;</li>
* <li>assesementItem: strip html code from &lt;prompt&gt;
* </ul>
*
*
* Initial date: 25.06.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class OnyxToQtiWorksHandler extends DefaultHandler2 {
private final XMLStreamWriter xtw;
private boolean itemBody = false;
private StringBuilder itemCharacterBuffer;
private boolean rubricBlock = false;
private StringBuilder rubricCharacterBuffer;
private boolean prompt = false;
public OnyxToQtiWorksHandler(XMLStreamWriter xtw) {
this.xtw = xtw;
}
@Override
public void startDocument() throws SAXException {
try{
xtw.writeStartDocument("utf-8", "1.0");
} catch (XMLStreamException e) {
throw new SAXException(e);
}
}
@Override
public void comment(char[] ch, int start, int length)
throws SAXException {
try{
String comment = new String(ch, start, length);
xtw.writeComment(comment);
} catch (XMLStreamException e) {
throw new SAXException(e);
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
try {
if(prompt) {
return;
}
if(itemBody) {
String characters = itemCharacterBuffer.toString().trim();
if(characters.length() > 0) {
xtw.writeStartElement("p");
xtw.writeCharacters(characters);
xtw.writeEndElement();
}
itemBody = false;
itemCharacterBuffer = null;
} else if(rubricBlock) {
String characters = rubricCharacterBuffer.toString().trim();
if(characters.length() > 0) {
xtw.writeStartElement("p");
xtw.writeCharacters(characters);
xtw.writeEndElement();
}
rubricBlock = false;
rubricCharacterBuffer = null;
}
xtw.writeStartElement(qName);
if("imscp:resource".equals(qName)) {
int numOfAttributes = attributes.getLength();
for(int i=0;i<numOfAttributes; i++) {
String attrQName = attributes.getQName(i);
String attrValue = attributes.getValue(i);
if("type".equals(attrQName) && "imsqti_assessment_xmlv2p1".equals(attrValue)) {
xtw.writeAttribute(attrQName, "imsqti_test_xmlv2p1");
} else {
xtw.writeAttribute(attrQName, attrValue);
}
}
} else {
int numOfAttributes = attributes.getLength();
for(int i=0;i<numOfAttributes; i++) {
String attrQName = attributes.getQName(i);
String attrValue = attributes.getValue(i);
xtw.writeAttribute(attrQName, attrValue);
}
}
if("itemBody".equals(qName)) {
itemBody = true;
itemCharacterBuffer = new StringBuilder();
} else if("rubricBlock".equals(qName)) {
rubricBlock = true;
rubricCharacterBuffer = new StringBuilder();
} else if("prompt".equals(qName)) {
prompt = true;
}
} catch (XMLStreamException e) {
throw new SAXException(e);
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if(itemBody) {
itemCharacterBuffer.append(ch, start, length);
} else if(rubricBlock) {
rubricCharacterBuffer.append(ch, start, length);
} else {
try {
xtw.writeCharacters(ch, start, length);
} catch (XMLStreamException e) {
throw new SAXException(e);
}
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
try {
if(rubricBlock) {
String characters = rubricCharacterBuffer.toString().trim();
if(characters.length() > 0) {
xtw.writeStartElement("p");
xtw.writeCharacters(characters);
xtw.writeEndElement();
}
rubricBlock = false;
rubricCharacterBuffer = null;
} else if(prompt) {
if(!"prompt".equals(qName)) {
return;//only print characters
} else {
prompt = false;
}
}
xtw.writeEndElement();
} catch (XMLStreamException e) {
throw new SAXException(e);
}
}
@Override
public void endDocument() throws SAXException {
try {
xtw.writeEndDocument();
xtw.flush();
xtw.close();
} catch (XMLStreamException e) {
throw new SAXException(e);
}
}
}
\ No newline at end of file
......@@ -20,12 +20,24 @@
package org.olat.ims.qti21.repository.handlers;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Locale;
import java.util.UUID;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DBFactory;
......@@ -40,6 +52,7 @@ 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.PathUtils.YesMatcher;
import org.olat.core.util.coordinate.LockResult;
import org.olat.course.assessment.AssessmentMode;
import org.olat.fileresource.FileResourceManager;
......@@ -47,16 +60,11 @@ 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.model.xml.OnyxToQtiWorksHandler;
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;
......@@ -145,13 +153,89 @@ public class QTI21AssessmentTestHandler extends FileHandler {
OLATResource resource = OLATResourceManager.getInstance().createAndPersistOLATResourceInstance(ores);
File fResourceFileroot = FileResourceManager.getInstance().getFileResourceRootImpl(resource).getBasefile();
File zipDir = new File(fResourceFileroot, FileResourceManager.ZIPDIR);
FileResource.copyResource(file, filename, zipDir);
copyResource(file, filename, zipDir);
RepositoryEntry re = CoreSpringFactory.getImpl(RepositoryService.class)
.create(initialAuthor, null, "", displayname, description, resource, RepositoryEntry.ACC_OWNERS);
DBFactory.getInstance().commit();
return re;
}
private boolean copyResource(File file, String filename, File targetDirectory) {
try {
Path path = FileResource.getResource(file, filename);
if(path == null) {
return false;
}
Path destDir = targetDirectory.toPath();
Files.walkFileTree(path, new CopyAndConvertVisitor(path, destDir, new YesMatcher()));
return true;
} catch (IOException e) {
log.error("", e);
return false;
}
}
private static class CopyAndConvertVisitor extends SimpleFileVisitor<Path> {
private final Path source;
private final Path destDir;
private final PathMatcher filter;
public CopyAndConvertVisitor(Path source, Path destDir, PathMatcher filter) {
this.source = source;
this.destDir = destDir;
this.filter = filter;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Path relativeFile = source.relativize(file);
final Path destFile = Paths.get(destDir.toString(), relativeFile.toString());
if(filter.matches(file)) {
String filename = file.getFileName().toString();
if(filename != null && filename.endsWith("xml")) {
convertXmlFile(file, destFile);
} else {
Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING);
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
Path relativeDir = source.relativize(dir);
final Path dirToCreate = Paths.get(destDir.toString(), relativeDir.toString());
if(Files.notExists(dirToCreate)){
Files.createDirectory(dirToCreate);
}
return FileVisitResult.CONTINUE;
}
/**
* Convert the XML files
* @param inputFile
* @param outputFile
*/
private void convertXmlFile(Path inputFile, Path outputFile) {
try(InputStream in = Files.newInputStream(inputFile);
Writer out = Files.newBufferedWriter(outputFile, Charset.forName("UTF-8"))) {
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xtw = xof.createXMLStreamWriter(out);
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
OnyxToQtiWorksHandler myHandler = new OnyxToQtiWorksHandler(xtw);
saxParser.setProperty("http://xml.org/sax/properties/lexical-handler", myHandler);
saxParser.parse(in, myHandler);
} catch(Exception e) {
log.error("", e);
}
}
}
@Override
public MediaResource getAsMediaResource(OLATResourceable res, boolean backwardsCompatible) {
......
/**
* <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.restapi;
import java.util.Date;
......@@ -16,6 +35,14 @@ import uk.ac.ed.ph.jqtiplus.xmlutils.xslt.XsltStylesheetCache;
import uk.ac.ed.ph.qtiworks.mathassess.XsltStylesheetCacheAdapter;
import uk.ac.ed.ph.qtiworks.mathassess.glue.AsciiMathHelper;
/**
*
*
*
* Initial date: 25.06.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
@Path("math")
public class MathWebService {
......@@ -28,7 +55,6 @@ public class MathWebService {
@Path("verifyAsciiMath")
@Produces({MediaType.APPLICATION_JSON})
public Response verifyAsciiMath(@FormParam("input") String asciiMathInput) {
XsltStylesheetCache stylesheetCache = new SimpleXsltStylesheetCache();
AsciiMathHelper asciiMathHelper = new AsciiMathHelper(new XsltStylesheetCacheAdapter(stylesheetCache));
Map<String, String> upConvertedAsciiMathInput = asciiMathHelper.upConvertAsciiMathInput(asciiMathInput);
......
/**
* <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;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import org.junit.Test;
import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader;
import uk.ac.ed.ph.jqtiplus.reading.QtiModelBuildingError;
import uk.ac.ed.ph.jqtiplus.reading.QtiXmlInterpretationException;
import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader;
import uk.ac.ed.ph.jqtiplus.validation.ItemValidationResult;
import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator;
/**
* Only to play with
*
*
* Initial date: 25.06.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class OnyxToQtiWorksTest {
@Test
public void fixItem() {
File itemFile = new File("/Users/srosse/Desktop/QTI/Beispielaufgaben_Onyx_3_6/Auswahlaufgabe_1509468352.xml");
File rubricFile = new File("/Users/srosse/Desktop/QTI/fxtest_Onyx_3_1_1/Fxtest_684549665.xml");
File manifestFile = new File("/Users/srosse/Desktop/QTI/Beispielaufgaben_Onyx_3_6/imsmanifest.xml");
File promptFile = new File("/Users/srosse/Desktop/QTI/fxtest_Onyx_3_1_1/Task_1597435347.xml");
File xmlFile = promptFile;
SAXParserFactory factory = SAXParserFactory.newInstance();
File outFile = new File("/Users/srosse/Desktop/QTI/text.xml");
if(outFile.exists()) {
outFile.delete();
outFile = new File("/Users/srosse/Desktop/QTI/text.xml");
}
try (OutputStream byteOut = new FileOutputStream(outFile);
OutputStreamWriter out = new OutputStreamWriter(byteOut, "UTF8")) {
// Parse the input
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xtw = xof.createXMLStreamWriter(out);
SAXParser saxParser = factory.newSAXParser();
OnyxToQtiWorksHandler myHandler = new OnyxToQtiWorksHandler(xtw);
saxParser.setProperty("http://xml.org/sax/properties/lexical-handler", myHandler);
saxParser.parse(xmlFile, myHandler);
out.flush();
byteOut.flush();
QtiXmlReader qtiXmlReader = new QtiXmlReader(new JqtiExtensionManager());
ResourceLocator fileResourceLocator = new PathResourceLocator(outFile.toPath());
AssessmentObjectXmlLoader assessmentObjectXmlLoader = new AssessmentObjectXmlLoader(qtiXmlReader, fileResourceLocator);
ItemValidationResult itemResult = assessmentObjectXmlLoader.loadResolveAndValidateItem(outFile.toURI());
System.out.println("Has errors: " + (itemResult.getModelValidationErrors().size() > 0));
BadResourceException e = itemResult.getResolvedAssessmentItem().getItemLookup().getBadResourceException();
if(e instanceof QtiXmlInterpretationException) {
QtiXmlInterpretationException qe = (QtiXmlInterpretationException)e;
for(QtiModelBuildingError error :qe.getQtiModelBuildingErrors()) {
String localName = error.getElementLocalName();
String msg = error.getException().getMessage();
int lineNumber = error.getElementLocation().getLineNumber();
System.out.println(lineNumber + " :: " + localName + " :: " + msg);
}
}
//System.out.println("Actual validation result: " + ObjectDumper.dumpObject(itemResult, DumpMode.DEEP));
} catch (Throwable t) {
t.printStackTrace();
}
}
}
/**
* <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;
import java.io.File;
import org.junit.Test;
import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
import uk.ac.ed.ph.jqtiplus.provision.BadResourceException;
import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader;
import uk.ac.ed.ph.jqtiplus.reading.QtiModelBuildingError;
import uk.ac.ed.ph.jqtiplus.reading.QtiXmlInterpretationException;
import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader;
import uk.ac.ed.ph.jqtiplus.validation.ItemValidationResult;
import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator;
/**
* Only to play with
*
* Initial date: 25.06.2015<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class ValidationTest {
@Test
public void validateItem() {
File itemFile = new File("/Users/srosse/Desktop/QTI/fxtest_Onyx_3_1_1/Task_1597435347.xml");
QtiXmlReader qtiXmlReader = new QtiXmlReader(new JqtiExtensionManager());
ResourceLocator fileResourceLocator = new PathResourceLocator(itemFile.toPath());
AssessmentObjectXmlLoader assessmentObjectXmlLoader = new AssessmentObjectXmlLoader(qtiXmlReader, fileResourceLocator);
ItemValidationResult itemResult = assessmentObjectXmlLoader.loadResolveAndValidateItem(itemFile.toURI());
System.out.println("Has errors: " + (itemResult.getModelValidationErrors().size() > 0));
BadResourceException e = itemResult.getResolvedAssessmentItem().getItemLookup().getBadResourceException();
if(e instanceof QtiXmlInterpretationException) {
QtiXmlInterpretationException qe = (QtiXmlInterpretationException)e;
for(QtiModelBuildingError error :qe.getQtiModelBuildingErrors()) {
String localName = error.getElementLocalName();
String msg = error.getException().getMessage();
int lineNumber = error.getElementLocation().getLineNumber();
System.out.println(lineNumber + " :: " + localName + " :: " + msg);
}
}
//System.out.println("Actual validation result: " + ObjectDumper.dumpObject(itemResult, DumpMode.DEEP));
}
}
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