/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/sam/trunk/samigo-app/src/java/org/sakaiproject/tool/assessment/ui/bean/evaluation/ExportResponsesBean.java $ * $Id: ExportResponsesBean.java 120646 2013-03-04 22:29:15Z ktsao@stanford.edu $ *********************************************************************************** * * Copyright (c) 2007, 2008, 2009 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.evaluation; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.jsf.model.PhaseAware; import org.sakaiproject.tool.assessment.jsf.convert.AnswerSurveyConverter; import org.sakaiproject.tool.assessment.services.GradingService; import org.sakaiproject.tool.assessment.services.assessment.PublishedAssessmentService; import org.sakaiproject.tool.assessment.ui.bean.util.Validator; import org.sakaiproject.tool.assessment.ui.listener.evaluation.HistogramListener; import org.sakaiproject.tool.assessment.ui.listener.util.ContextUtil; import org.sakaiproject.util.FormattedText; import org.sakaiproject.util.ResourceLoader; /** * <p>Description: class form for evaluating total scores</p> * */ public class ExportResponsesBean implements Serializable, PhaseAware { /** * */ private static final long serialVersionUID = 2854656853283125977L; /** * gopalrc - Jan 2008 * Marks the beginning of each new sheet. * If absent, treat as a single-sheet workbook. */ public static final String NEW_SHEET_MARKER = "<sheet/>"; public static final String HEADER_MARKER = "<header/>"; public static final String FORMAT = "<format "; public static final String FORMAT_BOLD = FORMAT + "bold/>"; private String assessmentId; private String assessmentName; private boolean anonymous; private static Log log = LogFactory.getLog(ExportResponsesBean.class); /** * Creates a new TotalScoresBean object. */ public ExportResponsesBean() { log.debug("Creating a new ExportResponsesBean"); } /** * get assessment id * * @return the assessment id */ public String getAssessmentId() { return Validator.check(assessmentId, "0"); } /** * set assessment id * * @param passessmentId the id */ public void setAssessmentId(String assessmentId) { this.assessmentId = assessmentId; } /** * get assessment name * * @return the name */ public String getAssessmentName() { return Validator.check(assessmentName, "N/A"); } /** * set assessment name * * @param passessmentName the name */ public void setAssessmentName(String assessmentName) { this.assessmentName = assessmentName; } /** * get anonymous * * @return anonymous */ public boolean getAnonymous() { return anonymous; } /** * set anonymous * * @param anonymous */ public void setAnonymous(boolean anonymous) { this.anonymous = anonymous; } // Following three methods are for interface PhaseAware public void endProcessValidators() { log.debug("endProcessValidators"); } public void endProcessUpdates() { log.debug("endProcessUpdates"); } public void startRenderResponse() { log.debug("startRenderResponse"); } public void exportExcel(ActionEvent event){ log.debug("exporting as Excel: assessment id = " + getAssessmentId()); /* SpreadsheetUtil.downloadSpreadsheetData(getSpreadsheetData(), getDownloadFileName(), new SpreadsheetDataFileWriterXls()); */ // changed from above by gopalrc - Jan 2008 // to allow local customization of spreadsheet output FacesContext faces = FacesContext.getCurrentInstance(); HttpServletResponse response = (HttpServletResponse)faces.getExternalContext().getResponse(); response.reset(); // Eliminate the added-on stuff response.setHeader("Pragma", "public"); // Override old-style cache control response.setHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0, max-age=0"); // New-style writeDataToResponse(getSpreadsheetData(), getDownloadFileName(), response); faces.responseComplete(); } private List<List<Object>> getSpreadsheetData() { TotalScoresBean totalScores = (TotalScoresBean) ContextUtil.lookupBean("totalScores"); Map useridMap = totalScores.getUserIdMap(TotalScoresBean.CALLED_FROM_EXPORT_LISTENER); // gopalrc Dec 2007 HistogramListener histogramListener = new HistogramListener(); Iterator detailedStats = histogramListener.getDetailedStatisticsSpreadsheetData(assessmentId).iterator(); //boolean showPartAndTotalScoreSpreadsheetColumns = (Boolean) detailedStats.next(); detailedStats.next(); boolean showPartAndTotalScoreSpreadsheetColumns = true; //boolean showDiscriminationColumn = (Boolean) detailedStats.next(); boolean showDetailedStatisticsSheet = (Boolean) detailedStats.next(); String audioMessage = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","audio_message"); String fileUploadMessage = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","file_upload_message"); String noSubmissionMessage = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","export_no_submission"); GradingService gradingService = new GradingService(); String poolString = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","pool"); String partString = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","part"); String questionString = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","question"); String responseString = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","response"); String rationaleString = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","rationale"); String itemGradingCommentsString = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","grader_comments"); List exportResponsesDataList = gradingService.getExportResponsesData(assessmentId, anonymous, audioMessage, fileUploadMessage, noSubmissionMessage, showPartAndTotalScoreSpreadsheetColumns, poolString, partString, questionString, responseString, rationaleString, itemGradingCommentsString, useridMap); //SAM-1693 the returned list could be null -DH List<List<Object>> list = new ArrayList<List<Object>>(); if (exportResponsesDataList != null) { list = (List<List<Object>>) exportResponsesDataList.get(0); } // Now insert the header line ArrayList<Object> headerList = new ArrayList<Object>(); headerList.add(HEADER_MARKER); if (anonymous) { headerList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","sub_id")); } else { headerList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","last_name")); headerList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","first_name")); headerList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","user_name")); headerList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","num_submission")); } PublishedAssessmentService pubService = new PublishedAssessmentService(); // gopalrc - Nov 2007 if (showPartAndTotalScoreSpreadsheetColumns) { int numberOfSections = pubService.getPublishedSectionCount(Long.valueOf(assessmentId)).intValue(); if (numberOfSections > 1) { for (int i = 1; i <= numberOfSections; i++) { headerList.add(partString + " " + i + " " + ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","score")); } } headerList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","tot")); headerList.add(itemGradingCommentsString); } //SAM-1693 the returned list could be null -DH if (exportResponsesDataList != null) { headerList.addAll((ArrayList) exportResponsesDataList.get(1)); } list.add(0,headerList); // gopalrc - Jan 2008 - New Sheet Marker ArrayList<Object> newSheetList; newSheetList = new ArrayList<Object>(); newSheetList.add(NEW_SHEET_MARKER); newSheetList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","responses")); list.add(0, newSheetList); // gopalrc - Jan 2008 - New Sheet Marker if (showDetailedStatisticsSheet) { newSheetList = new ArrayList<Object>(); newSheetList.add(NEW_SHEET_MARKER); newSheetList.add(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","item_analysis")); list.add(newSheetList); // gopalrc Dec 2007 while (detailedStats.hasNext()) { list.add((List)detailedStats.next()); } } return list; } /** * Generates a default filename (minus the extension) for a download from this Gradebook. * * @param prefix for filename * @return The appropriate filename for the export */ public String getDownloadFileName() { Date now = new Date(); String dateFormat = ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","export_filename_date_format"); DateFormat df = new SimpleDateFormat(dateFormat, new ResourceLoader().getLocale()); StringBuilder fileName = new StringBuilder(ContextUtil.getLocalizedString("org.sakaiproject.tool.assessment.bundle.EvaluationMessages","assessment")); if(StringUtils.trimToNull(assessmentName) != null) { assessmentName = assessmentName.replaceAll("\\s", "_"); // replace whitespace with '_' fileName.append("-"); fileName.append(assessmentName); } fileName.append("-"); fileName.append(df.format(now)); return fileName.toString(); } public void writeDataToResponse(List<List<Object>> spreadsheetData, String fileName, HttpServletResponse response) { String mimetype = "application/vnd.ms-excel;charset=UTF-8"; String extension = ".xls"; int columns = findColumnSize(spreadsheetData); if (columns >= 255) { // allows for greater than 255 columns - SAK-16560 mimetype = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; extension = ".xlsx"; log.info("Samigo export ("+columns+" columns): Using xlsx mimetype: " + mimetype); } response.setContentType(mimetype); String escapedFilename = org.sakaiproject.util.Validator.escapeUrl(fileName); HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); String userAgent = request.getHeader("User-Agent"); response.setHeader("Content-disposition", "attachment; filename=" + escapedFilename + extension + "; filename*=UTF-8''" + escapedFilename + extension); OutputStream out = null; try { out = response.getOutputStream(); getAsWorkbook(spreadsheetData).write(out); out.flush(); } catch (IOException e) { if (log.isErrorEnabled()) log.error(e); } finally { try { if (out != null) out.close(); } catch (IOException e) { if (log.isErrorEnabled()) log.error(e); } } } protected Workbook getAsWorkbookTest(List<List<Object>> spreadsheetData) { Workbook wb = new HSSFWorkbook(); Sheet sheet = wb.createSheet(); Iterator<List<Object>> dataIter = spreadsheetData.iterator(); // By convention, the first list in the list contains column headers. Row headerRow = sheet.createRow((short)0); List<Object> headerList = dataIter.next(); for (short i = 0; i < headerList.size(); i++) { createCell(headerRow, i, null).setCellValue(headerList.get(i).toString()); } short rowPos = 1; while (dataIter.hasNext()) { List<Object> rowData = dataIter.next(); Row row = sheet.createRow(rowPos++); for (short i = 0; i < rowData.size(); i++) { Cell cell = createCell(row, i, null); Object data = rowData.get(i); if (data != null) { if (data instanceof Double) { cell.setCellValue(((Double)data).doubleValue()); } else { cell.setCellValue(data.toString()); } } } } return wb; } public Workbook getAsWorkbook(List<List<Object>> spreadsheetData) { // outer list is rows, inner list is columns (cells in the row) int columns = findColumnSize(spreadsheetData); Workbook wb; if (columns < 255) { log.info("Samigo export ("+columns+" columns): Using xsl format"); wb = new HSSFWorkbook(); } else { // allows for greater than 255 columns - SAK-16560 log.info("Samigo export ("+columns+" columns): Using xslx format"); wb = new XSSFWorkbook(); } CellStyle boldStyle = wb.createCellStyle(); Font font = wb.createFont(); font.setBoldweight(Font.BOLDWEIGHT_BOLD); String fontName = ServerConfigurationService.getString("spreadsheet.font"); if (fontName != null) { font.setFontName(fontName); } boldStyle.setFont(font); CellStyle headerStyle = boldStyle; CellStyle cellStyle = null; if (fontName != null) { font = wb.createFont(); font.setFontName(fontName); cellStyle = wb.createCellStyle(); cellStyle.setFont(font); } Sheet sheet = null; Iterator<List<Object>> dataIter = spreadsheetData.iterator(); short rowPos = 0; while (dataIter.hasNext()) { List<Object> rowData = dataIter.next(); if (rowData.get(0).toString().equals(NEW_SHEET_MARKER)) { sheet = wb.createSheet(rowData.get(1).toString()); rowPos = 0; } // By convention, the first list in the list contains column headers. // This should only happen once and usually only in a single-sheet workbook else if (rowData.get(0).toString().equals(HEADER_MARKER)) { if (sheet == null) { sheet = wb.createSheet("responses"); // avoid NPE } Row headerRow = sheet.createRow(rowPos++); for (short i = 0; i < rowData.size()-1; i++) { createCell(headerRow, i, headerStyle).setCellValue(rowData.get(i+1).toString()); } } else { if (sheet == null) { sheet = wb.createSheet("responses"); // avoid NPE } Row row = sheet.createRow(rowPos++); short colPos = 0; Iterator colIter = rowData.iterator(); while (colIter.hasNext()) { //for (short i = 0; i < rowData.size(); i++) { Cell cell = null; //Object data = rowData.get(i); Object data = colIter.next(); if (data != null) { if (data.toString().startsWith(FORMAT)) { if (data.equals(FORMAT_BOLD)) { cell = createCell(row, colPos++, boldStyle); } data = colIter.next(); } else { cell = createCell(row, colPos++, cellStyle); } if (data != null) { if (data instanceof Double) { cell.setCellValue(((Double)data).doubleValue()); } else { AnswerSurveyConverter converter = new AnswerSurveyConverter(); String datac = converter.getAsString(null, null, data.toString()); // stripping html for export, SAK-17021 cell.setCellValue(FormattedText.convertFormattedTextToPlaintext(datac)); } } } } } } return wb; } private int findColumnSize(List<List<Object>> spreadsheetData) { int columns = 0; // the largest number of columns required for a row for (List<Object> list : spreadsheetData) { if (list != null && list.size() > columns) { columns = list.size(); } } return columns; } private Cell createCell(Row row, short column, CellStyle cellStyle) { Cell cell = row.createCell(column); //cell.setEncoding(HSSFCell.ENCODING_UTF_16); if (cellStyle != null) { cell.setCellStyle(cellStyle); } return cell; } }