/*
* Copyright (C) 2010-2012 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.analytics.dao;
import static com.gallatinsystems.common.util.MemCacheUtils.containsKey;
import static com.gallatinsystems.common.util.MemCacheUtils.putObjects;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.jdo.PersistenceManager;
import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import org.waterforpeople.mapping.analytics.domain.SurveyQuestionSummary;
import org.waterforpeople.mapping.domain.QuestionAnswerStore;
import com.gallatinsystems.common.util.MemCacheUtils;
import com.gallatinsystems.framework.dao.BaseDAO;
import com.gallatinsystems.framework.domain.BaseDomain;
import com.gallatinsystems.framework.servlet.PersistenceFilter;
/**
* updates survey question objects
*
* @author Christopher Fagiani
*/
public class SurveyQuestionSummaryDao extends BaseDAO<SurveyQuestionSummary> {
private Cache cache;
public SurveyQuestionSummaryDao() {
super(SurveyQuestionSummary.class);
cache = MemCacheUtils.initCache(4 * 60 * 60); // cache summary objects list for 4 hours
}
/**
* synchronized static method so that only 1 thread can be updating a summary at a time. This is
* inefficient but is the only way we can be sure we're keeping the count consistent since there
* is no "select for update" or sql dml-like construct
*
* @param answer
*/
@SuppressWarnings("rawtypes")
public static synchronized void incrementCount(QuestionAnswerStore answer,
int unit) {
PersistenceManager pm = PersistenceFilter.getManager();
String answerText = answer.getValue();
String[] answers;
if (answerText != null && answerText.contains("|")) {
answers = answerText.split("\\|");
} else {
answers = new String[] {
answerText
};
}
for (int i = 0; i < answers.length; i++) {
// find surveyQuestionSummary objects with the right question id and answer text
javax.jdo.Query query = pm.newQuery(SurveyQuestionSummary.class);
query
.setFilter("questionId == questionIdParam && response == answerParam");
query
.declareParameters("String questionIdParam, String answerParam");
List results = (List) query.execute(answer.getQuestionID(),
answers[i]);
SurveyQuestionSummary summary = null;
if ((results == null || results.size() == 0) && unit > 0) {
// no previous surveyQuestionSummary for this answer, make a new one
summary = new SurveyQuestionSummary();
summary.setCount(new Long(unit));
summary.setQuestionId(answer.getQuestionID());
summary.setResponse(answers[i]);
} else if (results != null && results.size() > 0) {
// update an existing questionAnswerSummary
summary = (SurveyQuestionSummary) results.get(0);
summary.setCount(summary.getCount() + unit);
}
if (summary != null) {
// if we have updated or created a surveyQuestionSummary, save it
SurveyQuestionSummaryDao summaryDao = new SurveyQuestionSummaryDao();
if (summary.getCount() > 0) {
summaryDao.save(summary);
} else if (summary.getKey() != null) {
// if count has been decremented to 0 and the object is
// already persisted, delete it
summaryDao.delete(summary);
}
}
}
}
/**
* this method will list all the summary objects for a given question id
*/
public List<SurveyQuestionSummary> listByQuestion(String qId) {
return listByProperty("questionId", qId, "String");
}
/**
* Retrieve the survey question summary object by response. If possible retrieve from cache
*
* @param questionId
* @param questionResponse
* @return
*/
public List<SurveyQuestionSummary> listByResponse(String questionId, String questionResponse) {
List<SurveyQuestionSummary> result = null;
String cacheKey = null;
try {
cacheKey = getCacheKey(questionId + "-" + questionResponse);
if (MemCacheUtils.containsKey(cache, cacheKey)) {
result = new ArrayList<SurveyQuestionSummary>();
result.add((SurveyQuestionSummary) cache.get(cacheKey));
return result;
}
} catch (CacheException e) {
log.log(Level.WARNING, e.getMessage());
}
PersistenceManager pm = PersistenceFilter.getManager();
javax.jdo.Query query = pm.newQuery(SurveyQuestionSummary.class);
StringBuilder filterString = new StringBuilder();
StringBuilder paramString = new StringBuilder();
Map<String, Object> paramMap = null;
paramMap = new HashMap<String, Object>();
appendNonNullParam("questionId", filterString, paramString, "String", questionId, paramMap);
appendNonNullParam("response", filterString, paramString, "String", questionResponse,
paramMap);
query.setFilter(filterString.toString());
query.declareParameters(paramString.toString());
result = (List<SurveyQuestionSummary>) query.executeWithMap(paramMap);
cache(result);
return result;
}
/**
* Add a collection of SurveyQuestionSummary objects to the cache. If the object already exists
* in the cached SurveyQuestionSummarys list, they are replaced by the ones passed in through
* this list
*
* @param summaryList
*/
private void cache(List<SurveyQuestionSummary> summaryList) {
if (summaryList == null || summaryList.isEmpty()) {
return;
}
Map<Object, Object> cacheMap = new HashMap<Object, Object>();
for (SurveyQuestionSummary summary : summaryList) {
if (summary == null) {
continue;
}
String cacheKey;
try {
cacheKey = getCacheKey(summary);
cacheMap.put(cacheKey, summary);
} catch (CacheException e) {
log.log(Level.WARNING, e.getMessage());
}
}
putObjects(cache, cacheMap);
}
/**
* Remove a collection of SurveyQuestionSummarys from the cache
*
* @param summaryList
*/
private void uncache(List<SurveyQuestionSummary> summaryList) {
if (summaryList == null || summaryList.isEmpty()) {
return;
}
for (SurveyQuestionSummary summary : summaryList) {
if (summary == null) {
continue;
}
String cacheKey;
try {
cacheKey = getCacheKey(summary);
if (containsKey(cache, cacheKey)) {
cache.remove(cacheKey);
}
} catch (CacheException e) {
log.log(Level.WARNING, e.getMessage());
}
}
}
/**
* Save and cache question summary
*
* @param summary
*/
public SurveyQuestionSummary save(SurveyQuestionSummary summary) {
SurveyQuestionSummary savedSummary = super.save(summary);
cache(Arrays.asList(savedSummary));
return savedSummary;
}
/**
* Save and cache question summary list
*
* @param summary
*/
public List<SurveyQuestionSummary> save(List<SurveyQuestionSummary> summary) {
List<SurveyQuestionSummary> savedSummaryList = (List<SurveyQuestionSummary>) super
.save(summary);
cache(savedSummaryList);
return savedSummaryList;
}
/**
* Delete from cache and datastore
*
* @param summary
*/
public void delete(SurveyQuestionSummary summary) {
uncache(Arrays.asList(summary));
super.delete(summary);
}
/**
* Delete summary list from cache and datastore
*
* @param summaryList
*/
public void delete(List<SurveyQuestionSummary> summaryList) {
uncache(summaryList);
super.delete(summaryList);
}
/**
* Construct cache key for SurveyQuestionSummary objects. Assumes the combination of questionId
* and questionResponse are unique across all SurveyQuestionSummary entities.
*
* @param summary
* @return
* @throws CacheException
*/
@Override
public String getCacheKey(BaseDomain object) throws CacheException {
SurveyQuestionSummary summary = (SurveyQuestionSummary) object;
if (summary.getQuestionId() == null || summary.getResponse() == null) {
throw new CacheException("Cannnot create cache key without questionId and response");
}
return summary.getClass().getSimpleName() + "-" + summary.getQuestionId() + "-"
+ summary.getResponse();
}
}