Index: sam/component/src/java/org/sakaiproject/tool/assessment/qti/helper/AuthoringHelper.java =================================================================== --- sam/component/src/java/org/sakaiproject/tool/assessment/qti/helper/AuthoringHelper.java (revision 18192) +++ sam/component/src/java/org/sakaiproject/tool/assessment/qti/helper/AuthoringHelper.java (working copy) @@ -26,6 +26,8 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; @@ -45,16 +47,20 @@ import org.xml.sax.SAXException; import org.sakaiproject.tool.assessment.data.dao.assessment.ItemMetaData; +import org.sakaiproject.tool.assessment.data.dao.questionpool.QuestionPoolItemData; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentAccessControlIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentFeedbackIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.AssessmentMetaDataIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemDataIfc; import org.sakaiproject.tool.assessment.data.ifc.assessment.SectionDataIfc; +import org.sakaiproject.tool.assessment.data.ifc.questionpool.QuestionPoolItemIfc; import org.sakaiproject.tool.assessment.data.ifc.shared.TypeIfc; +import org.sakaiproject.tool.assessment.data.model.Tree; import org.sakaiproject.tool.assessment.facade.AgentFacade; import org.sakaiproject.tool.assessment.facade.AssessmentFacade; import org.sakaiproject.tool.assessment.facade.ItemFacade; +import org.sakaiproject.tool.assessment.facade.QuestionPoolFacade; import org.sakaiproject.tool.assessment.facade.SectionFacade; import org.sakaiproject.tool.assessment.qti.asi.Assessment; import org.sakaiproject.tool.assessment.qti.asi.Item; @@ -65,6 +71,7 @@ import org.sakaiproject.tool.assessment.qti.helper.section.SectionHelperIfc; import org.sakaiproject.tool.assessment.qti.util.XmlStringBuffer; import org.sakaiproject.tool.assessment.services.ItemService; +import org.sakaiproject.tool.assessment.services.QuestionPoolService; import org.sakaiproject.tool.assessment.services.assessment.AssessmentService; import org.sakaiproject.tool.assessment.qti.util.XmlUtil; import java.util.Set; @@ -453,7 +460,8 @@ public AssessmentFacade createImportedAssessment(Document document) { AssessmentFacade assessment = null; - + AssessmentService assessmentService = new AssessmentService(); + try { // we need to know who we are @@ -461,7 +469,6 @@ // create the assessment ExtractionHelper exHelper = new ExtractionHelper(this.qtiVersion); - AssessmentService assessmentService = new AssessmentService(); ItemService itemService = new ItemService(); Assessment assessmentXml = new Assessment(document); Map assessmentMap = exHelper.mapAssessment(assessmentXml); @@ -575,7 +582,129 @@ return assessment; } + /** + * Import an assessment XML document in QTI format, extract & persist the data. + * import process assumes assessment structure, not objectbank or itembank + * @param document the assessment XML document in QTI format + * @return a persisted assessment + */ + public QuestionPoolFacade createImportedQuestionPool(Document document) + { + QuestionPoolFacade questionpool = new QuestionPoolFacade(); + QuestionPoolService questionPoolService = new QuestionPoolService(); + + try + { + // identify user to assign as question pool owner + String me = AgentFacade.getAgentString(); + + // create the questionpool as an assessment + ExtractionHelper exHelper = new ExtractionHelper(this.qtiVersion); + ItemService itemService = new ItemService(); + Assessment assessmentXml = new Assessment(document); + Map assessmentMap = exHelper.mapAssessment(assessmentXml); + String title = (String) assessmentMap.get("title"); + + // save questionpool with required info only at this point + questionpool.setOwnerId(me); + questionpool.setTitle(title); + questionpool.setLastModifiedById(me); + questionpool.setAccessTypeId(QuestionPoolFacade.ACCESS_DENIED); // set as default + questionpool = questionPoolService.savePool(questionpool); + // update the remaining questionpool properties + exHelper.updateQuestionPool(questionpool, assessmentMap); + + // now make sure we have a unique name for the question pool + String baseId = questionpool.getQuestionPoolId().toString(); + boolean isUnique=questionPoolService.poolIsUnique(baseId,title,"0"); + + // if the title is not unique, increment with a number per renameDuplicate() + if (!isUnique) { + + synchronized (title) + { + log.debug("Questionpool "+ title + " is not unique."); + int count = 0; // alternate exit condition + + while (!isUnique) + { + title = exHelper.renameDuplicate(title); + log.debug("renameDuplicate(title): " + title); + questionpool.setTitle(title); + //recheck to confirm that new title is not a duplicate too + isUnique = questionPoolService.poolIsUnique(baseId,title,"0"); + if (count++ > 99) break;// exit condition in case bug is introduced + } + } + } + + + // process each section and each item within assessment each section + List sectionList = exHelper.getSectionXmlList(assessmentXml); + int sectionListSize = sectionList.size(); + int sec = sectionListSize-1; + log.debug("sections=" + sectionListSize); + + // initialize setQuestionPoolItems so items can be added + Set itemSet = new HashSet(); + questionpool.setQuestionPoolItems(itemSet); + + // use case for single section + // most common for Respondus & BB migrations + if (sectionListSize == 1) + { + Section sectionXml = (Section) sectionList.get(sec); + Map sectionMap = exHelper.mapSection(sectionXml); + // for single section, do not create subpool + + List itemList = exHelper.getItemXmlList(sectionXml); + for (int itm = 0; itm < itemList.size(); itm++) // for each item + { + log.debug("items=" + itemList.size()); + Item itemXml = (Item) itemList.get(itm); + Map itemMap = exHelper.mapItem(itemXml); + + ItemFacade item = new ItemFacade(); + exHelper.updateItem(item, itemMap); + // make sure required fields are set + item.setCreatedBy(me); + item.setCreatedDate(questionpool.getLastModified()); + item.setLastModifiedBy(me); + item.setLastModifiedDate(questionpool.getLastModified()); + item.setStatus(ItemDataIfc.ACTIVE_STATUS); + itemService.saveItem(item); + + // QuestionPoolItemData is used for later Sakai versions + //QuestionPoolItemData questionPoolItem = new QuestionPoolItemData(); + //questionPoolItem.setQuestionPoolId(questionpool.getQuestionPoolId()); + //questionPoolItem.setItemId(item.getItemIdString()); + //questionpool.addQuestionPoolItem((QuestionPoolItemIfc) questionPoolItem); + questionPoolService.addItemToPool(item.getItemIdString(),questionpool.getQuestionPoolId()); + + + } // ... end for each item + + } + // need error message if more than one section, for now + + + // update the questionpoool with all sections and items + questionPoolService.savePool(questionpool); + + return questionpool; + } + catch (Exception e) + { + log.error(e.getMessage(), e); + Tree tree = null; + questionPoolService.deletePool(questionpool.getQuestionPoolId(), AgentFacade.getAgentString(), tree); + throw new RuntimeException(e); + } + } + + + /** * @deprecated * Import an item XML document in QTI format, extract & persist the data. * @param document the item XML document in QTI format Index: sam/component/src/java/org/sakaiproject/tool/assessment/qti/helper/ExtractionHelper.java =================================================================== --- sam/component/src/java/org/sakaiproject/tool/assessment/qti/helper/ExtractionHelper.java (revision 18192) +++ sam/component/src/java/org/sakaiproject/tool/assessment/qti/helper/ExtractionHelper.java (working copy) @@ -55,6 +55,7 @@ import org.sakaiproject.tool.assessment.data.ifc.shared.TypeIfc; import org.sakaiproject.tool.assessment.facade.AssessmentFacade; import org.sakaiproject.tool.assessment.facade.ItemFacade; +import org.sakaiproject.tool.assessment.facade.QuestionPoolFacade; import org.sakaiproject.tool.assessment.facade.SectionFacade; import org.sakaiproject.tool.assessment.qti.asi.ASIBaseClass; import org.sakaiproject.tool.assessment.qti.asi.Assessment; @@ -945,7 +946,34 @@ } } + /** + * Update questionpool from the extracted properties. + * Note: you need to do a save when you are done. + * @param questionpool, which will be persisted + * @param assessmentMap, the extracted properties + */ + public void updateQuestionPool(QuestionPoolFacade questionpool, + Map assessmentMap) + { + + String title = ((String)assessmentMap.get("title")); + questionpool.setDescription((String)assessmentMap.get("description")); + //questionpool.setLastModifiedById("Sakai Import"); + questionpool.setLastModified(new Date()); + // note: currently dateCreated field not in use + //questionpool.setDateCreated(new Date()); + questionpool.setOrganizationName((String)assessmentMap.get("ASSESSMENT_ORGANIZATIONNAME")); + questionpool.setObjectives((String)assessmentMap.get("ASSESSMENT_OBJECTIVES")); + questionpool.setKeywords((String)assessmentMap.get("ASSESSMENT_KEYWORDS")); + questionpool.setRubric((String)assessmentMap.get("ASSESSMENT_RUBRICS")); + questionpool.setIntellectualPropertyId((Long)assessmentMap.get("INTELLECTUALPROPERTYID")); + + log.debug("QPOOL ASSESSMENT updating metadata information"); + + } + + /** * Update section from the extracted properties. * Note: you need to do a save when you are done. * @param section the section, which will be persisted Index: sam/component/src/java/org/sakaiproject/tool/assessment/services/qti/QTIService.java =================================================================== --- sam/component/src/java/org/sakaiproject/tool/assessment/services/qti/QTIService.java (revision 18192) +++ sam/component/src/java/org/sakaiproject/tool/assessment/services/qti/QTIService.java (working copy) @@ -29,6 +29,7 @@ import org.sakaiproject.tool.assessment.facade.AssessmentFacade; import org.sakaiproject.tool.assessment.facade.ItemFacade; +import org.sakaiproject.tool.assessment.facade.QuestionPoolFacade; import org.sakaiproject.tool.assessment.qti.constants.QTIVersion; import org.sakaiproject.tool.assessment.qti.helper.AuthoringHelper; @@ -70,7 +71,34 @@ } } + /** + * Import an assessment XML document in QTI format, extract & persist the data. + * import process assumes assessment structure, not objectbank or itembank + * based on usage in other potential migration systems, Respondus, BlackBoard, etc. + * QTI version 2.x will probably focus on content packaging for question pools + * @param document the assessment XML document in QTI format + * @param qtiVersion QTIVersion.VERSION_1_2; + * @return a persisted assessment + */ + public QuestionPoolFacade createImportedQuestionPool(Document document, int qtiVersion) + { + testQtiVersion(qtiVersion); + + try + { + AuthoringHelper helper = new AuthoringHelper(qtiVersion); + return helper.createImportedQuestionPool(document); + + } + catch (Exception ex) + { + throw new QTIServiceException(ex); + } + } + + + /** * Import an item XML document in QTI format, extract & persist the data. * @param document the item XML document in QTI format * @param qtiVersion either QTIVersion.VERSION_1_2 or QTIVersion.VERSION_2_0; Index: sam/tool/src/java/org/sakaiproject/tool/assessment/bundle/AuthorImportExport.properties =================================================================== --- sam/tool/src/java/org/sakaiproject/tool/assessment/bundle/AuthorImportExport.properties (revision 18192) +++ sam/tool/src/java/org/sakaiproject/tool/assessment/bundle/AuthorImportExport.properties (working copy) @@ -16,6 +16,7 @@ import_a=Import Assessment import_p=Import Part import_q=Import Question +import_qp=Import Pool import_action=Import import_cancel_action=Cancel choose_file=Choose a file: Index: sam/tool/src/java/org/sakaiproject/tool/assessment/bundle/QuestionPoolMessages.properties =================================================================== --- sam/tool/src/java/org/sakaiproject/tool/assessment/bundle/QuestionPoolMessages.properties (revision 18192) +++ sam/tool/src/java/org/sakaiproject/tool/assessment/bundle/QuestionPoolMessages.properties (working copy) @@ -174,12 +174,12 @@ t_addSubpool=Add Subpool t_addQuestion=Add Question t_addNewPool=Add New Pool -t_addSubpool=Add Subpool t_editPool=Edit Pool t_editQuestion=Edit Question t_copyPool=Copy Pool t_movePool=Move Pool t_removePool=RemovePool +t_importPool=Import Pool t_copyQuestion=Copy Question t_moveQuestion=Move Question t_exportQuestion=Export Question Index: sam/tool/src/java/org/sakaiproject/tool/assessment/ui/bean/qti/XMLImportBean.java =================================================================== --- sam/tool/src/java/org/sakaiproject/tool/assessment/ui/bean/qti/XMLImportBean.java (revision 18192) +++ sam/tool/src/java/org/sakaiproject/tool/assessment/ui/bean/qti/XMLImportBean.java (working copy) @@ -29,18 +29,21 @@ import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.ValueChangeEvent; +import java.io.File; import org.w3c.dom.Document; import org.sakaiproject.tool.assessment.facade.AssessmentFacade; import org.sakaiproject.tool.assessment.facade.AssessmentFacadeQueries; import org.sakaiproject.tool.assessment.facade.AssessmentTemplateFacade; +import org.sakaiproject.tool.assessment.facade.QuestionPoolFacade; import org.sakaiproject.tool.assessment.qti.constants.QTIVersion; import org.sakaiproject.tool.assessment.services.assessment.AssessmentService; import org.sakaiproject.tool.assessment.services.qti.QTIService; import org.sakaiproject.tool.assessment.ui.bean.author.AssessmentBean; import org.sakaiproject.tool.assessment.ui.bean.author.AuthorBean; import org.sakaiproject.tool.assessment.ui.bean.author.ItemAuthorBean; +import org.sakaiproject.tool.assessment.ui.bean.questionpool.QuestionPoolBean; import org.sakaiproject.tool.assessment.qti.util.XmlUtil; /** @@ -58,6 +61,7 @@ private AuthorBean authorBean; private AssessmentBean assessmentBean; private ItemAuthorBean itemAuthorBean; + private QuestionPoolBean questionPoolBean; private ResourceBundle rb = ResourceBundle.getBundle("org.sakaiproject.tool.assessment.bundle.AuthorImportExport"); public XMLImportBean() @@ -172,6 +176,7 @@ AssessmentFacadeQueries.TITLE,true); // authorBean.setAssessments(list); + } /** @@ -217,5 +222,75 @@ { this.itemAuthorBean = itemAuthorBean; } + + /** + * Value change on upload + * @param e the event + */ + public void importPoolFromQti(ValueChangeEvent e) + { + String uploadFile = (String) e.getNewValue(); + try + { + processAsPoolFile(uploadFile); + } + catch (Exception ex) + { + FacesMessage message = new FacesMessage( rb.getString("import_err") + ex ); + FacesContext.getCurrentInstance().addMessage(null, message); + } + } + + + /** + * Process uploaded QTI XML + * assessment as question pool + */ + private void processAsPoolFile(String uploadFile) + { + itemAuthorBean.setTarget(ItemAuthorBean.FROM_QUESTIONPOOL); // save to questionpool + + // Get the file name + String fileName = uploadFile; + + // Create a questionpool based on the uploaded assessment file + QuestionPoolFacade questionPool = createImportedQuestionPool(fileName, qtiVersion); + + // remove uploaded file + try{ + //System.out.println("****filename="+fileName); + File upload = new File(fileName); + upload.delete(); + } + catch(Exception e){ + System.out.println(e.getMessage()); + } + } + + /** + * Create questionpool from uploaded QTI assessment XML + * @param fullFileName file name and path + * @param qti QTI version + * @return + */ + private QuestionPoolFacade createImportedQuestionPool(String fullFileName, int qti) + { + //trim = true so that xml processing instruction at top line, even if not. + Document document = XmlUtil.readDocument(fullFileName, true); + QTIService qtiService = new QTIService(); + return qtiService.createImportedQuestionPool(document, qti); + } + + + public QuestionPoolBean getQuestionPoolBean() + { + return questionPoolBean; + } + + public void setQuestionPoolBean(QuestionPoolBean questionPoolBean) + { + this.questionPoolBean = questionPoolBean; + } + } Index: sam/tool/src/webapp/jsf/qti/importPool.jsp =================================================================== --- sam/tool/src/webapp/jsf/qti/importPool.jsp (revision 0) +++ sam/tool/src/webapp/jsf/qti/importPool.jsp (revision 0) @@ -0,0 +1,79 @@ +<%@ page contentType="text/html;charset=utf-8" pageEncoding="utf-8" language="java" %> +<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> +<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> +<%@ taglib uri="http://www.sakaiproject.org/samigo" prefix="samigo" %> +<%@ taglib uri="http://java.sun.com/upload" prefix="corejsf" %> + + + + + + + + <%= request.getAttribute("html.head") %> + <h:outputText value="#{msg.import_qp}" /> + + "> +
+ + + + +

+
+
+ + <%-- currently import pool mirrors import assessment --%> + +
+
+
+ + <%-- target represents location where import will be temporarily stored + check valueChangeListener for final destination --%> + +
+
+
+ <%-- activates the valueChangeListener --%> + + <%-- immediate=true bypasses the valueChangeListener --%> + +
+ +
+
+ + + + +
Index: sam/tool/src/webapp/jsf/questionpool/poolList.jsp =================================================================== --- sam/tool/src/webapp/jsf/questionpool/poolList.jsp (revision 18192) +++ sam/tool/src/webapp/jsf/questionpool/poolList.jsp (working copy) @@ -93,10 +93,18 @@
"/> - + + + + + + + + +
Index: sam/tool/src/webapp/WEB-INF/faces-config.xml =================================================================== --- sam/tool/src/webapp/WEB-INF/faces-config.xml (revision 18192) +++ sam/tool/src/webapp/WEB-INF/faces-config.xml (working copy) @@ -321,6 +321,10 @@ itemAuthorBean #{itemauthor} + + questionPoolBean + #{questionpool} + XML Controller. @@ -440,10 +444,10 @@ /jsf/author/authorSettings.jsp - timeError /jsf/author/authorSettings.jsp + editPublishedAssessmentSettings /jsf/author/publishedSettings.jsp @@ -610,7 +614,7 @@ importPool - /jsf/questionpool/importPool.jsp + /jsf/qti/importPool.jsp exportPool