/*
* Copyright 2012 Axel Winkler, Daniel Dunér
*
* This file is part of Daxplore Presenter.
*
* Daxplore Presenter is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* Daxplore Presenter 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Daxplore Presenter. If not, see <http://www.gnu.org/licenses/>.
*/
package org.daxplore.presenter.server.servlets;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.daxplore.presenter.server.ChartDataParserServer;
import org.daxplore.presenter.server.storage.PMF;
import org.daxplore.presenter.server.storage.QuestionMetadataServerImpl;
import org.daxplore.presenter.server.storage.SettingItemStore;
import org.daxplore.presenter.server.storage.StatDataItemStore;
import org.daxplore.presenter.server.storage.TextFileStore;
import org.daxplore.presenter.server.throwable.BadRequestException;
import org.daxplore.presenter.server.throwable.InternalServerException;
import org.daxplore.presenter.shared.ChartDataItem;
import org.daxplore.presenter.shared.QueryDefinition;
import org.daxplore.presenter.shared.QueryDefinition.QueryFlag;
import org.daxplore.presenter.shared.QuestionMetadata;
import org.daxplore.shared.SharedResourceTools;
import au.com.bytecode.opencsv.CSVWriter;
/**
* Generate and return a csv-file containing data for a specific chart.
*
* <p>This servlet is used by the client to generate and serve a downloadable
* csv-file to the user. The content of the file matches the data as defined
* by a single {@link QueryDefinition}.</p>
*
* <p>The servlet takes the arguments:
* <ul>
* <li>q, which is a queryString that defines the query</li>
* <li>l, which defines the locale</li>
* <li>prefix, which defines which prefix to read the data from</li>
* </ul>
*/
@SuppressWarnings("serial")
public class GetCsvServlet extends HttpServlet {
private Logger logger = Logger.getLogger(GetCsvServlet.class.getName());
private static Map<String, QuestionMetadata> metadataMap = new HashMap<>();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
PersistenceManager pm = null;
try {
// Get input from URL
String prefix = request.getParameter("prefix");
String queryString = request.getParameter("q");
String localeString = request.getParameter("l");
// Clean user input
if(prefix==null || !SharedResourceTools.isSyntacticallyValidPrefix(prefix)) {
throw new BadRequestException("Someone tried to access a syntactically invalid prefix: '" + prefix + "'");
}
if (localeString==null) {
localeString = "";
}
pm = PMF.get().getPersistenceManager();
List<String[]> csvTable = new LinkedList<>();
Locale locale = new Locale(localeString);
QuestionMetadata questionMetadata;
String key = prefix + "_" + locale.toLanguageTag();
if(metadataMap.containsKey(key)) {
questionMetadata = metadataMap.get(key);
} else {
String questionText = TextFileStore.getLocalizedFile(pm, prefix, "questions", locale, ".json");
questionMetadata = new QuestionMetadataServerImpl(new StringReader(questionText));
metadataMap.put(key, questionMetadata);
}
QueryDefinition queryDefinition = new QueryDefinition(questionMetadata, queryString);
List<String> questionOptionTexts = queryDefinition.getQuestionOptionTexts();
List<String> perspectiveOptionTexts = queryDefinition.getPerspectiveOptionTexts();
List<Integer> usedPerspectiveOptions = queryDefinition.getUsedPerspectiveOptions();
questionOptionTexts.add(0, queryDefinition.getPerspectiveShortText() + " \\ " + queryDefinition.getQuestionShortText());
//TODO handle timepoints properly
String timepoint0Text = SettingItemStore.getLocalizedProperty(pm, prefix, "usertexts", locale, "timepoint_0");
String timepoint1Text = SettingItemStore.getLocalizedProperty(pm, prefix, "usertexts", locale, "timepoint_1");
String statString = StatDataItemStore.getStats(pm, prefix, queryDefinition);
ChartDataParserServer data = new ChartDataParserServer(statString);
List<ChartDataItem> dataItems = data.getDataItems();
int columnCount = 4 + queryDefinition.getQuestionOptionCount();
int rowCount = 0;
csvTable.add(questionOptionTexts.toArray(new String[columnCount]));
if(queryDefinition.getPerspectiveOptionCount()>0){
for(int perspectiveOption : usedPerspectiveOptions) {
String[] row = new String[columnCount];
row[0] = MessageFormat.format("{0} ({1})", perspectiveOptionTexts.get(perspectiveOption), timepoint0Text);
int[] primaryData = dataItems.get(perspectiveOption).getCountData();
for (int i=1; i<=queryDefinition.getQuestionOptionCount(); i++) {
row[i] = Integer.toString(primaryData[i-1]);
}
csvTable.add(row);
rowCount++;
}
}
if(queryDefinition.hasFlag(QueryFlag.TOTAL) || queryDefinition.getPerspectiveOptionCount()==0){
String[] row = new String[columnCount];
String allRespondents = SettingItemStore.getLocalizedProperty(pm, prefix, "usertexts", locale, "all_respondents");
row[0] = MessageFormat.format("{0} ({1})", allRespondents, timepoint0Text);
int[] primaryData = data.getTotalDataItem().getCountData();
for (int i=1; i<=queryDefinition.getQuestionOptionCount(); i++) {
row[i] = Integer.toString(primaryData[i-1]);
}
csvTable.add(row);
rowCount++;
}
if (queryDefinition.hasFlag(QueryFlag.SECONDARY)) {
for(int perspectiveOption : usedPerspectiveOptions) {
String[] row = new String[columnCount];
row[0] = MessageFormat.format("{0} ({1})", perspectiveOptionTexts.get(perspectiveOption), timepoint1Text);
int[] secondaryData = dataItems.get(perspectiveOption).getCountDataSecondary();
for(int i=1; i<=queryDefinition.getQuestionOptionCount(); i++) {
row[i] = Integer.toString(secondaryData[i-1]);
}
csvTable.add(row);
rowCount++;
}
if(queryDefinition.hasFlag(QueryFlag.TOTAL) || queryDefinition.getPerspectiveOptionCount()==0){
String[] row = new String[columnCount];
String allRespondents = SettingItemStore.getLocalizedProperty(pm, prefix, "usertexts", locale, "all_respondents");
row[0] = MessageFormat.format("{0} ({1})", allRespondents, timepoint1Text);
int[] secondaryData = data.getTotalDataItem().getCountDataSecondary();
for (int i=1; i<=queryDefinition.getQuestionOptionCount(); i++) {
row[i] = Integer.toString(secondaryData[i-1]);
}
csvTable.add(row);
rowCount++;
}
}
while(rowCount++ < 3) {
csvTable.add(new String[columnCount]);
}
int metaDataColumn = queryDefinition.getQuestionOptionCount() + 2;
csvTable.get(0)[metaDataColumn] = SettingItemStore.getLocalizedProperty(pm, prefix, "usertexts", locale, "pageTitle");
csvTable.get(1)[metaDataColumn] = queryDefinition.getPerspectiveShortText() + ":";
csvTable.get(1)[metaDataColumn+1] = queryDefinition.getPerspectiveFullText();
csvTable.get(2)[metaDataColumn] = queryDefinition.getQuestionShortText() + ":";
csvTable.get(2)[metaDataColumn+1] = queryDefinition.getQuestionFullText();
// write response
try (PrintWriter respWriter = new PrintWriter(new OutputStreamWriter(response.getOutputStream(), "UTF8"), true);
CSVWriter csvWriter = new CSVWriter(respWriter);) {
response.setContentType("text/csv; charset=UTF-8");
csvWriter.writeAll(csvTable);
csvWriter.close();
response.setStatus(HttpServletResponse.SC_OK);
} catch (IOException e) {
throw new InternalServerException("Failed to write csv response", e);
}
} catch (BadRequestException e) {
logger.log(Level.WARNING, e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
} catch (InternalServerException e) {
logger.log(Level.SEVERE, e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} catch (RuntimeException e) {
logger.log(Level.SEVERE, "Unexpected exception: " + e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
} finally {
if (pm!=null) {
pm.close();
}
}
}
public static void clearServletCache() {
metadataMap.clear();
}
}