package org.sakaiproject.lessonbuildertool.cc; import java.io.*; import java.util.List; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.ArrayList; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.DOMException; import org.sakaiproject.lessonbuildertool.tool.beans.SimplePageBean; public class QtiImport { String title = null; int noscore = 0; int paras = 0; PrintWriter out = null; boolean debug = false; boolean needHeader = true; CharArrayWriter charout = null; String filebase = null; SimplePageBean bean = null; boolean feedbackpermitted = true; String timelimit = null; boolean allowlate = true; String maxattempts = "1"; String siteId = null; boolean usesPatternMatch = false; boolean usesCurriculum = false; class Pair { String left; String leftident; String right; String rightident; Pair (String left, String leftident, String right, String rightident) { this.left = left; this.leftident = leftident; this.right = right; this.rightident = rightident; } } class Shortans { String qident; String rident; String answer; Shortans (String qident, String rident) { this.qident = qident; this.rident = rident; this.answer = null; } } class Mcans { String ident; String answer; List<String> fbident; String newident; String feedback; boolean correct; Mcans (String ident, String newident, String answer) { this.ident = ident; this.newident = newident; this.answer = answer; this.fbident = new ArrayList<String>(); this.feedback = null; this.correct = false; } } Document document; // supporting routines public String guessQuestionType (Node itemnode) { // explicit type? NodeList meta=((Element)itemnode).getElementsByTagName("qmd_itemtype"); if (meta != null && meta.item(0) != null) return meta.item(0).getTextContent(); // response_lid is for multiple choice, true false and matching // matching has an explicit type, so just need to see if true false NodeList lid=((Element)itemnode).getElementsByTagName("response_lid"); if (lid != null && lid.item(0) != null) { String cardinality = ((Element)lid.item(0)).getAttribute("rcardinality"); if (cardinality != null && cardinality.equalsIgnoreCase("multiple")) return "Multiple Correct"; NodeList responses=((Element)lid.item(0)).getElementsByTagName("response_label"); if (responses == null || responses.getLength() != 2) return "Multiple Choice"; String answer1 = ""; Element response1 = (Element)responses.item(0); if (response1 != null) { NodeList text1 = response1.getElementsByTagName("mattext"); if (text1 != null && text1.item(0) != null) answer1 = text1.item(0).getTextContent(); } String answer2 = ""; Element response2 = (Element)responses.item(1); if (response2 != null) { NodeList text2 = response2.getElementsByTagName("mattext"); if (text2 != null && text2.item(0) != null) answer2 = text2.item(0).getTextContent(); } if (answer1.equalsIgnoreCase("True") && answer2.equalsIgnoreCase("False") || answer1.equalsIgnoreCase("False") && answer2.equalsIgnoreCase("True")) return "True False"; return "Multiple Choice"; } // response_fib is for short ans/essay and fib // matching has an explicit type, so just need to see if true false NodeList fib=((Element)itemnode).getElementsByTagName("render_fib"); if (fib != null && fib.item(0) != null) { NodeList varequal=((Element)itemnode).getElementsByTagName("varequal"); if (varequal != null && varequal.item(0) != null) return "Fill In the Blank"; NodeList varsubstring=((Element)itemnode).getElementsByTagName("varsubstring"); if (varsubstring != null && varsubstring.item(0) != null) return "Fill In the Blank"; } return "Short Answers/Essay"; } // from Processing XML with Java, Elliotte Rusty Harold, // http://www.cafeconleche.org/books/xmljava/ public String escapeText(String s) { if (s.indexOf('&') != -1 || s.indexOf('<') != -1 || s.indexOf('>') != -1 || s.indexOf('"') != -1 || s.indexOf('\'') != -1 ) { StringBuffer result = new StringBuffer(s.length() + 6); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '&') result.append("&"); else if (c == '<') result.append("<"); else if (c == '"') result.append("""); else if (c == '\'') result.append("'"); else if (c == '>') result.append(">"); else result.append(c); } return result.toString(); } else { return s; } } String appString(String s1, String s2) { if (s1 != null && !s1.equals("")) { if (s2 != null && !s2.equals("")) return s1 + "<br/>" + s2; else return s1; } else return s2; } Node getFirstByName(Node node, String name) { NodeList nodes = ((Element)node).getElementsByTagName(name); int numnodes; if (nodes == null) numnodes = 0; else numnodes = nodes.getLength(); if (numnodes == 0) return null; else return nodes.item(0); } Node getNextByName(Node node, String name) { Node child = node.getNextSibling(); while (child != null && !child.getNodeName().equals(name)) { child = child.getNextSibling(); } return child; } Node getNextElement(Node node) { while (node != null && node.getNodeType() != Node.ELEMENT_NODE) node = node.getNextSibling(); return node; } // at least in 2.3, Samigo is unable to import MATIMAGE, despite the // fact that it produces them. So I stick the image into the HTML // of the question String getText(Node mattext) { String retText = ""; Element mattextl = null; try { mattextl = (Element)mattext; } catch (Exception e) { System.err.println("mattext is not an element"); return null; } NodeList textNodes = mattext.getChildNodes(); for (int i = 0; i < textNodes.getLength(); i++) { Node textchild = textNodes.item(i); if (textchild == null) continue; String text = textchild.getNodeValue(); if (text == null) continue; retText = retText + text; } String texttype = mattextl.getAttribute("texttype"); if (texttype != null && texttype.equals("text/plain")) retText = "<pre>\n" + retText + "\n</pre>"; else retText = retText.replaceAll("\\$IMS-CC-FILEBASE\\$", filebase); return retText; } String getMatText(Node material) { StringBuilder rettext = new StringBuilder(""); if (material == null) { System.err.println("<material> is null"); return null; } Node stuff = material.getFirstChild(); // System.err.println("start"); while (stuff != null) { // System.err.println("node: " + // stuff.getNodeName() + ": " + // stuff.getNodeValue()); if (stuff.getNodeName().equalsIgnoreCase("mattext")) { String thistext = getText(stuff); if (thistext != null) rettext.append(thistext); } else if (stuff.getNodeName().equalsIgnoreCase("matimage")) { String uri = getAttribute(stuff, "uri"); if (uri != null) { rettext.append("<p><img src=\""); rettext.append(uri); rettext.append("\" alt=\""); rettext.append(uri); rettext.append("\" />"); } } else if (stuff.getNodeName().equals("#text")) { // apparently the whitespace is reported as #text nodes; // ignore them } else System.err.println("unknown contents in material: " + stuff.getNodeName() + ": " + stuff.getNodeValue()); stuff = stuff.getNextSibling(); } return rettext.toString(); } // process question types String getAttribute(Node n, String name) { Element element; try { element = (Element)n; } catch (Exception e) { System.err.println("node is not an element: " + n.getNodeName()); return null; } return element.getAttribute(name); } String getNodeText(Node n) { Node vartext = n.getFirstChild(); if (vartext == null) return null; return vartext.getNodeValue(); } // matching question boolean procmatch(Node item)throws IOException { if (debug) System.err.println("match"); String title = null; List<Pair> pairs = new ArrayList<Pair>(); String feedback = null; Double score = 0.0; boolean scoreset = false; title = getAttribute(item, "title"); if (debug) System.err.println("title: " + title); Node presentation = getFirstByName(item, "presentation"); if (presentation == null) { System.err.println("can't find <presentation>"); return false; } Node material = getFirstByName(presentation, "material"); StringBuilder question = new StringBuilder(getMatText(material)); // optional list of pairs to display before the answers material = getNextByName(material, "material"); if (material != null) { Node materialr = getNextByName(material, "material"); if (materialr != null) { question.append("<p><table>"); // have left and right list Node mattextl = null; String textl = null; Node mattextr = null; String textr = null; mattextl = getFirstByName(material, "mattext"); mattextr = getFirstByName(materialr, "mattext"); while (mattextl != null && mattextr != null) { textl = getText(mattextl); textr = getText(mattextr); if (textl == null || textr == null) break; question.append("<tr><td>"); question.append(textl); question.append("</td><td>"); question.append(textr); question.append("</td></tr>"); mattextl = getNextByName(mattextl, "mattext"); mattextr = getNextByName(mattextr, "mattext"); } question.append("</table>"); } } if (debug) System.err.println("question: " + question.toString()); // now build the pairs. we depend upon the specific approach // webct uses // finding the real pairs is complex. it's easier to start out // by looking for the right answers, as that will give us // the entries on the right List<Pair> rightanswers = new ArrayList<Pair>(); Node resproc = getFirstByName(item, "resprocessing"); if (presentation == null) { System.err.println("can't find <resprocessing>"); return false; } Node outcomes = getFirstByName(resproc, "outcomes"); if (outcomes != null) { Node decvar = getFirstByName(outcomes, "decvar"); while (decvar != null) { try { String varname = getAttribute(decvar, "varname"); // System.out.println("decvar " + varname); String maxval = getAttribute(decvar, "maxvalue"); Double numval = Double.parseDouble(maxval); if (varname != null && varname.equals("que_score") && numval > score) { score = numval; // System.out.println("scoreset " + numval + " " + maxval); scoreset = true; break; } } catch (Exception ignore) {}; decvar = getNextByName(decvar, "decvar"); } } Node respcondl = getFirstByName(resproc, "respcondition"); if (respcondl == null) { System.err.println("can't find <respconditionl>"); return false; } while (respcondl != null) { Node conditionvar = getFirstByName(respcondl, "conditionvar"); if (conditionvar != null) { Node varequal = getFirstByName(conditionvar, "varequal"); if (varequal != null) { String respident = getAttribute(varequal, "respident"); String vartext = getNodeText(varequal); if (vartext != null) { Node setvar = getNextByName(conditionvar, "setvar"); if (setvar != null) { String varname = getAttribute(setvar , "varname"); if (varname != null && (varname.equals("WebCT_Correct") || (varname.equals("Respondus_Correct")))) { String action = getAttribute(setvar , "action"); if (!scoreset) try { String value = getNodeText(setvar); Double numval = Double.parseDouble(value); if (action.equalsIgnoreCase("Set")) { if (numval > score) score = numval; } else if (action.equalsIgnoreCase("Add")) { if (numval > 0.0) score = score + numval; // System.out.println("add " + score + " " + numval); } } catch (Exception ignore) {}; rightanswers.add(new Pair(null, respident, null, vartext)); if (debug) System.err.println("right answer: " + respident + ":" + vartext); } } } } } respcondl = getNextByName(respcondl, "respcondition"); } Node respgroup = getFirstByName(presentation, "response_grp"); if (respgroup == null) { respgroup = getFirstByName(presentation, "response_lid"); if (respgroup == null) { System.err.println("can't find <response_grp>"); return false; } } // loop over pairs while (respgroup != null) { String lident = getAttribute(respgroup, "ident"); if (lident == null) { System.err.println("Response group has no ident"); return false; } Node mat = getFirstByName(respgroup, "material"); String left = getMatText(mat); // finding the matching text is more complex // we have to look through the responses for the one they // count right Node choice = getFirstByName(respgroup, "render_choice"); if (choice == null) { System.err.println("can't find <render_choice> in response"); return false; } Node label = getFirstByName(choice, "response_label"); if (label == null) { System.err.println("can't find <response_label> in response"); return false; } while (label != null) { String ident = getAttribute(label, "ident"); // see if this is a right answer. only process it if so if (ident != null) { Iterator rights = rightanswers.iterator(); while (rights.hasNext()) { Pair rightans = (Pair)rights.next(); if (debug) System.err.println("allpairs: " + lident + ":" + ident + "=" + rightans.leftident + ":" + rightans.rightident); if ((rightans.leftident == null || rightans.leftident .equals(lident)) && rightans.rightident.equals(ident)) { // this is the right answer. find the text Node matr = getFirstByName(label, "material"); String right = getMatText(matr); if (debug) System.err.println("pair " + left + ":" + lident + ":" + right + ":" + ident); pairs.add(new Pair(left, lident, right, ident)); } } } label = getNextByName(label, "response_label"); } Node nextgroup = getNextByName(respgroup, "response_grp"); if (nextgroup == null) nextgroup = getNextByName(respgroup, "response_lid"); respgroup = nextgroup; } Node feeditem = getFirstByName(item, "itemfeedback"); if (feeditem != null) { Node matf = getFirstByName(feeditem, "material"); feedback = getMatText(matf); } if (debug) System.err.println("feedback " + feedback); doHeader(); out.print("<item ident=\""+getAttribute(item, "ident")+"\""); if (title != null) out.print(" title=\"" + escapeText(title) + "\""); out.println(">"); out.println(" <itemmetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>qmd_itemtype</fieldlabel>"); out.println(" <fieldentry>Matching</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>TEXT_FORMAT</fieldlabel>"); out.println(" <fieldentry>HTML</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" </itemmetadata>"); out.println(" <presentation>"); out.println(" <flow class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + question.toString() + "]]></mattext>"); out.println(" </material>"); out.println(" <response_grp ident=\"resp_grp\" rcardinality=\"Ordered\" rtiming=\"No\">"); out.println(" <render_choice shuffle=\"No\">"); for (Pair leftc: pairs) { out.println(" <response_label ident=\"" + leftc.leftident + "\" rarea=\"Ellipse\" rrange=\"Exact\" rshuffle=\"Yes\"><material><mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + leftc.left + "]]></mattext></material></response_label>"); } for (Pair rightc: pairs) { out.println(" <response_label ident=\"" + rightc.rightident + "\" match_group=\"\" match_max=\"1\" rarea=\"Ellipse\" rrange=\"Exact\" rshuffle=\"Yes\"><material><mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + rightc.right + "]]></mattext></material></response_label>"); } out.println("<response_label rarea=\"Ellipse\" rrange=\"Exact\" rshuffle=\"Yes\"></response_label>"); out.println("<response_label rarea=\"Ellipse\" rrange=\"Exact\" rshuffle=\"Yes\"></response_label>"); out.println("</render_choice>"); out.println(" </response_grp>"); out.println(" </flow>"); out.println(" </presentation>"); out.println(" <resprocessing>"); if (score <= 0.0) { score = 1.0; noscore++; } out.println(" <outcomes>"); out.println(" <decvar defaultval=\"0\" maxvalue=\"" + score +"\" minvalue=\"0\" varname=\"SCORE\" vartype=\"Integer\"></decvar>"); out.println(" </outcomes>"); int numpairs = pairs.size(); int index = 1; // there's a problem here. You're supposed to output every // pair, in order to specify feedback for wrong answers. // but when I do that, it fails to identify which is right and // which is wrong. It appears that it picks up the right and // wrong feedback even without any links here, which is wrong // but useful. for (Pair respl: pairs) { for (Pair respr: pairs) { if (respr == respl) { out.println("<respcondition continue=\"No\">"); out.println("<conditionvar>"); out.println("<varequal case=\"Yes\" index=\""+index+"\" respident=\"" + respl.leftident + "\">" + respr.rightident + "</varequal>"); out.println("</conditionvar>"); out.println("<setvar action=\"Add\" varname=\"SCORE\">"+score/numpairs+"</setvar>"); out.println("</respcondition>"); out.println(""); index++; } } } out.println("</resprocessing>"); // WebCT has general feedback only. For this question type // samigo doesn't have it, so I implement by setting // correct and incorrect to the same thing. if (feedback != null && !feedback.trim().equals("")) { out.println(" <itemfeedback ident=\"Correct\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+feedback+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); out.println(" <itemfeedback ident=\"InCorrect\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+feedback+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); } out.println("</item>"); return true; } boolean procpara(Node item) throws IOException{ if (debug) System.err.println("para"); String title = null; String ident = null; String question = null; String model = null; String feedback = null; title = getAttribute(item, "title"); ident = getAttribute(item, "ident"); if (debug) System.err.println("title: " + title); Node presentation = getFirstByName(item, "presentation"); if (presentation == null) { System.err.println("can't find <presentation>"); return false; } Node material = getFirstByName(presentation, "material"); question = getMatText(material); if (debug) System.err.println("question: " + question); Node feeditem = getFirstByName(item, "itemfeedback"); boolean haveanswerfeedback = false; while (feeditem != null) { String fident = getAttribute(feeditem, "ident"); if (fident == null) return false; Node matf = getFirstByName(feeditem, "material"); if (matf == null) return false; String feedtext = getMatText(matf); if (feedtext == null) return false; // unlike other types, we're not looking for idents from question processing // feedback can be normal, hint or solution NodeList children = feeditem.getChildNodes(); Node child = null; // set child to first real child of feeditem for (int i = 0; i < children.getLength(); i++) { if (children.item(i).getNodeType() == Node.ELEMENT_NODE) { child = children.item(i); break; } } if (child != null) { String tagName = child.getNodeName(); if (tagName.equalsIgnoreCase("flow_mat") || tagName.equalsIgnoreCase("material")) { feedback = appString(feedback, feedtext); } else if (tagName.equalsIgnoreCase("solution")) { model = appString(model, feedtext); } } feeditem = getNextByName(feeditem, "itemfeedback"); } if (debug) System.err.println("feedback " + feedback); if (debug) System.err.println("model " + model); doHeader(); out.print("<item ident=\""+ident+"\""); if (title != null) out.print(" title=\"" + escapeText(title) + "\""); out.println(">"); out.println(" <itemmetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>qmd_itemtype</fieldlabel>"); out.println(" <fieldentry>Essay</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>TEXT_FORMAT</fieldlabel>"); out.println(" <fieldentry>HTML</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>hasRationale</fieldlabel>"); out.println(" <fieldentry>false</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" </itemmetadata>"); out.println(" <presentation label=\"Model Short Answer\">"); out.println(" <flow class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + question + "]]></mattext>"); out.println(" </material>"); out.println(" <response_lid ident=\"LID01\" rcardinality=\"Single\" rtiming=\"No\">"); out.println(" <render_choice shuffle=\"No\">"); out.println(" <response_label ident=\"A\" rarea=\"Ellipse\" rrange=\"Exact\" rshuffle=\"Yes\">"); out.println(" <material>"); if (model == null) model = ""; out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+model+"]]></mattext>"); out.println(" </material>"); out.println(" </response_label>"); out.println("</render_choice>"); out.println(" </response_lid>"); out.println(" </flow>"); out.println(" </presentation>"); out.println(" <resprocessing>"); out.println(" <outcomes>"); out.println(" <decvar defaultval=\"0\" maxvalue=\"1.0\" minvalue=\"0\" varname=\"SCORE\" vartype=\"Integer\"></decvar>"); out.println(" </outcomes>"); out.println(" </resprocessing>"); paras++; if (feedback != null && !feedback.trim().equals("")) { out.println(" <itemfeedback ident=\""+ident+"\" view=\"All\"><flow_mat class=\"Block\"><material><mattext charset=\"ascii-us\" texttype=\"text/plain\" xml:space=\"default\"><![CDATA["+feedback+"]]></mattext>"); out.println("</material>"); out.println("</flow_mat>"); out.println("</itemfeedback>"); } out.println("</item>"); return true; } boolean procshort(Node item) throws IOException{ if (debug) System.err.println("short"); String title = null; String ident = null; String question = null; List<Shortans> answers = new ArrayList<Shortans>(); // there are only two possible answers, Samigo only supports correct/incorrect // except that essay has only general String icfeedback = null; String cfeedback = null; String gfeedback = null; List<String> cident = new ArrayList<String>(); List<String> icident = new ArrayList<String>(); List<String> gident = new ArrayList<String>(); boolean casesens = false; // case sensitive Double score = 0.0; boolean isPattern = false; for (Node meta = getFirstByName(item, "qtimetadatafield"); meta != null; meta = getNextByName(meta,"qtimetadatafield")) { Node labelNode = getFirstByName(meta, "fieldlabel"); if (labelNode== null) { System.err.println("No fieldlabel for qtimetadatafield"); return false; } String label = getNodeText(labelNode); if (!"cc_profile".equals(label)) continue; Node valueNode = getFirstByName(meta, "fieldentry"); if (valueNode== null) { System.err.println("No fieldentry for qtimetadatafield"); return false; } String value = getNodeText(valueNode); if (value.startsWith("cc.pattern_match")) { isPattern = true; usesPatternMatch = true; break; } } title = getAttribute(item, "title"); ident = getAttribute(item, "ident"); if (debug) System.err.println("title: " + title); Node presentation = getFirstByName(item, "presentation"); if (presentation == null) { System.err.println("can't find <presentation>"); return false; } Node material = getFirstByName(presentation, "material"); question = getMatText(material); // flag pattern match questions as needing review. // no longer. we actually implement them correctly // if (isPattern) // question = bean.getMessageLocator().getMessage("simplepage.import_cc_pattern") + " " + question; if (debug) System.err.println("question: " + question); // the full Qti spec has multiple material and response_str, alternating. // So roses are {} and violets are {} also // is shown as // material: roses are // response_str // material: and violets are // response_str // also // However the CC profile only allows one material and response_str. // thus this loop is unnecessary, but for the moment I'm leaving it Node response = getFirstByName(presentation, "response_str"); while (response != null) { Node fib = getFirstByName(response, "render_fib"); if (fib == null) { System.err.println("No render_fib for response_str"); return false; } String qident = getAttribute(response, "ident"); String rident = null; Node label = getFirstByName(fib, "response_label"); if (label != null) rident = getAttribute(label, "ident"); if (qident == null && rident == null) { System.err.println("No ident for response_label"); return false; } if (debug) System.err.println("blank: " + qident + ":" + rident); answers.add(new Shortans(qident, rident)); response = getNextByName(response,"response_str"); } Node resproc = getFirstByName(item, "resprocessing"); if (presentation == null) { System.err.println("can't find <resprocessing>"); return false; } Node outcomes = getFirstByName(resproc, "outcomes"); if (outcomes != null) { Node decvar = getFirstByName(outcomes, "decvar"); if (decvar != null) { try { String maxval = getAttribute(decvar, "maxvalue"); Double numval = Double.parseDouble(maxval); if (numval > score) score = numval; } catch (Exception ignore) {}; } } Node respcondl = getFirstByName(resproc, "respcondition"); if (respcondl == null) { System.err.println("can't find <respconditionl>"); return false; } if (debug) System.err.println("point 1"); while (respcondl != null) { String contin = getAttribute(respcondl, "continue"); Node conditionvar = getFirstByName(respcondl, "conditionvar"); if (contin.equalsIgnoreCase("Yes") && getFirstByName(conditionvar, "other") != null) { // general feedback Node disfeedback = getFirstByName(respcondl, "displayfeedback"); while (disfeedback != null) { String feedstring = getAttribute(disfeedback, "linkrefid"); if (feedstring != null && feedstring.length() > 0) { cident.add(feedstring); // handle general by both correct/incorrect icident.add(feedstring); } disfeedback = getNextByName(disfeedback, "displayfeedback"); } } else if (getFirstByName(conditionvar, "other") != null) { // incorrect feedback Node disfeedback = getFirstByName(respcondl, "displayfeedback"); while (disfeedback != null) { String feedstring = getAttribute(disfeedback, "linkrefid"); if (feedstring != null && feedstring.length() > 0) icident.add(feedstring); disfeedback = getNextByName(disfeedback, "displayfeedback"); } } else if (conditionvar != null) { // if there's an <or>, use it // now check for both varequal and varsubstring Node varequal = getFirstByName(conditionvar, "varequal"); while (varequal != null) { String vtext = getNodeText(varequal); String vident = getAttribute(varequal, "respident"); String vcase = getAttribute(varequal, "case"); if (vtext != null && vident != null) { for (Shortans ans: answers) { if (ans.qident != null && ans.qident.equals(vident) || ans.rident != null && ans.rident.equals(vident)) { if (ans.answer == null) ans.answer = vtext; else ans.answer = ans.answer + "|" + vtext; if (debug) System.err.println("answer: " + ans.answer); } } if (vcase != null && vcase.equalsIgnoreCase("Yes")) casesens = true; } varequal = getNextByName(varequal, "varequal"); } // and varsubset Node varsubset = getFirstByName(conditionvar, "varsubstring"); while (varsubset != null) { String vtext = getNodeText(varsubset); String vident = getAttribute(varsubset, "respident"); String vcase = getAttribute(varsubset, "case"); if (vtext != null && vident != null) { for (Shortans ans: answers) { if (ans.qident != null && ans.qident.equals(vident) || ans.rident != null && ans.rident.equals(vident)) { if (ans.answer == null) ans.answer = "*" + vtext + "*"; else ans.answer = ans.answer + "|*" + vtext + "*"; } } if (vcase != null && vcase.equalsIgnoreCase("Yes")) casesens = true; } varsubset = getNextByName(varsubset, "varsubset"); } // regex; preserve it to make conversion easier Node varregexp = getFirstByName(conditionvar, "var_extension"); while (varregexp != null) { Node webct = getFirstByName(varregexp, "webct:x_webct_v01_varregex"); if (webct == null) continue; String vtext = getNodeText(webct); String vident = getAttribute(webct, "respident"); String vcase = getAttribute(webct, "case"); if (vtext != null && vident != null) { for (Shortans ans: answers) { if (ans.qident != null && ans.qident.equals(vident) || ans.rident != null && ans.rident.equals(vident)) { if (ans.answer == null) ans.answer = "REGEXP:" + vtext; else ans.answer = ans.answer + "|REGEXP:" + vtext; } } if (vcase != null && vcase.equalsIgnoreCase("Yes")) casesens = true; } varregexp = getNextByName(varregexp, "var_extension"); } // correct feedback Node disfeedback = getFirstByName(respcondl, "displayfeedback"); while (disfeedback != null) { String feedstring = getAttribute(disfeedback, "linkrefid"); if (feedstring != null && feedstring.length() > 0) cident.add(feedstring); disfeedback = getNextByName(disfeedback, "displayfeedback"); } } respcondl = getNextByName(respcondl, "respcondition"); } if (debug) System.err.println("point 2"); Node feeditem = getFirstByName(item, "itemfeedback"); while (feeditem != null) { String fident = getAttribute(feeditem, "ident"); if (fident == null) return false; Node matf = getFirstByName(feeditem, "material"); if (matf == null) return false; String feedtext = getMatText(matf); if (feedtext == null) return false; if (cident.contains(fident)) cfeedback = appString(cfeedback, feedtext); if (icident.contains(fident)) icfeedback = appString(icfeedback, feedtext); feeditem = getNextByName(feeditem, "itemfeedback"); } if (debug) System.err.println("feedback " + cfeedback); doHeader(); out.print("<item ident=\""+ident+"\""); if (title != null) out.print(" title=\"" + escapeText(title) + "\""); out.println(">"); out.println(" <itemmetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>qmd_itemtype</fieldlabel>"); out.println(" <fieldentry>Fill In the Blank</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>TEXT_FORMAT</fieldlabel>"); out.println(" <fieldentry>HTML</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>CASE_SENSITIVE</fieldlabel>"); out.println(" <fieldentry>"+casesens+"</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>hasRationale</fieldlabel>"); out.println(" <fieldentry>false</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" </itemmetadata>"); out.println(" <presentation label=\"FIB\">"); out.println(" <flow class=\"Block\">"); out.println(" <flow class=\"Block\">"); out.println(" <material>"); // there's an issue here. The restricted CC profile is unable to handle a blank in the middle // of the question. So the samples all put ___ in the question to show where the real blank is. // Samigo will then insert a box at the end of the question. I originally thought it would be // nice to put that on a separate line. The probelm is that if we then do a CC export, we've added // junk that wasn't in the original, so repeated export and import keeps adding things. It seems // like it's safest to leave the text alone. //out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + question + "<p> 1. ]]></mattext>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + question + "]]></mattext>"); out.println(" </material>"); // following should not be needed with CC profile // if it is actually needed we need to save the original text for (int i = 2; i <= answers.size(); i++) { out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[<p> "+i+". ]]></mattext>"); out.println(" </material>"); } // this loop should happen only once. for (Shortans ans: answers) { String rident = ans.rident; if (ident == null) ident = ans.qident; out.println("<response_str ident=\""+rident+"\" rcardinality=\"Ordered\" rtiming=\"No\"><render_fib charset=\"ascii-us\" columns=\"20\" encoding=\"UTF_8\" fibtype=\"String\" prompt=\"Box\" rows=\"1\"></render_fib>"); out.println(""); out.println("</response_str>"); out.println(""); out.println("<material><mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[]]></mattext>"); out.println(""); out.println("</material>"); } out.println("</flow>"); out.println("</flow>"); out.println("</presentation>"); if (score <= 0.0) { score = 1.0; noscore++; } out.println(" <resprocessing>"); out.println(" <outcomes>"); out.println(" <decvar defaultval=\"0\" maxvalue=\"" + score + "\" minvalue=\"0\" varname=\"SCORE\" vartype=\"Integer\"></decvar>"); out.println(""); out.println(""); out.println(" </outcomes>"); for (Shortans ans: answers) { String rident = ans.rident; if (rident == null) rident = ans.qident; out.println(" <respcondition continue=\"Yes\"><conditionvar><or><varequal case=\"No\" respident=\""+rident+"\"><![CDATA["+ans.answer+"]]></varequal>"); out.println(""); out.println("</or>"); out.println(""); out.println("</conditionvar>"); out.println(""); out.println("<setvar action=\"Add\" varname=\"SCORE\">"+score/answers.size()+"</setvar>"); out.println(""); out.println("</respcondition>"); } out.println("</resprocessing>"); if (cfeedback != null && !cfeedback.trim().equals("")) { out.println(" <itemfeedback ident=\"Correct\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+cfeedback+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); } if (icfeedback != null && !icfeedback.trim().equals("")) { out.println(" <itemfeedback ident=\"InCorrect\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+icfeedback+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); } out.println("</item>"); return true; } boolean procmc(Node item, boolean truefalse) throws IOException{ if (debug) System.err.println("mc"); String title = null; String ident = null; String rlident = null; String question = null; List<Mcans> answers = new ArrayList<Mcans>(); String feedback = null; String icfeedback = null; String cfeedback = null; List<String> fbident = new ArrayList<String>(); List<String> icident = new ArrayList<String>(); List<String> cident = new ArrayList<String>(); Double score = 0.0; boolean scoreset = false; title = getAttribute(item, "title"); ident = getAttribute(item, "ident"); if (debug) System.err.println("title: " + title); Node presentation = getFirstByName(item, "presentation"); if (presentation == null) { System.err.println("can't find <presentation>"); return false; } Node material = getFirstByName(presentation, "material"); question = getMatText(material); if (debug) System.err.println("question: " + question); Node response = getFirstByName(presentation, "response_lid"); if (response == null) { System.err.println("No response_lid"); return false; } String rcardinality = getAttribute(response, "rcardinality"); Node choice = getFirstByName(response, "render_choice"); if (choice == null) { System.err.println("No render_choice"); return false; } Node label = getFirstByName(choice, "response_label"); if (label == null) { System.err.println("No response_label"); return false; } byte [] newident = {65}; while (label != null) { String lident = getAttribute(label, "ident"); Node lmaterial = getFirstByName(label, "material"); String ltext = getMatText(lmaterial); if (lident != null && ltext != null) { if (debug) System.err.println("answer " + lident + " " + newident + " " + ltext); answers.add(new Mcans(lident, new String(newident), ltext)); newident[0]++; } label = getNextByName(label, "response_label"); } Node resproc = getFirstByName(item, "resprocessing"); if (resproc == null) { System.err.println("can't find <resprocessing>"); return false; } Node outcomes = getFirstByName(resproc, "outcomes"); if (outcomes != null) { Node decvar = getFirstByName(outcomes, "decvar"); if (decvar != null) { try { String maxval = getAttribute(decvar, "maxvalue"); Double numval = Double.parseDouble(maxval); if (numval > score) { score = numval; scoreset = true; if (debug) System.err.println("set score " + score); } } catch (Exception ignore) {}; } } Node respcondl = getFirstByName(resproc, "respcondition"); if (respcondl == null) { System.err.println("can't find <respconditionl>"); return false; } int numcorrect = 0; while (respcondl != null) { String rctitle = getAttribute(respcondl, "title"); String contin = getAttribute(respcondl, "continue"); Node conditionvar = getFirstByName(respcondl, "conditionvar"); if (contin.equalsIgnoreCase("Yes") && getFirstByName(conditionvar, "other") != null) { // general feedback Node disfeedback = getFirstByName(respcondl, "displayfeedback"); while (disfeedback != null) { String feedstring = getAttribute(disfeedback, "linkrefid"); if (feedstring != null && feedstring.length() > 0) fbident.add(feedstring); disfeedback = getNextByName(disfeedback, "displayfeedback"); } } else if (getFirstByName(conditionvar, "other") != null) { // incorrect feedback Node disfeedback = getFirstByName(respcondl, "displayfeedback"); while (disfeedback != null) { String feedstring = getAttribute(disfeedback, "linkrefid"); if (feedstring != null && feedstring.length() > 0) icident.add(feedstring); disfeedback = getNextByName(disfeedback, "displayfeedback"); } } else if (conditionvar != null) { // normal alternative. can't do much if no conditionvar // in this profile, the only possibilities are a simple varequal, an <or> of varequals, or an <and> of varequal and <not> varequal. Node firsttest = getNextElement(conditionvar.getFirstChild()); Node varequal = null; if (firsttest.getNodeName().equals("varequal")) varequal = firsttest; // just one varequal. use it else if (firsttest.getNodeName().equals("or")) varequal = getNextElement(firsttest.getFirstChild()); // or, use all of its children else if (firsttest.getNodeName().equals("and")) { varequal = firsttest.getFirstChild(); // and, has both varequal and not under it. if (!varequal.getNodeName().equals("varequal")) varequal = getNextByName(varequal,"varequal"); // first next must be a <not>, find first varequal } while (varequal != null) { String vtext = getNodeText(varequal); // don't use the respident because there's only one variable Mcans answer = null; Mcans altanswer = null; for (Mcans ans: answers) { if (vtext != null && vtext.equals(ans.ident)) { answer = ans; break; } // this is absolutely wrong, but some of the validation tests do it for true/false if (vtext != null && vtext.equalsIgnoreCase(ans.answer)) { altanswer = ans; break; } } if (answer == null) { if (altanswer != null) { System.err.println("id in respcondition not matching question " + question + " but answer matches "); answer = altanswer; } else { System.err.println("id in respcondition not matching question " + question); break; } } // for this profile correct answer is always // <setvar action="Set" varname="SCORE">100</setvar> Node setvar = getFirstByName(respcondl, "setvar"); if (setvar != null && getAttribute(setvar, "action").equalsIgnoreCase("set") && getAttribute(setvar, "varname").equalsIgnoreCase("score") && "100".equals(getNodeText(setvar))) { answer.correct = true; numcorrect++; } // look for answer specific feedback // yes this is inside the varequal loop. if more than one answer is // being tested need to give this feedback to them all Node disfeedback = getFirstByName(respcondl, "displayfeedback"); // correct actually goes in answewr-specific. The only way I see to // distinguish is the label. Yuck. while (disfeedback != null) { String feedstring = getAttribute(disfeedback, "linkrefid"); if (feedstring != null && feedstring.equals("correct_fb")) { // do this only once if (numcorrect == 1) cident.add(feedstring); } else if (feedstring != null) answer.fbident.add(feedstring); disfeedback = getNextByName(disfeedback, "displayfeedback"); } varequal = getNextByName(varequal, "varequal"); } } respcondl = getNextByName(respcondl, "respcondition"); } // in answers, add actual feedback text Node feeditem = getFirstByName(item, "itemfeedback"); boolean haveanswerfeedback = false; while (feeditem != null) { String fident = getAttribute(feeditem, "ident"); if (fident == null) return false; Node matf = getFirstByName(feeditem, "material"); if (matf == null) return false; String feedtext = getMatText(matf); if (feedtext == null) return false; if (fbident.contains(fident)) feedback = appString(feedback, feedtext); if (icident.contains(fident)) icfeedback = appString(icfeedback, feedtext); if (cident.contains(fident)) cfeedback = appString(icfeedback, feedtext); for (Mcans ans: answers) { if (ans.fbident.contains(fident)){ if (truefalse) { // samigo doesn't seem to use item feedback for truefalse if (ans.correct) cfeedback = appString(cfeedback, feedtext); else icfeedback = appString(icfeedback, feedtext); } else { if (!feedtext.trim().equals("")) haveanswerfeedback = true; ans.feedback = appString(ans.feedback, feedtext); } } } feeditem = getNextByName(feeditem, "itemfeedback"); } if (debug) System.err.println("feedback " + feedback); doHeader(); out.print("<item ident=\""+ident+"\""); if (title != null) out.print(" title=\"" + escapeText(title) + "\""); out.println(">"); out.println(" <itemmetadata>"); out.println(" <qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>qmd_itemtype</fieldlabel>"); if (truefalse) out.println(" <fieldentry>True False</fieldentry>"); else if (rcardinality != null && rcardinality.toLowerCase().equals("multiple")) out.println(" <fieldentry>Multiple Correct Answer</fieldentry>"); else if (numcorrect > 1) out.println(" <fieldentry>Multiple Correct Single Selection</fieldentry>"); else out.println(" <fieldentry>Multiple Choice</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>hasRationale</fieldlabel>"); out.println(" <fieldentry>false</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>TEXT_FORMAT</fieldlabel>"); out.println(" <fieldentry>HTML</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>RANDOMIZE</fieldlabel>"); out.println(" <fieldentry>true</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" </qtimetadata>"); out.println(" </itemmetadata>"); out.println(" <presentation>"); out.println(" <flow class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA[" + question + "]]></mattext>"); out.println(" </material>"); if (rcardinality != null) out.println(" <response_lid ident=\"MCSC\" rcardinality=\"" + rcardinality + "\" rtiming=\"No\">"); else out.println(" <response_lid ident=\"MCSC\" rcardinality=\"Single\" rtiming=\"No\">"); out.println(" <render_choice shuffle=\"No\">"); for (Mcans ans:answers) { out.println(" <response_label ident=\""+ans.newident+"\" rarea=\"Ellipse\" rrange=\"Exact\" rshuffle=\"Yes\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+ans.answer+"]]></mattext>"); out.println(" </material>"); out.println(" </response_label>"); } out.println("</render_choice>"); out.println(" </response_lid>"); out.println(" </flow>"); out.println(" </presentation>"); out.println(" <resprocessing>"); if (score <= 0.0) { score = 1.0; noscore++; } out.println(" <outcomes>"); out.println(" <decvar defaultval=\"0\" maxvalue=\"" + score + "\" minvalue=\"0\" varname=\"SCORE\" vartype=\"Integer\"></decvar>"); out.println(" </outcomes>"); // if we have any answer specific feedback we have to set it for // all, or the input parser blows up for (Mcans ans:answers) { if (ans.correct) out.println(" <respcondition continue=\"No\" title=\"Correct\">"); else out.println(" <respcondition continue=\"No\">"); out.println(" <conditionvar>"); out.println(" <varequal case=\"Yes\" respident=\"MCSC\">"+ans.newident+"</varequal>"); out.println(" </conditionvar>"); if (ans.correct) out.println(" <setvar action=\"Add\" varname=\"SCORE\">" + score + "</setvar>"); out.println(" <displayfeedback feedbacktype=\"Response\" linkrefid=\""+(ans.correct?"Correct":"Incorrect")+"\"></displayfeedback>"); if (haveanswerfeedback) { out.println(" <displayfeedback feedbacktype=\"Response\" linkrefid=\"AnswerFeedback\"><![CDATA["+(ans.feedback!=null?ans.feedback:"")+"]]></displayfeedback>"); // this is what the QTI spec would call for, but above seems to be what samigo wants // out.println(" <displayfeedback feedbacktype=\"Response\" linkrefid=\""+ans.newident+"1\"></displayfeedback>"); } out.println(" </respcondition>"); } out.println(" </resprocessing>"); if (feedback != null && !feedback.trim().equals("") || cfeedback != null && !cfeedback.trim().equals("")) { out.println(" <itemfeedback ident=\"Correct\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+appString(cfeedback, feedback)+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); } if (feedback != null && !feedback.trim().equals("") || icfeedback != null && !icfeedback.trim().equals("")) { out.println(" <itemfeedback ident=\"InCorrect\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+appString(icfeedback, feedback)+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); } if (false) { // this is what we'd expect from QTI, but Samigo doesn't seem to want it if (haveanswerfeedback) { for (Mcans ans:answers) { out.println(" <itemfeedback ident=\""+ans.newident+"1\" view=\"All\">"); out.println(" <flow_mat class=\"Block\">"); out.println(" <material>"); out.println(" <mattext charset=\"ascii-us\" texttype=\"text/html\" xml:space=\"default\"><![CDATA["+ans.feedback+"]]></mattext>"); out.println(" </material>"); out.println(" </flow_mat>"); out.println(" </itemfeedback>"); } } } out.println("</item>"); return true; } boolean procitem(Node item)throws IOException { Node resproc = getFirstByName(item, "resprocessing"); if (resproc == null) { System.err.println("Can't find <resprocessing>"); } if (resproc != null) { Node extension = getFirstByName(resproc, "itemproc_extension"); if (extension != null) { if (debug) System.err.println("Item is a WebCT extension, can't process"); return false; } } Node qticomment = null; if (resproc != null) qticomment = getFirstByName(resproc, "qticomment"); if (qticomment == null) { String type = guessQuestionType(item); if (debug) System.err.println("main we guess type " + type); // matching not used in CC, not tested if (type.equalsIgnoreCase("Matching")) return procmatch(item); else if (type.equalsIgnoreCase("Short Answers/Essay")) return procpara(item); // pattern match will be seen as FIB, which is best we can do else if (type.equalsIgnoreCase("Fill In the Blank")) return procshort(item); else if (type.equalsIgnoreCase("Multiple Choice")) return procmc(item, false); else if (type.equalsIgnoreCase("Multiple Correct")) return procmc(item, false); else if (type.equalsIgnoreCase("True False")) return procmc(item, true); else { if (debug) System.err.println("type not matched"); return false; } } String commenttext = getNodeText(qticomment); if (commenttext == null) { System.err.println("No text in <qticomment>"); return false; } if (commenttext.startsWith("Match Question")) return procmatch(item); else if (commenttext.startsWith("Paragraph Question")) return procpara(item); else if (commenttext.startsWith("Short answer Question")) return procshort(item); else if (commenttext.startsWith("Multiple Choice Question")) return procmc(item, false); return false; } int fileno = -1; int identno = 1; int partno = 1; public void doHeader() throws IOException { if (!needHeader) return; fileno++; out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); out.println("<questestinterop>"); out.println(" <assessment ident=\"samigo" + identno + "\" title=\"" + escapeText(title + (partno == 1 ? "" : (" Part " + partno))) + "\">"); identno++; out.println(""); if (timelimit != null && !timelimit.equals("")) out.println("<duration>PT" + timelimit + "M</duration>"); out.println("<qtimetadata>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_DELIVERY</fieldlabel>"); out.println(" <fieldentry>" + (feedbackpermitted ? "ON_SUBMISSION" : "NONE") + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>ASSESSMENT_RELEASED_TO</fieldlabel>"); out.println(" <fieldentry>" + escapeText(siteId) + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_COMPONENT_OPTION</fieldlabel>"); out.println(" <fieldentry>" + (feedbackpermitted ? "SELECT_COMPONENTS" : "SHOW_TOTALSCORE_ONLY") + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_ITEM_LEVEL</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_STUDENT_SCORE</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_QUESTION</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_RESPONSE</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_GRADER_COMMENT</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_STATS</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>FEEDBACK_SHOW_SELECTION_LEVEL</fieldlabel>"); out.println(" <fieldentry>" + feedbackpermitted + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>LATE_HANDLING</fieldlabel>"); out.println(" <fieldentry>" + allowlate + "</fieldentry>"); out.println(" </qtimetadatafield>"); if (maxattempts != null && !maxattempts.equals("")) { if (maxattempts.equalsIgnoreCase("unlimited")) maxattempts = "9999"; } out.println(" <qtimetadatafield>"); out.println(" <fieldlabel>MAX_ATTEMPTS</fieldlabel>"); out.println(" <fieldentry>" + maxattempts + "</fieldentry>"); out.println(" </qtimetadatafield>"); out.println("</qtimetadata>"); out.println(""); out.println(" <section ident=\"samigo"+identno+"\" title=\"Default\">"); identno++; needHeader = false; } public String getChars () { if (out != null) return charout.toString(); else return null; } public String getTitle () { return title; } public int getNoScore () { return noscore; } public int getParas () { return paras; } public boolean getUsesCurriculum() { return usesCurriculum; } public boolean mainproc (InputStream i, PrintWriter o, boolean isBank, String base, String s, SimplePageBean b) throws IOException { out = o; filebase = base; siteId = s; bean = b; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.parse(i); NodeList sections = null; if (isBank) { sections = document.getElementsByTagName("objectbank"); title = "Imported Pool"; } else { NodeList assessments = document.getElementsByTagName("assessment"); Node assessment = assessments.item(0); title = getAttribute(assessment, "title"); sections = document.getElementsByTagName("section"); Node md = getFirstByName(assessment, "qtimetadata"); if (md != null) { NodeList mds = ((Element)md).getElementsByTagName("qtimetadatafield"); if (mds != null) { for (int n = 0; n < mds.getLength(); n++) { Node item = mds.item(n); if (item == null) continue; Node l = getFirstByName(item, "fieldlabel"); String label = (l == null ? null : getNodeText(l)); Node e = getFirstByName(item, "fieldentry"); String entry = (e == null ? null : getNodeText(e)); if ("qmd_feedbackpermitted".equals(label)) feedbackpermitted = "yes".equalsIgnoreCase(entry); else if ("qmd_timelimit".equals(label)) timelimit = entry; else if ("cc_allow_late_submissions".equals(label)) allowlate = "yes".equalsIgnoreCase(entry); else if ("cc_maxattempts".equals(label)) maxattempts = entry; } } } } Node section; int numsections; if (sections == null) numsections = 0; else numsections = sections.getLength(); int questionno = 0; for (int sectionno=0; sectionno < numsections; sectionno++) { section = sections.item(sectionno); if (debug) System.err.println("section"); NodeList items = ((Element)section).getElementsByTagName("item"); int numitems; Node item; if (items == null) numitems = 0; else numitems = items.getLength(); for (int itemno=0; itemno < numitems; itemno++) { item = items.item(itemno); String newtitle = getAttribute(section, "title"); if (newtitle == null || newtitle.equals("")) newtitle = "Respondus Converted Questions"; Node md = getFirstByName(item, "itemmetadata"); if (md != null && getFirstByName(md, "curriculumStandardsMetadataSet") != null) usesCurriculum = true; // for the moment can only write one section if (false) { if (title == null || !title.equals(newtitle)) { if (out != null) { out.println("</section>"); out.println("</assessment>"); out.println("</questestinterop>"); out.close(); } needHeader = true; partno = 1; questionno = 0; title = newtitle; } } // only count question if it is put to the output if (procitem(item)) questionno++; if (false) { if (questionno >= 100) { out.println("</section>"); out.println("</assessment>"); out.println("</questestinterop>"); out.close(); needHeader = true; partno++; questionno = 0; } } item = item.getNextSibling(); } } // if there are no valid items, still need the header. This will // only output the header if it hasn't been done already doHeader(); if (out != null) { out.println("</section>"); out.println("</assessment>"); out.println("</questestinterop>"); out.close(); } } catch (SAXException sxe) { // Error generated during parsing Exception x = sxe; if (sxe.getException() != null) x = sxe.getException(); x.printStackTrace(); } catch (ParserConfigurationException pce) { // Parser with specified options can't be built pce.printStackTrace(); } catch (IOException ioe) { // I/O error ioe.printStackTrace(); } return usesPatternMatch; } }