Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java =================================================================== diff -u -r0cf1ce0d39dab80bbb0e11968010c727f5e002e4 -rcc1b2b3fdc49088abf515c55bc8533c762e95412 --- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java (.../QuestionExporter.java) (revision 0cf1ce0d39dab80bbb0e11968010c727f5e002e4) +++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionExporter.java (.../QuestionExporter.java) (revision cc1b2b3fdc49088abf515c55bc8533c762e95412) @@ -126,8 +126,8 @@ * @return Path to the created ZIP file */ public String exportQTIPackage() { - if (log.isDebugEnabled()) { - log.debug("Exporting QTI ZIP package \"" + packageTitle + "\""); + if (QuestionExporter.log.isDebugEnabled()) { + QuestionExporter.log.debug("Exporting QTI ZIP package \"" + packageTitle + "\""); } try { String rootDir = FileUtil.createTempDirectory(QuestionExporter.EXPORT_TEMP_FOLDER_SUFFIX); @@ -189,12 +189,16 @@ for (Question question : questions) { Element itemElem = null; - if (Question.QUESTION_TYPE_MULTIPLE_CHOICE.equals(question.getType())) { + if (Question.QUESTION_TYPE_MULTIPLE_CHOICE.equals(question.getType()) + || Question.QUESTION_TYPE_TRUE_FALSE.equals(question.getType()) + || Question.QUESTION_TYPE_MULTIPLE_RESPONSE.equals(question.getType())) { itemElem = exportMultipleChoiceQuestion(question); + } else if (Question.QUESTION_TYPE_ESSAY.equals(question.getType())) { + itemElem = exportEssayQuestion(question); } if (itemElem == null) { - QuestionExporter.log.warn("Unknow type \"" + question.getType() + " of question \"" + QuestionExporter.log.warn("Unknow type \"" + question.getType() + "\" of question \"" + question.getTitle() + "\""); } else { sectionElem.appendChild(itemElem); @@ -205,7 +209,7 @@ } /** - * Creates a XML element with contents of a single multiple choice question. + * Creates a XML element with contents of a single multiple choice or true/false or multiple response question. */ private Element exportMultipleChoiceQuestion(Question question) { Element itemElem = doc.createElement("item"); @@ -224,15 +228,17 @@ String responseLidIdentifier = "QUE_" + itemId + "_RL"; Element responseLidElem = (Element) presentationElem.appendChild(doc.createElement("response_lid")); responseLidElem.setAttribute("ident", responseLidIdentifier); - responseLidElem.setAttribute("rcardinality", "Single"); + responseLidElem.setAttribute("rcardinality", + Question.QUESTION_TYPE_MULTIPLE_RESPONSE.equals(question.getType()) ? "Multiple" : "Single"); responseLidElem.setAttribute("rtiming", "No"); // question feedback (displayed no matter what answer was choosed) List feedbackList = new ArrayList(); + String correctFeedbackLabel = null; String incorrectFeedbackLabel = null; Element overallFeedbackElem = null; if (!StringUtils.isBlank(question.getFeedback())) { - overallFeedbackElem = createFeedbackElem("_ALL", question.getFeedback()); + overallFeedbackElem = createFeedbackElem("_ALL", question.getFeedback(), "All"); feedbackList.add(overallFeedbackElem); } @@ -242,6 +248,23 @@ // iterate through answers, collecting some info along the way for (Answer answer : question.getAnswers()) { + // just labels for feedback for correct/incorrect answer + boolean isCorrect = answer.getScore() > 0; + if (!StringUtils.isBlank(answer.getFeedback())) { + if (!isCorrect && (incorrectFeedbackLabel == null)) { + Element feedbackElem = createFeedbackElem("_IC", answer.getFeedback(), "Candidate"); + feedbackList.add(feedbackElem); + incorrectFeedbackLabel = feedbackElem.getAttribute("ident"); + } else if (isCorrect && (correctFeedbackLabel == null)) { + Element feedbackElem = createFeedbackElem("_C", answer.getFeedback(), "Candidate"); + feedbackList.add(feedbackElem); + correctFeedbackLabel = feedbackElem.getAttribute("ident"); + } + } + } + + // proper iteration + for (Answer answer : question.getAnswers()) { Element responseLabelElem = (Element) renderChoiceElem.appendChild(doc.createElement("response_label")); itemId++; answerId++; @@ -254,37 +277,27 @@ appendMaterialElements(materialElem, answer.getText()); } - // just labels for feedback for correct/incorrect answer - boolean isCorrect = answer.getScore() > 0; - Element feedbackElem = null; - if (!StringUtils.isBlank(answer.getFeedback())) { - feedbackElem = createFeedbackElem((isCorrect ? "_C" : "_IC"), answer.getFeedback()); - feedbackList.add(feedbackElem); - - if (!isCorrect && (incorrectFeedbackLabel == null)) { - incorrectFeedbackLabel = feedbackElem.getAttribute("ident"); - } - } - // mark which answer is correct by setting score for each of them Element respconditionElem = doc.createElement("respcondition"); Element conditionvarElem = (Element) respconditionElem.appendChild(doc.createElement("conditionvar")); Element varequalElem = (Element) conditionvarElem.appendChild(doc.createElement("varequal")); varequalElem.setAttribute("respident", responseLidIdentifier); varequalElem.setTextContent(answerLabel); + boolean isCorrect = answer.getScore() > 0; Element setvarElem = (Element) respconditionElem.appendChild(doc.createElement("setvar")); setvarElem.setAttribute("varname", "que_score"); - setvarElem.setAttribute("action", isCorrect ? "Set" : "Add"); + setvarElem.setAttribute("action", + isCorrect && !Question.QUESTION_TYPE_MULTIPLE_RESPONSE.equals(question.getType()) ? "Set" : "Add"); setvarElem.setTextContent(String.valueOf(answer.getScore())); // link feedback for correct/incorrect answer - if (feedbackElem != null) { + if (isCorrect) { Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc .createElement("displayfeedback")); displayfeedbackElem.setAttribute("feedbacktype", "Response"); - displayfeedbackElem.setAttribute("linkrefid", feedbackElem.getAttribute("ident")); - } else if (!isCorrect && (incorrectFeedbackLabel != null)) { + displayfeedbackElem.setAttribute("linkrefid", correctFeedbackLabel); + } else { Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc .createElement("displayfeedback")); displayfeedbackElem.setAttribute("feedbacktype", "Response"); @@ -304,7 +317,7 @@ if (overallFeedbackElem != null) { Element respconditionElem = doc.createElement("respcondition"); Element conditionvarElem = (Element) respconditionElem.appendChild(doc.createElement("conditionvar")); - conditionvarElem.appendChild(doc.createElement("elem")); + conditionvarElem.appendChild(doc.createElement("other")); Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc.createElement("displayfeedback")); displayfeedbackElem.setAttribute("feedbacktype", "Response"); displayfeedbackElem.setAttribute("linkrefid", overallFeedbackElem.getAttribute("ident")); @@ -332,13 +345,61 @@ } /** + * Creates a XML element with contents of a single essay question. + */ + private Element exportEssayQuestion(Question question) { + Element itemElem = doc.createElement("item"); + itemElem.setAttribute("title", question.getTitle()); + itemId++; + itemElem.setAttribute("ident", "QUE_" + itemId); + + // question text + Element presentationElem = (Element) itemElem.appendChild(doc.createElement("presentation")); + if (!StringUtils.isBlank(question.getText())) { + Element materialElem = (Element) presentationElem.appendChild(doc.createElement("material")); + appendMaterialElements(materialElem, question.getText()); + } + + // just a single response element + itemId++; + Element responseStrElem = (Element) presentationElem.appendChild(doc.createElement("response_str")); + responseStrElem.setAttribute("ident", "QUE_" + itemId + "_RS"); + Element renderFibElem = (Element) responseStrElem.appendChild(doc.createElement("render_fib")); + renderFibElem.setAttribute("fibtype", "String"); + renderFibElem.setAttribute("prompt", "Box"); + renderFibElem.setAttribute("rows", "5"); + renderFibElem.setAttribute("columns", "50"); + itemId++; + Element responseLabelElem = (Element) renderFibElem.appendChild(doc.createElement("response_label")); + responseLabelElem.setAttribute("ident", "QUE_" + itemId + "_ANS"); + + // just a single feedback element + if (!StringUtils.isBlank(question.getFeedback())) { + Element overallFeedbackElem = createFeedbackElem("_ALL", question.getFeedback(), "All"); + + Element resprocessingElem = (Element) itemElem.appendChild(doc.createElement("resprocessing")); + Element respconditionElem = (Element) resprocessingElem.appendChild(doc.createElement("respcondition")); + Element conditionvarElem = (Element) respconditionElem.appendChild(doc.createElement("conditionvar")); + conditionvarElem.appendChild(doc.createElement("other")); + Element displayfeedbackElem = (Element) respconditionElem.appendChild(doc.createElement("displayfeedback")); + displayfeedbackElem.setAttribute("feedbacktype", "Response"); + displayfeedbackElem.setAttribute("linkrefid", overallFeedbackElem.getAttribute("ident")); + + itemElem.appendChild(overallFeedbackElem); + } + + return itemElem; + } + + /** * Creates a feedback XML element. */ - private Element createFeedbackElem(String labelSuffix, String feedback) { + private Element createFeedbackElem(String labelSuffix, String feedback, String type) { itemId++; String label = "QUE_" + itemId + labelSuffix; Element feedbackElem = doc.createElement("itemfeedback"); feedbackElem.setAttribute("ident", label); + feedbackElem.setAttribute("view", type); Element materialElem = (Element) feedbackElem.appendChild(doc.createElement("material")); appendMaterialElements(materialElem, feedback); Index: lams_tool_assessment/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r9dcdc46408a8664a0fb27f09ae58a340c15f5764 -rcc1b2b3fdc49088abf515c55bc8533c762e95412 --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 9dcdc46408a8664a0fb27f09ae58a340c15f5764) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision cc1b2b3fdc49088abf515c55bc8533c762e95412) @@ -257,6 +257,7 @@ label.authoring.basic.export.questions =Export questions label.authoring.advance.display.summary =Display all questions and answers once the learner finishes. label.authoring.basic.import.qti =Import IMS QTI +label.authoring.basic.export.qti =Export IMS QTI advanced.reflectOnActivity =Add a notebook at end of Assessment with the following instructions: monitor.summary.td.addNotebook =Add a notebook at end of Assessment monitor.summary.td.notebookInstructions =Notebook instructions Index: lams_tool_assessment/conf/xdoclet/struts-actions.xml =================================================================== diff -u -ref851974c4dc3f5468b188fab9997a37b73c0e50 -rcc1b2b3fdc49088abf515c55bc8533c762e95412 --- lams_tool_assessment/conf/xdoclet/struts-actions.xml (.../struts-actions.xml) (revision ef851974c4dc3f5468b188fab9997a37b73c0e50) +++ lams_tool_assessment/conf/xdoclet/struts-actions.xml (.../struts-actions.xml) (revision cc1b2b3fdc49088abf515c55bc8533c762e95412) @@ -85,6 +85,13 @@ + + + Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/action/AuthoringAction.java =================================================================== diff -u -ref851974c4dc3f5468b188fab9997a37b73c0e50 -rcc1b2b3fdc49088abf515c55bc8533c762e95412 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/action/AuthoringAction.java (.../AuthoringAction.java) (revision ef851974c4dc3f5468b188fab9997a37b73c0e50) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/action/AuthoringAction.java (.../AuthoringAction.java) (revision cc1b2b3fdc49088abf515c55bc8533c762e95412) @@ -35,6 +35,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -61,6 +62,7 @@ import org.lamsfoundation.lams.learningdesign.service.ExportToolContentException; import org.lamsfoundation.lams.questions.Answer; import org.lamsfoundation.lams.questions.Question; +import org.lamsfoundation.lams.questions.QuestionExporter; import org.lamsfoundation.lams.questions.QuestionParser; import org.lamsfoundation.lams.tool.ToolAccessMode; import org.lamsfoundation.lams.tool.assessment.AssessmentConstants; @@ -141,6 +143,9 @@ if (param.equals("saveQTI")) { return saveQTI(mapping, form, request, response); } + if (param.equals("exportQTI")) { + return exportQTI(mapping, form, request, response); + } if (param.equals("removeQuestion")) { return removeQuestion(mapping, form, request, response); } @@ -372,7 +377,7 @@ } assessmentPO.setCreatedBy(assessmentUser); - + // ************************* Handle assessment questions ******************* // Handle assessment questions Set questions = new LinkedHashSet(); @@ -572,8 +577,8 @@ assessmentQuestion.setTitle(question.getTitle()); assessmentQuestion.setQuestion(QuestionParser.processHTMLField(question.getText(), false, contentFolderID, question.getResourcesFolderPath())); - assessmentQuestion.setGeneralFeedback(QuestionParser.processHTMLField(question.getFeedback(), false, contentFolderID, - question.getResourcesFolderPath())); + assessmentQuestion.setGeneralFeedback(QuestionParser.processHTMLField(question.getFeedback(), false, + contentFolderID, question.getResourcesFolderPath())); assessmentQuestion.setPenaltyFactor(0); int questionGrade = 1; @@ -759,6 +764,118 @@ } /** + * Prepares Assessment content for QTI packing + */ + @SuppressWarnings("rawtypes") + private ActionForward exportQTI(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws UnsupportedEncodingException { + String sessionMapID = WebUtil.readStrParam(request, AssessmentConstants.ATTR_SESSION_MAP_ID); + SessionMap sessionMap = (SessionMap) request.getSession().getAttribute(sessionMapID); + + SortedSet questionList = getQuestionList(sessionMap); + List questions = new LinkedList(); + for (AssessmentQuestion assessmentQuestion : questionList) { + Question question = new Question(); + List answers = new ArrayList(); + + switch (assessmentQuestion.getType()) { + + case AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE: + if (assessmentQuestion.isMultipleAnswersAllowed()) { + question.setType(Question.QUESTION_TYPE_MULTIPLE_RESPONSE); + int correctAnswerCount = 0; + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getQuestionOptions()) { + if (assessmentAnswer.getGrade() > 0) { + correctAnswerCount++; + } + } + + Float correctAnswerScore = correctAnswerCount > 0 ? new Integer(100 / correctAnswerCount) + .floatValue() : null; + int incorrectAnswerCount = assessmentQuestion.getQuestionOptions().size() - correctAnswerCount; + Float incorrectAnswerScore = incorrectAnswerCount > 0 ? new Integer(-100 / incorrectAnswerCount) + .floatValue() : null; + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getQuestionOptions()) { + Answer answer = new Answer(); + boolean isCorrectAnswer = assessmentAnswer.getGrade() > 0; + + answer.setText(assessmentAnswer.getOptionString()); + answer.setScore(isCorrectAnswer ? correctAnswerScore : incorrectAnswerScore); + answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + + answers.add(assessmentAnswer.getSequenceId(), answer); + } + } else { + question.setType(Question.QUESTION_TYPE_MULTIPLE_CHOICE); + + for (AssessmentQuestionOption assessmentAnswer : assessmentQuestion.getQuestionOptions()) { + Answer answer = new Answer(); + boolean isCorrectAnswer = assessmentAnswer.getGrade() == 1F; + + answer.setText(assessmentAnswer.getOptionString()); + answer.setScore(isCorrectAnswer ? new Integer(assessmentQuestion.getDefaultGrade()) + .floatValue() : 0); + answer.setFeedback(isCorrectAnswer ? assessmentQuestion.getFeedbackOnCorrect() + : assessmentQuestion.getFeedbackOnIncorrect()); + + answers.add(assessmentAnswer.getSequenceId(), answer); + } + } + + break; + + case AssessmentConstants.QUESTION_TYPE_TRUE_FALSE: + question.setType(Question.QUESTION_TYPE_TRUE_FALSE); + boolean isTrueCorrect = assessmentQuestion.getCorrectAnswer(); + + // true/false question is basically the same for QTI, just with special answers + Answer trueAnswer = new Answer(); + trueAnswer.setText("True"); + trueAnswer.setScore(isTrueCorrect ? new Integer(assessmentQuestion.getDefaultGrade()).floatValue() : 0); + trueAnswer.setFeedback(isTrueCorrect ? assessmentQuestion.getFeedbackOnCorrect() : assessmentQuestion + .getFeedbackOnIncorrect()); + answers.add(trueAnswer); + + Answer falseAnswer = new Answer(); + falseAnswer.setText("False"); + falseAnswer.setScore(!isTrueCorrect ? new Integer(assessmentQuestion.getDefaultGrade()).floatValue() + : 0); + falseAnswer.setFeedback(!isTrueCorrect ? assessmentQuestion.getFeedbackOnCorrect() : assessmentQuestion + .getFeedbackOnIncorrect()); + answers.add(falseAnswer); + + break; + + case AssessmentConstants.QUESTION_TYPE_ESSAY: + // not much to do with essay + question.setType(Question.QUESTION_TYPE_ESSAY); + answers = null; + + break; + + default: + continue; + } + + question.setTitle(assessmentQuestion.getTitle()); + question.setText(assessmentQuestion.getQuestion()); + question.setFeedback(assessmentQuestion.getGeneralFeedback()); + question.setAnswers(answers); + + questions.add(assessmentQuestion.getSequenceId() - 1, question); + } + + String title = request.getParameter("title"); + QuestionExporter exporter = new QuestionExporter(title, questions.toArray(Question.QUESTION_ARRAY_TYPE)); + exporter.exportQTIPackage(request, response); + + return null; + } + + /** * Remove assessment question from HttpSession list and update page display. As authoring rule, all persist only * happen when user submit whole page. So this remove is just impact HttpSession values. * @@ -796,9 +913,9 @@ questionReferenceToDelete = questionReference; } } - //check if we need to delete random question reference + // check if we need to delete random question reference if ((questionReferenceToDelete == null) && (questionReferences.size() > questionList.size())) { - //find the first random question + // find the first random question for (QuestionReference questionReference : questionReferences) { if (questionReference.isRandomQuestion()) { questionReferenceToDelete = questionReference; Index: lams_tool_assessment/web/pages/authoring/basic.jsp =================================================================== diff -u -ra1e990f3ee5ec7091752683571061da802f58bb2 -rcc1b2b3fdc49088abf515c55bc8533c762e95412 --- lams_tool_assessment/web/pages/authoring/basic.jsp (.../basic.jsp) (revision a1e990f3ee5ec7091752683571061da802f58bb2) +++ lams_tool_assessment/web/pages/authoring/basic.jsp (.../basic.jsp) (revision cc1b2b3fdc49088abf515c55bc8533c762e95412) @@ -150,7 +150,7 @@ }; function importQTI(){ - window.open('questionFile.jsp', + window.open('questions/questionFile.jsp', 'QuestionFile','width=500,height=200,scrollbars=yes'); } @@ -167,7 +167,12 @@ }); } - + function exportQTI(){ + var frame = document.getElementById("downloadFileDummyIframe"), + title = encodeURIComponent(document.getElementsByName("assessment.title")[0].value); + frame.src = '' + + '&title=' + title; + } @@ -225,8 +230,17 @@ - + + + + + + +

-
\ No newline at end of file +
+ + + \ No newline at end of file Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/AuthoringAction.java =================================================================== diff -u -r0cf1ce0d39dab80bbb0e11968010c727f5e002e4 -rcc1b2b3fdc49088abf515c55bc8533c762e95412 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/AuthoringAction.java (.../AuthoringAction.java) (revision 0cf1ce0d39dab80bbb0e11968010c727f5e002e4) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/AuthoringAction.java (.../AuthoringAction.java) (revision cc1b2b3fdc49088abf515c55bc8533c762e95412) @@ -544,7 +544,6 @@ private ActionForward exportQTI(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID); - request.setAttribute(ScratchieConstants.ATTR_SESSION_MAP_ID, sessionMapID); SessionMap sessionMap = (SessionMap) request.getSession().getAttribute(sessionMapID); SortedSet itemList = getItemList(sessionMap); List questions = new LinkedList();