/*
* 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/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Akvo Flow. If not, see <http://www.gnu.org/licenses/>.
*/
package org.akvo.flow.domain;
import org.akvo.flow.util.ConstantUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* data structure for individual survey questions. Questions have a type which
* can be any one of:
* <ul>
* <li>option - radio-button like selection</li>
* <li>free - free text</li>
* <li>video - video capture</li>
* <li>photo - photo capture</li>
* <li>geo - geographic detection (GPS)</li>
* </ul>
*
* @author Christopher Fagiani
*/
public class Question {
private String id;
private String text;
private int order;
private ValidationRule validationRule;
private String renderType;
private List<QuestionHelp> questionHelp;
private boolean mandatory;
private String type;
private List<Option> options;
private boolean allowOther;
private boolean allowMultiple;
private boolean locked;
/**
* Stores all available translations for each survey
* Key: the language code, ej: en
* Value: text of the question in the language represented by the key
*/
private Map<String, AltText> languageTranslationMap = new HashMap<>();
private List<Dependency> dependencies;
private List<ScoringRule> scoringRules;
private boolean useStrength;
private int strengthMin;
private int strengthMax;
private boolean localeName = false;
private boolean localeLocation = false;
private String sourceQuestionId;// "Copied-from" question Id
private boolean isDoubleEntry;
private boolean useExternalSource;
private boolean allowPoints, allowLine, allowPolygon;
private String caddisflyRes;
// cascading question specific attrs
private String src;
private List<Level> levels;
public void setIsLocaleName(boolean localeName) {
this.localeName = localeName;
}
public void setIsLocaleLocation(boolean localeLocation) {
this.localeLocation = localeLocation;
}
public boolean isLocaleName() {
return localeName;
}
public boolean isLocaleLocation() {
return localeLocation;
}
public void setUseStrength(boolean val) {
useStrength = val;
}
public boolean useStrength() {
return useStrength;
}
public int getStrengthMin() {
return strengthMin;
}
public void setStrengthMin(int strengthMin) {
this.strengthMin = strengthMin;
}
public int getStrengthMax() {
return strengthMax;
}
public void setStrengthMax(int strengthMax) {
this.strengthMax = strengthMax;
}
public List<QuestionHelp> getQuestionHelp() {
return questionHelp;
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public Map<String, AltText> getLanguageTranslationMap() {
return languageTranslationMap;
}
public AltText getAltText(String lang) {
return languageTranslationMap.get(lang);
}
public void addAltText(AltText altText) {
languageTranslationMap.put(altText.getLanguage(), altText);
}
public boolean isAllowMultiple() {
return allowMultiple;
}
public void setAllowMultiple(boolean allowMultiple) {
this.allowMultiple = allowMultiple;
}
public String getRenderType() {
return renderType;
}
public void setRenderType(String renderType) {
this.renderType = renderType;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public List<Dependency> getDependencies() {
return dependencies;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public boolean isMandatory() {
return mandatory;
}
public void setMandatory(boolean mandatory) {
this.mandatory = mandatory;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Option> getOptions() {
return options;
}
public void setOptions(ArrayList<Option> options) {
this.options = options;
}
public boolean isAllowOther() {
return allowOther;
}
public void setAllowOther(boolean allowOther) {
this.allowOther = allowOther;
}
public void addDependency(Dependency dep) {
if (dependencies == null) {
dependencies = new ArrayList<Dependency>();
}
dependencies.add(dep);
}
public List<QuestionHelp> getHelpByType(String type) {
List<QuestionHelp> help = new ArrayList<QuestionHelp>();
if (questionHelp != null && type != null) {
for (int i = 0; i < questionHelp.size(); i++) {
if (type.equalsIgnoreCase(questionHelp.get(i).getType())) {
help.add(questionHelp.get(i));
}
}
}
return help;
}
public void addQuestionHelp(QuestionHelp help) {
if (questionHelp == null) {
questionHelp = new ArrayList<QuestionHelp>();
}
questionHelp.add(help);
}
public ValidationRule getValidationRule() {
return validationRule;
}
public void setSourceQuestionId(String sourceQuestionId) {
this.sourceQuestionId = sourceQuestionId;
}
public String getSourceQuestionId() {
return sourceQuestionId;
}
public void setValidationRule(ValidationRule validationRule) {
this.validationRule = validationRule;
}
/**
* counts the number of non-empty help tip types
*
* @return
*/
public int getHelpTypeCount() {
int count = 0;
if (getHelpByType(ConstantUtil.IMAGE_HELP_TYPE).size() > 0) {
count++;
}
if (getHelpByType(ConstantUtil.TIP_HELP_TYPE).size() > 0) {
count++;
}
if (getHelpByType(ConstantUtil.VIDEO_HELP_TYPE).size() > 0) {
count++;
}
return count;
}
public void addScoringRule(ScoringRule rule) {
if (scoringRules == null) {
scoringRules = new ArrayList<ScoringRule>();
}
scoringRules.add(rule);
}
public List<ScoringRule> getScoringRules() {
return scoringRules;
}
/**
* scores a response according to the question's scoring rules. If there are
* no rules or none of the rules match, this method will return null
* otherwise it will return the scored value
*
* @param response
* @return
*/
public String getResponseScore(String response) {
String result = null;
if (scoringRules != null) {
int i = 0;
while (i < scoringRules.size() && result == null) {
result = scoringRules.get(i++).scoreResponse(response);
}
}
return result;
}
public String toString() {
return text;
}
public void setIsDoubleEntry(boolean isdoubleEntry) {
this.isDoubleEntry = isdoubleEntry;
}
public boolean isDoubleEntry() {
return isDoubleEntry;
}
public void useExternalSource(boolean useExternalSource) {
this.useExternalSource = useExternalSource;
}
public boolean useExternalSource() {
return useExternalSource;
}
public void setSrc(String src) {
this.src = src;
}
public String getSrc() {
return src;
}
public void setCaddisflyRes(String res) {
this.caddisflyRes = res;
}
public String getCaddisflyRes() {
return caddisflyRes;
}
public List<Level> getLevels() {
return levels;
}
public void setLevels(List<Level> levels) {
this.levels = levels;
}
public void setAllowPoints(boolean allowPoints) {
this.allowPoints = allowPoints;
}
public boolean isAllowPoints() {
return allowPoints;
}
public void setAllowLine(boolean allowLine) {
this.allowLine = allowLine;
}
public boolean isAllowLine() {
return allowLine;
}
public void setAllowPolygon(boolean allowPolygon) {
this.allowPolygon = allowPolygon;
}
public boolean isAllowPolygon() {
return allowPolygon;
}
/**
* Clone a question and update the question ID. This is only relevant for repeat-question-groups,
* which require different instances of the question for each iteration.
* Note: Excluding dependencies, all non-primitive variables are *not* deep-copied.
*/
public static Question copy(Question question, String questionId) {
Question q = new Question();
q.id = questionId;
q.text = question.getText();
q.order = question.getOrder();
q.renderType = question.getRenderType();
q.mandatory = question.isMandatory();
q.type = question.getType();
q.allowOther = question.isAllowOther();
q.allowMultiple = question.isAllowMultiple();
q.locked = question.isLocked();
q.useStrength = question.useStrength();
q.strengthMin = question.getStrengthMin();
q.strengthMax = question.getStrengthMax();
q.localeName = question.isLocaleName();
q.localeLocation = question.isLocaleLocation();
q.sourceQuestionId = question.getSourceQuestionId();
q.isDoubleEntry = question.isDoubleEntry();
q.useExternalSource = question.useExternalSource();
q.allowPoints = question.isAllowPoints();
q.allowLine = question.isAllowLine();
q.allowPolygon = question.isAllowPolygon();
q.src = question.getSrc();
q.validationRule = question.getValidationRule();// Shallow copy
q.questionHelp = question.getQuestionHelp();// Shallow copy
q.languageTranslationMap = question.getLanguageTranslationMap();// Shallow copy
q.scoringRules = question.getScoringRules();// Shallow copy
q.levels = question.getLevels();// Shallow copy
q.caddisflyRes = question.getCaddisflyRes();
// Deep-copy dependencies
if (question.dependencies != null) {
q.dependencies = new ArrayList<>();
for (Dependency d : question.getDependencies()) {
q.dependencies.add(new Dependency(d));
}
}
// Deep-copy options
if (question.options != null) {
q.options = new ArrayList<>();
for (Option o : question.options) {
q.options.add(new Option(o));
}
}
return q;
}
}