/*
* Copyright (C) 2010-2015 Stichting Akvo (Akvo Foundation)
*
* This file is part of Akvo FLOW.
*
* Akvo FLOW is free software: you can redistribute it and modify it under the terms of
* the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
* either version 3 of the License or any later version.
*
* Akvo FLOW 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 Affero General Public License included below for more details.
*
* The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
*/
package org.waterforpeople.mapping.dataexport;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.waterforpeople.mapping.app.gwt.client.survey.QuestionDto;
import org.waterforpeople.mapping.app.gwt.client.survey.QuestionGroupDto;
import org.waterforpeople.mapping.app.gwt.client.survey.QuestionOptionDto;
import org.waterforpeople.mapping.app.gwt.client.survey.SurveyDto;
import org.waterforpeople.mapping.app.gwt.client.survey.TranslationDto;
import org.waterforpeople.mapping.dataexport.service.BulkDataServiceClient;
import com.gallatinsystems.framework.dataexport.applet.DataExporter;
/**
* Data exporter to write excel files containing the survey questions
*
* @author Christopher Fagiani
*/
public class SurveyFormExporter implements DataExporter {
private static final Logger log = Logger.getLogger(SurveyFormExporter.class);
private static final int COL_WIDTH = 10000;
private static final String LANG_DELIM = " / ";
private static final String DEP_HEAD = "Only answer if you responded ";
private static final String DEP_HEAD_TO = " to ";
private static final String BLANK = "_________________________";
private static final String SMALL_BLANK = "______";
private static final String QUESTION_HEADER = "Question";
private static final String RESPONSE_HEADER = "Response";
private static final String SURVEY_ID_KEY = "surveyId";
private Map<Long, Long> idToNumberMap;
private List<QuestionGroupDto> groupList;
private String surveyTitle;
private Map<QuestionGroupDto, List<QuestionDto>> questionMap;
@Override
public void export(Map<String, String> criteria, File fileName,
String serverBase, Map<String, String> options) {
try {
String surveyId = criteria.get(SURVEY_ID_KEY);
String apiKey = criteria.get("apiKey");
populateQuestionMap(surveyId, serverBase, apiKey);
List<SurveyDto> surveys = BulkDataServiceClient.fetchSurvey(Long.parseLong(surveyId), serverBase, apiKey);
if (surveys == null || surveys.isEmpty()) {
surveyTitle = "";
} else {
SurveyDto surveyDto = surveys.get(0);
surveyTitle = String.format("%s (v. %s)", surveyDto.getName(), surveyDto.getVersion());
}
writeSurvey(surveyTitle, fileName, groupList, questionMap);
} catch (Exception e) {
log.error("Could not write survey", e);
}
}
/**
* Calls the server to fetch all question groups for the survey and then will iteratee over all
* the questions for the group and call loadQuestionDetails for each one to get the fully
* hydrated object. This will populate a number of member variables to store the results.
*/
private void populateQuestionMap(String surveyId, String serverBase, String apiKey)
throws Exception {
groupList = BulkDataServiceClient.fetchQuestionGroups(serverBase,
surveyId, apiKey);
questionMap = new HashMap<QuestionGroupDto, List<QuestionDto>>();
idToNumberMap = new HashMap<Long, Long>();
if (groupList != null) {
Long count = 1L;
for (QuestionGroupDto group : groupList) {
List<QuestionDto> questions = BulkDataServiceClient
.fetchQuestions(serverBase, group.getKeyId(), apiKey);
if (questions != null) {
List<QuestionDto> fullQuestions = new ArrayList<QuestionDto>();
for (QuestionDto q : questions) {
QuestionDto fullQ = BulkDataServiceClient
.loadQuestionDetails(serverBase, q.getKeyId(), apiKey);
if (fullQ != null) {
fullQuestions.add(fullQ);
idToNumberMap.put(fullQ.getKeyId(), count++);
}
}
questionMap.put(group, fullQuestions);
}
}
}
}
/**
* Writes the survey as an XLS document
*/
private void writeSurvey(String title, File fileName,
List<QuestionGroupDto> groupList,
Map<QuestionGroupDto, List<QuestionDto>> questions)
throws Exception {
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet();
sheet.setColumnWidth(0, COL_WIDTH);
sheet.setColumnWidth(1, COL_WIDTH);
HSSFCellStyle headerStyle = wb.createCellStyle();
headerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
HSSFFont headerFont = wb.createFont();
headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
headerStyle.setFont(headerFont);
HSSFCellStyle questionStyle = wb.createCellStyle();
questionStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_TOP);
questionStyle.setWrapText(true);
HSSFCellStyle depStyle = wb.createCellStyle();
depStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
HSSFFont depFont = wb.createFont();
depFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
depFont.setItalic(true);
depStyle.setFont(depFont);
int curRow = 0;
HSSFRow row = sheet.createRow(curRow++);
sheet
.addMergedRegion(new CellRangeAddress(curRow - 1, curRow - 1,
0, 1));
createCell(row, 0, title, headerStyle);
row = sheet.createRow(curRow++);
createCell(row, 0, QUESTION_HEADER, headerStyle);
createCell(row, 1, RESPONSE_HEADER, headerStyle);
Long count = 1L;
if (questions != null) {
for (int i = 0; i < groupList.size(); i++) {
HSSFRow groupHeaderRow = sheet.createRow(curRow++);
sheet.addMergedRegion(new CellRangeAddress(curRow - 1,
curRow - 1, 0, 1));
createCell(groupHeaderRow, 0,
groupList.get(i).getDisplayName(), headerStyle);
for (QuestionDto q : questions.get(groupList.get(i))) {
int questionStartRow = curRow;
HSSFRow tempRow = sheet.createRow(curRow++);
if (q.getQuestionDependency() != null) {
// if there is a dependency, add a row about not
// answering unless the dependency is satisfied
sheet.addMergedRegion(new CellRangeAddress(curRow - 1,
curRow - 1, 0, 1));
Long qNum = idToNumberMap.get(q.getQuestionDependency()
.getQuestionId());
createCell(tempRow, 0, DEP_HEAD
+ q.getQuestionDependency().getAnswerValue()
+ DEP_HEAD_TO + "Q" + qNum, depStyle);
tempRow = sheet.createRow(curRow++);
questionStartRow = curRow;
}
createCell(tempRow, 0, (count++) + ". "
+ formText(q.getText(), q.getTranslationMap()),
questionStyle);
if (q.getOptionContainerDto() != null
&& q.getOptionContainerDto().getOptionsList() != null) {
for (QuestionOptionDto opt : q.getOptionContainerDto()
.getOptionsList()) {
tempRow = sheet.createRow(curRow++);
createCell(tempRow, 1, formText(opt.getText(), opt
.getTranslationMap())
+ SMALL_BLANK, null);
}
sheet.addMergedRegion(new CellRangeAddress(
questionStartRow, curRow - 1, 0, 0));
} else {
createCell(tempRow, 1, BLANK, null);
}
}
}
}
FileOutputStream fileOut = new FileOutputStream(fileName);
wb.write(fileOut);
fileOut.close();
}
/**
* creates a cell in the row passed in and sets the style and value (if non-null)
*/
private HSSFCell createCell(HSSFRow row, int col, String value,
HSSFCellStyle style) {
HSSFCell cell = row.createCell(col);
if (style != null) {
cell.setCellStyle(style);
}
if (value != null) {
cell.setCellValue(value);
}
return cell;
}
/**
* forms a string that has all languages in the translation map delimited by the LANG_DELIM
*/
private String formText(String text,
Map<String, TranslationDto> translationMap) {
StringBuilder buff = new StringBuilder();
buff.append(text);
if (translationMap != null) {
TreeMap<String, TranslationDto> sortedMap = null;
if (translationMap instanceof TreeMap) {
sortedMap = (TreeMap<String, TranslationDto>) translationMap;
} else {
sortedMap = new TreeMap<String, TranslationDto>(translationMap);
}
for (Entry<String, TranslationDto> trans : sortedMap.entrySet()) {
if (trans.getValue() != null
&& trans.getValue().getText() != null) {
if (!trans.getValue().getText().trim().equalsIgnoreCase(
"null")) {
buff.append(LANG_DELIM);
buff.append(trans.getValue().getText());
}
}
}
}
return buff.toString();
}
}