/*
* Copyright (C) 2012-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.rest;
import static com.gallatinsystems.common.Constants.ANCESTOR_IDS_FIELD;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
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.analytics.dao.SurveyInstanceSummaryDao;
import org.waterforpeople.mapping.analytics.domain.SurveyInstanceSummary;
import org.waterforpeople.mapping.app.gwt.client.survey.SurveyDto;
import org.waterforpeople.mapping.app.util.DtoMarshaller;
import org.waterforpeople.mapping.app.web.dto.SurveyTaskRequest;
import org.waterforpeople.mapping.app.web.rest.dto.RestStatusDto;
import org.waterforpeople.mapping.app.web.rest.dto.SurveyPayload;
import org.waterforpeople.mapping.dao.QuestionAnswerStoreDao;
import com.gallatinsystems.survey.dao.SurveyDAO;
import com.gallatinsystems.survey.dao.SurveyUtils;
import com.gallatinsystems.survey.domain.Survey;
import com.gallatinsystems.surveyal.dao.SurveyalValueDao;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
@Controller
@RequestMapping("/surveys")
public class SurveyRestService {
private static final Logger log = Logger.getLogger(SurveyRestService.class.getName());
@Inject
private SurveyDAO surveyDao;
@Inject
private SurveyInstanceSummaryDao sisDao;
@Inject
private SurveyalValueDao svDao;
@Inject
private QuestionAnswerStoreDao qasDao;
// TODO put in meta information?
// list all surveys
public Map<String, Object> listSurveys() {
final Map<String, Object> response = new HashMap<String, Object>();
List<SurveyDto> results = new ArrayList<SurveyDto>();
SurveyInstanceSummary sis = null;
List<Survey> surveys = surveyDao.listAllFilteredByUserAuthorization();
if (surveys != null) {
for (Survey s : surveys) {
SurveyDto dto = new SurveyDto();
DtoMarshaller.copyToDto(s, dto);
// add surveyInstance Count
sis = sisDao.findBySurveyId(s.getKey().getId());
if (sis != null) {
dto.setInstanceCount(sis.getCount());
}
// needed because of different names for description in survey
// and surveyDto
dto.setDescription(s.getDesc());
results.add(dto);
}
}
response.put("surveys", results);
return response;
}
@RequestMapping(method = RequestMethod.GET, value = "")
@ResponseBody
public Map<String, Object> listSurveysByGroupId(
@RequestParam(value = "surveyGroupId", defaultValue = "") Long surveyGroupId,
@RequestParam(value = "ids[]", defaultValue = "") Long[] ids,
@RequestParam(value = "preflight", defaultValue = "") String preflight,
@RequestParam(value = "surveyId", defaultValue = "") Long surveyId) {
// If none of the optional query params are specified, return all surveys
if (surveyGroupId == null
&& ids[0] == null
&& preflight.equals("")
&& surveyId == null) {
return listSurveys();
}
final Map<String, Object> response = new HashMap<String, Object>();
List<SurveyDto> results = new ArrayList<SurveyDto>();
List<Survey> surveys = null;
SurveyInstanceSummary sis = null;
RestStatusDto statusDto = new RestStatusDto();
statusDto.setStatus("");
statusDto.setMessage("");
// if this is a pre-flight delete check, handle that
if (preflight != null && preflight.equals("delete") && surveyId != null) {
statusDto.setStatus("preflight-delete-survey");
statusDto.setMessage("cannot_delete");
if (qasDao.listBySurvey(surveyId).size() == 0
&& svDao.listBySurvey(surveyId, null, null).size() == 0) {
statusDto.setMessage("can_delete");
statusDto.setKeyId(surveyId);
}
response.put("surveys", results);
response.put("meta", statusDto);
return response;
}
// if we are here, it is a regular request and not preflight
if (surveyGroupId != null) {
surveys = surveyDao.listSurveysByGroup(surveyGroupId);
} else if (ids[0] != null) {
surveys = surveyDao.listByKeys(ids);
}
if (surveys != null) {
for (Survey s : surveyDao.filterByUserAuthorizationObjectId(surveys)) {
SurveyDto dto = new SurveyDto();
DtoMarshaller.copyToDto(s, dto);
// add surveyInstance Count
sis = sisDao.findBySurveyId(s.getKey().getId());
if (sis != null) {
dto.setInstanceCount(sis.getCount());
}
// needed because of different names for description in survey
// and surveyDto
dto.setDescription(s.getDesc());
results.add(dto);
}
}
response.put("surveys", results);
return response;
}
// find a single survey by the surveyId
@RequestMapping(method = RequestMethod.GET, value = "/{id}")
@ResponseBody
public Map<String, SurveyDto> findSurvey(@PathVariable("id") Long id) {
final Map<String, SurveyDto> response = new HashMap<String, SurveyDto>();
Survey s = surveyDao.getByKey(id);
SurveyDto dto = null;
SurveyInstanceSummary sis = null;
if (s != null) {
dto = new SurveyDto();
DtoMarshaller.copyToDto(s, dto);
// add surveyInstance Count
sis = sisDao.findBySurveyId(s.getKey().getId());
if (sis != null) {
dto.setInstanceCount(sis.getCount());
}
// needed because of different names for description in survey and
// surveyDto
dto.setDescription(s.getDesc());
}
response.put("survey", dto);
return response;
}
/**
* Spawns a task to delete a survey by survey id
*
* @param surveyId
* @return
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{id}")
@ResponseBody
public Map<String, RestStatusDto> deleteSurveyById(
@PathVariable("id") Long surveyId) {
final Map<String, RestStatusDto> response = new HashMap<String, RestStatusDto>();
Survey survey = surveyDao.getByKey(surveyId);
RestStatusDto statusDto = null;
statusDto = new RestStatusDto();
statusDto.setStatus("failed");
// check if survey exists in the datastore
if (survey != null) {
try {
TaskOptions deleteSurveyTask = TaskOptions.Builder
.withUrl("/app_worker/surveytask")
.param(SurveyTaskRequest.ACTION_PARAM,
SurveyTaskRequest.DELETE_SURVEY_ACTION)
.param(SurveyTaskRequest.ID_PARAM, surveyId.toString());
QueueFactory.getQueue("deletequeue").add(deleteSurveyTask);
statusDto.setStatus("ok");
statusDto.setMessage("deleted");
} catch (Exception e) {
statusDto.setStatus("failed");
statusDto.setMessage(e.getMessage());
}
}
response.put("meta", statusDto);
return response;
}
// update existing survey
@RequestMapping(method = RequestMethod.PUT, value = "/{id}")
@ResponseBody
public Map<String, Object> saveExistingSurvey(
@RequestBody SurveyPayload payLoad) {
final SurveyDto requestDto = payLoad.getSurvey();
final Map<String, Object> response = new HashMap<String, Object>();
SurveyDto responseDto = null;
RestStatusDto statusDto = new RestStatusDto();
statusDto.setStatus("failed");
// if the POST data contains a valid surveyDto, continue. Otherwise,
// server will respond with 400 Bad Request
if (requestDto != null && requestDto.getKeyId() != null) {
Survey s = surveyDao.getByKey(requestDto.getKeyId());
if (s != null) {
// copy the properties, except the createdDateTime property,
// because it is set in the Dao.
BeanUtils.copyProperties(requestDto, s, new String[] {
"createdDateTime", "status", "sector", "version",
"lastUpdateDateTime", "description",
"instanceCount", ANCESTOR_IDS_FIELD
});
s.setAncestorIds(SurveyUtils.retrieveAncestorIds(s));
s.setDesc(requestDto.getDescription());
String name = s.getName();
if (name != null) {
String trimmedName = name.replaceAll(",", " ").trim();
s.setName(trimmedName);
s.setCode(trimmedName);
s.setPath(SurveyUtils.fixPath(s.getPath(), trimmedName));
}
if (requestDto.getStatus() != null) {
// increment version for surveys already published
if (s.getStatus().equals(Survey.Status.PUBLISHED)
&& !s.getStatus().equals(
Survey.Status.valueOf(requestDto.getStatus()))) {
s.incrementVersion();
}
}
s.setStatus(Survey.Status.NOT_PUBLISHED);
if (requestDto.getSector() != null) {
s.setSector(Survey.Sector.valueOf(requestDto.getSector()));
}
if (!requestDto.getVersion().equals(s.getVersion().toString())) {
log.log(Level.WARNING, "Survey version does not match (dashboard="
+ requestDto.getVersion() + " datastore=" + s.getVersion() + ")");
}
s = surveyDao.save(s);
responseDto = new SurveyDto();
DtoMarshaller.copyToDto(s, responseDto);
responseDto.setDescription(s.getDesc());
statusDto.setStatus("ok");
}
}
response.put("meta", statusDto);
response.put("survey", responseDto);
return response;
}
// create new survey
@RequestMapping(method = RequestMethod.POST, value = "")
@ResponseBody
public Map<String, Object> saveNewSurvey(@RequestBody SurveyPayload payLoad) {
final SurveyDto surveyDto = payLoad.getSurvey();
final Map<String, Object> response = new HashMap<String, Object>();
// if the POST data contains a valid surveyDto, continue. Otherwise,
// server will respond with 400 Bad Request
if (surveyDto == null) {
return getErrorResponse();
}
Survey s = null;
if (surveyDto.getSourceId() == null) {
s = newSurvey(surveyDto);
} else {
s = copySurvey(surveyDto);
}
if (s == null) {
return getErrorResponse();
}
final RestStatusDto statusDto = new RestStatusDto();
final SurveyDto dto = new SurveyDto();
DtoMarshaller.copyToDto(s, dto);
dto.setDescription(s.getDesc());
statusDto.setStatus("ok");
response.put("meta", statusDto);
response.put("survey", dto);
return response;
}
private Survey newSurvey(SurveyDto dto) {
final Survey result = surveyDao.save(marshallToDomain(dto));
return result;
}
private Survey copySurvey(SurveyDto dto) {
final Survey source = surveyDao.getById(dto.getSourceId());
if (source == null) {
// source survey not found, the getById already logged the problem
return null;
}
return SurveyUtils.copySurvey(source, dto);
}
private Survey marshallToDomain(SurveyDto dto) {
final Survey s = new Survey();
// copy the properties, except the createdDateTime property, because
// it is set in the Dao.
BeanUtils.copyProperties(dto, s, new String[] {
"createdDateTime",
"status", "sector", "version", "lastUpdateDateTime",
"displayName", "questionGroupList", "instanceCount", ANCESTOR_IDS_FIELD
});
if (dto.getStatus() != null) {
s.setStatus(Survey.Status
.valueOf(dto.getStatus().toString()));
}
if (dto.getSector() != null) {
s.setSector(Survey.Sector.valueOf(dto.getSector()));
}
s.setAncestorIds(SurveyUtils.retrieveAncestorIds(s));
// ignore version number sent by Dashboard and initialise
s.getVersion();
// Make sure that code and name are the same
s.setCode(s.getName());
return s;
}
private Map<String, Object> getErrorResponse() {
final Map<String, Object> response = new HashMap<String, Object>();
final RestStatusDto statusDto = new RestStatusDto();
statusDto.setStatus("failed");
response.put("meta", statusDto);
response.put("survey", null);
return response;
}
}