/* * Copyright (C) 2010-2017 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.app.web; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import org.codehaus.jackson.map.ObjectMapper; import org.waterforpeople.mapping.analytics.dao.SurveyQuestionSummaryDao; import org.waterforpeople.mapping.analytics.domain.SurveyQuestionSummary; import org.waterforpeople.mapping.app.gwt.client.survey.QuestionDto; import org.waterforpeople.mapping.app.gwt.client.survey.QuestionDtoMapper; 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.QuestionOptionDtoMapper; import org.waterforpeople.mapping.app.gwt.client.survey.SurveyDto; import org.waterforpeople.mapping.app.gwt.client.survey.SurveyGroupDto; import org.waterforpeople.mapping.app.gwt.client.survey.SurveySummaryDto; import org.waterforpeople.mapping.app.gwt.client.surveyinstance.SurveyInstanceDto; import org.waterforpeople.mapping.app.gwt.client.surveyinstance.SurveyInstanceService; import org.waterforpeople.mapping.app.gwt.server.survey.SurveyServiceImpl; import org.waterforpeople.mapping.app.gwt.server.surveyinstance.SurveyInstanceServiceImpl; import org.waterforpeople.mapping.app.util.DtoMarshaller; import org.waterforpeople.mapping.app.web.dto.SurveyRestRequest; import org.waterforpeople.mapping.app.web.dto.SurveyRestResponse; import org.waterforpeople.mapping.dao.SurveyInstanceDAO; import org.waterforpeople.mapping.domain.SurveyInstance; import com.gallatinsystems.framework.gwt.dto.client.BaseDto; import com.gallatinsystems.framework.rest.AbstractRestApiServlet; import com.gallatinsystems.framework.rest.RestRequest; import com.gallatinsystems.framework.rest.RestResponse; import com.gallatinsystems.metric.dao.MetricDao; import com.gallatinsystems.metric.dao.SurveyMetricMappingDao; import com.gallatinsystems.metric.domain.Metric; import com.gallatinsystems.metric.domain.SurveyMetricMapping; import com.gallatinsystems.survey.dao.CascadeResourceDao; import com.gallatinsystems.survey.dao.QuestionDao; import com.gallatinsystems.survey.dao.QuestionGroupDao; import com.gallatinsystems.survey.dao.QuestionOptionDao; import com.gallatinsystems.survey.dao.ScoringRuleDao; import com.gallatinsystems.survey.dao.SurveyDAO; import com.gallatinsystems.survey.dao.SurveyGroupDAO; import com.gallatinsystems.survey.dao.TranslationDao; import com.gallatinsystems.survey.domain.CascadeResource; import com.gallatinsystems.survey.domain.Question; import com.gallatinsystems.survey.domain.Question.Type; import com.gallatinsystems.survey.domain.QuestionGroup; import com.gallatinsystems.survey.domain.QuestionOption; import com.gallatinsystems.survey.domain.ScoringRule; import com.gallatinsystems.survey.domain.Survey; import com.gallatinsystems.survey.domain.SurveyGroup; import com.gallatinsystems.survey.domain.Translation; import com.gallatinsystems.survey.domain.Translation.ParentType; import com.gallatinsystems.surveyal.dao.SurveyedLocaleDao; import com.gallatinsystems.surveyal.domain.SurveyedLocale; import com.google.appengine.api.datastore.KeyFactory; public class SurveyRestServlet extends AbstractRestApiServlet { private static final Logger log = Logger.getLogger(SurveyRestServlet.class.getName()); private static final String CHART_API_URL = "http://chart.apis.google.com/chart?chs=300x225&cht=p&chtt="; private static final String CHART_API_DATA_PARAM = "&chd=t:"; private static final String CHART_API_LEGEND_PARAM = "&chdl="; private SurveyGroupDAO sgDao; private SurveyDAO surveyDao; private QuestionOptionDao optionDao; private TranslationDao translationDao; private ScoringRuleDao scoringRuleDao; private QuestionGroupDao qgDao; private QuestionDao qDao; private QuestionOptionDao qoDao; private SurveyQuestionSummaryDao summaryDao; private SurveyInstanceDAO instanceDao; public SurveyRestServlet() { setMode(JSON_MODE); sgDao = new SurveyGroupDAO(); surveyDao = new SurveyDAO(); instanceDao = new SurveyInstanceDAO(); optionDao = new QuestionOptionDao(); translationDao = new TranslationDao(); scoringRuleDao = new ScoringRuleDao(); qgDao = new QuestionGroupDao(); qDao = new QuestionDao(); qoDao = new QuestionOptionDao(); summaryDao = new SurveyQuestionSummaryDao(); } private static final long serialVersionUID = 1165507917062204859L; @Override protected RestRequest convertRequest() throws Exception { HttpServletRequest req = getRequest(); RestRequest restRequest = new SurveyRestRequest(); restRequest.populateFromHttpRequest(req); return restRequest; } @Override protected RestResponse handleRequest(RestRequest req) throws Exception { SurveyRestResponse response = new SurveyRestResponse(); SurveyRestRequest surveyReq = (SurveyRestRequest) req; Boolean questionSaved = null; if (SurveyRestRequest.SAVE_QUESTION_ACTION .equals(surveyReq.getAction())) { questionSaved = saveQuestion(surveyReq); response.setCode("200"); response.setMessage("Record Saved status: " + questionSaved); } else if (SurveyRestRequest.LIST_SURVEY_GROUPS_ACTION.equals(surveyReq .getAction())) { response = listSurveyGroups(surveyReq.getCursor(), response); } else if (SurveyRestRequest.GET_SURVEY_GROUP_ACTION.equals(surveyReq .getAction())) { List<SurveyGroupDto> sgList = new ArrayList<SurveyGroupDto>(); Long surveyGroupId = null; if (surveyReq.getSurveyGroupId() != null) { surveyGroupId = surveyReq.getSurveyGroupId(); } else if (surveyReq.getSurveyId() != null) { Survey s = surveyDao.getById(surveyReq.getSurveyId()); if (s != null) { surveyGroupId = s.getSurveyGroupId(); } } if (surveyGroupId != null) { SurveyGroup sg = sgDao.getByKey(surveyGroupId); if (sg != null) { sgList.add(new SurveyGroupDto(sg)); } } response.setDtoList(sgList); } else if (SurveyRestRequest.LIST_SURVEYS_ACTION.equals(surveyReq .getAction())) { response = listSurveys(surveyReq.getSurveyGroupId(), surveyReq.getCursor(), response); } else if (SurveyRestRequest.GET_SURVEY_ACTION.equals(surveyReq .getAction())) { List<SurveyDto> sDtoList = new ArrayList<SurveyDto>(); sDtoList.add(getSurvey(new Long(surveyReq.getSurveyId()))); response.setDtoList(sDtoList); } else if (SurveyRestRequest.LIST_GROUP_ACTION.equals(surveyReq .getAction()) || SurveyRestRequest.LIST_QUESTION_GROUP_ACTION .equals(surveyReq.getAction())) { response.setDtoList(listQuestionGroups(new Long(surveyReq .getSurveyId()))); } else if (SurveyRestRequest.LIST_QUESTION_ACTION.equals(surveyReq.getAction())) { response.setDtoList(listGroupQuestionsWithLevelNames(new Long(surveyReq.getQuestionGroupId()))); } else if (SurveyRestRequest.LIST_SURVEY_QUESTIONS_ACTION.equals(surveyReq.getAction())) { response.setDtoList(listSurveyQuestionsWithLevelNames(new Long(surveyReq.getSurveyId()))); } else if (SurveyRestRequest.LIST_QUESTION_OPTIONS_ACTION.equals(surveyReq.getAction())) { response.setDtoList(listQuestionOptions(surveyReq.getQuestionId())); } else if (SurveyRestRequest.LIST_SURVEY_QUESTION_OPTIONS_ACTION.equals(surveyReq.getAction())) { response.setDtoList(listSurveyQuestionOptions(surveyReq.getSurveyId())); } else if (SurveyRestRequest.GET_SUMMARY_ACTION.equals(surveyReq .getAction())) { response.setDtoList(listSummaries(new Long(surveyReq .getQuestionId()))); } else if (SurveyRestRequest.GET_QUESTION_DETAILS_ACTION .equals(surveyReq.getAction())) { QuestionDto dto = loadQuestionDetails(new Long( surveyReq.getQuestionId())); List<BaseDto> dtoList = new ArrayList<BaseDto>(); dtoList.add(dto); response.setDtoList(dtoList); } else if (SurveyRestRequest.GET_SURVEY_INSTANCE_ACTION .equals(surveyReq.getAction())) { SurveyInstanceDto dto = findSurveyInstance(surveyReq .getInstanceId()); List<BaseDto> dtoList = new ArrayList<BaseDto>(); dtoList.add(dto); response.setDtoList(dtoList); } else if (SurveyRestRequest.DELETE_SURVEY_INSTANCE.equals(surveyReq .getAction())) { SurveyInstanceService sis = new SurveyInstanceServiceImpl(); sis.deleteSurveyInstance(surveyReq.getInstanceId()); } else if (SurveyRestRequest.GET_GRAPH_ACTION.equals(surveyReq .getAction())) { response.setUrl(constructChartUrl(surveyReq.getQuestionId(), surveyReq.getGraphType())); } else if (SurveyRestRequest.UPDATE_QUESTION_ORDER_ACTION .equals(surveyReq.getAction())) { Question q = new Question(); q.setKey(KeyFactory.createKey("Question", surveyReq.getQuestionId())); q.setOrder(surveyReq.getQuestionOrder()); List<Question> questionList = new ArrayList<Question>(); questionList.add(q); qDao.updateQuestionOrder(questionList); } return response; } /** * constructs a Google Charts API url for creating an image chart using the data in the data * store for the selected question TODO: support other graph types. Right now, we always return * pie charts * * @param questionId * @param graphType * @return */ private String constructChartUrl(Long questionId, String graphType) { StringBuilder url = new StringBuilder(CHART_API_URL); SurveyQuestionSummaryDao summaryDao = new SurveyQuestionSummaryDao(); List<SurveyQuestionSummary> summaries = summaryDao .listByQuestion(questionId.toString()); Question q = qDao.getByKey(questionId); if (q != null && summaries != null) { url.append(q.getText()).append(CHART_API_LEGEND_PARAM); StringBuilder legend = new StringBuilder(); StringBuilder data = new StringBuilder(); int i = 0; for (SurveyQuestionSummary sum : summaries) { if (i > 0) { legend.append("|"); data.append(","); } legend.append(sum.getResponse()); data.append(sum.getCount()); } url.append(legend.toString()).append(CHART_API_DATA_PARAM) .append(data.toString()); } return url.toString(); } private SurveyRestResponse listSurveys(Long surveyGroupId, String cursorString, SurveyRestResponse response) { SurveyDAO sDao = new SurveyDAO(); List<Survey> groups = sDao.listSurveysByGroup(surveyGroupId); List<SurveyDto> dtoList = new ArrayList<SurveyDto>(); cursorString = SurveyDAO.getCursor(groups); if (groups != null) { for (Survey s : groups) { SurveyDto dto = new SurveyDto(); DtoMarshaller.copyToDto(s, dto); // due to difference in property names between Survey and SurveyDto dto.setDescription(s.getDesc()); dtoList.add(dto); } } response.setDtoList(dtoList); response.setCursor(cursorString); return response; } private SurveyDto getSurvey(Long surveyId) { SurveyDAO surveyDao = new SurveyDAO(); SurveyDto dto = new SurveyDto(); Survey s = surveyDao.getById(surveyId); DtoMarshaller.copyToDto(s, dto); // difference in property names between Survey and SurveyDto dto.setDescription(s.getDesc()); return dto; } /** * sets the http code to success and writes the RestResponse as a new JSON object to the * response output stream */ @Override protected void writeOkResponse(RestResponse resp) throws Exception { getResponse().setStatus(200); new ObjectMapper().writeValue(getResponse().getWriter(), resp); } /** * gets all questionGroups for a given survey * * @param surveyId * @return */ private List<QuestionGroupDto> listQuestionGroups(Long surveyId) { TreeMap<Integer, QuestionGroup> groups = qgDao .listQuestionGroupsBySurvey(surveyId); List<QuestionGroupDto> dtoList = new ArrayList<QuestionGroupDto>(); if (groups != null) { for (QuestionGroup q : groups.values()) { QuestionGroupDto dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(q, dto); dtoList.add(dto); } } return dtoList; } /** * gets all surveyGroups for a given survey * * @param surveyId * @return */ private SurveyRestResponse listSurveyGroups(String cursorString, SurveyRestResponse response) { SurveyGroupDAO sgDao = new SurveyGroupDAO(); List<SurveyGroup> groups = sgDao.list(cursorString); List<SurveyGroupDto> dtoList = new ArrayList<SurveyGroupDto>(); cursorString = SurveyGroupDAO.getCursor(groups); if (groups != null) { for (SurveyGroup sg : groups) { SurveyGroupDto dto = new SurveyGroupDto(); DtoMarshaller.copyToDto(sg, dto); dtoList.add(dto); } } response.setDtoList(dtoList); response.setCursor(cursorString); return response; } /** * gets the full details of the base surveyInstance object (no answers) * * @param surveyInstanceId * @return */ private SurveyInstanceDto findSurveyInstance(Long surveyInstanceId) { SurveyInstance instance = instanceDao.getByKey(surveyInstanceId); SurveyInstanceDto dto = null; if (instance != null) { dto = new SurveyInstanceDto(); DtoMarshaller.copyToDto(instance, dto); SurveyedLocaleDao slDao = new SurveyedLocaleDao(); SurveyedLocale sl = null; if (instance.getSurveyedLocaleId() != null) { sl = slDao.getById(instance.getSurveyedLocaleId()); } if (sl != null) { dto.setSurveyedLocaleIdentifier(sl.getIdentifier() == null ? "" : sl .getIdentifier()); dto.setSurveyedLocaleDisplayName(sl.getDisplayName() == null ? "" : sl .getDisplayName()); } else { dto.setSurveyedLocaleIdentifier(""); dto.setSurveyedLocaleDisplayName(""); } } return dto; } /** * lists questions * @param questions * @return */ private List<QuestionDto> listQuestions(Collection<Question> questions) { List<QuestionDto> dtoList = new ArrayList<QuestionDto>(); QuestionDtoMapper mapper = new QuestionDtoMapper(); if (questions != null) { for (Question q : questions) { dtoList.add(mapper.transform(q)); } } return dtoList; } /** * add cascade level names to a list * @param questions * @return */ private void addLevelNames(List<QuestionDto> qlList) { for (QuestionDto q : qlList) { if (q.getType().equals(QuestionDto.QuestionType.CASCADE) && q.getCascadeResourceId() != null) { CascadeResource cr = new CascadeResourceDao().getByKey(q.getCascadeResourceId()); if (cr != null) { q.setLevelNames(cr.getLevelNames()); } } } } /** * lists all questions for a given questionGroup * * @param groupId * @return */ private List<QuestionDto> listGroupQuestionsWithLevelNames(Long groupId) { List<QuestionDto> qlList = listQuestions(qDao.listQuestionsByQuestionGroup(groupId, false).values()); addLevelNames(qlList); return qlList; } /** * lists all questions for a given survey * * @param surveyId * @return */ private List<QuestionDto> listSurveyQuestionsWithLevelNames(Long surveyId) { List<QuestionDto> qlList = listQuestions(qDao.listQuestionsBySurvey(surveyId)); //useless ordering addLevelNames(qlList); return qlList; } /** * lists all options for a given question * * @param questionId * @return */ private List<QuestionOptionDto> listQuestionOptions(Long questionId) { List<QuestionOption> options = qoDao.listByQuestionId(questionId); List<QuestionOptionDto> dtoList = new ArrayList<>(); QuestionOptionDtoMapper mapper = new QuestionOptionDtoMapper(); if (options != null) { for (QuestionOption option : options) { dtoList.add(mapper.transform(option)); } } return dtoList; } /** * lists all question options in the entire survey * @param surveyId * @return */ private List<QuestionOptionDto> listSurveyQuestionOptions(Long surveyId) { List<QuestionOptionDto> dtoList = new ArrayList<>(); List<Question> questions = qDao.listQuestionsInOrder(surveyId, Type.OPTION); QuestionOptionDtoMapper mapper = new QuestionOptionDtoMapper(); for (Question question : questions) { List<QuestionOption> options = qoDao.listByQuestionId(question.getKey().getId()); if (options != null) { for (QuestionOption option : options) { dtoList.add(mapper.transform(option)); } } } return dtoList; } /** * loads all details (dependency, translation, options, etc) for a single question * * @param questionId * @return */ private QuestionDto loadQuestionDetails(Long questionId) { Question q = qDao.getByKey(questionId, true); QuestionDto result = null; if (q != null) { result = SurveyServiceImpl.marshalQuestionDto(q); } return result; } /** * lists all the SurveyQuestionSummary objects associated with a given questionId * * @param questionId * @return */ private List<SurveySummaryDto> listSummaries(Long questionId) { List<SurveyQuestionSummary> summaries = summaryDao .listByQuestion(questionId.toString()); List<SurveySummaryDto> dtoList = new ArrayList<SurveySummaryDto>(); if (summaries != null) { for (SurveyQuestionSummary s : summaries) { SurveySummaryDto dto = new SurveySummaryDto(); dto.setCount(s.getCount()); dto.setResponseText(s.getResponse()); dtoList.add(dto); } } return dtoList; } private Boolean saveQuestion(SurveyRestRequest req) throws UnsupportedEncodingException { // temp fix until we put a validation rule in String questionText = req.getQuestionText(); if (questionText.length() > 499) { questionText = questionText.substring(0, 499); } // TODO: Change Impl Later if we support multiple langs String surveyName = parseLangMap(req.getSurveyName()).get("en"); String questionGroupName = parseLangMap(req.getQuestionGroupName()) .get("en"); SurveyGroup sg = null; String surveyGroupName = req.getSurveyGroupName(); if (surveyGroupName != null) { sg = sgDao.findBySurveyGroupName(surveyGroupName); } if (sg == null) { sg = new SurveyGroup(); sg.setCode(surveyGroupName); sgDao.save(sg); } Survey survey = null; String surveyPath = surveyGroupName; // survey = surveyDao.getByPath(surveyName, surveyPath); survey = surveyDao .getByParentIdAndCode(surveyName, sg.getKey().getId()); if (survey == null) { survey = new Survey(); survey.setName(surveyName); survey.setPath(surveyPath); survey.setCode(surveyName); survey.setSurveyGroupId(sg.getKey().getId()); surveyDao.save(survey); } QuestionGroup qg = null; String qgPath = surveyGroupName + "/" + surveyName; // qg = qgDao.getByPath(questionGroupName, qgPath); qg = qgDao.getByParentIdandCode(questionGroupName, survey.getKey() .getId()); if (qg == null) { qg = new QuestionGroup(); qg.setName(questionGroupName); qg.setCode(questionGroupName); qg.setPath(qgPath); qg.setOrder(req.getQuestionGroupOrder()); survey.addQuestionGroup(req.getQuestionGroupOrder(), qg); qg.setSurveyId(survey.getKey().getId()); qgDao.save(qg); } String questionPath = qgPath + "/" + questionGroupName; // Question q = qDao.getByPath(questionOrder, questionPath); Question q = qDao.getByQuestionGroupId(qg.getKey().getId(), questionText); Integer questionOrder = req.getQuestionOrder(); // since questions can have the same name, it only counts as a dupe if // the order matches if (q == null || !questionOrder.equals(q.getOrder())) { q = new Question(); } else { // if the question already exists, delete it's children so we don't // get duplicates if (Question.Type.OPTION == q.getType() || Question.Type.STRENGTH == q.getType()) { optionDao.deleteOptionsForQuestion(q.getKey().getId()); } translationDao.deleteTranslationsForParent(q.getKey().getId(), ParentType.QUESTION_TEXT); scoringRuleDao.deleteRulesForQuestion(q.getKey().getId()); } q.setText(parseLangMap(questionText).get("en")); q.setPath(questionPath); q.setOrder(questionOrder); q.setReferenceId(questionOrder.toString()); q.setQuestionGroupId(qg.getKey().getId()); q.setSurveyId(survey.getKey().getId()); for (Map.Entry<String, String> qTextItem : parseLangMap(questionText) .entrySet()) { if (!qTextItem.getKey().equals("en")) { Translation t = new Translation(); t.setLanguageCode(qTextItem.getKey()); t.setText(qTextItem.getValue()); t.setParentType(ParentType.QUESTION_TEXT); q.addTranslation(t); } } String questionType = req.getQuestionType(); if (questionType.equals("GEO")) { q.setType(Question.Type.GEO); } else if (questionType.equals("FREE_TEXT")) { q.setType(Question.Type.FREE_TEXT); } else if (questionType.equals("OPTION") || questionType.equals("STRENGTH")) { q.setAllowMultipleFlag(req.getAllowMultipleFlag()); q.setAllowOtherFlag(req.getAllowOtherFlag()); if (questionType.equals("OPTION")) { q.setType(Type.OPTION); } else { q.setType(Type.STRENGTH); } int i = 1; for (QuestionOptionContainer qoc : parseQuestionOption(req .getOptions())) { QuestionOption qo = new QuestionOption(); qo.setText(qoc.getOption()); qo.setCode(qoc.getOption()); qo.setOrder(i++); if (qoc.getAltLangs() != null) { for (QuestionOptionContainer altOpt : qoc.getAltLangs()) { Translation t = new Translation(); t.setLanguageCode(altOpt.langCode); t.setText(altOpt.getOption()); t.setParentType(ParentType.QUESTION_TEXT); qo.addTranslation(t); } } q.addQuestionOption(qo); } } else if (questionType.equals("PHOTO")) { q.setType(Question.Type.PHOTO); } else if (questionType.equals("NUMBER")) { q.setType(Question.Type.NUMBER); q.setAllowDecimal(req.getAllowDecimal()); q.setAllowSign(req.getAllowSign()); q.setMinVal(req.getMinVal()); q.setMaxVal(req.getMaxVal()); } else if (questionType.equals("VIDEO")) { q.setType(Question.Type.VIDEO); } if (req.getMandatoryFlag() != null) { q.setMandatoryFlag(req.getMandatoryFlag()); } // deal with options and dependencies String dependentQuestion = req.getDependQuestion(); if (dependentQuestion != null && dependentQuestion.trim().length() > 1) { String[] parts = dependentQuestion.split("\\|"); Integer quesitonOrderId = new Integer(parts[0]); String answer = parts[1]; Question dependsOnQuestion = qDao.getByGroupIdAndOrder(qg.getKey() .getId(), quesitonOrderId); if (dependsOnQuestion != null) { q.setDependentFlag(true); q.setDependentQuestionId(dependsOnQuestion.getKey().getId()); q.setDependentQuestionAnswer(answer); } } else { q.setDependentFlag(false); } q = qDao.save(q); if (req.getMetricName() != null && req.getMetricName().trim().length() > 0) { MetricDao metricDao = new MetricDao(); List<Metric> metrics = metricDao.listMetrics(req.getMetricName(), req.getMetricGroup(), null, null, null); if (metrics != null && metrics.size() > 0) { SurveyMetricMappingDao mappingDao = new SurveyMetricMappingDao(); SurveyMetricMapping mapping = new SurveyMetricMapping(); mapping.setSurveyId(q.getSurveyId()); mapping.setQuestionGroupId(q.getQuestionGroupId()); mapping.setSurveyQuestionId(q.getKey().getId()); mapping.setMetricId(metrics.get(0).getKey().getId()); mappingDao.save(mapping); } } // now update the question id in the children and save if (q.getQuestionOptionMap() != null) { for (QuestionOption opt : q.getQuestionOptionMap().values()) { opt.setQuestionId(q.getKey().getId()); if (opt.getText() != null && opt.getText().contains(",")) { opt.setText(opt.getText().replaceAll(",", "-")); if (opt.getCode() != null) { opt.setCode(opt.getCode().replaceAll(",", "-")); } } optionDao.save(opt); if (opt.getTranslationMap() != null) { for (Translation t : opt.getTranslationMap().values()) { t.setParentId(opt.getKey().getId()); t.setParentType(ParentType.QUESTION_OPTION); translationDao.save(t); } } } } if (q.getTranslationMap() != null) { for (Translation t : q.getTranslationMap().values()) { t.setParentId(q.getKey().getId()); t.setParentType(ParentType.QUESTION_TEXT); translationDao.save(t); } } String scoring = req.getScoring(); if (scoring != null && scoring.trim().length() > 0 && !"null".equalsIgnoreCase(scoring)) { List<ScoringRule> rules = parseScoring(scoring, q.getKey().getId()); scoringRuleDao.save(rules); q.setScoringRules(rules); } qg.addQuestion(questionOrder, q); qgDao.save(qg); surveyDao.save(survey); sgDao.save(sg); log.info("Just saved " + surveyGroupName + ":" + surveyName + ":" + questionGroupName + ":" + questionOrder); return true; } private ArrayList<QuestionOptionContainer> parseQuestionOption( String questionOption) { ArrayList<QuestionOptionContainer> qoList = new ArrayList<QuestionOptionContainer>(); String[] parts = questionOption.split("#"); if (parts != null && parts.length == 1) { // if parts is only 1 then we either have 1 option with multiple // languages or only 1 option if (!parts[0].contains("|")) { // if there is no pipe, then it's 1 language only String[] options = parts[0].split(";"); for (int i = 0; i < options.length; i++) { QuestionOptionContainer opt = parseEnglishOnlyOption(options[i] .trim()); if (opt != null) { qoList.add(opt); } } } else { // if we're here, we have only 1 option but in multiple // languages qoList.add(composeContainer(parts[0])); } } else if (parts != null) { for (String option : parts) { qoList.add(composeContainer(option)); } } return qoList; } private QuestionOptionContainer composeContainer(String option) { Map<String, String> langVals = parseLangMap(option); String english = langVals.remove("en"); QuestionOptionContainer container = new QuestionOptionContainer("en", english); for (Map.Entry<String, String> entry : langVals.entrySet()) { container.addAltLang(new QuestionOptionContainer(entry.getKey(), entry.getValue())); } return container; } /** * constructs a translation map based on the contents of the lang param. The parameters are * tuples of <b>langCode|text</b> with multiple tuples separated by a ; * * @param scoringParam * @return */ private HashMap<String, String> parseLangMap(String unparsedLangParam) { HashMap<String, String> langMap = new HashMap<String, String>(); String[] parts = unparsedLangParam.split(";"); for (String item : parts) { String[] langParts = item.split("\\|"); if (langParts.length == 1) { // if there is no language indicator, assume it's English langMap.put("en", langParts[0].trim()); } else { langMap.put(langParts[0].trim(), langParts[1].trim()); } } return langMap; } /** * constructs a list of ScoringRules based on the contents of the scoringParam string. This * string is a packed-value string consisting of the following 3-tuples: <b>min|max|value</b> * Multiple rules are delimited by a ; * * @param scoringParam * @return */ private List<ScoringRule> parseScoring(String scoringParam, Long questionId) { List<ScoringRule> rules = new ArrayList<ScoringRule>(); String[] parts = scoringParam.split(";"); for (String item : parts) { String[] ruleParts = item.split("\\|"); // right now, we only support NUMERIC type. Change this once we // support other types of rules. if (ruleParts.length == 3) { rules.add(new ScoringRule(questionId, "NUMERIC", ruleParts[0], ruleParts[1], ruleParts[2])); } else if (ruleParts.length > 3) { rules.add(new ScoringRule(questionId, ruleParts[0], ruleParts[1], ruleParts[2], ruleParts[3])); } else { log.log(Level.WARNING, "Scoring rule cannot be parsed: " + scoringParam); } } return rules; } /** * handles parsing of the "old" style question options that only have a single language. The * language will be defaulted to English * * @param option * @return */ private QuestionOptionContainer parseEnglishOnlyOption(String option) { QuestionOptionContainer opt = null; String[] val = option.split("\\|"); String value = null; if (val.length == 2) { value = val[1]; } else if (val.length == 1) { value = val[0]; } if (value != null) { opt = new QuestionOptionContainer("en", value); } return opt; } private class QuestionOptionContainer { private String langCode = null; private String option = null; private List<QuestionOptionContainer> altLangs; public QuestionOptionContainer(String langCode, String optionText) { this.setLangCode(langCode); this.setOption(optionText); } public List<QuestionOptionContainer> getAltLangs() { return altLangs; } public void addAltLang(QuestionOptionContainer container) { if (altLangs == null) { altLangs = new ArrayList<QuestionOptionContainer>(); } altLangs.add(container); } public void setLangCode(String langCode) { this.langCode = langCode; } public void setOption(String option) { this.option = option; } public String getOption() { return option; } } }