/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-qti/src/java/org/sakaiproject/tool/assessment/qti/helper/item/ItemHelper20Impl.java $ * $Id: ItemHelper20Impl.java 121258 2013-03-15 15:03:36Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2003, 2004, 2005, 2006, 2008, 2009 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.sakaiproject.tool.assessment.qti.helper.item; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.sakaiproject.tool.assessment.data.ifc.assessment.AnswerIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemTextIfc; import org.sakaiproject.tool.assessment.qti.asi.Item; import org.sakaiproject.tool.assessment.qti.constants.AuthoringConstantStrings; import org.sakaiproject.tool.assessment.qti.constants.QTIConstantStrings; import org.sakaiproject.tool.assessment.qti.constants.QTIVersion; import org.sakaiproject.tool.assessment.qti.helper.AuthoringXml; /** * <p>Copyright: Copyright (c) 2004</p> * <p>Organization: Sakai Project</p> * <p>Version for QTI 2.0 item XML, significant differences between 1.2 and 2.0</p> * @author Ed Smiley esmiley@stanford.edu * @version $Id: ItemHelper20Impl.java 121258 2013-03-15 15:03:36Z ottenhoff@longsight.com $ */ public class ItemHelper20Impl extends ItemHelperBase implements ItemHelperIfc { private static Log log = LogFactory.getLog(ItemHelper20Impl.class); private AuthoringXml authoringXml; public ItemHelper20Impl() { super(); authoringXml = new AuthoringXml(getQtiVersion()); log.debug("ItemHelper20Impl"); } protected AuthoringXml getAuthoringXml() { return authoringXml; } /** * Add maximum score to item XML. * @param score * @param itemXml */ public void addMaxScore(Double score, Item itemXml) { // normalize if null if (score == null) { score = Double.valueOf(0); } // set the responseElse baseValue, if it exists String xPath = "assessmentItem/responseCondition/responseIf/" + "setOutcomeValue/baseValue"; // test if this is a type that has this baseValue List list = itemXml.selectNodes(xPath); if (list == null || list.size() == 0) { return; } updateItemXml(itemXml, xPath, score.toString()); } /** * Add minimum score to item XML * @param score * @param itemXml */ public void addMinScore(Double score, Item itemXml) { // normalize if null if (score == null) { score = Double.valueOf(0); } // first, set the outcomeDeclaration defaultValue, if it exists String xPath = "assessmentItem/responseDeclaration/outcomeDeclaration/defaultValue"; // test if this is a type that has a defaultValue List list = itemXml.selectNodes(xPath); if (list == null || list.size() == 0) { return; } updateItemXml(itemXml, xPath, score.toString()); // next, set the responseElse baseValue, if it exists xPath = "assessmentItem/responseCondition/responseElse/" + "setOutcomeValue/baseValue"; // test if this is a type that has this baseValue list = itemXml.selectNodes(xPath); if (list == null || list.size() == 0) { return; } updateItemXml(itemXml, xPath, score.toString()); } /** * Flags an answer as correct. * @param correctAnswerLabel */ public void addCorrectAnswer(String correctAnswerLabel, Item itemXml) { String xPath = "assessmentItem/responseDeclaration/correctResponse/value"; updateItemXml(itemXml, xPath, correctAnswerLabel); } /** * assessmentItem/qtiMetadata not be permissible in QTI 2.0 * this this should be used by manifest * Get the metadata field entry XPath * @return the XPath */ public String getMetaXPath() { String xpath = "assessmentItem/qtiMetadata"; return xpath; } /** * assessmentItem/qtiMetadata not be permissible in QTI 2.0 * this this should be used by manifest * Get the metadata field entry XPath for a given label * @param fieldlabel * @return the XPath */ public String getMetaLabelXPath(String fieldlabel) { String xpath = "assessmentItem/qtiMetadata/qtimetadatafield/fieldlabel[text()='" + fieldlabel + "']/following-sibling::fieldentry"; return xpath; } /** * Get the text for the item * @param itemXml * @return the text */ public String getText(Item itemXml) { String xpath = "assessmentItem/itemBody"; String itemType = itemXml.getItemType(); if (itemType.equals(AuthoringConstantStrings.MATCHING)) { xpath = "assessmentItem/itemBody/matchInteraction/simpleMatchSet/simpleAssociableChoice"; } return makeItemNodeText(itemXml, xpath); } /** * Set the (one or more) item texts. * Valid for single and multiple texts. * @todo FIB, MATCHING TEXT * @param itemXml * @param itemText text to be updated */ public void setItemTexts(ArrayList itemTextList, Item itemXml) { String xPath = "assessmentItem/itemBody"; if (itemTextList.size() < 1) { return; } String text = ( (ItemTextIfc) itemTextList.get(0)).getText(); log.debug("item text: " + text); if (itemXml.isFIB()) { // process fib // return; } if (itemXml.isFIN()) { // process fin // return; } try { itemXml.update(xPath, text); } catch (Exception ex) { throw new RuntimeException(ex); } } // } /** * get item type string * we use title for this for now * @param itemXml * @return type as string */ public String getItemType(Item itemXml) { String type = ""; String xpath = "assessmentItem"; List list = itemXml.selectNodes(xpath); if (list.size() > 0) { Element element = (Element) list.get(0); element.getAttribute(QTIConstantStrings.TITLE); } return type; } /** * Set the answer texts for item. * @param itemTextList the text(s) for item */ public void setAnswers(ArrayList itemTextList, Item itemXml) { // other types either have no answer or include them in their template if (!itemXml.isMatching() && !itemXml.isFIB() && !itemXml.isFIN() && !itemXml.isMCSC() && !itemXml.isMCMC() && !itemXml.isMCMCSS() &&!itemXml.isMXSURVEY()) { return; } // OK, so now we are in business. String xpath = "assessmentItem/itemBody/choiceInteraction/<simpleChoice"; List list = itemXml.selectNodes(xpath); log.debug("xpath size:" + list.size()); Iterator nodeIter = list.iterator(); Iterator iter = itemTextList.iterator(); Set answerSet = new HashSet(); char label = 'A'; int xpathIndex = 1; while (iter.hasNext()) { answerSet = ( (ItemTextIfc) iter.next()).getAnswerSet(); Iterator aiter = answerSet.iterator(); while (aiter.hasNext()) { AnswerIfc answer = (AnswerIfc) aiter.next(); if (Boolean.TRUE.equals(answer.getIsCorrect())) { this.addCorrectAnswer("" + label, itemXml); } String value = answer.getText(); log.debug("answer: " + answer.getText()); // process into XML // we assume that we have equal to or more than the requisite elements // if we have more than the existing elements we manufacture more // with labels 'A', 'B'....etc. Node node = null; try { boolean isInsert = true; if (nodeIter.hasNext()) { isInsert = false; } this.addIndexedEntry(itemXml, xpath, value, isInsert, xpathIndex, "" + label); } catch (Exception ex) { log.error("Cannot process source document.", ex); } label++; xpathIndex++; } } } /** * @todo NEED TO SET CORRECT, INCORRECT, GENERAL FEEDBACK * Set the feedback texts for item. * @param itemTextList the text(s) for item */ public void setFeedback(ArrayList itemTextList, Item itemXml) { String xpath = "assessmentItem/itemBody/choiceInteraction/<simpleChoice/feedbackInline"; // for any answers that are now in the template, create a feedback int xpathIndex = 1; List list = itemXml.selectNodes(xpath); if (list == null) { return; } Iterator nodeIter = list.iterator(); Iterator iter = itemTextList.iterator(); Set answerSet = new HashSet(); char label = 'A'; boolean first = true; while (iter.hasNext()) { ItemTextIfc itemTextIfc = (ItemTextIfc) iter.next(); if (first) // then do once { // add in Correct and InCorrect Feedback String correctFeedback = itemTextIfc.getItem().getCorrectItemFeedback(); String incorrectFeedback = itemTextIfc.getItem(). getInCorrectItemFeedback(); String generalFeedback = itemTextIfc.getItem().getGeneralItemFeedback(); log.debug("NEED TO SET CORRECT FEEDBACK: " + correctFeedback); log.debug("NEED TO SET INCORRECT FEEDBACK: " + incorrectFeedback); log.debug("NEED TO SET GENERAL FEEDBACK: " + incorrectFeedback); first = false; } // answer feedback answerSet = itemTextIfc.getAnswerSet(); Iterator aiter = answerSet.iterator(); while (aiter.hasNext()) { AnswerIfc answer = (AnswerIfc) aiter.next(); String value = answer.getGeneralAnswerFeedback(); log.debug("answer feedback: " + answer.getText()); Node node = null; try { boolean isInsert = true; if (nodeIter.hasNext()) { isInsert = false; } addIndexedEntry(itemXml, xpath, value, isInsert, xpathIndex, null); } catch (Exception ex) { log.error("Cannot process source document.", ex); } label++; xpathIndex++; } } } /** * Add/insert the index-th value. * @param itemXml the item xml * @param xpath * @param value * @param isInsert * @param index the numnber * @param identifier set this attribute if not null) */ private void addIndexedEntry(Item itemXml, String xpath, String value, boolean isInsert, int index, String identifier) { String indexBrackets = "[" + index + "]"; String thisNode = xpath + indexBrackets; String thisNodeIdentity = thisNode + "/@identity"; if (isInsert) { log.debug("Adding entry: " + thisNode); itemXml.insertElement(thisNode, xpath, "itemfeedback"); } else { log.debug("Updating entry: " + thisNode); } try { if (value == null) { value = ""; } itemXml.update(thisNode, value); log.debug("updated value in addIndexedEntry()"); } catch (Exception ex) { log.error("Cannot update value in addIndexedEntry(): " + ex); } } /** * get QTI version * @return */ protected int getQtiVersion() { return QTIVersion.VERSION_2_0; } /** * @todo implement this method for 2.0 release * @param incorrectAnswerLabel * @param itemXml */ public void addIncorrectAnswer(String incorrectAnswerLabel, Item itemXml) { } public void setItemText(String itemText, Item itemXml) { //todo } }