Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -rbbcfb64b7bb6d251110a5a26299604958c4bc5b2 -r65ed4da382b320542dcd88cb1375662dcfac8769 Binary files differ Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rbbcfb64b7bb6d251110a5a26299604958c4bc5b2 -r65ed4da382b320542dcd88cb1375662dcfac8769 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision bbcfb64b7bb6d251110a5a26299604958c4bc5b2) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -394,4 +394,9 @@ label.tab.conditions.timelimit.individual =Is this time limit for each individual? label.tab.conditions.enable =Enable label.tab.conditions.dependencies.desc =Select a lesson that students will need to complete before they can see the lesson you are about to create. +label.questions.file.title =Choose IMS QTI file +label.questions.file.missing =Please select a ZIP file with questions in IMS QTI format. +label.questions.choice.title =Choose questions +label.questions.choice.select.all =Select all +label.questions.choice.missing =Please check at least one question. #======= End labels: Exported 345 labels for en AU ===== Index: lams_central/src/java/org/lamsfoundation/lams/web/QuestionsAction.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/QuestionsAction.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/QuestionsAction.java (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -0,0 +1,72 @@ +package org.lamsfoundation.lams.web; + +import java.io.InputStream; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.DiskFileUpload; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.lang.StringUtils; +import org.apache.struts.Globals; +import org.apache.struts.action.Action; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.apache.struts.action.ActionMessage; +import org.apache.struts.action.ActionMessages; +import org.lamsfoundation.lams.questions.Question; +import org.lamsfoundation.lams.questions.QuestionParser; +import org.lamsfoundation.lams.util.Configuration; +import org.lamsfoundation.lams.util.ConfigurationKeys; + +/** + * Runs extraction of chosen IMS QTI zip file and prepares form for user to manually choose interesting question. + * + * @struts.action path="/questions" validate="false" + * @struts.action-forward name="questionChoice" path="/questionChoice.jsp" + * @struts.action-forward name="questionFile" path="/questionFile.jsp" + */ +public class QuestionsAction extends Action { + @SuppressWarnings("unchecked") + @Override + public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws Exception { + // cumberstone way of extracting POSTed form with a file + DiskFileUpload formParser = new DiskFileUpload(); + formParser.setRepositoryPath(Configuration.get(ConfigurationKeys.LAMS_TEMP_DIR)); + List formFields = formParser.parseRequest(request); + + String returnURL = null; + Boolean chooseAnswers = null; + InputStream packageFileStream = null; + for (FileItem formField : formFields) { + String fieldName = formField.getFieldName(); + if ("returnURL".equals(fieldName)) { + // this can be empty; if so, another method of delivering results is used + returnURL = formField.getString(); + } else if ("chooseAnswers".equals(fieldName)) { + chooseAnswers = Boolean.parseBoolean(formField.getString()); + } else if ("file".equals(fieldName) && !StringUtils.isBlank(formField.getName())) { + packageFileStream = formField.getInputStream(); + } + } + + request.setAttribute("returnURL", returnURL); + request.setAttribute("chooseAnswers", chooseAnswers); + + // user did not choose a file + if (packageFileStream == null) { + ActionMessages errors = new ActionMessages(); + errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("label.questions.file.missing")); + request.setAttribute(Globals.ERROR_KEY, errors); + return mapping.findForward("questionFile"); + } + + Question[] questions = QuestionParser.parseQTIPackage(packageFileStream); + request.setAttribute("questions", questions); + + return mapping.findForward("questionChoice"); + } +} \ No newline at end of file Index: lams_central/web/questionChoice.jsp =================================================================== diff -u --- lams_central/web/questionChoice.jsp (revision 0) +++ lams_central/web/questionChoice.jsp (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -0,0 +1,169 @@ + + +<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %> +<%@ taglib uri="tags-lams" prefix="lams" %> +<%@ taglib uri="tags-fmt" prefix="fmt" %> +<%@ taglib uri="tags-core" prefix="c" %> +<%@ taglib uri="tags-function" prefix="fn" %> + + + + <fmt:message key="title.lams" /> :: <fmt:message key="label.questions.choice.title" /> + + + + + + + + +
+
+ +
+ +

+ +

+ +
+ + + + + + ${questionStatus.index + 1}.
+ + + + +
+ + + +
+ + + +
+
+
+
+ +
+ + +
+
+
+ + +
\ No newline at end of file Index: lams_central/web/questionFile.jsp =================================================================== diff -u --- lams_central/web/questionFile.jsp (revision 0) +++ lams_central/web/questionFile.jsp (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -0,0 +1,62 @@ + + +<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=utf-8" %> +<%@ taglib uri="tags-lams" prefix="lams" %> +<%@ taglib uri="tags-fmt" prefix="fmt" %> +<%@ taglib uri="tags-html" prefix="html" %> +<%@ taglib uri="tags-core" prefix="c"%> + + + + <fmt:message key="title.lams" /> :: <fmt:message key="label.questions.file.title" /> + + + + + + + + + +
+
+ +
+
+
+

+ +
+ + + + +
+ + +
+
+
+ + +
\ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/questions/Answer.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/questions/Answer.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/questions/Answer.java (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -0,0 +1,55 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.questions; + + +public class Answer { + private String text; + private String feedback; + private Float score; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getFeedback() { + return feedback; + } + + public void setFeedback(String feedback) { + this.feedback = feedback; + } + + public Float getScore() { + return score; + } + + public void setScore(Float score) { + this.score = score; + } +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/questions/Question.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/questions/Question.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/questions/Question.java (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -0,0 +1,56 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.questions; + +import java.util.List; + +public class Question { + private String text; + private String feedback; + private List answers; + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public String getFeedback() { + return feedback; + } + + public void setFeedback(String feedback) { + this.feedback = feedback; + } + + public List getAnswers() { + return answers; + } + + public void setAnswers(List answers) { + this.answers = answers; + } +} \ No newline at end of file Index: lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java =================================================================== diff -u --- lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java (revision 0) +++ lams_common/src/java/org/lamsfoundation/lams/questions/QuestionParser.java (revision 65ed4da382b320542dcd88cb1375662dcfac8769) @@ -0,0 +1,308 @@ +/**************************************************************** + * Copyright (C) 2005 LAMS Foundation (http://lamsfoundation.org) + * ============================================================= + * License Information: http://lamsfoundation.org/licensing/lams/2.0/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA + * + * http://www.gnu.org/licenses/gpl.txt + * **************************************************************** + */ + +/* $Id$ */ +package org.lamsfoundation.lams.questions; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.util.WebUtil; +import org.lamsfoundation.lams.util.zipfile.ZipFileUtil; +import org.lamsfoundation.lams.util.zipfile.ZipFileUtilException; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +/** + * Extract questions and answers from various formats. They can be later used in question-based tools. Currently it + * supports only IMS QTI but other methods can be added as needed. + * + * @author Marcin Cieslak + * + */ +public class QuestionParser { + private static Logger log = Logger.getLogger(QuestionParser.class); + + // just for convenience when returning from methods + private static final Question[] QUESTION_ARRAY_TYPE = new Question[] {}; + // can be anything + private static final String TEMP_PACKAGE_NAME_PREFIX = "QTI_PACKAGE_"; + + /** + * Extracts questions from IMS QTI zip file. + */ + public static Question[] parseQTIPackage(InputStream packageFileStream) throws SAXParseException, IOException, + SAXException, ParserConfigurationException, ZipFileUtilException { + List result = new ArrayList(); + + // unique folder name + String tempPackageName = QuestionParser.TEMP_PACKAGE_NAME_PREFIX + System.currentTimeMillis(); + String tempPackageDirPath = ZipFileUtil.expandZip(packageFileStream, tempPackageName); + try { + List resourceFiles = QuestionParser.getQTIResourceFiles(tempPackageDirPath); + if (resourceFiles.isEmpty()) { + QuestionParser.log.warn("No resource files found in QTI package"); + } else { + // extract from every XML file; usually there is just one + for (File resourceFile : resourceFiles) { + Question[] fileQuestions = QuestionParser.parseQTIFile(resourceFile); + if (fileQuestions != null) { + Collections.addAll(result, fileQuestions); + } + } + } + } finally { + // clean up + ZipFileUtil.deleteDirectory(tempPackageDirPath); + } + + return result.toArray(QuestionParser.QUESTION_ARRAY_TYPE); + } + + /** + * Extracts questions from IMS QTI xml file. + */ + public static Question[] parseQTIFile(File xmlFile) throws ParserConfigurationException, SAXException, IOException { + List result = new ArrayList(); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = docBuilder.parse(xmlFile); + + NodeList questionItems = doc.getElementsByTagName("item"); + // yes, a label here for convenience + questionLoop: for (int questionItemIndex = 0; questionItemIndex < questionItems.getLength(); questionItemIndex++) { + Element questionItem = (Element) questionItems.item(questionItemIndex); + NodeList questionItemTypes = questionItem.getElementsByTagName("qmd_itemtype"); + String questionItemType = questionItemTypes.getLength() > 0 ? ((Text) questionItemTypes.item(0) + .getChildNodes().item(0)).getData() : null; + if ("Matching".equalsIgnoreCase(questionItemType)) { + // do not support matching question type + continue; + } + + Question question = new Question(); + Map answerMap = new TreeMap(); + + Element presentation = (Element) questionItem.getElementsByTagName("presentation").item(0); + NodeList presentationChildrenList = presentation.getChildNodes(); + // cumberstone parsing, but there is no other way using this API + for (int presentationChildIndex = 0; presentationChildIndex < presentationChildrenList.getLength(); presentationChildIndex++) { + Node presentationChild = presentationChildrenList.item(presentationChildIndex); + // here is where question data is stored + if ("material".equals(presentationChild.getNodeName())) { + Element questionElement = (Element) ((Element) presentationChild).getElementsByTagName("mattext") + .item(0); + String questionText = ((CDATASection) questionElement.getChildNodes().item(0)).getData(); + if (questionText.trim().startsWith("