001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/sam/trunk/component/src/java/org/sakaiproject/tool/assessment/qti/helper/item/ItemHelper20Impl.java $
003: * $Id: ItemHelper20Impl.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the"License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.assessment.qti.helper.item;
021:
022: import java.util.ArrayList;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Set;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.w3c.dom.Element;
031: import org.w3c.dom.Node;
032:
033: import org.sakaiproject.tool.assessment.data.ifc.assessment.AnswerIfc;
034: import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemTextIfc;
035: import org.sakaiproject.tool.assessment.qti.asi.Item;
036: import org.sakaiproject.tool.assessment.qti.constants.AuthoringConstantStrings;
037: import org.sakaiproject.tool.assessment.qti.constants.QTIConstantStrings;
038: import org.sakaiproject.tool.assessment.qti.constants.QTIVersion;
039: import org.sakaiproject.tool.assessment.qti.helper.AuthoringXml;
040:
041: /**
042: * <p>Copyright: Copyright (c) 2004</p>
043: * <p>Organization: Sakai Project</p>
044: * <p>Version for QTI 2.0 item XML, significant differences between 1.2 and 2.0</p>
045: * @author Ed Smiley esmiley@stanford.edu
046: * @version $Id: ItemHelper20Impl.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
047: */
048:
049: public class ItemHelper20Impl extends ItemHelperBase implements
050: ItemHelperIfc {
051: private static Log log = LogFactory.getLog(ItemHelper20Impl.class);
052: private AuthoringXml authoringXml;
053:
054: public ItemHelper20Impl() {
055: super ();
056: authoringXml = new AuthoringXml(getQtiVersion());
057: log.debug("ItemHelper20Impl");
058: }
059:
060: protected AuthoringXml getAuthoringXml() {
061: return authoringXml;
062: }
063:
064: /**
065: * Add maximum score to item XML.
066: * @param score
067: * @param itemXml
068: */
069: public void addMaxScore(Float score, Item itemXml) {
070: // normalize if null
071: if (score == null) {
072: score = new Float(0);
073: }
074: // set the responseElse baseValue, if it exists
075: String xPath = "assessmentItem/responseCondition/responseIf/"
076: + "setOutcomeValue/baseValue";
077: // test if this is a type that has this baseValue
078: List list = itemXml.selectNodes(xPath);
079: if (list == null || list.size() == 0) {
080: return;
081: }
082: updateItemXml(itemXml, xPath, score.toString());
083: }
084:
085: /**
086: * Add minimum score to item XML
087: * @param score
088: * @param itemXml
089: */
090: public void addMinScore(Float score, Item itemXml) {
091: // normalize if null
092: if (score == null) {
093: score = new Float(0);
094: }
095: // first, set the outcomeDeclaration defaultValue, if it exists
096: String xPath = "assessmentItem/responseDeclaration/outcomeDeclaration/defaultValue";
097: // test if this is a type that has a defaultValue
098: List list = itemXml.selectNodes(xPath);
099: if (list == null || list.size() == 0) {
100: return;
101: }
102: updateItemXml(itemXml, xPath, score.toString());
103: // next, set the responseElse baseValue, if it exists
104: xPath = "assessmentItem/responseCondition/responseElse/"
105: + "setOutcomeValue/baseValue";
106: // test if this is a type that has this baseValue
107: list = itemXml.selectNodes(xPath);
108: if (list == null || list.size() == 0) {
109: return;
110: }
111: updateItemXml(itemXml, xPath, score.toString());
112: }
113:
114: /**
115: * Flags an answer as correct.
116: * @param correctAnswerLabel
117: */
118: public void addCorrectAnswer(String correctAnswerLabel, Item itemXml) {
119: String xPath = "assessmentItem/responseDeclaration/correctResponse/value";
120: updateItemXml(itemXml, xPath, correctAnswerLabel);
121: }
122:
123: /**
124: * assessmentItem/qtiMetadata not be permissible in QTI 2.0
125: * this this should be used by manifest
126: * Get the metadata field entry XPath
127: * @return the XPath
128: */
129: public String getMetaXPath() {
130: String xpath = "assessmentItem/qtiMetadata";
131: return xpath;
132: }
133:
134: /**
135: * assessmentItem/qtiMetadata not be permissible in QTI 2.0
136: * this this should be used by manifest
137: * Get the metadata field entry XPath for a given label
138: * @param fieldlabel
139: * @return the XPath
140: */
141: public String getMetaLabelXPath(String fieldlabel) {
142: String xpath = "assessmentItem/qtiMetadata/qtimetadatafield/fieldlabel[text()='"
143: + fieldlabel + "']/following-sibling::fieldentry";
144: return xpath;
145: }
146:
147: /**
148: * Get the text for the item
149: * @param itemXml
150: * @return the text
151: */
152: public String getText(Item itemXml) {
153: String xpath = "assessmentItem/itemBody";
154: String itemType = itemXml.getItemType();
155: if (itemType.equals(AuthoringConstantStrings.MATCHING)) {
156: xpath = "assessmentItem/itemBody/matchInteraction/simpleMatchSet/simpleAssociableChoice";
157: }
158:
159: return makeItemNodeText(itemXml, xpath);
160: }
161:
162: /**
163: * Set the (one or more) item texts.
164: * Valid for single and multiple texts.
165: * @todo FIB, MATCHING TEXT
166: * @param itemXml
167: * @param itemText text to be updated
168: */
169: public void setItemTexts(ArrayList itemTextList, Item itemXml) {
170: String xPath = "assessmentItem/itemBody";
171: if (itemTextList.size() < 1) {
172: return;
173: }
174:
175: if (itemXml.isMatching()) {
176: // process matching
177: // return;
178: }
179:
180: String text = ((ItemTextIfc) itemTextList.get(0)).getText();
181: log.debug("item text: " + text);
182: if (itemXml.isFIB()) {
183: // process fib
184: // return;
185: }
186:
187: if (itemXml.isFIN()) {
188: // process fin
189: // return;
190: }
191:
192: try {
193: itemXml.update(xPath, text);
194: } catch (Exception ex) {
195: throw new RuntimeException(ex);
196: }
197: }
198:
199: // }
200:
201: /**
202: * get item type string
203: * we use title for this for now
204: * @param itemXml
205: * @return type as string
206: */
207: public String getItemType(Item itemXml) {
208: String type = "";
209: String xpath = "assessmentItem";
210: List list = itemXml.selectNodes(xpath);
211: if (list.size() > 0) {
212: Element element = (Element) list.get(0);
213: element.getAttribute(QTIConstantStrings.TITLE);
214: }
215:
216: return type;
217: }
218:
219: /**
220: * Set the answer texts for item.
221: * @param itemTextList the text(s) for item
222: */
223:
224: public void setAnswers(ArrayList itemTextList, Item itemXml) {
225: // other types either have no answer or include them in their template
226: if (!itemXml.isMatching() && !itemXml.isFIB()
227: && !itemXml.isFIN() && !itemXml.isMCSC()
228: && !itemXml.isMCMC()) {
229: return;
230: }
231: // OK, so now we are in business.
232: String xpath = "assessmentItem/itemBody/choiceInteraction/<simpleChoice";
233:
234: List list = itemXml.selectNodes(xpath);
235: log.debug("xpath size:" + list.size());
236: Iterator nodeIter = list.iterator();
237:
238: Iterator iter = itemTextList.iterator();
239: Set answerSet = new HashSet();
240:
241: char label = 'A';
242: int xpathIndex = 1;
243: while (iter.hasNext()) {
244: answerSet = ((ItemTextIfc) iter.next()).getAnswerSet();
245: Iterator aiter = answerSet.iterator();
246: while (aiter.hasNext()) {
247: AnswerIfc answer = (AnswerIfc) aiter.next();
248: if (Boolean.TRUE.equals(answer.getIsCorrect())) {
249: this .addCorrectAnswer("" + label, itemXml);
250: }
251: String value = answer.getText();
252: log.debug("answer: " + answer.getText());
253: // process into XML
254: // we assume that we have equal to or more than the requisite elements
255: // if we have more than the existing elements we manufacture more
256: // with labels 'A', 'B'....etc.
257: Node node = null;
258: try {
259: boolean isInsert = true;
260: if (nodeIter.hasNext()) {
261: isInsert = false;
262: }
263:
264: this .addIndexedEntry(itemXml, xpath, value,
265: isInsert, xpathIndex, "" + label);
266:
267: } catch (Exception ex) {
268: log.error("Cannot process source document.", ex);
269: }
270:
271: label++;
272: xpathIndex++;
273: }
274: }
275: }
276:
277: /**
278: * @todo NEED TO SET CORRECT, INCORRECT, GENERAL FEEDBACK
279: * Set the feedback texts for item.
280: * @param itemTextList the text(s) for item
281: */
282:
283: public void setFeedback(ArrayList itemTextList, Item itemXml) {
284: String xpath = "assessmentItem/itemBody/choiceInteraction/<simpleChoice/feedbackInline";
285: // for any answers that are now in the template, create a feedback
286: int xpathIndex = 1;
287: List list = itemXml.selectNodes(xpath);
288: if (list == null) {
289: return;
290: }
291:
292: Iterator nodeIter = list.iterator();
293: Iterator iter = itemTextList.iterator();
294: Set answerSet = new HashSet();
295:
296: char label = 'A';
297: boolean first = true;
298: while (iter.hasNext()) {
299: ItemTextIfc itemTextIfc = (ItemTextIfc) iter.next();
300:
301: if (first) // then do once
302: {
303: // add in Correct and InCorrect Feedback
304: String correctFeedback = itemTextIfc.getItem()
305: .getCorrectItemFeedback();
306: String incorrectFeedback = itemTextIfc.getItem()
307: .getInCorrectItemFeedback();
308: String generalFeedback = itemTextIfc.getItem()
309: .getGeneralItemFeedback();
310: log.debug("NEED TO SET CORRECT FEEDBACK: "
311: + correctFeedback);
312: log.debug("NEED TO SET INCORRECT FEEDBACK: "
313: + incorrectFeedback);
314: log.debug("NEED TO SET GENERAL FEEDBACK: "
315: + incorrectFeedback);
316: first = false;
317: }
318:
319: // answer feedback
320: answerSet = itemTextIfc.getAnswerSet();
321: Iterator aiter = answerSet.iterator();
322: while (aiter.hasNext()) {
323: AnswerIfc answer = (AnswerIfc) aiter.next();
324: String value = answer.getGeneralAnswerFeedback();
325: log.debug("answer feedback: " + answer.getText());
326: Node node = null;
327: try {
328: boolean isInsert = true;
329: if (nodeIter.hasNext()) {
330: isInsert = false;
331: }
332: addIndexedEntry(itemXml, xpath, value, isInsert,
333: xpathIndex, null);
334: } catch (Exception ex) {
335: log.error("Cannot process source document.", ex);
336: }
337:
338: label++;
339: xpathIndex++;
340: }
341: }
342: }
343:
344: /**
345: * Add/insert the index-th value.
346: * @param itemXml the item xml
347: * @param xpath
348: * @param value
349: * @param isInsert
350: * @param index the numnber
351: * @param identifier set this attribute if not null)
352: */
353: private void addIndexedEntry(Item itemXml, String xpath,
354: String value, boolean isInsert, int index, String identifier)
355:
356: {
357: String indexBrackets = "[" + index + "]";
358: String this Node = xpath + indexBrackets;
359: String this NodeIdentity = this Node + "/@identity";
360: if (isInsert) {
361: log.debug("Adding entry: " + this Node);
362: itemXml.insertElement(this Node, xpath, "itemfeedback");
363: } else {
364: log.debug("Updating entry: " + this Node);
365: }
366: try {
367: if (value == null) {
368: value = "";
369: }
370: itemXml.update(this Node, value);
371: log.debug("updated value in addIndexedEntry()");
372: } catch (Exception ex) {
373: log
374: .error("Cannot update value in addIndexedEntry(): "
375: + ex);
376: }
377: }
378:
379: /**
380: * get QTI version
381: * @return
382: */
383: protected int getQtiVersion() {
384: return QTIVersion.VERSION_2_0;
385: }
386:
387: /**
388: * @todo implement this method for 2.0 release
389: * @param incorrectAnswerLabel
390: * @param itemXml
391: */
392: public void addIncorrectAnswer(String incorrectAnswerLabel,
393: Item itemXml) {
394: }
395:
396: public void setItemText(String itemText, Item itemXml) { //todo
397: }
398: }
|