/* * Copyright (C) 2010-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.dao; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import javax.jdo.PersistenceManager; import javax.jdo.Query; 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.web.dto.DataProcessorRequest; import org.waterforpeople.mapping.app.web.dto.ImageCheckRequest; import org.waterforpeople.mapping.domain.QuestionAnswerStore; import org.waterforpeople.mapping.domain.SurveyInstance; import com.gallatinsystems.common.util.PropertyUtil; import com.gallatinsystems.device.dao.DeviceDAO; import com.gallatinsystems.device.domain.Device; import com.gallatinsystems.device.domain.DeviceFiles; import com.gallatinsystems.framework.dao.BaseDAO; import com.gallatinsystems.framework.servlet.PersistenceFilter; import com.gallatinsystems.survey.dao.QuestionDao; import com.gallatinsystems.survey.dao.SurveyUtils; import com.gallatinsystems.survey.domain.Question; import com.gallatinsystems.survey.domain.Survey; import com.gallatinsystems.survey.domain.SurveyGroup; import com.gallatinsystems.surveyal.app.web.SurveyalRestRequest; import com.gallatinsystems.surveyal.dao.SurveyalValueDao; import com.gallatinsystems.surveyal.dao.SurveyedLocaleDao; import com.gallatinsystems.surveyal.domain.SurveyalValue; import com.gallatinsystems.surveyal.domain.SurveyedLocale; import com.google.appengine.api.datastore.DatastoreService; import com.google.appengine.api.datastore.DatastoreServiceFactory; import com.google.appengine.api.datastore.DatastoreTimeoutException; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.FetchOptions; import com.google.appengine.api.datastore.PreparedQuery; import com.google.appengine.api.datastore.Query.CompositeFilterOperator; import com.google.appengine.api.datastore.Query.Filter; import com.google.appengine.api.datastore.Query.FilterOperator; import com.google.appengine.api.datastore.Query.FilterPredicate; import com.google.appengine.api.datastore.Query.SortDirection; import com.google.appengine.api.taskqueue.Queue; import com.google.appengine.api.taskqueue.QueueFactory; import com.google.appengine.api.taskqueue.TaskOptions; public class SurveyInstanceDAO extends BaseDAO<SurveyInstance> { private static final String DEFAULT_ORG_PROP = "defaultOrg"; private final QuestionAnswerStoreDao qasDao = new QuestionAnswerStoreDao(); private final DeviceDAO deviceDao = new DeviceDAO(); private final QuestionDao questionDao = new QuestionDao(); public SurveyInstanceDAO() { super(SurveyInstance.class); } public SurveyInstance save(SurveyInstance si, DeviceFiles deviceFile) { // Check whether the instance is already stored in the database. boolean isNew = true; SurveyInstance existing = findByUUID(si.getUuid()); if (existing != null) { si.setKey(existing.getKey()); si.setCreatedDateTime(existing.getCreatedDateTime()); isNew = false; } SurveyedLocale sl = saveSurveyedLocale(si); si.setSurveyedLocaleId(sl.getKey().getId()); si.setDeviceFile(deviceFile); si = save(si);// Save the SurveyInstance just once, ensuring the Key is set. final long surveyInstanceId = si.getKey().getId(); qasDao.listBySurveyInstance(surveyInstanceId);// Cache existing qas???? final Set<QuestionAnswerStore> images = new HashSet<>(); final Set<QuestionAnswerStore> locations = new HashSet<>(); final List<QuestionAnswerStore> responses = new ArrayList<>(); for (QuestionAnswerStore qas : si.getQuestionAnswersStore()) { if (isProcessable(qas, si)) { qas.setSurveyInstanceId(surveyInstanceId); if (Question.Type.GEO.toString().equals(qas.getType()) && isNew) { locations.add(qas); } else if ("IMAGE".equals(qas.getType())) { // The device send values as IMAGE and not PHOTO. images.add(qas); } responses.add(qas); } } // batch save all responses try { qasDao.save(responses); } catch (DatastoreTimeoutException te) { sleep(); qasDao.save(responses); } // Recompute data summarization for new locations. for (QuestionAnswerStore qas : locations) { long geoQasId = qas.getKey().getId(); Queue summQueue = QueueFactory.getQueue("dataSummarization"); summQueue.add(TaskOptions.Builder .withUrl("/app_worker/dataprocessor") .param(DataProcessorRequest.ACTION_PARAM, DataProcessorRequest.SURVEY_INSTANCE_SUMMARIZER) .param("surveyInstanceId", si.getKey().getId() + "") .param("qasId", geoQasId + "") .param("delta", 1 + "")); } // Now that QAS IDs are set, enqueue imagecheck tasks, // whereby the presence of an image in S3 will be checked. if (!images.isEmpty()) { Device d = deviceDao.getDevice(deviceFile.getAndroidId(), deviceFile.getImei(), deviceFile.getPhoneNumber()); String deviceId = d == null ? "null" : String.valueOf(d.getKey().getId()); for (QuestionAnswerStore qas : images) { String filename = qas.getValue().substring( qas.getValue().lastIndexOf("/") + 1); Queue queue = QueueFactory.getQueue("background-processing"); TaskOptions to = TaskOptions.Builder .withUrl("/app_worker/imagecheck") .param(ImageCheckRequest.FILENAME_PARAM, filename) .param(ImageCheckRequest.DEVICE_ID_PARAM, deviceId) .param(ImageCheckRequest.QAS_ID_PARAM, String.valueOf(qas.getKey().getId())) .param(ImageCheckRequest.ATTEMPT_PARAM, "1"); queue.add(to); } } deviceFile.setSurveyInstanceId(si.getKey().getId()); si.updateSummaryCounts(true); return si; } private boolean isProcessable(QuestionAnswerStore qas, SurveyInstance si) { Long qid = Long.valueOf(qas.getQuestionID()); if (qasDao.isCached(qid, si.getKey().getId())) { log.log(Level.INFO, "Skipping QAS already present in datasore [SurveyInstance, Survey, Question]: " + qas.getSurveyInstanceId() + ", " + si.getSurveyId() + ", " + qas.getQuestionID()); return false; } else if (questionDao.getByKey(qid) == null) { log.log(Level.WARNING, String.format("Question %d not found in the datastore", qid)); return false; } return true; } private SurveyedLocale saveSurveyedLocale(SurveyInstance si) { final SurveyedLocaleDao slDao = new SurveyedLocaleDao(); // Fetch or create the corresponding locale for this instance. SurveyedLocale sl = si.getSurveyedLocaleIdentifier() != null ? slDao.getByIdentifier(si.getSurveyedLocaleIdentifier()) : null; if (sl == null) { sl = new SurveyedLocale(); sl.setOrganization(PropertyUtil.getProperty(DEFAULT_ORG_PROP)); if (StringUtils.isNotBlank(si.getSurveyedLocaleIdentifier())) { sl.setIdentifier(si.getSurveyedLocaleIdentifier()); } else { // if we don't have an identifier, create a random UUID. sl.setIdentifier(SurveyedLocale.generateBase32Uuid()); } Survey survey = SurveyUtils.retrieveSurvey(si.getSurveyId()); if (survey != null) { SurveyGroup surveyGroup = SurveyUtils .retrieveSurveyGroup(survey.getSurveyGroupId()); sl.setLocaleType(surveyGroup.getPrivacyLevel().toString()); sl.setSurveyGroupId(survey.getSurveyGroupId()); sl.setCreationSurveyId(surveyGroup.getNewLocaleSurveyId()); } } // Update the display name and location, if applies. if (si.getSurveyedLocaleDisplayName() != null) { sl.setDisplayName(si.getSurveyedLocaleDisplayName()); } if (si.getLocaleGeoLocation() != null) { String[] tokens = si.getLocaleGeoLocation().split("\\|", -1); if (tokens.length >= 2) { try { sl.setLatitude(Double.parseDouble(tokens[0])); sl.setLongitude(Double.parseDouble(tokens[1])); } catch (NumberFormatException nfe) { log.log(Level.SEVERE, "Could not parse lat/lon from META_GEO: " + si.getLocaleGeoLocation()); } } } sl = slDao.save(sl); return sl; } @SuppressWarnings("unchecked") public List<SurveyInstance> listByDateRange(Date beginDate, String cursorString) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query q = pm.newQuery(SurveyInstance.class); q.setFilter("collectionDate >= pBeginDate"); q.declareParameters("java.util.Date pBeginDate"); q.setOrdering("collectionDate desc"); prepareCursor(cursorString, q); return (List<SurveyInstance>) q.execute(beginDate); } @SuppressWarnings("unchecked") public List<SurveyInstance> listByDateRange(Date beginDate, Date endDate, boolean unapprovedOnlyFlag, Long surveyId, String source, String cursorString) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query query = pm.newQuery(SurveyInstance.class); Map<String, Object> paramMap = null; StringBuilder filterString = new StringBuilder(); StringBuilder paramString = new StringBuilder(); paramMap = new HashMap<String, Object>(); appendNonNullParam("surveyId", filterString, paramString, "Long", surveyId, paramMap); appendNonNullParam("deviceIdentifier", filterString, paramString, "String", source, paramMap); appendNonNullParam("collectionDate", filterString, paramString, "Date", beginDate, paramMap, GTE_OP); appendNonNullParam("collectionDate", filterString, paramString, "Date", endDate, paramMap, LTE_OP); if (unapprovedOnlyFlag) { appendNonNullParam("approvedFlag", filterString, paramString, "String", "False", paramMap); } if (beginDate != null || endDate != null) { query.declareImports("import java.util.Date"); } query.setOrdering("collectionDate desc"); query.setFilter(filterString.toString()); query.declareParameters(paramString.toString()); prepareCursor(cursorString, query); return (List<SurveyInstance>) query.executeWithMap(paramMap); } // same as listByDateRange, but adds sumbitterName, country, and sublevels as search fields // @Author: M.T.Westra @SuppressWarnings("unchecked") public List<SurveyInstance> listByDateRangeAndSubmitter(Date beginDate, Date endDate, boolean unapprovedOnlyFlag, Long surveyId, String deviceIdentifier, String submitterName, String countryCode, String level1, String level2, String cursorString) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query query = pm.newQuery(SurveyInstance.class); Map<String, Object> paramMap = null; StringBuilder filterString = new StringBuilder(); StringBuilder paramString = new StringBuilder(); paramMap = new HashMap<String, Object>(); appendNonNullParam("surveyId", filterString, paramString, "Long", surveyId, paramMap); appendNonNullParam("deviceIdentifier", filterString, paramString, "String", deviceIdentifier, paramMap); appendNonNullParam("submitterName", filterString, paramString, "String", submitterName, paramMap); appendNonNullParam("countryCode", filterString, paramString, "String", countryCode, paramMap); appendNonNullParam("sublevel1", filterString, paramString, "String", level1, paramMap); appendNonNullParam("sublevel2", filterString, paramString, "String", level2, paramMap); appendNonNullParam("collectionDate", filterString, paramString, "Date", beginDate, paramMap, GTE_OP); appendNonNullParam("collectionDate", filterString, paramString, "Date", endDate, paramMap, LTE_OP); if (unapprovedOnlyFlag) { appendNonNullParam("approvedFlag", filterString, paramString, "String", "False", paramMap); } if (beginDate != null || endDate != null) { query.declareImports("import java.util.Date"); } query.setOrdering("collectionDate desc"); query.setFilter(filterString.toString()); query.declareParameters(paramString.toString()); prepareCursor(cursorString, query); return (List<SurveyInstance>) query.executeWithMap(paramMap); } /*********************** * returns raw entities * * @param returnKeysOnly * @param beginDate * @param endDate * @param surveyId * @return */ public Iterable<Entity> listRawEntity(Boolean returnKeysOnly, Date beginDate, Date endDate, Integer limit, Long surveyId) { DatastoreService datastore = DatastoreServiceFactory .getDatastoreService(); // The Query interface assembles a query com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query( "SurveyInstance"); List<Filter> filters = new ArrayList<Filter>(); if (returnKeysOnly) { q.setKeysOnly(); } if (surveyId != null) { filters.add(new FilterPredicate("surveyId", FilterOperator.EQUAL, surveyId)); } if (beginDate != null) { filters.add(new FilterPredicate("collectionDate", FilterOperator.GREATER_THAN_OR_EQUAL, beginDate)); } if (endDate != null) { filters.add(new FilterPredicate("collectionDate", FilterOperator.LESS_THAN_OR_EQUAL, endDate)); } if (filters.size() == 1) { q.setFilter(filters.get(0)); } if (filters.size() > 1) { q.setFilter(CompositeFilterOperator.and(filters)); } q.addSort("collectionDate", SortDirection.DESCENDING); PreparedQuery pq = datastore.prepare(q); // TODO: Should we add .withChunkSize as well? FetchOptions fetchOptions; if (limit != null) { fetchOptions = FetchOptions.Builder.withLimit(limit); } else { fetchOptions = FetchOptions.Builder.withDefaults(); } return pq.asIterable(fetchOptions); } /** * finds a questionAnswerStore object for the surveyInstance and questionId passed in (if it * exists) * * @param surveyInstanceId * @param questionId * @return */ @SuppressWarnings("unchecked") public QuestionAnswerStore findQuestionAnswerStoreForQuestion( Long surveyInstanceId, String questionId) { PersistenceManager pm = PersistenceFilter.getManager(); Query q = pm.newQuery(QuestionAnswerStore.class); q.setFilter("surveyInstanceId == surveyInstanceIdParam && questionID == questionIdParam"); q.declareParameters("Long surveyInstanceIdParam, String questionIdParam"); List<QuestionAnswerStore> result = (List<QuestionAnswerStore>) q .execute(surveyInstanceId, questionId); if (result != null && result.size() > 0) { return result.get(0); } else { return null; } } /** * lists all questionAnswerStore objects for a single surveyInstance, optionally filtered by * type * * @param surveyInstanceId - mandatory * @param type - optional * @return */ @SuppressWarnings("unchecked") public List<QuestionAnswerStore> listQuestionAnswerStoreByType( Long surveyInstanceId, String type) { if (surveyInstanceId != null) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query query = pm.newQuery(QuestionAnswerStore.class); Map<String, Object> paramMap = null; StringBuilder filterString = new StringBuilder(); StringBuilder paramString = new StringBuilder(); paramMap = new HashMap<String, Object>(); appendNonNullParam("surveyInstanceId", filterString, paramString, "Long", surveyInstanceId, paramMap); appendNonNullParam("type", filterString, paramString, "String", type, paramMap); query.setFilter(filterString.toString()); query.declareParameters(paramString.toString()); return (List<QuestionAnswerStore>) query.executeWithMap(paramMap); } else { throw new IllegalArgumentException( "surveyInstanceId may not be null"); } } /** * lists all questionAnswerStore objects for a survey instance * * @param instanceId * @return */ @SuppressWarnings("unchecked") public List<QuestionAnswerStore> listQuestionAnswerStore(Long instanceId, Integer count) { PersistenceManager pm = PersistenceFilter.getManager(); Query q = pm.newQuery(QuestionAnswerStore.class); q.setFilter("surveyInstanceId == surveyInstanceIdParam"); q.declareParameters("Long surveyInstanceIdParam"); if (count != null) { q.setRange(0, count); } return (List<QuestionAnswerStore>) q.execute(instanceId); } /** * lists all questionAnswerStore objects for a specific question * * @param questionId * @return */ @SuppressWarnings("unchecked") public List<QuestionAnswerStore> listQuestionAnswerStoreForQuestion( String questionId, String cursorString) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query q = pm.newQuery(QuestionAnswerStore.class); q.setFilter("questionID == qidParam"); q.declareParameters("String qidParam"); prepareCursor(cursorString, q); return (List<QuestionAnswerStore>) q.execute(questionId); } /** * Deletes a surveyInstance and all its related objects * * @param surveyInstance survey instance to be deleted */ // TODO update lastSurveyalInstanceId in surveydLocale objects @SuppressWarnings({ "unchecked", "rawtypes" }) public void deleteSurveyInstance(SurveyInstance surveyInstance) { final Long surveyInstanceId = surveyInstance.getKey().getId(); // update summary counts + delete question answers QuestionAnswerStoreDao qasDao = new QuestionAnswerStoreDao(); QuestionDao qDao = new QuestionDao(); List<QuestionAnswerStore> qasList = qasDao.listBySurveyInstance(surveyInstanceId); SurveyQuestionSummaryDao summDao = new SurveyQuestionSummaryDao(); if (qasList != null && !qasList.isEmpty()) { for (QuestionAnswerStore qasItem : qasList) { // question summaries Question question = qDao.getByKey(Long.parseLong(qasItem.getQuestionID())); if (question != null && question.canBeCharted()) { Queue questionSummaryQueue = QueueFactory.getQueue("surveyResponseCount"); List<SurveyQuestionSummary> summaryList = summDao.listByResponse( qasItem.getQuestionID(), qasItem.getValue()); if (summaryList != null && !summaryList.isEmpty()) { TaskOptions to = TaskOptions.Builder .withUrl("/app_worker/dataprocessor") .param(DataProcessorRequest.ACTION_PARAM, DataProcessorRequest.SURVEY_RESPONSE_COUNT) .param(DataProcessorRequest.COUNTER_ID_PARAM, summaryList.get(0).getKey().getId() + "") .param(DataProcessorRequest.DELTA_PARAM, "-1"); questionSummaryQueue.add(to); continue; } } // survey instance summary task if (Question.Type.GEO.toString().equals(qasItem.getType())) { Queue summaryQueue = QueueFactory.getQueue("dataSummarization"); TaskOptions to = TaskOptions.Builder .withUrl("/app_worker/dataprocessor") .param(DataProcessorRequest.ACTION_PARAM, DataProcessorRequest.SURVEY_INSTANCE_SUMMARIZER) .param(DataProcessorRequest.DELTA_PARAM, "-1"); summaryQueue.add(to); } } qasDao.delete(qasList); } // delete surveyal values SurveyedLocaleDao surveyedLocaleDao = new SurveyedLocaleDao(); SurveyalValueDao svDao = new SurveyalValueDao(); List<SurveyalValue> surveyalValues = surveyedLocaleDao .listSurveyalValuesByInstance(surveyInstanceId); if (surveyalValues != null && !surveyalValues.isEmpty()) { svDao.delete(surveyalValues); } // task to adapt cluster data + delete surveyedlocale if not needed anymore if (surveyInstance.getSurveyedLocaleId() != null) { Long surveyedLocaleId = surveyInstance.getSurveyedLocaleId(); List<SurveyInstance> relatedSurveyInstances = listByProperty("surveyedLocaleId", surveyedLocaleId, "Long"); if (relatedSurveyInstances.size() < 2) { // only the current (or no) survey instance is related to the locale. we fire task // to delete locale and update clusters // The locale is deleted in the decrement cluster task. Queue queue = QueueFactory.getDefaultQueue(); TaskOptions to = TaskOptions.Builder .withUrl("/app_worker/surveyalservlet") .param(SurveyalRestRequest.ACTION_PARAM, SurveyalRestRequest.ADAPT_CLUSTER_DATA_ACTION) .param(SurveyalRestRequest.SURVEYED_LOCALE_PARAM, surveyedLocaleId + "") .param(SurveyalRestRequest.DECREMENT_CLUSTER_COUNT_PARAM, Boolean.TRUE.toString()); queue.add(to); } } super.delete(surveyInstance); } /** * lists all surveyInstance records for a given survey * * @param surveyId * @return */ public List<SurveyInstance> listSurveyInstanceBySurvey(Long surveyId, Integer count) { return listSurveyInstanceBySurvey(surveyId, count, null); } @SuppressWarnings("unchecked") public List<SurveyInstance> listSurveyInstanceBySurvey(Long surveyId, Integer count, String cursorString) { PersistenceManager pm = PersistenceFilter.getManager(); Query q = pm.newQuery(SurveyInstance.class); q.setFilter("surveyId == surveyIdParam"); q.setOrdering("createdDateTime asc"); q.declareParameters("Long surveyIdParam"); prepareCursor(cursorString, count, q); List<SurveyInstance> siList = (List<SurveyInstance>) q .execute(surveyId); return siList; } public List<SurveyInstance> listSurveyInstanceBySurveyId(Long surveyId, String cursorString) { return listSurveyInstanceBySurvey(surveyId, null, cursorString); } public Iterable<Entity> listSurveyInstanceKeysBySurveyId(Long surveyId) { DatastoreService datastore = DatastoreServiceFactory .getDatastoreService(); com.google.appengine.api.datastore.Query q = new com.google.appengine.api.datastore.Query( "SurveyInstance"); q.setKeysOnly().setFilter(new FilterPredicate("surveyId", FilterOperator.EQUAL, surveyId)); PreparedQuery pq = datastore.prepare(q); return pq.asIterable(); } /** * lists instances for the given surveyedLocale optionally filtered by the dates passed in * * @param surveyedLocaleId * @return */ public List<SurveyInstance> listInstancesByLocale(Long surveyedLocaleId, Date dateFrom, Date dateTo, String cursor) { return listInstancesByLocale(surveyedLocaleId, dateFrom, dateTo, DEFAULT_RESULT_COUNT, cursor); } /** * lists instances for the given surveyedLocale optionally filtered by the dates passed in * * @param surveyedLocaleId * @return */ @SuppressWarnings("unchecked") public List<SurveyInstance> listInstancesByLocale(Long surveyedLocaleId, Date dateFrom, Date dateTo, Integer pageSize, String cursor) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query query = pm.newQuery(SurveyInstance.class); Map<String, Object> paramMap = null; StringBuilder filterString = new StringBuilder(); StringBuilder paramString = new StringBuilder(); paramMap = new HashMap<String, Object>(); appendNonNullParam("surveyedLocaleId", filterString, paramString, "Long", surveyedLocaleId, paramMap); if (dateFrom != null || dateTo != null) { appendNonNullParam("collectionDate", filterString, paramString, "Date", dateFrom, paramMap, GTE_OP); appendNonNullParam("collectionDate", filterString, paramString, "Date", dateTo, paramMap, LTE_OP); query.declareImports("import java.util.Date"); } query.setFilter(filterString.toString()); query.declareParameters(paramString.toString()); query.setOrdering("collectionDate desc"); prepareCursor(cursor, pageSize, query); return (List<SurveyInstance>) query.executeWithMap(paramMap); } /** * lists all survey instances by the submitter passed in * * @param submitter * @return */ @SuppressWarnings("unchecked") public List<SurveyInstance> listInstanceBySubmitter(String submitter) { if (submitter != null) { return listByProperty("submitterName", submitter, "String"); } else { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query query = pm.newQuery(SurveyInstance.class, "submitterName == null"); return (List<SurveyInstance>) query.execute(); } } /** * lists questionAnswerStore objects of particular types passed in */ @SuppressWarnings("unchecked") public List<QuestionAnswerStore> listQAOptions(String cursorString, Integer pageSize, String... options) { PersistenceManager pm = PersistenceFilter.getManager(); javax.jdo.Query q = pm.newQuery(QuestionAnswerStore.class); StringBuffer filter = new StringBuffer(); for (String op : options) { filter.append("type == '").append(op).append("' ||"); } q.setFilter(filter.substring(0, filter.length() - 3).toString()); prepareCursor(cursorString, pageSize, q); return (List<QuestionAnswerStore>) q.execute(); } /** * finds a single survey instance by uuid. This method will NOT load all QuestionAnswerStore * objects. * * @param uuid * @return */ public SurveyInstance findByUUID(String uuid) { return findByProperty("uuid", uuid, "String"); } public SurveyInstance getRegistrationSurveyInstance(SurveyedLocale locale, Long registrationSurveyId) { PersistenceManager pm = PersistenceFilter.getManager(); Query query = pm.newQuery(SurveyInstance.class); Map<String, Object> paramMap = null; StringBuilder filterString = new StringBuilder(); StringBuilder paramString = new StringBuilder(); paramMap = new HashMap<String, Object>(); appendNonNullParam("surveyId", filterString, paramString, "Long", registrationSurveyId, paramMap); appendNonNullParam("surveyedLocaleId", filterString, paramString, "Long", locale.getKey().getId(), paramMap); query.setFilter(filterString.toString()); query.declareParameters(paramString.toString()); query.setOrdering("collectionDate ascending"); List<SurveyInstance> res = (List<SurveyInstance>) query.executeWithMap(paramMap); if (res != null && !res.isEmpty()) { return res.get(0); } return null; } }