diff --git a/src/main/java/org/olat/ims/qti/QTI12EditorController.java b/src/main/java/org/olat/ims/qti/QTI12EditorController.java
index 5d79dc64f4a08427fc2ae8688034b581c14e4d90..3bada7e691de4c5d11e24c09a29e048b4e1ba9e0 100644
--- a/src/main/java/org/olat/ims/qti/QTI12EditorController.java
+++ b/src/main/java/org/olat/ims/qti/QTI12EditorController.java
@@ -19,7 +19,6 @@
  */
 package org.olat.ims.qti;
 
-import org.olat.core.CoreSpringFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.tabbedpane.TabbedPane;
@@ -42,6 +41,7 @@ import org.olat.modules.qpool.QPoolItemEditorController;
 import org.olat.modules.qpool.QPoolService;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.ui.events.QItemChangeEvent;
+import org.springframework.beans.factory.annotation.Autowired;
 /**
  * 
  * Initial date: 21.02.2013<br>
@@ -54,12 +54,13 @@ public class QTI12EditorController extends BasicController implements QPoolItemE
 	private final VelocityContainer mainVC;
 	private ItemNodeTabbedFormController editorsCtrl;
 	
-	private final QPoolService qpoolService;
 	private final QuestionItem qitem;
+	
+	@Autowired
+	private QPoolService qpoolService;
 
 	public QTI12EditorController(UserRequest ureq, WindowControl wControl, QuestionItem qitem) {
 		super(ureq, wControl);
-		qpoolService = CoreSpringFactory.getImpl(QPoolService.class);
 
 		this.qitem = qitem;
 		mainVC = createVelocityContainer("qti_preview");
diff --git a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
index 8c306d36371f2f4ab416610304da312d22a33b0d..cdace79f7bc1a3d0b547a7aab859a5a64a4ef708 100644
--- a/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
+++ b/src/main/java/org/olat/ims/qti/editor/QTIEditorMainController.java
@@ -98,6 +98,7 @@ import org.olat.course.nodes.CourseNode;
 import org.olat.course.nodes.iq.IQEditController;
 import org.olat.course.tree.TreePosition;
 import org.olat.fileresource.types.FileResource;
+import org.olat.fileresource.types.ImsQTI21Resource;
 import org.olat.ims.qti.QTIChangeLogMessage;
 import org.olat.ims.qti.QTIConstants;
 import org.olat.ims.qti.QTIResultManager;
@@ -133,6 +134,9 @@ import org.olat.modules.qpool.ui.events.QItemViewEvent;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.repository.RepositoryService;
+import org.olat.repository.handlers.RepositoryHandler;
+import org.olat.repository.handlers.RepositoryHandlerFactory;
+import org.olat.repository.ui.author.CreateRepositoryEntryController;
 import org.olat.resource.references.Reference;
 import org.olat.user.UserManager;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -176,6 +180,7 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 	private static final String CMD_TOOLS_EXPORT_QPOOL = "cmd.export.qpool";
 	private static final String CMD_TOOLS_EXPORT_DOCX = "cmd.export.docx";
 	private static final String CMD_TOOLS_IMPORT_TABLE = "cmd.import.xls";
+	private static final String CMD_TOOLS_CONVERT_TO_QTI21 = "cmd.convert.qti.21";
 
 	private static final String CMD_EXIT_SAVE = "exit.save";
 	private static final String CMD_EXIT_DISCARD = "exit.discard";
@@ -229,7 +234,7 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 	private TooledStackedPanel stackedPanel;
 	private LayoutMain3ColsController columnLayoutCtr;
 	
-	private Link previewLink, exportPoolLink, exportDocLink, importTableLink, closeLink;
+	private Link previewLink, exportPoolLink, convertQTI21Link, exportDocLink, importTableLink, closeLink;
 	private Link addPoolLink, addSectionLink, addSCLink, addMCLink, addFIBLink, addKPrimLink, addEssayLink;
 	private Link deleteLink, moveLink, copyLink;
 
@@ -255,6 +260,7 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 	private Link notEditableButton; 
 	private Set<String> deletableMediaFiles;
 	private StepsMainRunController importTableWizard;
+	private CreateRepositoryEntryController createTestController;
 	private InsertNodeController moveCtrl, copyCtrl, insertCtrl;
 
 	@Autowired
@@ -267,6 +273,8 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 	private RepositoryService repositoryService;
 	@Autowired
 	private QTIQPoolServiceProvider qtiQpoolServiceProvider;
+	@Autowired
+	private RepositoryHandlerFactory repositoryHandlerFactory;
 	
 	public QTIEditorMainController(UserRequest ureq, WindowControl wControl, RepositoryEntry qtiEntry, List<Reference> referencees, FileResource fileResource) {
 		super(ureq, wControl);
@@ -621,6 +629,8 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 			doExportQItem();
 		} else if (exportDocLink == source) {
 			doExportDocx(ureq);
+		} else if (convertQTI21Link == source) {
+			doConvertToQTI21(ureq);
 		} else if (importTableLink == source) {
 			doImportTable(ureq);
 		} else if (addSectionLink == source) {
@@ -1020,6 +1030,20 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 		cmc.activate();
 		listenTo(cmc);
 	}
+	
+	private void doConvertToQTI21(UserRequest ureq) {
+		removeAsListenerAndDispose(cmc);
+		removeAsListenerAndDispose(selectQItemCtrl);
+
+		RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler(ImsQTI21Resource.TYPE_NAME);
+		createTestController = new CreateRepositoryEntryController(ureq, getWindowControl(), handler);
+		createTestController.setCreateObject(qtiPackage);
+		listenTo(createTestController);
+
+		cmc = new CloseableModalController(getWindowControl(), translate("close"), selectQItemCtrl.getInitialComponent(), true, translate("title.add") );
+		cmc.activate();
+		listenTo(cmc);
+	}
 
 	private void doExportDocx(UserRequest ureq) {
 		AssessmentNode rootNode = (AssessmentNode)menuTreeModel.getRootNode();
@@ -1124,6 +1148,9 @@ public class QTIEditorMainController extends MainLayoutBasicController implement
 		exportDocLink = LinkFactory.createToolLink(CMD_TOOLS_EXPORT_DOCX, translate("tools.export.docx"), this, "o_mi_docx_export");
 		exportDocLink.setIconLeftCSS("o_icon o_icon_download");
 		exportTools.addComponent(exportDocLink);
+		convertQTI21Link = LinkFactory.createToolLink(CMD_TOOLS_CONVERT_TO_QTI21, translate("tools.convert.qti21"), this, "o_FileResource-IMSQTI21_icon");
+		convertQTI21Link.setIconLeftCSS("o_icon o_FileResource-IMSQTI21_icon");
+		exportTools.addComponent(convertQTI21Link);
 
 		//add
 		Dropdown addItemTools = new Dropdown("editTools", "tools.add.header", false, getTranslator());
diff --git a/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_de.properties
index d80b89268cc796ab58f7ee5e8aab4bf7392284d2..68a9a00b291ee0ce68344b82e5ea2b1eca65e35e 100644
--- a/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_de.properties
@@ -329,6 +329,7 @@ tools.change.header=Elemente \u00E4ndern
 tools.change.copy=Kopieren
 tools.change.delete=L\u00F6schen
 tools.change.move=Verschieben
+tools.convert.qti21=Zu QTI 2.1 umwandeln
 tools.matedit.back=Zur\u00FCck
 tools.matedit.header=Editorwerkzeuge
 tools.import.qpool=Fragen aus Pool importieren
diff --git a/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_en.properties
index 1ec08869cf58225a9cdde6311b4c112f20e0a5fd..1241eb7d6fb2d0864b40c92f4707f82d21d845e9 100644
--- a/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/ims/qti/editor/_i18n/LocalStrings_en.properties
@@ -214,6 +214,7 @@ tab.survey=Questionnaire
 title.add=Add element
 title.copy=Copy element
 title.move=Move element
+tools.convert.qti21=Convert to QTI 2.1
 tools.import.qpool=Import questions from pool
 tools.import.table=Import questions from Excel
 tools.export.header=Export
diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestPreWarm.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestPreWarm.java
index b882a301da4b3c26a3fc473a5899af31c55591c5..573113806987f38079da51d6612a5b725ac5ddce 100644
--- a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestPreWarm.java
+++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestPreWarm.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.manager;
 
 import java.io.File;
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java
index 96fc883d5867c20b021c61ae2352118b4e3603b4..4bb950b446a99ad681fd07cd1a13e77506ac1686 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestBuilder.java
@@ -36,6 +36,7 @@ import uk.ac.ed.ph.jqtiplus.node.test.TestFeedback;
 import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeCondition;
 import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeConditionChild;
 import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeIf;
+import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeProcessing;
 import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.OutcomeRule;
 import uk.ac.ed.ph.jqtiplus.node.test.outcome.processing.SetOutcomeValue;
 import uk.ac.ed.ph.jqtiplus.types.Identifier;
@@ -74,25 +75,27 @@ public class AssessmentTestBuilder {
 	}
 	
 	private void extractRules() {
-		List<OutcomeRule> outcomeRules = assessmentTest.getOutcomeProcessing().getOutcomeRules();
-		for(OutcomeRule outcomeRule:outcomeRules) {
-			// export test score
-			if(outcomeRule instanceof SetOutcomeValue) {
-				SetOutcomeValue setOutcomeValue = (SetOutcomeValue)outcomeRule;
-				if(QTI21Constants.SCORE_IDENTIFIER.equals(setOutcomeValue.getIdentifier())) {
-					testScore = true;
-					testScoreRule = outcomeRule;
+		if(assessmentTest.getOutcomeProcessing() != null) {
+			List<OutcomeRule> outcomeRules = assessmentTest.getOutcomeProcessing().getOutcomeRules();
+			for(OutcomeRule outcomeRule:outcomeRules) {
+				// export test score
+				if(outcomeRule instanceof SetOutcomeValue) {
+					SetOutcomeValue setOutcomeValue = (SetOutcomeValue)outcomeRule;
+					if(QTI21Constants.SCORE_IDENTIFIER.equals(setOutcomeValue.getIdentifier())) {
+						testScore = true;
+						testScoreRule = outcomeRule;
+					}
 				}
-			}
-			
-			// pass rule
-			if(outcomeRule instanceof OutcomeCondition) {
-				OutcomeCondition outcomeCondition = (OutcomeCondition)outcomeRule;
-				boolean findIf = findSetOutcomeValue(outcomeCondition.getOutcomeIf(), QTI21Constants.PASS_IDENTIFIER);
-				boolean findElse = findSetOutcomeValue(outcomeCondition.getOutcomeElse(), QTI21Constants.PASS_IDENTIFIER);
-				if(findIf && findElse) {
-					cutValue = extractCutValue(outcomeCondition.getOutcomeIf());
-					cutValueRule = outcomeCondition;
+				
+				// pass rule
+				if(outcomeRule instanceof OutcomeCondition) {
+					OutcomeCondition outcomeCondition = (OutcomeCondition)outcomeRule;
+					boolean findIf = findSetOutcomeValue(outcomeCondition.getOutcomeIf(), QTI21Constants.PASS_IDENTIFIER);
+					boolean findElse = findSetOutcomeValue(outcomeCondition.getOutcomeElse(), QTI21Constants.PASS_IDENTIFIER);
+					if(findIf && findElse) {
+						cutValue = extractCutValue(outcomeCondition.getOutcomeIf());
+						cutValueRule = outcomeCondition;
+					}
 				}
 			}
 		}
@@ -144,6 +147,10 @@ public class AssessmentTestBuilder {
 		}
 	}
 	
+	public AssessmentTest getAssessmentTest() {
+		return assessmentTest;
+	}
+	
 	public boolean isExportScore() {
 		return testScore;
 	}
@@ -178,16 +185,27 @@ public class AssessmentTestBuilder {
 		return failedFeedback;
 	}
 
-	
-	public void build() {
+	public AssessmentTest build() {
 		Double maxScore = 1.0d;
 		
+		if(assessmentTest.getOutcomeProcessing() == null) {
+			assessmentTest.setOutcomeProcessing(new OutcomeProcessing(assessmentTest));
+		}
+		
 		buildScore(maxScore);
 		buildTestScore();
 		buildCutValue();
 		buildFeedback();
+		
+		//clean up
+		if(assessmentTest.getOutcomeProcessing().getOutcomeRules().isEmpty()) {
+			assessmentTest.setOutcomeProcessing(null);
+		}
+		return assessmentTest;
 	}
 	
+
+	
 	private void buildScore(Double maxScore) {
 		OutcomeDeclaration scoreDeclaration = assessmentTest.getOutcomeDeclaration(QTI21Constants.SCORE_IDENTIFIER);
 		if(scoreDeclaration == null) {
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java
index d0401c57ca3a0c7c877ea323a78705fa10b8569e..9ea0c12ea138b598430510c8251e38adfcf0d052 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentTestFactory.java
@@ -98,7 +98,7 @@ public class AssessmentTestFactory {
 		
 		//test part
 		TestPart part = createTestPart(assessmentTest);
-		createAssessmentSection(part);
+		appendAssessmentSection(part);
 		
 		//outcome processing
 		OutcomeProcessing outcomeProcessing = new OutcomeProcessing(assessmentTest);
@@ -278,15 +278,21 @@ public class AssessmentTestFactory {
 		return part;
 	}
 	
-	public static AssessmentSection createAssessmentSection(TestPart part) {
-		return createAssessmentSectionInternal(part);
+	/**
+	 * create an assessmentSection with an empty rubricBlock for candidate,
+	 * not shuffled but visible and fixed.
+	 * @param part
+	 * @return
+	 */
+	public static AssessmentSection appendAssessmentSection(TestPart part) {
+		return appendAssessmentSectionInternal(part);
 	}
 	
-	public static AssessmentSection createAssessmentSection(AssessmentSection part) {
-		return createAssessmentSectionInternal(part);
+	public static AssessmentSection appendAssessmentSection(AssessmentSection part) {
+		return appendAssessmentSectionInternal(part);
 	}
 	
-	private final static AssessmentSection createAssessmentSectionInternal(AbstractPart part) {
+	private final static AssessmentSection appendAssessmentSectionInternal(AbstractPart part) {
 		// section
 		AssessmentSection section = new AssessmentSection(part);
 		section.setFixed(Boolean.TRUE);
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/ChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/ChoiceAssessmentItemBuilder.java
index 10a334422b2716c8d43388bbb1ab0df3681fb197..8de84981e731a50a7cbeca734d79163408b0c6aa 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/ChoiceAssessmentItemBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/ChoiceAssessmentItemBuilder.java
@@ -115,7 +115,7 @@ public abstract class ChoiceAssessmentItemBuilder extends AssessmentItemBuilder
 				responseIdentifier = choiceInteraction.getResponseIdentifier();
 				shuffle = choiceInteraction.getShuffle();
 				break;
-			} else {
+			} else if(block != null) {
 				qtiSerializer.serializeJqtiObject(block, new StreamResult(sb));
 			}
 		}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java
index 0b55b9d192d3c69b8b012d50adb53859095578ff..1f418dfca244b55a41b60456216d0e793360fb51 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/interactions/MultipleChoiceAssessmentItemBuilder.java
@@ -227,7 +227,8 @@ public class MultipleChoiceAssessmentItemBuilder extends ChoiceAssessmentItemBui
 			</responseElse>
 		</responseCondition>
 		 */
-		
+		//simple as build with / without feedback
+		ensureFeedbackBasicOutcomeDeclaration();
 		
 		ResponseIf responseIf = new ResponseIf(rule);
 		rule.setResponseIf(responseIf);
@@ -269,7 +270,7 @@ public class MultipleChoiceAssessmentItemBuilder extends ChoiceAssessmentItemBui
 		}
 	
 		{//outcome score
-			SetOutcomeValue scoreOutcomeValue = new SetOutcomeValue(responseIf);
+			SetOutcomeValue scoreOutcomeValue = new SetOutcomeValue(responseElseIf);
 			scoreOutcomeValue.setIdentifier(QTI21Constants.SCORE_IDENTIFIER);
 			responseElseIf.getResponseRules().add(scoreOutcomeValue);
 			
@@ -286,9 +287,9 @@ public class MultipleChoiceAssessmentItemBuilder extends ChoiceAssessmentItemBui
 		}
 			
 		{//outcome feedback
-			SetOutcomeValue correctOutcomeValue = new SetOutcomeValue(responseIf);
+			SetOutcomeValue correctOutcomeValue = new SetOutcomeValue(responseElseIf);
 			correctOutcomeValue.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
-			responseIf.getResponseRules().add(correctOutcomeValue);
+			responseElseIf.getResponseRules().add(correctOutcomeValue);
 			
 			BaseValue correctValue = new BaseValue(correctOutcomeValue);
 			correctValue.setBaseTypeAttrValue(BaseType.IDENTIFIER);
@@ -300,9 +301,9 @@ public class MultipleChoiceAssessmentItemBuilder extends ChoiceAssessmentItemBui
 		rule.setResponseElse(responseElse);
 		
 		{// outcome feedback
-			SetOutcomeValue incorrectOutcomeValue = new SetOutcomeValue(responseIf);
+			SetOutcomeValue incorrectOutcomeValue = new SetOutcomeValue(responseElse);
 			incorrectOutcomeValue.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
-			responseIf.getResponseRules().add(incorrectOutcomeValue);
+			responseElse.getResponseRules().add(incorrectOutcomeValue);
 			
 			BaseValue incorrectValue = new BaseValue(incorrectOutcomeValue);
 			incorrectValue.setBaseTypeAttrValue(BaseType.IDENTIFIER);
diff --git a/src/main/java/org/olat/ims/qti21/pool/QTI12To21Converter.java b/src/main/java/org/olat/ims/qti21/pool/QTI12To21Converter.java
new file mode 100644
index 0000000000000000000000000000000000000000..13628b3c7c1a85e46b4d2884dac70904dd7e9408
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/pool/QTI12To21Converter.java
@@ -0,0 +1,362 @@
+/**
+ * <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.pool;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.olat.core.helpers.Settings;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
+import org.olat.ims.qti.editor.QTIEditHelper;
+import org.olat.ims.qti.editor.QTIEditorPackage;
+import org.olat.ims.qti.editor.beecom.objects.Assessment;
+import org.olat.ims.qti.editor.beecom.objects.Control;
+import org.olat.ims.qti.editor.beecom.objects.Duration;
+import org.olat.ims.qti.editor.beecom.objects.Item;
+import org.olat.ims.qti.editor.beecom.objects.OutcomesProcessing;
+import org.olat.ims.qti.editor.beecom.objects.QTIDocument;
+import org.olat.ims.qti.editor.beecom.objects.Question;
+import org.olat.ims.qti.editor.beecom.objects.Response;
+import org.olat.ims.qti.editor.beecom.objects.Section;
+import org.olat.ims.qti.editor.beecom.objects.SelectionOrdering;
+import org.olat.ims.qti21.QTI21Constants;
+import org.olat.ims.qti21.model.IdentifierGenerator;
+import org.olat.ims.qti21.model.xml.AssessmentHtmlBuilder;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
+import org.olat.ims.qti21.model.xml.AssessmentItemFactory;
+import org.olat.ims.qti21.model.xml.AssessmentTestBuilder;
+import org.olat.ims.qti21.model.xml.AssessmentTestFactory;
+import org.olat.ims.qti21.model.xml.ManifestPackage;
+import org.olat.ims.qti21.model.xml.ModalFeedbackBuilder;
+import org.olat.ims.qti21.model.xml.interactions.ChoiceAssessmentItemBuilder.ScoreEvaluation;
+import org.olat.ims.qti21.model.xml.interactions.EssayAssessmentItemBuilder;
+import org.olat.ims.qti21.model.xml.interactions.FIBAssessmentItemBuilder;
+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.imscp.xml.manifest.ManifestType;
+
+import uk.ac.ed.ph.jqtiplus.node.AssessmentObject;
+import uk.ac.ed.ph.jqtiplus.node.content.variable.RubricBlock;
+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.choice.SimpleChoice;
+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.ControlObject;
+import uk.ac.ed.ph.jqtiplus.node.test.ItemSessionControl;
+import uk.ac.ed.ph.jqtiplus.node.test.Selection;
+import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
+import uk.ac.ed.ph.jqtiplus.node.test.TimeLimits;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
+
+/**
+ * 
+ * Initial date: 19.02.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QTI12To21Converter {
+	
+	private static final OLog log = Tracing.createLoggerFor(QTI12To21Converter.class);
+	
+	private final File unzippedDirRoot;
+	private final QtiSerializer qtiSerializer = new QtiSerializer(null);
+	private final AssessmentHtmlBuilder htmlBuilder = new AssessmentHtmlBuilder(qtiSerializer);
+	
+	private final ManifestType manifest;
+	
+	public QTI12To21Converter(File unzippedDirRoot) {
+		this.unzippedDirRoot = unzippedDirRoot;
+		manifest = ManifestPackage.createEmptyManifest();
+	}
+	
+	public AssessmentTest convert(QTIEditorPackage qtiEditorPackage)
+	throws URISyntaxException {
+		return convert(qtiEditorPackage.getQTIDocument());
+	}
+	
+	public AssessmentTest convert(QTIDocument doc)
+	throws URISyntaxException {
+		Assessment assessment = doc.getAssessment();
+
+		AssessmentTest assessmentTest = new AssessmentTest();
+		String assessmentTestIdentifier = IdentifierGenerator.newAssessmentTestFilename();
+		File testFile = new File(unzippedDirRoot, assessmentTestIdentifier + ".xml");
+		ManifestPackage.appendAssessmentTest(testFile.getName(), manifest);
+		
+		assessmentTest.setIdentifier(assessmentTestIdentifier);
+		assessmentTest.setTitle(assessment.getTitle());
+		assessmentTest.setToolName(QTI21Constants.TOOLNAME);
+		assessmentTest.setToolVersion(Settings.getVersion());
+		convertDuration((Duration)assessment.getDuration(), assessmentTest);
+		
+		TestPart testPart = AssessmentTestFactory.createTestPart(assessmentTest);
+		ItemSessionControl itemSessionControl = testPart.getItemSessionControl();
+		Control tmpControl = QTIEditHelper.getControl(assessment);
+		if(tmpControl.getFeedback() == Control.CTRL_YES) {
+			itemSessionControl.setShowFeedback(Boolean.TRUE);
+		}
+		if(tmpControl.getSolution() == Control.CTRL_YES) {
+			itemSessionControl.setShowSolution(Boolean.TRUE);
+		}
+
+		AssessmentTestBuilder assessmentTestBuilder = new AssessmentTestBuilder(assessmentTest);
+		//this are lost in QTI 2.1
+		//assessment.getSelection_ordering().getOrderType();
+		//assessment.getSelection_ordering().getSelectionNumber();
+		OutcomesProcessing outcomesProcessing = assessment.getOutcomes_processing();
+		if(outcomesProcessing != null) {
+			String cutValue = outcomesProcessing.getField(OutcomesProcessing.CUTVALUE);
+			if(StringHelper.containsNonWhitespace(cutValue)) {
+				try {
+					assessmentTestBuilder.setCutValue(Double.valueOf(cutValue));
+				} catch (NumberFormatException e) {
+					log.error("Cannot parse cut value: " + cutValue, e);
+				}
+			}
+		}
+
+		//root
+		List<Section> sections = assessment.getSections();
+		for(Section section:sections) {
+			convert(section, testPart);
+		}
+
+		assessmentTest = assessmentTestBuilder.build();
+		persistAssessmentObject(testFile, assessmentTest);
+		ManifestPackage.write(manifest, new File(unzippedDirRoot, "imsmanifest.xml"));
+		return assessmentTest;
+	}
+	
+	private void convert(Section section, TestPart testPart)
+	throws URISyntaxException {
+		AssessmentSection assessmentSection = AssessmentTestFactory.appendAssessmentSection(testPart);
+		assessmentSection.setTitle(section.getTitle());
+		convertDuration(section.getDuration(), assessmentSection);
+		
+		RubricBlock rubricBlock = assessmentSection.getRubricBlocks().get(0);
+		rubricBlock.getBlocks().clear();
+		htmlBuilder.appendHtml(rubricBlock, section.getObjectives());
+
+		boolean shuffle = SelectionOrdering.RANDOM.equals(section.getSelection_ordering().getOrderType());
+		assessmentSection.getOrdering().setShuffle(shuffle);
+		
+		int selectionNum = section.getSelection_ordering().getSelectionNumber();
+		if(selectionNum > 0) {
+			Selection selection = new Selection(assessmentSection);
+			selection.setSelect(selectionNum);
+			assessmentSection.setSelection(selection);
+		}
+		
+		List<Item> items = section.getItems();
+		for(Item item:items) {
+			AssessmentItemBuilder itemBuilder = null;
+			int questionType = item.getQuestion().getType();
+			switch (questionType) {
+				case Question.TYPE_SC:
+					itemBuilder = convertSingleChoice(item);
+					break;
+				case Question.TYPE_MC:
+					itemBuilder = convertMultipleChoice(item);
+					break;/*
+				case Question.TYPE_KPRIM:
+					itemBuilder = convertKPrim(item);
+					break;
+				case Question.TYPE_FIB:
+					itemBuilder = convertFIB(item);
+					break;
+				case Question.TYPE_ESSAY:
+					itemBuilder = convertEssay(item);
+					break;
+					*/
+			}
+
+			if(itemBuilder != null) {
+				itemBuilder.build();
+				
+				AssessmentItem assessmentItem = itemBuilder.getAssessmentItem();
+				
+				AssessmentItemRef itemRef = new AssessmentItemRef(assessmentSection);
+				String itemId = IdentifierGenerator.newAsString(itemBuilder.getQuestionType().getPrefix());
+				itemRef.setIdentifier(Identifier.parseString(itemId));
+				File itemFile = new File(unzippedDirRoot, itemId + ".xml");
+				itemRef.setHref(new URI(itemFile.getName()));
+				assessmentSection.getSectionParts().add(itemRef);
+				persistAssessmentObject(itemFile, assessmentItem);
+				ManifestPackage.appendAssessmentItem(itemFile.getName(), manifest);
+			}
+		}
+	}
+	
+	public boolean persistAssessmentObject(File resourceFile, AssessmentObject assessmentObject) {
+		try(FileOutputStream out = new FileOutputStream(resourceFile)) {
+			qtiSerializer.serializeJqtiObject(assessmentObject, out);
+			return true;
+		} catch(Exception e) {
+			log.error("", e);
+			return false;
+		}
+	}
+	
+	private AssessmentItemBuilder convertSingleChoice(Item item) {
+		SingleChoiceAssessmentItemBuilder itemBuilder = new SingleChoiceAssessmentItemBuilder(qtiSerializer);
+		convertItemBasics(item, itemBuilder);
+		itemBuilder.clearMapping();
+		itemBuilder.clearSimpleChoices();
+		itemBuilder.setScoreEvaluationMode(ScoreEvaluation.allCorrectAnswers);
+		
+		ChoiceInteraction interaction = itemBuilder.getChoiceInteraction();
+		
+		Question question = item.getQuestion();
+		itemBuilder.setShuffle(question.isShuffle());
+		
+		List<Response> responses = question.getResponses();
+		for(Response response:responses) {
+			String responseText = response.getContent().renderAsHtmlForEditor();
+			SimpleChoice newChoice = AssessmentItemFactory
+					.createSimpleChoice(interaction, responseText, itemBuilder.getQuestionType().getPrefix());
+			itemBuilder.addSimpleChoice(newChoice);
+			if(response.isCorrect()) {
+				itemBuilder.setCorrectAnswer(newChoice.getIdentifier());
+			}	
+		}
+		
+		double correctScore = question.getSingleCorrectScore();
+		if(correctScore >= 0.0d) {
+			itemBuilder.setMinScore(0.0d);
+			itemBuilder.setMaxScore(correctScore);
+		}
+		
+		return itemBuilder;
+	}
+	
+	private AssessmentItemBuilder convertMultipleChoice(Item item) {
+		MultipleChoiceAssessmentItemBuilder itemBuilder = new MultipleChoiceAssessmentItemBuilder(qtiSerializer);
+		convertItemBasics(item, itemBuilder);
+		itemBuilder.clearMapping();
+		itemBuilder.clearSimpleChoices();
+		
+		ChoiceInteraction interaction = itemBuilder.getChoiceInteraction();
+		
+		Question question = item.getQuestion();
+		itemBuilder.setShuffle(question.isShuffle());
+		
+		boolean singleCorrect = question.isSingleCorrect();
+		List<Response> responses = question.getResponses();
+		for(Response response:responses) {
+			String responseText = response.getContent().renderAsHtmlForEditor();
+			SimpleChoice newChoice = AssessmentItemFactory
+					.createSimpleChoice(interaction, responseText, itemBuilder.getQuestionType().getPrefix());
+			itemBuilder.addSimpleChoice(newChoice);
+			if(response.isCorrect()) {
+				itemBuilder.addCorrectAnswer(newChoice.getIdentifier());
+			}
+			double score = response.getPoints();
+			if(singleCorrect) {
+				if(score > 0.0f) {
+					itemBuilder.setMaxScore(score);
+				}
+			} else {
+				itemBuilder.setMapping(newChoice.getIdentifier(), score);
+			}
+		}
+
+		if(singleCorrect) {
+			itemBuilder.setScoreEvaluationMode(ScoreEvaluation.allCorrectAnswers);
+		} else {
+			itemBuilder.setScoreEvaluationMode(ScoreEvaluation.perAnswer);
+		}
+		
+		return itemBuilder;
+	}
+	
+	private AssessmentItemBuilder convertKPrim(Item item) {
+		KPrimAssessmentItemBuilder itemBuilder = new KPrimAssessmentItemBuilder(qtiSerializer);
+		convertItemBasics(item, itemBuilder);
+
+		return itemBuilder;
+	}
+	
+	private AssessmentItemBuilder convertFIB(Item item) {
+		FIBAssessmentItemBuilder itemBuilder = new FIBAssessmentItemBuilder(qtiSerializer);
+		convertItemBasics(item, itemBuilder);
+
+
+		return itemBuilder;
+	}
+	
+	private AssessmentItemBuilder convertEssay(Item item) {
+		EssayAssessmentItemBuilder itemBuilder = new EssayAssessmentItemBuilder(qtiSerializer);
+		convertItemBasics(item, itemBuilder);
+
+		return itemBuilder;
+	}
+	
+	private void convertItemBasics(Item item, AssessmentItemBuilder itemBuilder) {
+		AssessmentItem assessmentItem = itemBuilder.getAssessmentItem();
+		if(StringHelper.containsNonWhitespace(item.getTitle())) {
+			assessmentItem.setTitle(item.getTitle());
+		}
+		if(StringHelper.containsNonWhitespace(item.getLabel())) {
+			assessmentItem.setLabel(item.getLabel());
+		}
+		if(StringHelper.containsNonWhitespace(item.getObjectives())) {
+			//metadata description item.getObjectives()
+		}
+
+		if(item.getMaxattempts() > 0
+				|| (item.getDuration() != null && item.getDuration().isSet())) {
+			//not supported
+		}
+		
+		Question question = item.getQuestion();
+		String questionText = question.getQuestion().renderAsHtmlForEditor();
+		itemBuilder.setQuestion(questionText);
+		
+		String feedbackMastery = QTIEditHelper.getFeedbackMasteryText(item);
+		if(StringHelper.containsNonWhitespace(feedbackMastery)) {
+			ModalFeedbackBuilder feedback = itemBuilder.createCorrectFeedback();
+			feedback.setText(feedbackMastery);
+		}
+
+		String feedbackFail = QTIEditHelper.getFeedbackFailText(item);
+		if(StringHelper.containsNonWhitespace(feedbackFail)) {
+			ModalFeedbackBuilder feedback = itemBuilder.createIncorrectFeedback();
+			feedback.setText(feedbackFail);
+		}
+	}
+
+	private void convertDuration(Duration duration, ControlObject<?> parent) {
+		if(duration != null && duration.isSet()) {
+			TimeLimits timeLimits = new TimeLimits(parent);
+			double timeInSeconds = (60 * duration.getMin()) + duration.getSec();
+			timeLimits.setMaximum(timeInSeconds);
+			parent.setTimeLimits(timeLimits);
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/pool/QTI21QPoolServiceProvider.java b/src/main/java/org/olat/ims/qti21/pool/QTI21QPoolServiceProvider.java
index 11c997b1a1d2b1aeb52f958c6ad5e47613aefa8a..7d217b22853ed96c0b34279f1ed6f31ab356ff23 100644
--- a/src/main/java/org/olat/ims/qti21/pool/QTI21QPoolServiceProvider.java
+++ b/src/main/java/org/olat/ims/qti21/pool/QTI21QPoolServiceProvider.java
@@ -44,6 +44,7 @@ import org.olat.core.util.vfs.VFSItem;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSManager;
 import org.olat.fileresource.types.ImsQTI21Resource;
+import org.olat.ims.qti.editor.QTIEditorPackage;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.QTI21Service;
 import org.olat.ims.qti21.model.QTI21QuestionType;
@@ -72,6 +73,7 @@ import org.olat.modules.qpool.manager.QuestionItemDAO;
 import org.olat.modules.qpool.manager.TaxonomyLevelDAO;
 import org.olat.modules.qpool.model.DefaultExportFormat;
 import org.olat.modules.qpool.model.QuestionItemImpl;
+import org.olat.repository.RepositoryEntry;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.xml.sax.InputSource;
@@ -303,6 +305,16 @@ public class QTI21QPoolServiceProvider implements QPoolSPI {
 		processor.assembleTest(fullItems, exportDir);
 	}
 	
+	/**
+	 * Convert from QTI 1.2 to 2.1
+	 * 
+	 * @param qtiEditorPackage
+	 */
+	public void convertFromEditorPackage(RepositoryEntry newEntry, QTIEditorPackage qtiEditorPackage) {
+		qtiEditorPackage.getQTIDocument();
+		
+	}
+	
 	private List<Long> toKeys(List<? extends QuestionItemShort> items) {
 		List<Long> keys = new ArrayList<Long>(items.size());
 		for(QuestionItemShort item:items) {
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 45b8fea64d01c34fbd9c38ec629d8d26374b9c67..bbb68b84c6fb5579452179124399c43ef0bff78a 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
@@ -42,6 +42,7 @@ import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamWriter;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.stack.TooledStackedPanel;
@@ -62,6 +63,7 @@ import org.olat.fileresource.ZippedDirectoryMediaResource;
 import org.olat.fileresource.types.FileResource;
 import org.olat.fileresource.types.ImsQTI21Resource;
 import org.olat.fileresource.types.ResourceEvaluation;
+import org.olat.ims.qti.editor.QTIEditorPackage;
 import org.olat.ims.qti21.QTI21Service;
 import org.olat.ims.qti21.model.IdentifierGenerator;
 import org.olat.ims.qti21.model.QTI21QuestionType;
@@ -85,6 +87,7 @@ import org.olat.repository.model.RepositoryEntrySecurity;
 import org.olat.repository.ui.RepositoryEntryRuntimeController.RuntimeControllerCreator;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceManager;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
@@ -102,6 +105,13 @@ import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
 public class QTI21AssessmentTestHandler extends FileHandler {
 	
 	private static final OLog log = Tracing.createLoggerFor(QTI21AssessmentTestHandler.class);
+	
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private RepositoryService repositoryService;
+	@Autowired
+	private QTI21QPoolServiceProvider qpoolServiceProvider;
 
 	@Override
 	public String getSupportedType() {
@@ -122,10 +132,9 @@ public class QTI21AssessmentTestHandler extends FileHandler {
 	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();
+		dbInstance.commit();
 		
 		File repositoryDir = new File(FileResourceManager.getInstance().getFileResourceRoot(re.getOlatResource()), FileResourceManager.ZIPDIR);
 		if(!repositoryDir.exists()) {
@@ -133,8 +142,10 @@ public class QTI21AssessmentTestHandler extends FileHandler {
 		}
 		if(createObject instanceof QItemList) {
 			QItemList itemToImport = (QItemList)createObject;
-			QTI21QPoolServiceProvider provider = CoreSpringFactory.getImpl(QTI21QPoolServiceProvider.class);
-			provider.exportToEditorPackage(repositoryDir, itemToImport.getItems());
+			qpoolServiceProvider.exportToEditorPackage(repositoryDir, itemToImport.getItems());
+		} else if(createObject instanceof QTIEditorPackage) {
+			QTIEditorPackage testToConvert = (QTIEditorPackage)createObject;
+			qpoolServiceProvider.convertFromEditorPackage(re, testToConvert);
 		} else {
 			createMinimalAssessmentTest(displayname, repositoryDir);
 		}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
index ccba8473261d949a0c53b913f148bdec23dc1b34..0688483485fd1c580f0c8eaf3026b8b7803e8430 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentTestComposerController.java
@@ -24,7 +24,6 @@ import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.List;
-import java.util.UUID;
 
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
 import org.olat.core.gui.UserRequest;
@@ -54,6 +53,7 @@ import org.olat.core.util.Util;
 import org.olat.fileresource.FileResourceManager;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.QTI21Service;
+import org.olat.ims.qti21.model.IdentifierGenerator;
 import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 import org.olat.ims.qti21.model.xml.AssessmentTestFactory;
 import org.olat.ims.qti21.model.xml.ManifestPackage;
@@ -451,9 +451,9 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 
 		AssessmentSection newSection;
 		if(parentPart instanceof TestPart) {
-			newSection = AssessmentTestFactory.createAssessmentSection((TestPart)parentPart);
+			newSection = AssessmentTestFactory.appendAssessmentSection((TestPart)parentPart);
 		} else if(parentPart instanceof AssessmentSection) {
-			newSection = AssessmentTestFactory.createAssessmentSection((AssessmentSection)parentPart);
+			newSection = AssessmentTestFactory.appendAssessmentSection((AssessmentSection)parentPart);
 		} else {
 			showWarning("error.cannot.create.section");
 			return;
@@ -487,7 +487,7 @@ public class AssessmentTestComposerController extends MainLayoutBasicController
 			AssessmentSection section = (AssessmentSection)sectionNode.getUserObject();
 			
 			AssessmentItemRef itemRef = new AssessmentItemRef(section);
-			String itemId = "sc" + UUID.randomUUID();
+			String itemId = IdentifierGenerator.newAsString(itemBuilder.getQuestionType().getPrefix());
 			itemRef.setIdentifier(Identifier.parseString(itemId));
 			File itemFile = new File(unzippedDirRoot, itemId + ".xml");
 			itemRef.setHref(new URI(itemFile.getName()));
diff --git a/src/test/java/org/olat/ims/qti21/pool/QTI12To21ConverterTest.java b/src/test/java/org/olat/ims/qti21/pool/QTI12To21ConverterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc7a84788060d3e5fafe7c384a9df9d470e26f1c
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/pool/QTI12To21ConverterTest.java
@@ -0,0 +1,69 @@
+/**
+ * <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.pool;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+
+import org.dom4j.Document;
+import org.junit.Test;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.xml.XMLParser;
+import org.olat.ims.qti.editor.beecom.objects.QTIDocument;
+import org.olat.ims.qti.editor.beecom.parser.ParserManager;
+import org.olat.ims.resources.IMSEntityResolver;
+
+/**
+ * 
+ * Initial date: 19.02.2016<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QTI12To21ConverterTest {
+	
+	private static final OLog log = Tracing.createLoggerFor(QTI12To21ConverterTest.class);
+	
+	@Test
+	public void convert() throws URISyntaxException {
+		QTIDocument doc = loadDocument("qti12_4questiontypes.xml");
+		File exportDir = new File("/HotCoffee/QTI/today/");
+		exportDir.mkdirs();
+		QTI12To21Converter converter = new QTI12To21Converter(exportDir);
+		
+		converter.convert(doc);
+		
+		
+		
+	}
+	
+	private QTIDocument loadDocument(String filename) {
+		try(InputStream in = QTI12To21ConverterTest.class.getResourceAsStream(filename)) {
+			XMLParser xmlParser = new XMLParser(new IMSEntityResolver());
+			Document doc = xmlParser.parse(in, true);
+			ParserManager parser = new ParserManager();
+			return (QTIDocument)parser.parse(doc);
+		} catch (Exception e) {			
+			log.error("Exception when parsing input QTI input stream for " + filename, e);
+			return null;
+		}
+	}
+}
diff --git a/src/test/java/org/olat/ims/qti21/pool/qti12_4questiontypes.xml b/src/test/java/org/olat/ims/qti21/pool/qti12_4questiontypes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..38c496d2bd3c26df80d1030834d339fce3ff6d5a
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/pool/qti12_4questiontypes.xml
@@ -0,0 +1,409 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE questestinterop SYSTEM "ims_qtiasiv1p2p1.dtd">
+
+<questestinterop>
+  <assessment ident="test8_8_84528095851561" title="E4 - Test SR">
+    <qtimetadata>
+      <qtimetadatafield>
+        <fieldlabel>qmd_assessmenttype</fieldlabel>
+        <fieldentry>Assessment</fieldentry>
+      </qtimetadatafield>
+    </qtimetadata>
+    <selection_ordering>
+      <selection/>
+      <order order_type="Sequential"/>
+    </selection_ordering>
+    <section ident="test8_8_84528095851562" title="Neue Sektion">
+      <selection_ordering>
+        <selection/>
+        <order order_type="Sequential"/>
+      </selection_ordering>
+      <item ident="QTIEDIT:SCQ:8000138058" title="Neue Frage">
+        <presentation>
+          <material>
+            <mattext texttype="text/html"><![CDATA[Neue Frage]]></mattext>
+          </material>
+          <response_lid ident="8000138060" rcardinality="Single" rtiming="No">
+            <render_choice shuffle="No" minnumber="1" maxnumber="1">
+              <flow_label class="List">
+                <response_label ident="8000138063" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[Richtige Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000146297" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[Falsche Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+            </render_choice>
+          </response_lid>
+        </presentation>
+        <resprocessing>
+          <outcomes>
+            <decvar varname="SCORE" vartype="Decimal" defaultval="0" minvalue="0.0" maxvalue="1.0" cutvalue="1.0"/>
+          </outcomes>
+          <respcondition title="Mastery" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000138060" case="Yes">8000138063</varequal>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">1.0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Mastery"/>
+          </respcondition>
+          <respcondition title="Fail" continue="Yes">
+            <conditionvar>
+              <or>
+                <varequal respident="8000138060" case="Yes">8000146297</varequal>
+              </or>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Fail"/>
+            <displayfeedback feedbacktype="Solution" linkrefid="Solution"/>
+            <displayfeedback feedbacktype="Hint" linkrefid="Hint"/>
+          </respcondition>
+          <respcondition title="_olat_resp_feedback" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000138060" case="Yes">8000138063</varequal>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="8000138063"/>
+          </respcondition>
+          <respcondition title="_olat_resp_feedback" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000138060" case="Yes">8000146297</varequal>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="8000146297"/>
+          </respcondition>
+          <respcondition title="Fail" continue="Yes">
+            <conditionvar>
+              <other/>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Fail"/>
+            <displayfeedback feedbacktype="Solution" linkrefid="Solution"/>
+            <displayfeedback feedbacktype="Hint" linkrefid="Hint"/>
+          </respcondition>
+        </resprocessing>
+      </item>
+      <item ident="QTIEDIT:MCQ:8000150743" title="Multiple Frage">
+        <itemcontrol feedbackswitch="No" hintswitch="No" solutionswitch="No"/>
+        <presentation>
+          <material>
+            <mattext texttype="text/html"><![CDATA[Neue Frage]]></mattext>
+          </material>
+          <response_lid ident="8000150745" rcardinality="Multiple" rtiming="No">
+            <render_choice shuffle="No" minnumber="0" maxnumber="4">
+              <flow_label class="List">
+                <response_label ident="8000150748" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[Richtige Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000152077" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[Falsche Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000152080" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[Richtige Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000152091" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[Falsche Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+            </render_choice>
+          </response_lid>
+        </presentation>
+        <resprocessing>
+          <outcomes>
+            <decvar varname="SCORE" vartype="Decimal" defaultval="0" minvalue="0.0" maxvalue="1.0" cutvalue="1.0"/>
+          </outcomes>
+          <respcondition title="Mastery" continue="Yes">
+            <conditionvar>
+              <and>
+                <varequal respident="8000150745" case="Yes">8000150748</varequal>
+                <varequal respident="8000150745" case="Yes">8000152080</varequal>
+              </and>
+              <not>
+                <or>
+                  <varequal respident="8000150745" case="Yes">8000152077</varequal>
+                  <varequal respident="8000150745" case="Yes">8000152091</varequal>
+                </or>
+              </not>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">1.0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Mastery"/>
+          </respcondition>
+          <respcondition title="Fail" continue="Yes">
+            <conditionvar>
+              <or>
+                <varequal respident="8000150745" case="Yes">8000152077</varequal>
+                <varequal respident="8000150745" case="Yes">8000152091</varequal>
+              </or>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Fail"/>
+            <displayfeedback feedbacktype="Solution" linkrefid="Solution"/>
+            <displayfeedback feedbacktype="Hint" linkrefid="Hint"/>
+          </respcondition>
+          <respcondition title="_olat_resp_feedback" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000150745" case="Yes">8000150748</varequal>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="8000150748"/>
+          </respcondition>
+          <respcondition title="_olat_resp_feedback" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000150745" case="Yes">8000152077</varequal>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="8000152077"/>
+          </respcondition>
+          <respcondition title="_olat_resp_feedback" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000150745" case="Yes">8000152080</varequal>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="8000152080"/>
+          </respcondition>
+          <respcondition title="_olat_resp_feedback" continue="Yes">
+            <conditionvar>
+              <varequal respident="8000150745" case="Yes">8000152091</varequal>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="8000152091"/>
+          </respcondition>
+          <respcondition title="Fail" continue="Yes">
+            <conditionvar>
+              <other/>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Fail"/>
+            <displayfeedback feedbacktype="Solution" linkrefid="Solution"/>
+            <displayfeedback feedbacktype="Hint" linkrefid="Hint"/>
+          </respcondition>
+        </resprocessing>
+        <itemfeedback ident="Hint" view="All">
+          <hint feedbackstyle="Incremental">
+            <hintmaterial>
+              <material>
+                <mattext><![CDATA[
+]]></mattext>
+              </material>
+            </hintmaterial>
+          </hint>
+        </itemfeedback>
+        <itemfeedback ident="Solution" view="All">
+          <solution>
+            <solutionmaterial>
+              <material>
+                <mattext><![CDATA[
+]]></mattext>
+              </material>
+            </solutionmaterial>
+          </solution>
+        </itemfeedback>
+      </item>
+      <item ident="QTIEDIT:KPRIM:8000171225" title="Neue Frage">
+        <presentation>
+          <material>
+            <mattext texttype="text/html"><![CDATA[Neue Frage]]></mattext>
+          </material>
+          <response_lid ident="8000171227" rcardinality="Multiple" rtiming="No">
+            <render_choice shuffle="No" minnumber="0" maxnumber="4">
+              <flow_label class="List">
+                <response_label ident="8000171230" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[+ Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000171233" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[- Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000171236" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[+ Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+              <flow_label class="List">
+                <response_label ident="8000171239" rshuffle="Yes">
+                  <material>
+                    <mattext texttype="text/html"><![CDATA[- Antwort]]></mattext>
+                  </material>
+                </response_label>
+              </flow_label>
+            </render_choice>
+          </response_lid>
+        </presentation>
+        <resprocessing>
+          <outcomes>
+            <decvar varname="SCORE" vartype="Decimal" defaultval="0" minvalue="0.0" maxvalue="1.0" cutvalue="1.0"/>
+          </outcomes>
+          <respcondition continue="Yes" title="Mastery">
+            <conditionvar>
+              <varequal respident="8000171227" case="Yes">8000171230:correct</varequal>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Fail">
+            <conditionvar>
+              <not>
+                <varequal respident="8000171227" case="Yes">8000171230:correct</varequal>
+              </not>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">-0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Mastery">
+            <conditionvar>
+              <varequal respident="8000171227" case="Yes">8000171233:wrong</varequal>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Fail">
+            <conditionvar>
+              <not>
+                <varequal respident="8000171227" case="Yes">8000171233:wrong</varequal>
+              </not>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">-0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Mastery">
+            <conditionvar>
+              <varequal respident="8000171227" case="Yes">8000171236:correct</varequal>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Fail">
+            <conditionvar>
+              <not>
+                <varequal respident="8000171227" case="Yes">8000171236:correct</varequal>
+              </not>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">-0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Mastery">
+            <conditionvar>
+              <varequal respident="8000171227" case="Yes">8000171239:wrong</varequal>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">0.25</setvar>
+          </respcondition>
+          <respcondition continue="Yes" title="Fail">
+            <conditionvar>
+              <not>
+                <varequal respident="8000171227" case="Yes">8000171239:wrong</varequal>
+              </not>
+            </conditionvar>
+            <setvar varname="SCORE" action="Add">-0.25</setvar>
+          </respcondition>
+          <respcondition title="Mastery" continue="Yes">
+            <conditionvar>
+              <and>
+                <varequal respident="8000171227" case="Yes">8000171230:correct</varequal>
+                <varequal respident="8000171227" case="Yes">8000171233:wrong</varequal>
+                <varequal respident="8000171227" case="Yes">8000171236:correct</varequal>
+                <varequal respident="8000171227" case="Yes">8000171239:wrong</varequal>
+              </and>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Mastery"/>
+          </respcondition>
+          <respcondition title="Fail" continue="Yes">
+            <conditionvar>
+              <not>
+                <and>
+                  <varequal respident="8000171227" case="Yes">8000171230:correct</varequal>
+                  <varequal respident="8000171227" case="Yes">8000171233:wrong</varequal>
+                  <varequal respident="8000171227" case="Yes">8000171236:correct</varequal>
+                  <varequal respident="8000171227" case="Yes">8000171239:wrong</varequal>
+                </and>
+              </not>
+            </conditionvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Fail"/>
+            <displayfeedback feedbacktype="Solution" linkrefid="Solution"/>
+            <displayfeedback feedbacktype="Hint" linkrefid="Hint"/>
+          </respcondition>
+        </resprocessing>
+      </item>
+      <item ident="QTIEDIT:FIB:8000173476" title="Lückentext Frage">
+        <itemcontrol feedbackswitch="No" hintswitch="No" solutionswitch="No"/>
+        <presentation label="notset">
+          <flow>
+            <material>
+              <mattext texttype="text/html"><![CDATA[<p>To be or</p>]]></mattext>
+            </material>
+            <response_str ident="8000177110" rcardinality="Single">
+              <render_fib columns="3" maxchars="10">
+                <flow_label class="Block">
+                  <response_label ident="8000177110" rshuffle="Yes"/>
+                </flow_label>
+              </render_fib>
+            </response_str>
+            <material>
+              <mattext texttype="text/html"><![CDATA[<p>to be</p>]]></mattext>
+            </material>
+          </flow>
+        </presentation>
+        <resprocessing>
+          <outcomes>
+            <decvar varname="SCORE" vartype="Decimal" defaultval="0" minvalue="0.0" maxvalue="1.0" cutvalue="1.0"/>
+          </outcomes>
+          <respcondition title="Mastery" continue="Yes">
+            <conditionvar>
+              <and>
+                <or>
+                  <varequal respident="8000177110" case="No"><![CDATA[not]]></varequal>
+                </or>
+              </and>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">1.0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Mastery"/>
+          </respcondition>
+          <respcondition title="Fail" continue="Yes">
+            <conditionvar>
+              <other/>
+            </conditionvar>
+            <setvar varname="SCORE" action="Set">0</setvar>
+            <displayfeedback feedbacktype="Response" linkrefid="Fail"/>
+            <displayfeedback feedbacktype="Solution" linkrefid="Solution"/>
+            <displayfeedback feedbacktype="Hint" linkrefid="Hint"/>
+          </respcondition>
+        </resprocessing>
+        <itemfeedback ident="Hint" view="All">
+          <hint feedbackstyle="Incremental">
+            <hintmaterial>
+              <material>
+                <mattext><![CDATA[
+]]></mattext>
+              </material>
+            </hintmaterial>
+          </hint>
+        </itemfeedback>
+        <itemfeedback ident="Solution" view="All">
+          <solution>
+            <solutionmaterial>
+              <material>
+                <mattext><![CDATA[
+]]></mattext>
+              </material>
+            </solutionmaterial>
+          </solution>
+        </itemfeedback>
+      </item>
+    </section>
+  </assessment>
+</questestinterop>