/*
* Copyright (C) 2010-2016 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.domain;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import org.akvo.flow.domain.DataUtils;
import org.akvo.flow.domain.SecuredObject;
import org.apache.commons.lang.StringUtils;
import org.waterforpeople.mapping.analytics.dao.SurveyQuestionSummaryDao;
import org.waterforpeople.mapping.analytics.domain.SurveyQuestionSummary;
import org.waterforpeople.mapping.app.gwt.client.survey.QuestionDto.QuestionType;
import org.waterforpeople.mapping.dao.SurveyInstanceDAO;
import com.gallatinsystems.device.domain.DeviceFiles;
import com.gallatinsystems.framework.domain.BaseDomain;
import com.gallatinsystems.gis.map.MapUtils;
import com.gallatinsystems.survey.dao.QuestionDao;
import com.gallatinsystems.survey.dao.SurveyDAO;
import com.gallatinsystems.survey.domain.Question;
import static com.gallatinsystems.common.Constants.MAX_LENGTH;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class SurveyInstance extends BaseDomain implements SecuredObject {
private static final long serialVersionUID = 5840846001731305734L;
@Persistent
private Long userID;
@Persistent
private Date collectionDate;
private Long deviceFileId;
@NotPersistent
private DeviceFiles deviceFile;
@NotPersistent
private List<QuestionAnswerStore> questionAnswersStore;
private Long surveyId;
private String deviceIdentifier;
private String submitterName;
private String approvedFlag;
private String uuid;
private String approximateLocationFlag;
private Long surveyedLocaleId;
private String surveyedLocaleIdentifier;
private String surveyedLocaleDisplayName;
private String countryCode;
private String community;
private String localeGeoLocation;
private String sublevel1;
private String sublevel2;
private String sublevel3;
private String sublevel4;
private String sublevel5;
private String sublevel6;
private Long surveyalTime;
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getCommunity() {
return community;
}
public void setCommunity(String community) {
this.community = community;
}
public String getSublevel1() {
return sublevel1;
}
public void setSublevel1(String sublevel1) {
this.sublevel1 = sublevel1;
}
public String getSublevel2() {
return sublevel2;
}
public void setSublevel2(String sublevel2) {
this.sublevel2 = sublevel2;
}
public String getSublevel3() {
return sublevel3;
}
public void setSublevel3(String sublevel3) {
this.sublevel3 = sublevel3;
}
public String getSublevel4() {
return sublevel4;
}
public void setSublevel4(String sublevel4) {
this.sublevel4 = sublevel4;
}
public String getSublevel5() {
return sublevel5;
}
public void setSublevel5(String sublevel5) {
this.sublevel5 = sublevel5;
}
public String getSublevel6() {
return sublevel6;
}
public void setSublevel6(String sublevel6) {
this.sublevel6 = sublevel6;
}
public Long getSurveyedLocaleId() {
return surveyedLocaleId;
}
public void setSurveyedLocaleId(Long surveyedLocaleId) {
this.surveyedLocaleId = surveyedLocaleId;
}
@Deprecated
public String getApproximateLocationFlag() {
return approximateLocationFlag;
}
@Deprecated
public void setApproximateLocationFlag(String approximateLocationFlag) {
this.approximateLocationFlag = approximateLocationFlag;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getApprovedFlag() {
return approvedFlag;
}
public void setApprovedFlag(String approvedFlag) {
this.approvedFlag = approvedFlag;
}
public Long getSurveyId() {
return surveyId;
}
public void setSurveyId(Long surveyId) {
this.surveyId = surveyId;
}
public Long getUserID() {
return userID;
}
public void setUserID(Long userID) {
this.userID = userID;
}
public Date getCollectionDate() {
return collectionDate;
}
public void setCollectionDate(Date collectionDate) {
this.collectionDate = collectionDate;
}
public DeviceFiles getDeviceFile() {
return deviceFile;
}
public void setDeviceFile(DeviceFiles deviceFile) {
this.deviceFile = deviceFile;
if (deviceFile.getKey() != null)
deviceFileId = deviceFile.getKey().getId();
}
public List<QuestionAnswerStore> getQuestionAnswersStore() {
return questionAnswersStore;
}
public void setQuestionAnswersStore(
List<QuestionAnswerStore> questionAnswersStore) {
this.questionAnswersStore = questionAnswersStore;
}
public void setSubmitterName(String name) {
submitterName = name;
}
public String getSubmitterName() {
return submitterName;
}
public void setDeviceIdentifier(String id) {
deviceIdentifier = id;
}
public String getDeviceIdentifier() {
return deviceIdentifier;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
String newLine = System.getProperty("line.separator");
result.append(this.getClass().getName());
result.append(" Object {");
result.append(newLine);
// determine fields declared in this class only (no fields of
// superclass)
Field[] fields = this.getClass().getDeclaredFields();
// print field names paired with their values
for (Field field : fields) {
result.append(" ");
try {
result.append(field.getName());
result.append(": ");
// requires access to private field:
result.append(field.get(this));
} catch (IllegalAccessException ex) {
System.out.println(ex);
}
result.append(newLine);
}
result.append("}");
return result.toString();
}
public void setDeviceFileId(Long deviceFileId) {
this.deviceFileId = deviceFileId;
}
public Long getDeviceFileId() {
return deviceFileId;
}
public void setSurveyalTime(Long survetalTime) {
this.surveyalTime = survetalTime;
}
public Long getSurveyalTime() {
return surveyalTime;
}
public String getSurveyedLocaleIdentifier() {
return surveyedLocaleIdentifier;
}
public void setSurveyedLocaleIdentifier(String surveyedLocaleIdentifier) {
this.surveyedLocaleIdentifier = surveyedLocaleIdentifier;
}
public String getLocaleGeoLocation() {
return localeGeoLocation;
}
public void setLocaleGeoLocation(String localeGeoLocation) {
this.localeGeoLocation = localeGeoLocation;
}
public String getSurveyedLocaleDisplayName() {
return surveyedLocaleDisplayName;
}
public void setSurveyedLocaleDisplayName(String surveyedLocaleDisplayName) {
this.surveyedLocaleDisplayName = surveyedLocaleDisplayName.length() > MAX_LENGTH ? surveyedLocaleDisplayName
.substring(0, MAX_LENGTH).trim()
: surveyedLocaleDisplayName;
}
/**
* Extract geolocation information from a survey instance
*
* @param surveyInstance
* @return a map containing latitude and longitude entries null if a null string is provided
*/
public static Map<String, Object> retrieveGeoLocation(
SurveyInstance surveyInstance) throws NumberFormatException {
Map<String, Object> geoLocationMap = null;
// retrieve geo location string
String geoLocationString = null;
// if the GEO information was present as Meta data, get it from there
if (StringUtils.isNotBlank(surveyInstance.getLocaleGeoLocation())) {
geoLocationString = surveyInstance.getLocaleGeoLocation();
} else {
// else, try to look for a GEO question
List<QuestionAnswerStore> geoAnswers = new SurveyInstanceDAO()
.listQuestionAnswerStoreByType(surveyInstance.getKey()
.getId(), QuestionType.GEO.toString());
if (geoAnswers != null && !geoAnswers.isEmpty()) {
geoLocationString = geoAnswers.get(0).getValue();
}
}
String[] tokens = StringUtils.split(geoLocationString, "\\|");
if (tokens != null && tokens.length >= 2) {
geoLocationMap = new HashMap<String, Object>();
geoLocationMap.put(MapUtils.LATITUDE, Double.parseDouble(tokens[0]));
geoLocationMap.put(MapUtils.LONGITUDE, Double.parseDouble(tokens[1]));
// if(tokens.length > 2) {
// TODO: currently a string is generated for altitude. need to fix
// geoLocationMap.put(ALTITUDE, Long.parseLong(tokens[2]));
// }
}
return geoLocationMap;
}
/**
* Update counts of SurveyQuestionSummary entities related to responses from this survey
* instance.
*/
public void updateSummaryCounts(boolean increment) {
// retrieve all summary objects
SurveyQuestionSummaryDao summaryDao = new SurveyQuestionSummaryDao();
QuestionDao qDao = new QuestionDao();
List<SurveyQuestionSummary> saveList = new ArrayList<SurveyQuestionSummary>();
List<SurveyQuestionSummary> deleteList = new ArrayList<SurveyQuestionSummary>();
for (QuestionAnswerStore response : questionAnswersStore) {
final Long questionId = Long.parseLong(response.getQuestionID());
Question question = qDao.getByKey(questionId);
if (question == null || !question.canBeCharted()) {
continue;
}
final String questionIdStr = response.getQuestionID();
final String[] questionResponse = DataUtils.optionResponsesTextArray(response
.getValue());
for (int i = 0; i < questionResponse.length; i++) {
List<SurveyQuestionSummary> questionSummaryList = summaryDao
.listByResponse(questionIdStr, questionResponse[i]);
SurveyQuestionSummary questionSummary = null;
if (questionSummaryList.isEmpty()) {
questionSummary = new SurveyQuestionSummary();
questionSummary.setQuestionId(response.getQuestionID());
questionSummary.setResponse(questionResponse[i]);
questionSummary.setCount(0L);
} else {
questionSummary = questionSummaryList.get(0);
}
// update and save or delete
long count = questionSummary.getCount() == null ? 0 : questionSummary.getCount();
count = increment ? ++count : --count;
questionSummary.setCount(count);
if (count > 0) {
saveList.add(questionSummary);
} else {
deleteList.add(questionSummary);
}
}
}
summaryDao.save(saveList);
summaryDao.delete(deleteList);
}
@Override
public SecuredObject getParentObject() {
if (surveyId == null) {
return null;
}
return new SurveyDAO().getByKey(surveyId);
}
@Override
public Long getObjectId() {
if (key == null) {
return null;
}
return key.getId();
}
@Override
public List<Long> listAncestorIds() {
if (surveyId == null) {
return Collections.emptyList();
}
SecuredObject s = new SurveyDAO().getByKey(surveyId);
if (s == null) {
return Collections.emptyList();
}
return s.listAncestorIds();
}
@Override
public List<BaseDomain> updateAncestorIds(boolean cascade) {
// do not update or return any child objects. Survey entities are the leaves
return Collections.emptyList();
}
}