/*
* Copyright (C) 2010-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;
import com.gallatinsystems.common.domain.UploadStatusContainer;
import com.gallatinsystems.common.util.PropertyUtil;
import com.gallatinsystems.common.util.S3Util;
import com.gallatinsystems.common.util.ZipUtil;
import com.gallatinsystems.framework.rest.AbstractRestApiServlet;
import com.gallatinsystems.framework.rest.RestRequest;
import com.gallatinsystems.framework.rest.RestResponse;
import com.gallatinsystems.messaging.dao.MessageDao;
import com.gallatinsystems.messaging.domain.Message;
import com.gallatinsystems.survey.dao.*;
import com.gallatinsystems.survey.domain.*;
import com.gallatinsystems.survey.domain.Question;
import com.gallatinsystems.survey.domain.QuestionGroup;
import com.gallatinsystems.survey.domain.Survey;
import com.gallatinsystems.survey.domain.SurveyXMLFragment.FRAGMENT_TYPE;
import com.gallatinsystems.survey.domain.xml.*;
import com.gallatinsystems.survey.xml.SurveyXMLAdapter;
import com.google.appengine.api.backends.BackendServiceFactory;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.utils.SystemProperty;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.log4j.Logger;
import org.waterforpeople.mapping.app.web.dto.SurveyAssemblyRequest;
import org.waterforpeople.mapping.dao.SurveyContainerDao;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
public class SurveyAssemblyServlet extends AbstractRestApiServlet {
private static final Logger log = Logger
.getLogger(SurveyAssemblyServlet.class.getName());
private static final int BACKEND_QUESTION_THRESHOLD = 80;
private static final String BACKEND_PUBLISH_PROP = "backendpublish";
private static final long serialVersionUID = -6044156962558183224L;
private static final String OPTION_RENDER_MODE_PROP = "optionRenderMode";
public static final String FREE_QUESTION_TYPE = "free";
public static final String OPTION_QUESTION_TYPE = "option";
public static final String GEO_QUESTION_TYPE = "geo";
public static final String VIDEO_QUESTION_TYPE = "video";
public static final String PHOTO_QUESTION_TYPE = "photo";
public static final String SCAN_QUESTION_TYPE = "scan";
public static final String STRENGTH_QUESTION_TYPE = "strength";
public static final String DATE_QUESTION_TYPE = "date";
public static final String CASCADE_QUESTION_TYPE = "cascade";
public static final String GEOSHAPE_QUESTION_TYPE = "geoshape";
public static final String SIGNATURE_QUESTION_TYPE = "signature";
public static final String CADDISFLY_QUESTION_TYPE = "caddisfly";
private static final String SURVEY_UPLOAD_URL = "surveyuploadurl";
private static final String SURVEY_UPLOAD_DIR = "surveyuploaddir";
private Random randomNumber = new Random();
@Override
protected RestRequest convertRequest() throws Exception {
HttpServletRequest req = getRequest();
RestRequest restRequest = new SurveyAssemblyRequest();
restRequest.populateFromHttpRequest(req);
return restRequest;
}
@Override
protected RestResponse handleRequest(RestRequest req) throws Exception {
RestResponse response = new RestResponse();
SurveyAssemblyRequest importReq = (SurveyAssemblyRequest) req;
if (SurveyAssemblyRequest.ASSEMBLE_SURVEY.equalsIgnoreCase(importReq
.getAction())) {
QuestionDao questionDao = new QuestionDao();
boolean useBackend = false;
// make sure we're not already running on a backend and that we are
// allowed to use one
if (!importReq.getIsForwarded()
&& "true".equalsIgnoreCase(PropertyUtil
.getProperty(BACKEND_PUBLISH_PROP))) {
// if we're allowed to use a backend, then check to see if we
// need to (based on survey size)
List<Question> questionList = questionDao
.listQuestionsBySurvey(importReq.getSurveyId());
if (questionList != null
&& questionList.size() > BACKEND_QUESTION_THRESHOLD) {
useBackend = true;
}
}
if (useBackend) {
com.google.appengine.api.taskqueue.TaskOptions options = com.google.appengine.api.taskqueue.TaskOptions.Builder
.withUrl("/app_worker/surveyassembly")
.param(SurveyAssemblyRequest.ACTION_PARAM,
SurveyAssemblyRequest.ASSEMBLE_SURVEY)
.param(SurveyAssemblyRequest.IS_FWD_PARAM, "true")
.param(SurveyAssemblyRequest.SURVEY_ID_PARAM,
importReq.getSurveyId().toString());
// change the host so the queue invokes the backend
options = options
.header("Host",
BackendServiceFactory.getBackendService()
.getBackendAddress("dataprocessor"));
com.google.appengine.api.taskqueue.Queue queue = com.google.appengine.api.taskqueue.QueueFactory
.getQueue("surveyAssembly");
queue.add(options);
} else {
assembleSurveyOnePass(importReq.getSurveyId());
}
List<Long> ids = new ArrayList<Long>();
ids.add(importReq.getSurveyId());
SurveyUtils.notifyReportService(ids, "invalidate");
} else if (SurveyAssemblyRequest.DISPATCH_ASSEMBLE_QUESTION_GROUP
.equalsIgnoreCase(importReq.getAction())) {
this.dispatchAssembleQuestionGroup(importReq.getSurveyId(),
importReq.getQuestionGroupId(),
importReq.getTransactionId());
} else if (SurveyAssemblyRequest.ASSEMBLE_QUESTION_GROUP
.equalsIgnoreCase(importReq.getAction())) {
assembleQuestionGroups(importReq.getSurveyId(),
importReq.getTransactionId());
} else if (SurveyAssemblyRequest.DISTRIBUTE_SURVEY
.equalsIgnoreCase(importReq.getAction())) {
uploadSurvey(importReq.getSurveyId(), importReq.getTransactionId());
} else if (SurveyAssemblyRequest.CLEANUP.equalsIgnoreCase(importReq
.getAction())) {
cleanupFragments(importReq.getSurveyId(),
importReq.getTransactionId());
}
return response;
}
/**
* uploads full survey XML to S3
*
* @param surveyId
*/
private void uploadSurvey(Long surveyId, Long transactionId) {
SurveyContainerDao scDao = new SurveyContainerDao();
SurveyContainer sc = scDao.findBySurveyId(surveyId);
Properties props = System.getProperties();
String document = sc.getSurveyDocument().getValue();
String bucketName = props.getProperty("s3bucket");
boolean uploadedFile = false;
boolean uploadedZip = false;
try {
uploadedFile = S3Util.put(bucketName,
props.getProperty(SURVEY_UPLOAD_DIR) + "/" + sc.getSurveyId() + ".xml",
document.getBytes("UTF-8"), "text/xml", true);
} catch (IOException e) {
log.error("Error uploading file " + e.getMessage(), e);
}
ByteArrayOutputStream os = ZipUtil.generateZip(document,
sc.getSurveyId() + ".xml");
try {
uploadedZip = S3Util.put(bucketName,
props.getProperty(SURVEY_UPLOAD_DIR) + "/" + sc.getSurveyId() + ".zip",
os.toByteArray(),
"application/zip", true);
} catch (Exception e) {
log.error("Error uploading zip file: " + e.getMessage(), e);
}
sendQueueMessage(SurveyAssemblyRequest.CLEANUP, surveyId, null,
transactionId);
Message message = new Message();
message.setActionAbout("surveyAssembly");
message.setObjectId(surveyId);
if (uploadedFile && uploadedZip) {
// increment the version so devices know to pick up the changes
SurveyDAO surveyDao = new SurveyDAO();
String messageText = "Published. Please check: "
+ props.getProperty(SURVEY_UPLOAD_URL)
+ props.getProperty(SURVEY_UPLOAD_DIR) + "/" + surveyId
+ ".xml";
message.setShortMessage(messageText);
Survey s = surveyDao.getById(surveyId);
if (s != null) {
message.setObjectTitle(s.getPath() + "/" + s.getName());
}
message.setTransactionUUID(transactionId.toString());
MessageDao messageDao = new MessageDao();
messageDao.save(message);
} else {
String messageText = "Failed to publish: " + surveyId + "\n";
message.setTransactionUUID(transactionId.toString());
message.setShortMessage(messageText);
MessageDao messageDao = new MessageDao();
messageDao.save(message);
}
}
/**
* deletes fragments for the survey
*
* @param surveyId
*/
private void cleanupFragments(Long surveyId, Long transactionId) {
SurveyXMLFragmentDao sxmlfDao = new SurveyXMLFragmentDao();
sxmlfDao.deleteFragmentsForSurvey(surveyId, transactionId);
}
@Override
protected void writeOkResponse(RestResponse resp) throws Exception {
HttpServletResponse httpResp = getResponse();
httpResp.setStatus(HttpServletResponse.SC_OK);
// httpResp.setContentType("text/plain");
httpResp.getWriter().print("OK");
httpResp.flushBuffer();
}
private void assembleSurveyOnePass(Long surveyId) {
/**************
* 1, Select survey based on surveyId 2. Retrieve all question groups fire off queue tasks
*/
log.warn("Starting assembly of " + surveyId);
// Swap with proper UUID
SurveyDAO surveyDao = new SurveyDAO();
Survey s = surveyDao.getById(surveyId);
SurveyGroupDAO surveyGroupDao = new SurveyGroupDAO();
SurveyGroup sg = surveyGroupDao.getByKey(s.getSurveyGroupId());
Long transactionId = randomNumber.nextLong();
String lang = "en";
if (s != null && s.getDefaultLanguageCode() != null) {
lang = s.getDefaultLanguageCode();
}
final String versionAttribute = s.getVersion() == null ? "" : "version='"
+ s.getVersion() + "'";
final String app = String.format("app=\"%s\"",
StringEscapeUtils.escapeXml(SystemProperty.applicationId.get()));
String name = s.getName();
String surveyGroupId = "";
String surveyGroupName = "";
String registrationForm = "";
String surveyIdKeyValue = "surveyId=\""+surveyId+"\"";
if (sg != null) {
surveyGroupId = "surveyGroupId=\"" + sg.getKey().getId() + "\"";
surveyGroupName = "surveyGroupName=\"" + StringEscapeUtils.escapeXml(sg.getCode())
+ "\"";
if (Boolean.TRUE.equals(sg.getMonitoringGroup())) {
registrationForm = " registrationSurvey=\""
+ sg.getNewLocaleSurveyId() + "\"";
}
}
String surveyHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><survey"
+ " name=\"" + StringEscapeUtils.escapeXml(name)
+ "\"" + " defaultLanguageCode=\"" + lang + "\" " + versionAttribute + " " + app
+ registrationForm + " " + surveyGroupId + " " + surveyGroupName + " "
+ surveyIdKeyValue + ">";
String surveyFooter = "</survey>";
QuestionGroupDao qgDao = new QuestionGroupDao();
TreeMap<Integer, QuestionGroup> qgList = qgDao
.listQuestionGroupsBySurvey(surveyId);
if (qgList != null) {
StringBuilder surveyXML = new StringBuilder();
surveyXML.append(surveyHeader);
for (QuestionGroup item : qgList.values()) {
log.warn("Assembling group " + item.getKey().getId()
+ " for survey " + surveyId);
surveyXML.append(buildQuestionGroupXML(item));
}
surveyXML.append(surveyFooter);
log.warn("Uploading " + surveyId);
UploadStatusContainer uc = uploadSurveyXML(surveyId,
surveyXML.toString());
Message message = new Message();
message.setActionAbout("surveyAssembly");
message.setObjectId(surveyId);
message.setObjectTitle(sg.getCode() + " / " + s.getName());
// String messageText = CONSTANTS.surveyPublishOkMessage() + " "
// + url;
if (uc.getUploadedFile() && uc.getUploadedZip()) {
// increment the version so devices know to pick up the changes
log.warn("Finishing assembly of " + surveyId);
s.setStatus(Survey.Status.PUBLISHED);
surveyDao.save(s);
String messageText = "Published. Please check: " + uc.getUrl();
message.setShortMessage(messageText);
message.setTransactionUUID(transactionId.toString());
MessageDao messageDao = new MessageDao();
messageDao.save(message);
} else {
// String messageText =
// CONSTANTS.surveyPublishErrorMessage();
String messageText = "Failed to publish: " + surveyId + "\n"
+ uc.getMessage();
message.setTransactionUUID(transactionId.toString());
message.setShortMessage(messageText);
MessageDao messageDao = new MessageDao();
messageDao.save(message);
}
log.warn("Completed onepass assembly method for " + surveyId);
}
}
public UploadStatusContainer uploadSurveyXML(Long surveyId, String surveyXML) {
Properties props = System.getProperties();
String bucketName = props.getProperty("s3bucket");
String document = surveyXML;
boolean uploadedFile = false;
boolean uploadedZip = false;
try {
uploadedFile = S3Util.put(bucketName, props.getProperty(SURVEY_UPLOAD_DIR) + "/"
+ surveyId
+ ".xml", document.getBytes("UTF-8"), "text/xml", true);
} catch (IOException e) {
log.error("Error uploading file: " + e.getMessage(), e);
}
ByteArrayOutputStream os = ZipUtil.generateZip(document, surveyId
+ ".xml");
UploadStatusContainer uc = new UploadStatusContainer();
try {
uploadedZip = S3Util.put(bucketName, props.getProperty(SURVEY_UPLOAD_DIR) + "/"
+ surveyId + ".zip", os.toByteArray(), "application/zip", true);
} catch (IOException e) {
log.error("Error uploading file: " + e.getMessage(), e);
}
uc.setUploadedFile(uploadedFile);
uc.setUploadedZip(uploadedZip);
uc.setUrl(props.getProperty(SURVEY_UPLOAD_URL)
+ props.getProperty(SURVEY_UPLOAD_DIR) + "/" + surveyId
+ ".xml");
return uc;
}
public String buildQuestionGroupXML(QuestionGroup item) {
QuestionDao questionDao = new QuestionDao();
QuestionGroupDao questionGroupDao = new QuestionGroupDao();
QuestionGroup group = questionGroupDao.getByKey(item.getKey().getId());
TreeMap<Integer, Question> questionList = questionDao
.listQuestionsByQuestionGroup(item.getKey().getId(), true);
StringBuilder sb = new StringBuilder("<questionGroup")
.append(Boolean.TRUE.equals(group.getRepeatable()) ? " repeatable=\"true\"" : "")
.append("><heading>").append(StringEscapeUtils.escapeXml(group.getCode()))
.append("</heading>");
if (questionList != null) {
for (Question q : questionList.values()) {
sb.append(marshallQuestion(q));
}
}
return sb.toString() + "</questionGroup>";
}
/**
* sends a message to the task queue for survey assembly
*
* @param action
* @param surveyId
* @param questionGroups
*/
private void sendQueueMessage(String action, Long surveyId,
String questionGroups, Long transactionId) {
Queue surveyAssemblyQueue = QueueFactory.getQueue("surveyAssembly");
TaskOptions task = TaskOptions.Builder.withUrl("/app_worker/surveyassembly")
.param("action",
action).param("surveyId", surveyId.toString());
if (questionGroups != null) {
task.param("questionGroupId", questionGroups);
}
if (transactionId != null) {
task.param("transactionId", transactionId.toString());
}
surveyAssemblyQueue.add(task);
}
private void dispatchAssembleQuestionGroup(Long surveyId,
String questionGroupIds, Long transactionId) {
boolean isLast = true;
String currentId = questionGroupIds;
String remainingIds = null;
if (questionGroupIds.contains(",")) {
isLast = false;
currentId = questionGroupIds.substring(0,
questionGroupIds.indexOf(","));
remainingIds = questionGroupIds.substring(questionGroupIds
.indexOf(",") + 1);
}
QuestionDao questionDao = new QuestionDao();
QuestionGroupDao questionGroupDao = new QuestionGroupDao();
QuestionGroup group = questionGroupDao.getByKey(Long
.parseLong(currentId));
TreeMap<Integer, Question> questionList = questionDao
.listQuestionsByQuestionGroup(Long.parseLong(currentId), true);
StringBuilder sb = new StringBuilder("<questionGroup")
.append(Boolean.TRUE.equals(group.getRepeatable()) ? " repeatable=\"true\"" : "")
.append("><heading>").append(group.getCode()).append("</heading>");
if (questionList != null) {
for (Question q : questionList.values()) {
sb.append(marshallQuestion(q));
}
}
SurveyXMLFragment sxf = new SurveyXMLFragment();
sxf.setSurveyId(surveyId);
sxf.setQuestionGroupId(Long.parseLong(currentId));
sxf.setFragmentOrder(group.getOrder());
sxf.setFragment(new Text(sb.append("</questionGroup>").toString()));
sxf.setTransactionId(transactionId);
sxf.setFragmentType(FRAGMENT_TYPE.QUESTION_GROUP);
SurveyXMLFragmentDao sxmlfDao = new SurveyXMLFragmentDao();
sxmlfDao.save(sxf);
if (isLast) {
// Assemble the fragments
sendQueueMessage(SurveyAssemblyRequest.ASSEMBLE_QUESTION_GROUP,
surveyId, null, transactionId);
} else {
sendQueueMessage(
SurveyAssemblyRequest.DISPATCH_ASSEMBLE_QUESTION_GROUP,
surveyId, remainingIds, transactionId);
}
}
private String marshallQuestion(Question q) {
SurveyXMLAdapter sax = new SurveyXMLAdapter();
ObjectFactory objFactory = new ObjectFactory();
com.gallatinsystems.survey.domain.xml.Question qXML = objFactory
.createQuestion();
qXML.setId(new String("" + q.getKey().getId() + ""));
// ToDo fix
qXML.setMandatory("false");
if (q.getText() != null) {
com.gallatinsystems.survey.domain.xml.Text t = new com.gallatinsystems.survey.domain.xml.Text();
t.setContent(q.getText());
qXML.setText(t);
}
List<Help> helpList = new ArrayList<Help>();
// this is here for backward compatibility
// however, we don't use the helpMedia at the moment
if (q.getTip() != null) {
Help tip = new Help();
com.gallatinsystems.survey.domain.xml.Text t = new com.gallatinsystems.survey.domain.xml.Text();
t.setContent(q.getTip());
tip.setText(t);
tip.setType("tip");
if (q.getTip() != null && q.getTip().trim().length() > 0
&& !"null".equalsIgnoreCase(q.getTip().trim())) {
TranslationDao tDao = new TranslationDao();
Map<String, Translation> tipTrans = tDao.findTranslations(
Translation.ParentType.QUESTION_TIP, q.getKey().getId());
// any translations for question tooltip?
List<AltText> translationList = new ArrayList<AltText>();
for (Translation trans : tipTrans
.values()) {
AltText aText = new AltText();
aText.setContent(trans.getText());
aText.setLanguage(trans.getLanguageCode());
aText.setType("translation");
translationList.add(aText);
}
if (translationList.size() > 0) {
tip.setAltText(translationList);
}
helpList.add(tip);
}
}
if (q.getQuestionHelpMediaMap() != null) {
for (QuestionHelpMedia helpItem : q.getQuestionHelpMediaMap()
.values()) {
Help tip = new Help();
com.gallatinsystems.survey.domain.xml.Text t = new com.gallatinsystems.survey.domain.xml.Text();
t.setContent(helpItem.getText());
if (helpItem.getType() == QuestionHelpMedia.Type.TEXT) {
tip.setType("tip");
} else {
tip.setType(helpItem.getType().toString().toLowerCase());
tip.setValue(helpItem.getResourceUrl());
}
if (helpItem.getTranslationMap() != null) {
List<AltText> translationList = new ArrayList<AltText>();
for (Translation trans : helpItem.getTranslationMap()
.values()) {
AltText aText = new AltText();
aText.setContent(trans.getText());
aText.setLanguage(trans.getLanguageCode());
aText.setType("translation");
translationList.add(aText);
}
if (translationList.size() > 0) {
tip.setAltText(translationList);
}
}
helpList.add(tip);
}
}
if (helpList.size() > 0) {
qXML.setHelp(helpList);
}
boolean hasValidation = false;
if (q.getType() == Question.Type.NUMBER
&& (q.getAllowDecimal() != null || q.getAllowSign() != null
|| q.getMinVal() != null || q.getMaxVal() != null)) {
ValidationRule validationRule = objFactory.createValidationRule();
validationRule.setValidationType("numeric");
validationRule.setAllowDecimal(q.getAllowDecimal() != null ? q
.getAllowDecimal().toString().toLowerCase() : "false");
validationRule.setSigned(q.getAllowSign() != null ? q
.getAllowSign().toString().toLowerCase() : "false");
if (q.getMinVal() != null) {
validationRule.setMinVal(q.getMinVal().toString());
}
if (q.getMaxVal() != null) {
validationRule.setMaxVal(q.getMaxVal().toString());
}
qXML.setValidationRule(validationRule);
hasValidation = true;
}
qXML.setAltText(formAltText(q.getTranslationMap()));
if (q.getType().equals(Question.Type.FREE_TEXT)) {
qXML.setType(FREE_QUESTION_TYPE);
// add requireDoubleEntry flag if the field is true in the question
if (q.getRequireDoubleEntry() != null && q.getRequireDoubleEntry()) {
qXML.setRequireDoubleEntry(q.getRequireDoubleEntry().toString());
}
} else if (q.getType().equals(Question.Type.GEO)) {
qXML.setType(GEO_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.NUMBER)) {
qXML.setType(FREE_QUESTION_TYPE);
if (!hasValidation) {
ValidationRule vrule = new ValidationRule();
vrule.setValidationType("numeric");
vrule.setSigned("false");
qXML.setValidationRule(vrule);
}
// add requireDoubleEntry flag if the field is true in the question
if (q.getRequireDoubleEntry() != null && q.getRequireDoubleEntry()) {
qXML.setRequireDoubleEntry(q.getRequireDoubleEntry().toString());
}
} else if (q.getType().equals(Question.Type.OPTION)) {
qXML.setType(OPTION_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.PHOTO)) {
qXML.setType(PHOTO_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.VIDEO)) {
qXML.setType(VIDEO_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.SCAN)) {
qXML.setType(SCAN_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.STRENGTH)) {
qXML.setType(STRENGTH_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.DATE)) {
qXML.setType(DATE_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.CASCADE)) {
qXML.setType(CASCADE_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.GEOSHAPE)) {
qXML.setType(GEOSHAPE_QUESTION_TYPE);
qXML.setAllowPoints(Boolean.toString(q.getAllowPoints()));
qXML.setAllowLine(Boolean.toString(q.getAllowLine()));
qXML.setAllowPolygon(Boolean.toString(q.getAllowPolygon()));
} else if (q.getType().equals(Question.Type.SIGNATURE)) {
qXML.setType(SIGNATURE_QUESTION_TYPE);
} else if (q.getType().equals(Question.Type.CADDISFLY)) {
qXML.setType(CADDISFLY_QUESTION_TYPE);
}
if (q.getType().equals(Question.Type.CADDISFLY) && q.getCaddisflyResourceUuid() != null) {
qXML.setCaddisflyResourceUuid(q.getCaddisflyResourceUuid());
}
if (q.getType().equals(Question.Type.CASCADE) && q.getCascadeResourceId() != null) {
CascadeResourceDao crDao = new CascadeResourceDao();
CascadeResource cr = crDao.getByKey(q.getCascadeResourceId());
if (cr != null) {
qXML.setCascadeResource(cr.getResourceId());
List<String> levelNames = cr.getLevelNames();
if (levelNames != null && levelNames.size() > 0) {
Levels levels = objFactory.createLevels();
ArrayList<Level> levelList = new ArrayList<Level>();
for (int i = 0; i < cr.getNumLevels(); i++) {
Level levelItem = objFactory.createLevel();
com.gallatinsystems.survey.domain.xml.Text t = new com.gallatinsystems.survey.domain.xml.Text();
t.setContent(levelNames.get(i));
levelItem.addContent(t);
levelList.add(levelItem);
// TODO sort out translations
}
levels.setLevel(levelList);
qXML.setLevels(levels);
}
}
}
if (q.getOrder() != null) {
qXML.setOrder(q.getOrder().toString());
}
if (q.getMandatoryFlag() != null) {
qXML.setMandatory(q.getMandatoryFlag().toString());
}
qXML.setLocaleNameFlag("false");
if (q.getLocaleNameFlag() != null) {
qXML.setLocaleNameFlag(q.getLocaleNameFlag().toString());
}
if (q.getLocaleLocationFlag() != null) {
if (q.getLocaleLocationFlag()) {
qXML.setLocaleLocationFlag("true");
}
}
if (Boolean.TRUE.equals(q.getAllowMultipleFlag())) {
qXML.setAllowMultiple("true");
}
// Both GEO and GEOSHAPE question types can block manual input
if (Boolean.TRUE.equals(q.getGeoLocked())) {
qXML.setLocked("true");
}
Dependency dependency = objFactory.createDependency();
if (q.getDependentQuestionId() != null) {
dependency.setQuestion(q.getDependentQuestionId().toString());
dependency.setAnswerValue(q.getDependentQuestionAnswer());
qXML.setDependency(dependency);
}
if (q.getQuestionOptionMap() != null
&& q.getQuestionOptionMap().size() > 0) {
Options options = objFactory.createOptions();
if (q.getAllowOtherFlag() != null) {
options.setAllowOther(q.getAllowOtherFlag().toString());
}
if (q.getAllowMultipleFlag() != null) {
options.setAllowMultiple(q.getAllowMultipleFlag().toString());
}
if (options.getAllowMultiple() == null
|| "false".equals(options.getAllowMultiple())) {
options.setRenderType(PropertyUtil
.getProperty(OPTION_RENDER_MODE_PROP));
}
ArrayList<Option> optionList = new ArrayList<Option>();
for (QuestionOption qo : q.getQuestionOptionMap().values()) {
Option option = objFactory.createOption();
com.gallatinsystems.survey.domain.xml.Text t = new com.gallatinsystems.survey.domain.xml.Text();
t.setContent(qo.getText());
option.addContent(t);
option.setCode(qo.getCode());
// to maintain backwards compatibility with older app versions, we set the value
// attribute and text to the same
option.setValue(qo.getText());
List<AltText> altTextList = formAltText(qo.getTranslationMap());
if (altTextList != null) {
for (AltText alt : altTextList) {
option.addContent(alt);
}
}
optionList.add(option);
}
options.setOption(optionList);
qXML.setOptions(options);
}
if (q.getScoringRules() != null) {
Scoring scoring = new Scoring();
for (ScoringRule rule : q.getScoringRules()) {
Score score = new Score();
if (scoring.getType() == null) {
scoring.setType(rule.getType().toLowerCase());
}
score.setRangeHigh(rule.getRangeMax());
score.setRangeLow(rule.getRangeMin());
score.setValue(rule.getValue());
scoring.addScore(score);
}
if (scoring.getScore() != null && scoring.getScore().size() > 0) {
qXML.setScoring(scoring);
}
}
if ("true".equalsIgnoreCase(String.valueOf(q.getAllowExternalSources()))) {
qXML.setAllowExternalSources(String.valueOf(q.getAllowExternalSources()));
}
String questionDocument = null;
try {
questionDocument = sax.marshal(qXML);
} catch (JAXBException e) {
log.warn("Could not marshal question: " + qXML, e);
}
questionDocument = questionDocument
.replace(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>",
"");
return questionDocument;
}
private List<AltText> formAltText(Map<String, Translation> translationMap) {
List<AltText> altTextList = new ArrayList<AltText>();
if (translationMap != null) {
for (Translation lang : translationMap.values()) {
AltText alt = new AltText();
alt.setContent(lang.getText());
alt.setType("translation");
alt.setLanguage(lang.getLanguageCode());
altTextList.add(alt);
}
}
return altTextList;
}
private void assembleQuestionGroups(Long surveyId, Long transactionId) {
SurveyXMLFragmentDao sxmlfDao = new SurveyXMLFragmentDao();
List<SurveyXMLFragment> sxmlfList = sxmlfDao.listSurveyFragments(
surveyId, SurveyXMLFragment.FRAGMENT_TYPE.QUESTION_GROUP,
transactionId);
StringBuilder sbQG = new StringBuilder();
for (SurveyXMLFragment item : sxmlfList) {
sbQG.append(item.getFragment().getValue());
}
StringBuilder completeSurvey = new StringBuilder();
String surveyHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><survey>";
String surveyFooter = "</survey>";
completeSurvey.append(surveyHeader);
completeSurvey.append(sbQG.toString());
sbQG = null;
completeSurvey.append(surveyFooter);
SurveyContainerDao scDao = new SurveyContainerDao();
SurveyContainer sc = scDao.findBySurveyId(surveyId);
if (sc == null) {
sc = new SurveyContainer();
}
sc.setSurveyDocument(new Text(completeSurvey.toString()));
sc.setSurveyId(surveyId);
scDao.save(sc);
sendQueueMessage(SurveyAssemblyRequest.DISTRIBUTE_SURVEY, surveyId,
null, transactionId);
}
}