/* * Copyright (C) 2012-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.app.web.rest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.apache.commons.lang.ArrayUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.waterforpeople.mapping.app.gwt.client.survey.QuestionGroupDto; import org.waterforpeople.mapping.app.util.DtoMarshaller; import org.waterforpeople.mapping.app.web.dto.DataProcessorRequest; import org.waterforpeople.mapping.app.web.dto.SurveyTaskRequest; import org.waterforpeople.mapping.app.web.rest.dto.QuestionGroupPayload; import org.waterforpeople.mapping.app.web.rest.dto.RestStatusDto; import org.waterforpeople.mapping.dao.QuestionAnswerStoreDao; import com.gallatinsystems.common.Constants; import com.gallatinsystems.survey.dao.QuestionDao; import com.gallatinsystems.survey.dao.QuestionGroupDao; import com.gallatinsystems.survey.domain.Question; import com.gallatinsystems.survey.domain.QuestionGroup; import com.gallatinsystems.surveyal.dao.SurveyalValueDao; import com.google.appengine.api.backends.BackendServiceFactory; import com.google.appengine.api.taskqueue.Queue; import com.google.appengine.api.taskqueue.QueueFactory; import com.google.appengine.api.taskqueue.TaskOptions; @Controller @RequestMapping("/question_groups") public class QuestionGroupRestService { @Inject private QuestionGroupDao questionGroupDao; @Inject private QuestionDao questionDao; @Inject private SurveyalValueDao svDao; @Inject private QuestionAnswerStoreDao qasDao; // Properties to exclude when copying question groups private static final String[] QUESTION_GROUP_COPY_EXCLUDED_PROPS = (String[]) ArrayUtils.add( Constants.EXCLUDED_PROPERTIES, "status"); // TODO put in meta information? // list all questionGroups @RequestMapping(method = RequestMethod.GET, value = "/all") @ResponseBody public Map<String, List<QuestionGroupDto>> listQuestionGroups() { final Map<String, List<QuestionGroupDto>> response = new HashMap<String, List<QuestionGroupDto>>(); List<QuestionGroupDto> results = new ArrayList<QuestionGroupDto>(); List<QuestionGroup> questionGroups = questionGroupDao .list(Constants.ALL_RESULTS); if (questionGroups != null) { for (QuestionGroup s : questionGroups) { QuestionGroupDto dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(s, dto); // needed because of different names for description in // questionGroup // and questionGroupDto dto.setDescription(s.getDesc()); results.add(dto); } } response.put("question_groups", results); return response; } /** * list questionGroups by survey id Perform preflight check for deletion of question group * * @param surveyId * @param preflight * @param questionGroupId * @return */ @RequestMapping(method = RequestMethod.GET, value = "") @ResponseBody public Map<String, Object> listQuestionGroupBySurvey( @RequestParam(value = "surveyId", defaultValue = "") Long surveyId, @RequestParam(value = "preflight", defaultValue = "") String preflight, @RequestParam(value = "questionGroupId", defaultValue = "") Long questionGroupId) { final Map<String, Object> response = new HashMap<String, Object>(); final RestStatusDto statusDto = new RestStatusDto(); statusDto.setStatus(""); statusDto.setMessage(""); if (preflight.equals("delete") && questionGroupId != null) { statusDto.setStatus("preflight-delete-questiongroup"); statusDto.setMessage("can_delete"); statusDto.setKeyId(questionGroupId); for (Question q : questionDao.listQuestionsByQuestionGroup(questionGroupId, Boolean.FALSE).values()) { if (qasDao.listByQuestion(q.getKey().getId()).size() > 0 && svDao.listByQuestion(q.getKey().getId()).size() > 0) { statusDto.setMessage("cannot_delete"); statusDto.setKeyId(null); } } } final List<QuestionGroupDto> results = new ArrayList<QuestionGroupDto>(); if (surveyId != null) { final List<QuestionGroup> questionGroups = questionGroupDao .listQuestionGroupBySurvey(surveyId); if (questionGroups != null) { for (QuestionGroup s : questionGroups) { QuestionGroupDto dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(s, dto); // needed because of different names for description in // questionGroup // and questionGroupDto dto.setDescription(s.getDesc()); results.add(dto); } } } // in this case, we are just trying to get a single question group if (questionGroupId != null && preflight.isEmpty()) { QuestionGroup qg = questionGroupDao.getByKey(questionGroupId); if (qg != null) { QuestionGroupDto dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(qg, dto); dto.setDescription(qg.getDesc()); results.add(dto); } } response.put("question_groups", results); response.put("meta", statusDto); return response; } // find a single questionGroup by the questionGroupId @RequestMapping(method = RequestMethod.GET, value = "/{id}") @ResponseBody public Map<String, QuestionGroupDto> findQuestionGroup( @PathVariable("id") Long id) { final Map<String, QuestionGroupDto> response = new HashMap<String, QuestionGroupDto>(); QuestionGroup s = questionGroupDao.getByKey(id); QuestionGroupDto dto = null; if (s != null) { dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(s, dto); // needed because of different names for description in // questionGroup and questionGroupDto dto.setDescription(s.getDesc()); } response.put("question_group", dto); return response; } // delete questionGroup by id @RequestMapping(method = RequestMethod.DELETE, value = "/{id}") @ResponseBody public Map<String, RestStatusDto> deleteQuestionGroupById( @PathVariable("id") Long questionGroupId) { final Map<String, RestStatusDto> response = new HashMap<String, RestStatusDto>(); QuestionGroup group = questionGroupDao.getByKey(questionGroupId); RestStatusDto statusDto = null; statusDto = new RestStatusDto(); statusDto.setStatus("failed"); // check if questionGroup exists in the datastore if (group != null) { try { TaskOptions deleteQuestionGroupTask = TaskOptions.Builder .withUrl("/app_worker/surveytask") .param(SurveyTaskRequest.ACTION_PARAM, SurveyTaskRequest.DELETE_QUESTION_GROUP_ACTION) .param(SurveyTaskRequest.ID_PARAM, Long.toString(group.getKey().getId())); QueueFactory.getQueue("deletequeue").add(deleteQuestionGroupTask); statusDto.setStatus("ok"); statusDto.setMessage("deleted"); } catch (Exception e) { statusDto.setStatus("failed"); statusDto.setMessage(e.getMessage()); } } response.put("meta", statusDto); return response; } // update existing questionGroup @RequestMapping(method = RequestMethod.PUT, value = "/{id}") @ResponseBody public Map<String, Object> saveExistingQuestionGroup( @RequestBody QuestionGroupPayload payLoad) { final QuestionGroupDto questionGroupDto = payLoad.getQuestion_group(); final Map<String, Object> response = new HashMap<String, Object>(); QuestionGroupDto dto = null; RestStatusDto statusDto = new RestStatusDto(); statusDto.setStatus("failed"); statusDto.setMessage("Cannot find question group"); // if the POST data contains a valid questionGroupDto, continue. // Otherwise, // server will respond with 400 Bad Request if (questionGroupDto != null) { Long keyId = questionGroupDto.getKeyId(); QuestionGroup qg; // if the questionGroupDto has a key, try to get the questionGroup. if (keyId != null) { qg = questionGroupDao.getByKey(keyId); // if we find the questionGroup, update it's properties if (qg != null) { // Integer origOrder = qg.getOrder(); BeanUtils.copyProperties(questionGroupDto, qg, new String[] { "createdDateTime", "status" }); qg = questionGroupDao.save(qg); dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(qg, dto); statusDto.setStatus("ok"); statusDto.setMessage(""); } } } response.put("meta", statusDto); response.put("question_group", dto); return response; } // create new questionGroup @RequestMapping(method = RequestMethod.POST, value = "") @ResponseBody public Map<String, Object> saveNewQuestionGroup( @RequestBody QuestionGroupPayload payLoad) { final QuestionGroupDto questionGroupDto = payLoad.getQuestion_group(); RestStatusDto statusDto = new RestStatusDto(); statusDto.setStatus("failed"); statusDto.setMessage("Cannot create question group"); final Map<String, Object> response = new HashMap<String, Object>(); response.put("meta", statusDto); response.put("question_group", null); // if the POST data contains a valid questionGroupDto, continue. // Otherwise, server will respond with 400 Bad Request if (questionGroupDto == null) { return response; } QuestionGroup questionGroup = null; // deal with copying a question group if (questionGroupDto.getSourceId() != null) { // copy question group questionGroup = copyGroup(questionGroupDto); } else { // new question group questionGroup = new QuestionGroup(); BeanUtils.copyProperties(questionGroupDto, questionGroup, new String[] { "createdDateTime", "status" }); questionGroup.setStatus(QuestionGroup.Status.valueOf(questionGroupDto.getStatus())); questionGroup = questionGroupDao.save(questionGroup); } if (questionGroup == null) { return response; } QuestionGroupDto dto = new QuestionGroupDto(); DtoMarshaller.copyToDto(questionGroup, dto); statusDto.setStatus("ok"); statusDto.setMessage(""); response.put("meta", statusDto); response.put("question_group", dto); return response; } /** * Copy a question group within a survey * * @param questionGroupDto * @return */ private QuestionGroup copyGroup(QuestionGroupDto questionGroupDto) { // need a temp group to avoid state sharing exception QuestionGroup tmpGroup = new QuestionGroup(); BeanUtils.copyProperties(questionGroupDto, tmpGroup, QUESTION_GROUP_COPY_EXCLUDED_PROPS); tmpGroup.setStatus(QuestionGroup.Status.COPYING); final QuestionGroup copyGroup = questionGroupDao.save(tmpGroup); // schedule deep copy final Queue queue = QueueFactory.getDefaultQueue(); final TaskOptions options = TaskOptions.Builder .withUrl("/app_worker/dataprocessor") .param(DataProcessorRequest.ACTION_PARAM, DataProcessorRequest.COPY_QUESTION_GROUP) .param(DataProcessorRequest.QUESTION_GROUP_ID_PARAM, String.valueOf(copyGroup.getKey().getId())) .param(DataProcessorRequest.SOURCE_PARAM, questionGroupDto.getSourceId().toString()) .header("Host", BackendServiceFactory.getBackendService() .getBackendAddress("dataprocessor")); queue.add(options); return copyGroup; } }