package org.sakaiproject.tool.assessment.samlite.impl; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlbeans.XmlOptions; import org.imsglobal.xsd.imsQtiasiv1P2.AssessfeedbackType; import org.imsglobal.xsd.imsQtiasiv1P2.AssessmentType; import org.imsglobal.xsd.imsQtiasiv1P2.AssessmentcontrolType; import org.imsglobal.xsd.imsQtiasiv1P2.ConditionvarType; import org.imsglobal.xsd.imsQtiasiv1P2.DecvarType; import org.imsglobal.xsd.imsQtiasiv1P2.DisplayfeedbackType; import org.imsglobal.xsd.imsQtiasiv1P2.FlowMatType; import org.imsglobal.xsd.imsQtiasiv1P2.FlowType; import org.imsglobal.xsd.imsQtiasiv1P2.ItemType; import org.imsglobal.xsd.imsQtiasiv1P2.ItemfeedbackType; import org.imsglobal.xsd.imsQtiasiv1P2.ItemmetadataType; import org.imsglobal.xsd.imsQtiasiv1P2.ItemrubricType; import org.imsglobal.xsd.imsQtiasiv1P2.MaterialType; import org.imsglobal.xsd.imsQtiasiv1P2.MatimageType; import org.imsglobal.xsd.imsQtiasiv1P2.MattextType; import org.imsglobal.xsd.imsQtiasiv1P2.OrType; import org.imsglobal.xsd.imsQtiasiv1P2.OrderType; import org.imsglobal.xsd.imsQtiasiv1P2.OutcomesType; import org.imsglobal.xsd.imsQtiasiv1P2.PresentationMaterialType; import org.imsglobal.xsd.imsQtiasiv1P2.PresentationType; import org.imsglobal.xsd.imsQtiasiv1P2.QtimetadataType; import org.imsglobal.xsd.imsQtiasiv1P2.QtimetadatafieldType; import org.imsglobal.xsd.imsQtiasiv1P2.QuestestinteropDocument; import org.imsglobal.xsd.imsQtiasiv1P2.RenderChoiceType; import org.imsglobal.xsd.imsQtiasiv1P2.RenderFibType; import org.imsglobal.xsd.imsQtiasiv1P2.RespconditionType; import org.imsglobal.xsd.imsQtiasiv1P2.ResponseLabelType; import org.imsglobal.xsd.imsQtiasiv1P2.ResponseLidType; import org.imsglobal.xsd.imsQtiasiv1P2.ResponseStrType; import org.imsglobal.xsd.imsQtiasiv1P2.ResprocessingType; import org.imsglobal.xsd.imsQtiasiv1P2.RubricType; import org.imsglobal.xsd.imsQtiasiv1P2.SectionType; import org.imsglobal.xsd.imsQtiasiv1P2.SelectionOrderingType; import org.imsglobal.xsd.imsQtiasiv1P2.SetvarType; import org.imsglobal.xsd.imsQtiasiv1P2.VarequalType; import org.sakaiproject.tool.assessment.samlite.api.Answer; import org.sakaiproject.tool.assessment.samlite.api.Question; import org.sakaiproject.tool.assessment.samlite.api.QuestionGroup; import org.sakaiproject.tool.assessment.samlite.api.SamLiteService; import org.sakaiproject.tool.assessment.services.assessment.AssessmentService; import org.sakaiproject.tool.assessment.data.dao.assessment.AssessmentMetaData; import org.w3c.dom.Document; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.util.ResourceLoader; public class SamLiteServiceImpl implements SamLiteService { private static Log log = LogFactory.getLog(SamLiteServiceImpl.class); public static final String DEFAULT_CHARSET = "UTF-8"; private Pattern justQuestionPattern, startOfQuestionPattern, correctAnswerPattern; private Pattern correctFillInPattern, answerPattern, endQuestionPattern, correctMultipleChoicePattern; private Pattern shortEssayPattern, correctTruePattern, correctFalsePattern, unnecessaryTruePattern, unnecessaryFalsePattern; private Pattern startOfQuestionNumericPattern; private Pattern pointsPattern; public void init() { // Initialization startOfQuestionNumericPattern = Pattern.compile("^(\\d+\\.|\\)|\\]\\s*)", Pattern.CASE_INSENSITIVE); correctAnswerPattern = Pattern.compile("^\\*"); correctMultipleChoicePattern = Pattern.compile("^\\*\\s*([a-z])\\.\\s*(.*)", Pattern.CASE_INSENSITIVE); correctFillInPattern = Pattern.compile("^\\*\\s*(.*)"); answerPattern = Pattern.compile("^([a-z])\\.\\s*(.*)", Pattern.CASE_INSENSITIVE); } public Question saveLast(QuestionGroup questionGroup, Question question) { if (null != question) { // Short essay questions don't have any answer, so it's the default type if (Question.UNDEFINED_QUESTION == question.getQuestionType()) question.setQuestionType(Question.SHORT_ESSAY_QUESTION); // If it doesn't have points yet, it's not going to get any if (!question.hasPoints()) question.setQuestionPoints("0"); questionGroup.addQuestion(question); } return new Question(); } private String getPoints(String line) { Matcher pointsMatcher = pointsPattern.matcher(line); String points = ""; if (pointsMatcher.find()) points = pointsMatcher.group(1); return points; } private String stripPoints(String line) { Matcher pointsMatcher = pointsPattern.matcher(line); StringBuffer sb = new StringBuffer(); while (pointsMatcher.find()) { pointsMatcher.appendReplacement(sb, ""); } pointsMatcher.appendTail(sb); return sb.toString(); } private String removeMatchedPattern(Matcher m) { StringBuffer buffer = new StringBuffer(); m.appendReplacement(buffer, ""); m.appendTail(buffer); return buffer.toString(); } public QuestionGroup parse(String name, String description, String data) { ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.SamLitePatternMessages"); String stQuestion = rb.getString("question"); startOfQuestionPattern = Pattern.compile("^(" + stQuestion + "\\s*\\d*\\s*)", Pattern.CASE_INSENSITIVE); String stPoints = rb.getString("points"); pointsPattern = Pattern.compile("\\((\\d*\\.?\\d*)\\s+" + stPoints + "\\)", Pattern.CASE_INSENSITIVE); String stSaveAnswer = rb.getString("save_answer"); endQuestionPattern = Pattern.compile("^" + stSaveAnswer, Pattern.CASE_INSENSITIVE); String stTrue = rb.getString("true"); String stFalse = rb.getString("false"); correctTruePattern = Pattern.compile("^\\*\\s*" + stTrue + "$"); correctFalsePattern = Pattern.compile("^\\*\\s*" + stFalse + "$"); unnecessaryTruePattern = Pattern.compile("^" + stTrue + "$"); unnecessaryFalsePattern = Pattern.compile("^" + stFalse + "$"); QuestionGroup questionGroup = new QuestionGroup(name, description); String cleanData = data; String[] lines = cleanData.split("\\n"); Question question = null; int questionNumber = 1; for (int i=0;i<lines.length;i++) { if (lines[i].endsWith("<br />")) lines[i] = lines[i].replaceAll("<br />", "").replace('\r', ' '); String line = lines[i].trim(); if (null != line && !"".equals(line)) { Matcher startOfQuestionMatcher = startOfQuestionPattern.matcher(line); Matcher startOfQuestionNumericMatcher = startOfQuestionNumericPattern.matcher(line); Matcher pointsMatcher = pointsPattern.matcher(line); // The question can begin with the word 'Question' boolean isQuestionStart = startOfQuestionMatcher.find(); // Or it can begin with a number followed by a delimitor boolean isQuestionNumericStart = startOfQuestionNumericMatcher.find(); // Some users may prefer to delineate questions with just the points line boolean isJustPoints = pointsMatcher.find(); if (isQuestionStart || isQuestionNumericStart || isJustPoints) { question = saveLast(questionGroup, question); if (isQuestionStart) line = removeMatchedPattern(startOfQuestionMatcher); else if (isQuestionNumericStart) line = removeMatchedPattern(startOfQuestionNumericMatcher); String points = getPoints(line); question.setQuestionPoints(points); String questionText = stripPoints(line); question.append(questionText.trim()); // Don't bother looking at what the user enters for question numbers, since they // could introduce errors -- we're going to number by order of questions question.setQuestionNumber(questionNumber); questionNumber++; } else if (null != question) parseLine(question, line); } } // Make sure we get that last question if (null != question) saveLast(questionGroup, question); return questionGroup; } private void parseLine(Question question, String line) { boolean isEndOfQuestion = endQuestionPattern.matcher(line).find(); boolean isCorrectAnswer = correctAnswerPattern.matcher(line).lookingAt(); Matcher answerMatcher = answerPattern.matcher(line); boolean isAnswer = answerMatcher.find(); boolean isEmptyTrue = unnecessaryTruePattern.matcher(line).find(); boolean isEmptyFalse = unnecessaryFalsePattern.matcher(line).find(); if (isEndOfQuestion) { // Do nothing, we just want to ignore this line } else if (isAnswer) { question.addAnswer(answerMatcher.group(1), answerMatcher.group(2), false); } else if (isCorrectAnswer) { Matcher multipleChoiceMatcher = correctMultipleChoicePattern.matcher(line); boolean isMC = multipleChoiceMatcher.find(); Matcher fillInMatcher = correctFillInPattern.matcher(line); boolean isFI = fillInMatcher.find(); boolean isTrue = correctTruePattern.matcher(line).find(); boolean isFalse = correctFalsePattern.matcher(line).find(); if (isMC) { String earlierCorrectAnswer = question.getCorrectAnswer(); boolean hasOneCorrectAnswerAlready = (null != earlierCorrectAnswer && !"".equals(earlierCorrectAnswer.trim())); question.setCorrectAnswer(multipleChoiceMatcher.group(1)); question.addAnswer(multipleChoiceMatcher.group(1), multipleChoiceMatcher.group(2), true); if (hasOneCorrectAnswerAlready) question.setQuestionType(Question.MULTIPLE_CHOICE_MULTIPLE_ANSWER_QUESTION); else question.setQuestionType(Question.MULTIPLE_CHOICE_QUESTION); } else if (isTrue) { question.addAnswer("A", "True", true); question.addAnswer("B", "False", false); question.setQuestionType(Question.TRUE_FALSE_QUESTION); question.setCorrectAnswer("True"); } else if (isFalse) { question.addAnswer("A", "True", false); question.addAnswer("B", "False", true); question.setQuestionType(Question.TRUE_FALSE_QUESTION); question.setCorrectAnswer("False"); } else if (isFI) { question.setCorrectAnswer(fillInMatcher.group(1)); question.setQuestionType(Question.FILL_IN_THE_BLANK_QUESTION); } } else if (isEmptyTrue || isEmptyFalse) { // Do nothing, since the 'correct' true or false answer is all we need. } else { // If we didn't match anything, then assume it's just part of the question text question.append(line); } } public Document createDocument(QuestionGroup questionGroup) { log.debug("Creating a new qti document with the following name: " + questionGroup.getName()); QuestestinteropDocument document = createQTIDocument(questionGroup); XmlOptions options = new XmlOptions(); options.setSavePrettyPrintIndent(4); options.setSavePrettyPrint(); options.setUseDefaultNamespace(); Map prefixes = new HashMap(); prefixes.put("", "http://www.imsglobal.org/xsd/ims_qtiasiv1p2"); options.setSaveImplicitNamespaces(prefixes); /* try { document.save(new File("/home/jrenfro/TestQuiz.xml"), options); } catch (Exception e) { e.printStackTrace(); } */ InputStream inputStream = null; try { inputStream = document.newInputStream(options); DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); builderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder(); Document doc = documentBuilder.parse(inputStream); return doc; } catch (ParserConfigurationException pce) { log.error("Unable to parse quiz document ", pce); } catch (IOException ioe) { log.error("Low-level IOException caught reading quiz document ", ioe); } catch (Exception qse) { log.error("Unable to save this quiz as an imported assessment ", qse); } finally { if (null != inputStream) { try { inputStream.close(); } catch (IOException ioe) { log.warn("Unimportant ioe"); } } } return null; } public QuestestinteropDocument createQTIDocument(QuestionGroup questionGroup) { // Create a new instance of the document object QuestestinteropDocument doc = QuestestinteropDocument.Factory.newInstance(); // Add a Questestinterop object doc.addNewQuestestinterop(); // Add an assessment object doc.getQuestestinterop().addNewAssessment(); // Grab the assessment in a local var AssessmentType assessment = doc.getQuestestinterop().getAssessment(); assessment.setTitle(questionGroup.getName()); // There's a bunch of metadata that we pre-populate here addAssessmentMetadata(assessment); // Now we're getting into the necessary xml fragments that make up the QTI schema AssessmentcontrolType assessmentControl = assessment.addNewAssessmentcontrol(); assessmentControl.setFeedbackswitch(AssessmentcontrolType.Feedbackswitch.YES); assessmentControl.setHintswitch(AssessmentcontrolType.Hintswitch.YES); assessmentControl.setSolutionswitch(AssessmentcontrolType.Solutionswitch.YES); assessmentControl.setView(AssessmentcontrolType.View.ALL); RubricType rubric = assessment.addNewRubric(); buildMattext(rubric.addNewMaterial().addNewMattext()); PresentationMaterialType presentationMaterial = assessment.addNewPresentationMaterial(); FlowMatType pFlowMat = presentationMaterial.addNewFlowMat(); pFlowMat.setClass1("Block"); buildMattext(pFlowMat.addNewMaterial().addNewMattext(), questionGroup.getDescription()); AssessfeedbackType assessFeedback = assessment.addNewAssessfeedback(); assessFeedback.setIdent("Feedback"); assessFeedback.setTitle("Feedback"); assessFeedback.setView(AssessfeedbackType.View.ALL); FlowMatType afFlowMat = assessFeedback.addNewFlowMat(); buildMattext(afFlowMat.addNewMaterial().addNewMattext()); // Add the section SectionType section = assessment.addNewSection(); section.setIdent("Multiple Choice Questions"); QtimetadataType sectionMetaData = section.addNewQtimetadata(); buildMetaDataField(sectionMetaData, "SECTION_OBJECTIVE", ""); buildMetaDataField(sectionMetaData, "SECTION_KEYWORD", ""); buildMetaDataField(sectionMetaData, "SECTION_RUBRIC", ""); PresentationMaterialType sectionPresentationMaterial = section.addNewPresentationMaterial(); FlowMatType spFlowMat = sectionPresentationMaterial.addNewFlowMat(); spFlowMat.setClass1("Block"); buildMattext(spFlowMat.addNewMaterial().addNewMattext()); buildEmptyMaterialImage(spFlowMat.addNewMaterial().addNewMatimage()); SelectionOrderingType selectionOrdering = section.addNewSelectionOrdering(); selectionOrdering.setSequenceType("Normal"); OrderType order = selectionOrdering.addNewOrder(); order.setOrderType("Sequential"); // Now, loop through the questions and add to the qti section object if (null != questionGroup.getQuestions()) { for (Iterator it = questionGroup.getQuestions().iterator();it.hasNext();) { Question question = (Question)it.next(); processQuestion(section, question); } } return doc; } private void buildMetaDataField(QtimetadataType metadata, String label, String entry) { QtimetadatafieldType field = metadata.addNewQtimetadatafield(); field.setFieldlabel(label); field.setFieldentry(entry); } private void addAssessmentMetadata(AssessmentType assessment) { QtimetadataType metadata = assessment.addNewQtimetadata(); AssessmentService service = new AssessmentService(); List metadataList = service.getDefaultMetaDataSet(); Iterator iter = metadataList.iterator(); String automaticSubmissionEditable = "true"; while (iter.hasNext()) { AssessmentMetaData mData = (AssessmentMetaData) iter.next(); if ("automaticSubmission_isInstructorEditable".equals((String) mData.getLabel())) { automaticSubmissionEditable = (String) mData.getEntry(); } else { String label = (String) mData.getLabel(); String entry = (String) mData.getEntry(); buildMetaDataField(metadata, label, entry); } } String autoSubmitEnabled = ServerConfigurationService.getString("samigo.autoSubmit.enabled"); if (autoSubmitEnabled == null || autoSubmitEnabled.equals("") || !autoSubmitEnabled.equals("true")) { buildMetaDataField(metadata, "automaticSubmission_isInstructorEditable", "false"); } else { buildMetaDataField(metadata, "automaticSubmission_isInstructorEditable", automaticSubmissionEditable); } } private void processQuestion(SectionType section, Question question) { log.debug("Processing a question"); switch (question.getQuestionType()) { case Question.MULTIPLE_CHOICE_QUESTION: processMultipleChoiceQuestion(section, question); break; case Question.MULTIPLE_CHOICE_MULTIPLE_ANSWER_QUESTION: processMultipleChoiceMultipleAnswerQuestion(section, question); break; case Question.FILL_IN_THE_BLANK_QUESTION: processFillInQuestion(section, question); break; case Question.SHORT_ESSAY_QUESTION: processShortEssayQuestion(section, question); break; case Question.TRUE_FALSE_QUESTION: processTrueFalseQuestion(section, question); break; default: // TODO: Notify the user that this question didn't work... }; } private void buildMattext(MattextType mattext) { mattext.setCharset(DEFAULT_CHARSET); mattext.setTexttype("text/plain"); } private void buildMattext(MattextType mattext, String value) { buildMattext(mattext); mattext.setStringValue(cdata(value)); } private String cdata(String value) { return value; //new StringBuffer().append("<![CDATA[").append(value).append("]]>").toString(); } private void buildEmptyMaterialImage(MatimageType matImage) { matImage.setEmbedded("base64"); matImage.setImagtype("text/html"); } private void buildItemFeedback(ItemType item, String identity) { ItemfeedbackType ifeedback = item.addNewItemfeedback(); ifeedback.setIdent(identity); ifeedback.setView(ItemfeedbackType.View.ALL); FlowMatType ifflow = ifeedback.addNewFlowMat(); ifflow.setClass1("Block"); buildMattext(ifflow.addNewMaterial().addNewMattext()); buildEmptyMaterialImage(ifflow.addNewMaterial().addNewMatimage()); } private void buildPresentationAndResponseLid(ItemType item, Question question, String label, String respLidId, ResponseLidType.Rcardinality.Enum rCardinality) { PresentationType presentation = item.addNewPresentation(); presentation.setLabel(label); FlowType flow = presentation.addNewFlow(); flow.setClass1("Block"); MaterialType material = flow.addNewMaterial(); buildMattext(material.addNewMattext(), cdata(question.getQuestion())); MaterialType material2 = flow.addNewMaterial(); buildMattext(material2.addNewMattext()); // Only for multiple choice if (Question.MULTIPLE_CHOICE_QUESTION == question.getQuestionType() || Question.MULTIPLE_CHOICE_MULTIPLE_ANSWER_QUESTION == question.getQuestionType()) { MatimageType matImage = material2.addNewMatimage(); matImage.setEmbedded("base64"); matImage.setImagtype("text/html"); matImage.setUri(""); } ResponseLidType responseLid = flow.addNewResponseLid(); responseLid.setIdent(respLidId); responseLid.setRcardinality(rCardinality); responseLid.setRtiming(ResponseLidType.Rtiming.NO); RenderChoiceType renderChoice = responseLid.addNewRenderChoice(); renderChoice.setShuffle(RenderChoiceType.Shuffle.NO); char c = 'A'; for (Iterator it = question.getAnswers().iterator();it.hasNext();) { Answer answer = (Answer)it.next(); ResponseLabelType responseLabel = renderChoice.addNewResponseLabel(); responseLabel.setIdent(String.valueOf(c)); responseLabel.setRarea(ResponseLabelType.Rarea.ELLIPSE); responseLabel.setRrange(ResponseLabelType.Rrange.EXACT); responseLabel.setRshuffle(ResponseLabelType.Rshuffle.YES); MaterialType mat = responseLabel.addNewMaterial(); MattextType text = mat.addNewMattext(); text.setCharset(DEFAULT_CHARSET); text.setTexttype("text/plain"); text.setStringValue(cdata(answer.getText())); MaterialType m2 = responseLabel.addNewMaterial(); MatimageType mi = m2.addNewMatimage(); mi.setEmbedded("base64"); mi.setImagtype("text/html"); mi.setUri(""); c++; } } private void addResponseLid(FlowType flow, Question question, boolean isMultipleAnswer) { ResponseLidType responseLid = flow.addNewResponseLid(); responseLid.setIdent("MCSC"); if (isMultipleAnswer) responseLid.setRcardinality(ResponseLidType.Rcardinality.MULTIPLE); else responseLid.setRcardinality(ResponseLidType.Rcardinality.SINGLE); responseLid.setRtiming(ResponseLidType.Rtiming.NO); RenderChoiceType renderChoice = responseLid.addNewRenderChoice(); renderChoice.setShuffle(RenderChoiceType.Shuffle.NO); char c = 'A'; for (Iterator it = question.getAnswers().iterator();it.hasNext();) { Answer answer = (Answer)it.next(); ResponseLabelType responseLabel = renderChoice.addNewResponseLabel(); responseLabel.setIdent(String.valueOf(c)); responseLabel.setRarea(ResponseLabelType.Rarea.ELLIPSE); responseLabel.setRrange(ResponseLabelType.Rrange.EXACT); responseLabel.setRshuffle(ResponseLabelType.Rshuffle.YES); MaterialType mat = responseLabel.addNewMaterial(); MattextType text = mat.addNewMattext(); text.setCharset(DEFAULT_CHARSET); text.setTexttype("text/plain"); text.setStringValue(cdata(answer.getText())); MaterialType m2 = responseLabel.addNewMaterial(); MatimageType mi = m2.addNewMatimage(); mi.setEmbedded("base64"); mi.setImagtype("text/html"); mi.setUri(""); c++; } } private void addRespProcessing(ItemType item, Question question, String respident) { ResprocessingType resProcessing = item.addNewResprocessing(); DecvarType decvar = resProcessing.addNewOutcomes().addNewDecvar(); decvar.setDefaultval("0"); decvar.setMaxvalue(question.getQuestionPoints()); decvar.setMinvalue("0"); decvar.setVarname("SCORE"); decvar.setVartype(DecvarType.Vartype.INTEGER); char alpha = 'A'; if (null != question.getAnswers()) { for (Iterator it = question.getAnswers().iterator();it.hasNext();) { Answer answer = (Answer)it.next(); String response = answer.isCorrect() ? "Correct" : "InCorrect"; RespconditionType respCondition = resProcessing.addNewRespcondition(); respCondition.setContinue(RespconditionType.Continue.NO); ConditionvarType conditionVar = respCondition.addNewConditionvar(); VarequalType varEqual = conditionVar.addNewVarequal(); varEqual.setCase(VarequalType.Case.YES); varEqual.setRespident(respident); varEqual.setStringValue(String.valueOf(alpha)); SetvarType setVar = respCondition.addNewSetvar(); setVar.setAction(SetvarType.Action.ADD); setVar.setVarname("SCORE"); setVar.setStringValue("0.0"); DisplayfeedbackType feedback1 = respCondition.addNewDisplayfeedback(); feedback1.setFeedbacktype(DisplayfeedbackType.Feedbacktype.RESPONSE); feedback1.setLinkrefid(response); DisplayfeedbackType feedback2 = respCondition.addNewDisplayfeedback(); feedback2.setFeedbacktype(DisplayfeedbackType.Feedbacktype.RESPONSE); feedback2.setLinkrefid("AnswerFeedback"); feedback2.setStringValue(cdata("")); alpha++; } } } private void processTrueFalseQuestion(SectionType section, Question question) { ItemType item = section.addNewItem(); item.setTitle("True-False"); ItemmetadataType itemMetaData = item.addNewItemmetadata(); QtimetadataType qtiMetaData = itemMetaData.addNewQtimetadata(); buildMetaDataField(qtiMetaData, "qmd_itemtype", "True False"); buildMetaDataField(qtiMetaData, "TEXT_FORMAT", "HTML"); ItemrubricType itemRubric = item.addNewItemrubric(); itemRubric.setView(ItemrubricType.View.ALL); MattextType mattext = itemRubric.addNewMaterial().addNewMattext(); mattext.setCharset(DEFAULT_CHARSET); mattext.setTexttype("text/plain"); buildPresentationAndResponseLid(item, question, "Resp001", "TF02", ResponseLidType.Rcardinality.SINGLE); addRespProcessing(item, question, "TF02"); buildItemFeedback(item, "Correct"); buildItemFeedback(item, "InCorrect"); } private void processMultipleChoiceQuestion(SectionType section, Question question) { ItemType item = section.addNewItem(); item.setTitle("Multiple Choice"); ItemmetadataType itemMetaData = item.addNewItemmetadata(); QtimetadataType qtiMetaData = itemMetaData.addNewQtimetadata(); buildMetaDataField(qtiMetaData, "qmd_itemtype", "Multiple Choice"); buildMetaDataField(qtiMetaData, "TEXT_FORMAT", "HTML"); buildMetaDataField(qtiMetaData, "hasRationale", "False"); ItemrubricType itemRubric = item.addNewItemrubric(); MattextType mattext = itemRubric.addNewMaterial().addNewMattext(); mattext.setCharset("UTF-8"); mattext.setTexttype("text/plain"); buildPresentationAndResponseLid(item, question, "Resp001", "MCSC", ResponseLidType.Rcardinality.SINGLE); addRespProcessing(item, question, "MCSC"); int numberOfAnswers = question.getAnswers().size(); char c = 'A'; for (int i=0;i<numberOfAnswers;i++) { buildItemFeedback(item, String.valueOf(c) + "1"); c++; } buildItemFeedback(item, "Correct"); buildItemFeedback(item, "InCorrect"); } private void processMultipleChoiceMultipleAnswerQuestion(SectionType section, Question question) { ItemType item = section.addNewItem(); item.setTitle("Multiple Correct"); ItemmetadataType itemMetaData = item.addNewItemmetadata(); QtimetadataType qtiMetaData = itemMetaData.addNewQtimetadata(); buildMetaDataField(qtiMetaData, "qmd_itemtype", "Multiple Correct Answer"); buildMetaDataField(qtiMetaData, "TEXT_FORMAT", "HTML"); buildMetaDataField(qtiMetaData, "hasRationale", "False"); ItemrubricType itemRubric = item.addNewItemrubric(); MattextType mattext = itemRubric.addNewMaterial().addNewMattext(); mattext.setCharset("UTF-8"); mattext.setTexttype("text/plain"); buildPresentationAndResponseLid(item, question, "Resp001", "MCMC", ResponseLidType.Rcardinality.MULTIPLE); addRespProcessing(item, question, "MCMC"); int numberOfAnswers = question.getAnswers().size(); char c = 'A'; for (int i=0;i<numberOfAnswers;i++) { buildItemFeedback(item, String.valueOf(c) + "1"); c++; } buildItemFeedback(item, "Correct"); buildItemFeedback(item, "InCorrect"); } private void processFillInQuestion(SectionType section, Question question) { ItemType item = section.addNewItem(); item.setTitle("Fill in the Blank"); ItemmetadataType itemMetaData = item.addNewItemmetadata(); QtimetadataType qtiMetaData = itemMetaData.addNewQtimetadata(); buildMetaDataField(qtiMetaData, "qmd_itemtype", "Fill In the Blank"); buildMetaDataField(qtiMetaData, "TEXT_FORMAT", "HTML"); buildMetaDataField(qtiMetaData, "MUTUALLY_EXCLUSIVE", "True"); ItemrubricType itemRubric = item.addNewItemrubric(); MattextType mattext = itemRubric.addNewMaterial().addNewMattext(); mattext.setCharset(DEFAULT_CHARSET); mattext.setTexttype("text/plain"); PresentationType presentation = item.addNewPresentation(); presentation.setLabel("FIB"); FlowType flow = presentation.addNewFlow(); flow.setClass1("Block"); MaterialType material = flow.addNewMaterial(); buildMattext(material.addNewMattext(), cdata(question.getQuestion())); MaterialType material2 = flow.addNewMaterial(); buildMattext(material2.addNewMattext()); ResponseStrType responseStr = flow.addNewResponseStr(); responseStr.setRcardinality(ResponseStrType.Rcardinality.ORDERED); responseStr.setRtiming(ResponseStrType.Rtiming.NO); RenderFibType renderFib = responseStr.addNewRenderFib(); renderFib.setCharset(DEFAULT_CHARSET); renderFib.setColumns("5"); renderFib.setEncoding("UTF_8"); renderFib.setFibtype(RenderFibType.Fibtype.STRING); renderFib.setPrompt(RenderFibType.Prompt.BOX); renderFib.setRows("1"); MaterialType material3 = flow.addNewMaterial(); buildMattext(material3.addNewMattext()); ResprocessingType resProcessing = item.addNewResprocessing(); OutcomesType outcomes = resProcessing.addNewOutcomes(); DecvarType decvar = outcomes.addNewDecvar(); decvar.setDefaultval("0"); decvar.setMaxvalue(question.getQuestionPoints()); decvar.setMinvalue("0"); decvar.setVarname("SCORE"); decvar.setVartype(DecvarType.Vartype.INTEGER); RespconditionType respCondition = resProcessing.addNewRespcondition(); respCondition.setContinue(RespconditionType.Continue.YES); ConditionvarType condition = respCondition.addNewConditionvar(); OrType or = condition.addNewOr(); VarequalType varequal = or.addNewVarequal(); varequal.setCase(VarequalType.Case.NO); varequal.setStringValue(cdata(question.getCorrectAnswer())); SetvarType setvar = respCondition.addNewSetvar(); setvar.setAction(SetvarType.Action.ADD); setvar.setVarname("SCORE"); setvar.setStringValue("0"); buildItemFeedback(item, "Correct"); buildItemFeedback(item, "InCorrect"); } private void processShortEssayQuestion(SectionType section, Question question) { ItemType item = section.addNewItem(); item.setTitle("Essay Question"); ItemmetadataType itemMetaData = item.addNewItemmetadata(); QtimetadataType qtiMetaData = itemMetaData.addNewQtimetadata(); buildMetaDataField(qtiMetaData, "qmd_itemtype", "Essay"); buildMetaDataField(qtiMetaData, "TEXT_FORMAT", "HTML"); ItemrubricType itemRubric = item.addNewItemrubric(); itemRubric.setView(ItemrubricType.View.ALL); buildMattext(itemRubric.addNewMaterial().addNewMattext()); PresentationType presentation = item.addNewPresentation(); presentation.setLabel("Model Short Answer"); FlowType flow = presentation.addNewFlow(); flow.setClass1("Block"); MaterialType material = flow.addNewMaterial(); buildMattext(material.addNewMattext(), cdata(question.getQuestion())); MaterialType material2 = flow.addNewMaterial(); buildMattext(material2.addNewMattext()); addResponseLid(flow, question, false); ResprocessingType resProcessing = item.addNewResprocessing(); OutcomesType outcomes = resProcessing.addNewOutcomes(); DecvarType decvar = outcomes.addNewDecvar(); decvar.setDefaultval("0"); decvar.setMaxvalue(question.getQuestionPoints()); decvar.setMinvalue("0"); decvar.setVarname("SCORE"); decvar.setVartype(DecvarType.Vartype.INTEGER); buildItemFeedback(item, "Correct"); buildItemFeedback(item, "InCorrect"); } }