/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/qti/XMLImportBean.java $
* $Id: XMLImportBean.java 121231 2013-03-14 20:08:58Z ktsao@stanford.edu $
***********************************************************************************
*
* Copyright (c) 2004, 2005, 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.tool.assessment.ui.bean.qti;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import javax.faces.event.ValueChangeEvent;
import javax.faces.application.FacesMessage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.service.gradebook.shared.GradebookExternalAssessmentService;
import org.sakaiproject.spring.SpringBeanLocator;
import org.sakaiproject.tool.assessment.contentpackaging.ImportService;
import org.sakaiproject.tool.assessment.data.ifc.assessment.EvaluationModelIfc;
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.integration.context.IntegrationContextFactory;
import org.sakaiproject.tool.assessment.integration.helper.ifc.GradebookServiceHelper;
import org.sakaiproject.tool.assessment.qti.constants.QTIVersion;
import org.sakaiproject.tool.assessment.qti.util.XmlUtil;
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.ui.listener.util.ContextUtil;
import org.sakaiproject.util.FormattedText;
import org.sakaiproject.util.ResourceLoader;
import org.w3c.dom.Document;
/**
* <p>Bean for QTI Import Data</p>
*/
public class XMLImportBean implements Serializable
{
/** Use serialVersionUID for interoperability. */
private final static long serialVersionUID = 418920360211039758L;
private static Log log = LogFactory.getLog(XMLImportBean.class);
private int qtiVersion;
private String uploadFileName;
private String importType;
private String pathToData;
private AuthorBean authorBean;
private AssessmentBean assessmentBean;
private ItemAuthorBean itemAuthorBean;
private QuestionPoolBean questionPoolBean;
private boolean isCP;
private String importType2;
private static final GradebookServiceHelper gbsHelper =
IntegrationContextFactory.getInstance().getGradebookServiceHelper();
private static final boolean integrated =
IntegrationContextFactory.getInstance().isIntegrated();
public XMLImportBean()
{
qtiVersion = QTIVersion.VERSION_1_2;//default
}
// put tests here...
public static void main(String[] args)
{
//XMLImportBean XMLImportBean1 = new XMLImportBean();
}
public void importAssessment(ValueChangeEvent e)
{
String sourceType = ContextUtil.lookupParam("sourceType");
String uploadFile = (String) e.getNewValue();
if (uploadFile!= null && uploadFile.startsWith("SizeTooBig:")) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext external = context.getExternalContext();
String paramValue = ((Long)((ServletContext)external.getContext()).getAttribute("FILEUPLOAD_SIZE_MAX")).toString();
Long sizeMax = null;
float sizeMax_float = 0f;
if (paramValue != null) {
sizeMax = Long.parseLong(paramValue);
sizeMax_float = sizeMax.floatValue()/1024;
}
int sizeMax_int = Math.round(sizeMax_float);
ResourceLoader rb =new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AuthorImportExport");
String sizeTooBigMessage = MessageFormat.format(rb.getString("import_size_too_big"), uploadFile.substring(11), sizeMax_int);
FacesMessage message = new FacesMessage(sizeTooBigMessage);
FacesContext.getCurrentInstance().addMessage(null, message);
// remove unsuccessful file
log.debug("****Clean up file:"+uploadFile);
File upload = new File(uploadFile);
upload.delete();
authorBean.setImportOutcome("importAssessment");
return;
}
authorBean.setImportOutcome("author");
if ("2".equals(sourceType)) {
if(uploadFile.toLowerCase().endsWith(".zip")) {
isCP = true;
importAssessment(uploadFile, true, true);
}
else {
isCP = false;
importAssessment(uploadFile, false, true);
}
}
else {
if(uploadFile.toLowerCase().endsWith(".zip")) {
isCP = true;
importAssessment(uploadFile,true, false);
}
else {
isCP = false;
importAssessment(uploadFile, false, false);
}
}
}
/**
* Value change on upload
* @param e the event
*/
public void importAssessment(String uploadFile, boolean isCP, boolean isRespondus)
{
String filename = uploadFile;
String unzipLocation = null;
boolean fileNotFound = false;
if (isCP) {
ImportService importService = new ImportService();
unzipLocation = importService.unzipImportFile(uploadFile);
filename = unzipLocation + "/" + importService.getQTIFilename();
}
try
{
processFile(filename, uploadFile, isRespondus);
}
catch (FileNotFoundException fnfex)
{
fileNotFound = true;
ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AuthorImportExport");
FacesMessage message = new FacesMessage( rb.getString("import_qti_not_found") );
FacesContext.getCurrentInstance().addMessage(null, message);
}
catch (Exception ex)
{
ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AuthorImportExport");
FacesMessage message = new FacesMessage( rb.getString("import_err") );
FacesContext.getCurrentInstance().addMessage(null, message);
}
finally {
boolean success = false;
// remove unsuccessful file
if (!fileNotFound) {
log.debug("****Clean up file: "+filename);
File f1 = new File(filename);
success = f1.delete();
if (!success) {
log.error ("Failed to delete file " + filename);
}
}
if (isCP) {
File f2 = new File(uploadFile);
success = f2.delete();
if (!success) {
log.error ("Failed to delete file " + uploadFile);
}
File f3 = new File(unzipLocation);
deleteDirectory(f3);
}
}
}
/**
*
* @return QTI version of XML file
*/
public int getQtiVersion()
{
return qtiVersion;
}
/**
*
* @param qtiVersion QTI version of XML file
*/
public void setQtiVersion(int qtiVersion)
{
if (!QTIVersion.isValid(qtiVersion))
{
throw new IllegalArgumentException("NOT Legal Qti Version.");
}
this.qtiVersion = qtiVersion;
}
/**
*
* @return file name and path
*/
public String getUploadFileName()
{
return uploadFileName;
}
/**
*
* @param uploadFileName file name and path
*/
public void setUploadFileName(String uploadFileName)
{
this.uploadFileName = uploadFileName;
}
/**
* A, S, I
* @return type of upload
*/
public String getImportType()
{
return importType;
}
/**
* A, S, I
* @param importType A, S, or I
*/
public void setImportType(String importType)
{
this.importType = importType;
}
public String getImportType2()
{
return importType2;
}
/**
* A, S, I
* @param importType A, S, or I
*/
public void setImportType2(String importType2)
{
this.importType2 = importType2;
}
private void processFile(String fileName, String uploadFile, boolean isRespondus) throws Exception
{
itemAuthorBean.setTarget(ItemAuthorBean.FROM_ASSESSMENT); // save to assessment
AssessmentService assessmentService = new AssessmentService();
// Create an assessment based on the uploaded file
ArrayList failedMatchingQuestions = new ArrayList();
AssessmentFacade assessment = createImportedAssessment(fileName, qtiVersion, isRespondus, failedMatchingQuestions);
if (failedMatchingQuestions.size() > 0)
{
String importedFilename = getImportedFilename(uploadFile);
ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AuthorImportExport");
StringBuffer sb = new StringBuffer("\"");
sb.append(importedFilename);
sb.append("\" ");
sb.append(rb.getString("respondus_matching_err_1"));
sb.append(" ");
for(int i = 0; i < failedMatchingQuestions.size() - 1; i++) {
sb.append(" ");
sb.append(failedMatchingQuestions.get(i));
sb.append(", ");
}
sb.append(failedMatchingQuestions.get(failedMatchingQuestions.size() - 1));
sb.append(". ");
sb.append(rb.getString("respondus_matching_err_2"));
FacesMessage message = new FacesMessage(sb.toString());
FacesContext.getCurrentInstance().addMessage(null, message);
}
// change grading book settings if there is no gradebook in the site
boolean hasGradebook = false;
GradebookExternalAssessmentService g = null;
if (integrated){
g = (GradebookExternalAssessmentService) SpringBeanLocator.getInstance().
getBean("org.sakaiproject.service.gradebook.GradebookExternalAssessmentService");
}
try{
if (gbsHelper.isAssignmentDefined(assessment.getTitle(), g)){
hasGradebook= true;
}
}
catch(Exception e){
log.debug("Error calling gradebookHelper");
}
// gradebook options, don't know how this is supposed to work, leave alone for now
if (!hasGradebook && assessment!=null){
assessment.getEvaluationModel().setToGradeBook(EvaluationModelIfc.NOT_TO_GRADEBOOK.toString());
}
assessmentService.saveAssessment(assessment);
// Go to editAssessment.jsp, so prepare assessmentBean
assessmentBean.setAssessment(assessment);
// reset in case anything hanging around
authorBean.setAssessTitle("");
authorBean.setAssessmentDescription("");
authorBean.setAssessmentTypeId("");
authorBean.setAssessmentTemplateId(AssessmentTemplateFacade.DEFAULTTEMPLATE.toString());
// update core AssessmentList: get the managed bean, author and set the list
ArrayList list = assessmentService.getBasicInfoOfAllActiveAssessments(
AssessmentFacadeQueries.TITLE,true);
Iterator iter = list.iterator();
while (iter.hasNext()) {
AssessmentFacade assessmentFacade= (AssessmentFacade) iter.next();
assessmentFacade.setTitle(FormattedText.convertFormattedTextToPlaintext(assessmentFacade.getTitle()));
}
authorBean.setAssessments(list);
}
private String getImportedFilename(String filename) {
String temp_filename_1 = filename.substring(filename.lastIndexOf("/") + 1);
String temp_filename_2 = temp_filename_1.substring(temp_filename_1.indexOf("."));
String temp_filename_3 = temp_filename_1.substring(0, temp_filename_1.substring(0, temp_filename_1.indexOf(".")).lastIndexOf("_"));
String final_filename = temp_filename_3 + temp_filename_2;
return final_filename;
}
private void deleteDirectory(File directory) {
if(directory.exists()) {
File[] files = directory.listFiles();
for(int i=0; i < files.length; i++) {
if(files[i].isDirectory()) {
deleteDirectory(files[i]);
}
else {
boolean success = files[i].delete();
if (!success)
log.error("Delete Failed.");
}
}
}
boolean success = directory.delete();
if (!success)
log.error("Delete Failed.");
}
/**
* Create assessment from uploaded QTI XML
* @param fullFileName file name and path
* @param qti QTI version
* @param isRespondus true/false
* @return
*/
private AssessmentFacade createImportedAssessment(String fullFileName, int qti, boolean isRespondus, ArrayList failedMatchingQuestions) throws Exception
{
//trim = true so that xml processing instruction at top line, even if not.
Document document = null;
try {
document = XmlUtil.readDocument(fullFileName, true);
} catch (Exception e) {
throw(e);
}
QTIService qtiService = new QTIService();
if (isCP) {
return qtiService.createImportedAssessment(document, qti, fullFileName.substring(0, fullFileName.lastIndexOf("/")), isRespondus, failedMatchingQuestions);
}
else {
return qtiService.createImportedAssessment(document, qti, null, isRespondus, failedMatchingQuestions);
}
}
public AuthorBean getAuthorBean()
{
return authorBean;
}
public void setAuthorBean(AuthorBean authorBean)
{
this.authorBean = authorBean;
}
public AssessmentBean getAssessmentBean()
{
return assessmentBean;
}
public void setAssessmentBean(AssessmentBean assessmentBean)
{
this.assessmentBean = assessmentBean;
}
public ItemAuthorBean getItemAuthorBean()
{
return itemAuthorBean;
}
public void setItemAuthorBean(ItemAuthorBean itemAuthorBean)
{
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)
{
ResourceLoader rb = new ResourceLoader("org.sakaiproject.tool.assessment.bundle.AuthorImportExport");
FacesMessage message = new FacesMessage( rb.getString("import_err") );
FacesContext.getCurrentInstance().addMessage(null, message);
}
}
/**
* Process uploaded QTI XML
* assessment as question pool
* @throws Exception
*/
private void processAsPoolFile(String uploadFile) throws Exception
{
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);
boolean success = upload.delete();
if (!success)
log.error ("Failed to delete file " + fileName);
}
catch(Exception e){
e.printStackTrace();
}
}
/**
* Create questionpool from uploaded QTI assessment XML
* @param fullFileName file name and path
* @param qti QTI version
* @return
* @throws Exception
*/
private QuestionPoolFacade createImportedQuestionPool(String fullFileName, int qti) throws Exception
{
//trim = true so that xml processing instruction at top line, even if not.
Document document;
try {
document = XmlUtil.readDocument(fullFileName, true);
} catch (Exception e) {
throw(e);
}
QTIService qtiService = new QTIService();
return qtiService.createImportedQuestionPool(document, qti);
}
public QuestionPoolBean getQuestionPoolBean()
{
return questionPoolBean;
}
public void setQuestionPoolBean(QuestionPoolBean questionPoolBean)
{
this.questionPoolBean = questionPoolBean;
}
public String getPathToData()
{
return pathToData;
}
public void setPathToData(String pathToData)
{
this.pathToData = pathToData;
}
}