/*******************************************************************************
* Copyright 2012 UPM, http://www.upm.es
Universidad Politécnica de Madrid
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.universAAL.ontology.questionnaire;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.universAAL.middleware.owl.ManagedIndividual;
import org.universAAL.ontology.profile.User;
/**
* This class describes the concept of AnswerQuestionnaire, its properties and
* its associated methods. An answered questionnaire is the questionnaire when a
* user starts completing it. It can be fully answer, partially answered or not
* answered at all.
*
* @author mdelafuente
*
*/
public class AnsweredQuestionnaire extends ManagedIndividual {
// NAMESPACE & PROPERTIES
public static final String MY_URI = QuestionnaireOntology.NAMESPACE
+ "AnsweredQuestionnaire";
public static final String PROP_IS_ASSOCIATED_TO = QuestionnaireOntology.NAMESPACE
+ "isAssociatedTo";
public static final String PROP_IS_COMPOSED_BY = QuestionnaireOntology.NAMESPACE
+ "isComposedBy";
public static final String PROP_IS_ANSWERED_BY = QuestionnaireOntology.NAMESPACE
+ "isAnsweredBy";
public static final String PROP_COMPLETENESS = QuestionnaireOntology.NAMESPACE
+ "completeness";
public static final int CORRECT_ANSWERS = 0;
public static final int INCORRECT_ANSWERS = 1;
public static final int NO_CORRECT_ANSWERS = 2;
// CONSTRUCTORS
public AnsweredQuestionnaire() {
super();
}
public AnsweredQuestionnaire(String uri) {
super(uri);
}
public AnsweredQuestionnaire(Questionnaire questionnaire, Answer[] answers,
User examinedUser) {
this.setAnswers(answers);
this.setIsAnsweredBy(examinedUser);
this.setAssociatedQuestionnaire(questionnaire);
this.setCompleteness(calculateCompleteness());
}
public AnsweredQuestionnaire(Questionnaire questionnaire, Answer answer,
User examinedUser) {
this.addAnswer(answer);
this.setIsAnsweredBy(examinedUser);
this.setAssociatedQuestionnaire(questionnaire);
this.setCompleteness(calculateCompleteness());
}
public AnsweredQuestionnaire(Questionnaire q, User examinedUser) {
this.setIsAnsweredBy(examinedUser);
this.setAssociatedQuestionnaire(q);
this.setCompleteness(calculateCompleteness());
}
public String getClassURI() {
return MY_URI;
}
public int getPropSerializationType(String arg0) {
return PROP_SERIALIZATION_FULL;
}
public boolean isWellFormed() {
return true && props.containsKey(PROP_IS_ASSOCIATED_TO)
&& props.containsKey(PROP_IS_COMPOSED_BY)
&& props.containsKey(PROP_IS_ANSWERED_BY)
&& props.containsKey(PROP_COMPLETENESS);
}
// GETTERS & SETTERS
/**
* The following method gets the user who answers the questionnaire
*
* @return User
* @see User
*/
public User getIsAnsweredBy() {
return (User) props.get(PROP_IS_ANSWERED_BY);
}
/**
* The following method sets the user who answers the questionnaire
*
* @param user
* {@link User}
* @see User
*/
public void setIsAnsweredBy(User user) {
if (user != null)
props.put(PROP_IS_ANSWERED_BY, user);
}
/**
* The following method gets the answers that composes the answered
* questionnaire. The name automatically-generated by the modeling tool has
* been changed to be clearer, from "isComposedBy" to "getAnswers"
*
* @return Answer[], the array in which the answers will be given (
* {@link Answer})
* @see Answer
*/
public Answer[] getAnswers() {
Object propList = props.get(PROP_IS_COMPOSED_BY);
if (propList instanceof List) {
return (Answer[]) ((List) propList).toArray(new Answer[0]);
} else {
List returnList = new ArrayList();
if (propList != null)
returnList.add((Answer) propList);
return (Answer[]) returnList.toArray(new Answer[0]);
}
}
/**
* The following method sets the answers that composes the answered
* questionnaire.
*
* @param answer
* , the answer that will be added to the answered questionnaire.
* ({@link Answer})
* @see Answer
*/
public void addAnswer(Answer answer) {
Object propList = props.get(PROP_IS_COMPOSED_BY);
if (propList instanceof List) {
List list = (List) propList;
list.add(answer);
props.put(PROP_IS_COMPOSED_BY, list);
} else if (propList == null) {
props.put(PROP_IS_COMPOSED_BY, answer);
} else {
List list = new ArrayList();
list.add((Answer) propList);
list.add(answer);
props.put(PROP_IS_COMPOSED_BY, list);
}
}
/**
* The following method sets the answers that composes the answered
* questionnaire.
*
* @param Answer
* , the answer that will be added to the answered questionnaire.
* ({@link Answer})
* @see Answer
*/
public void setAnswers(Answer[] answers) {
List propList = new ArrayList(answers.length);
for (int i = 0; i < answers.length; i++) {
propList.add(answers[i]);
}
props.put(PROP_IS_COMPOSED_BY, propList);
}
/**
* The following method gets the associated questionnaire to an answered
* questionnaire The name automatically-generated by the modeling tool has
* been changed to be clearer, from "getIsAssociatedTo" to
* "getAssociatedQuestionnaire"
*
* @return Questionnaire ({@link Questionnaire})
*/
public Questionnaire getAssociatedQuestionnaire() {
return (Questionnaire) props.get(PROP_IS_ASSOCIATED_TO);
}
/**
* The following method sets the associated questionnaire to an answered
* questionnaire The name automatically-generated by the modeling tool has
* been changed to be clearer, from "setIsAssociatedTo" to
* "setAssociatedQuestionnaire"
*
* @param questionnaire
* ({@link Questionnaire})
*/
public void setAssociatedQuestionnaire(Questionnaire questionnaire) {
if (questionnaire != null)
props.put(PROP_IS_ASSOCIATED_TO, questionnaire);
}
/**
* The following method gets the completeness of an answered questionnaire,
* given in percentage.
*
* @return completeness ({@link Double})
*/
public double getCompleteness() {
return ((Double) props.get(PROP_COMPLETENESS)).doubleValue();
}
/**
* The following method sets the completeness of an answered questionnaire,
* given in percentage.
*
* @param completeness
* ({@link Double})
*/
public void setCompleteness(double completeness) {
props.put(PROP_COMPLETENESS, Double.valueOf(completeness));
}
// OTHER METHODS
/**
* The following method returns the percentage of correct answers within the
* answered questionnaire.
*
* @return the percentage of the correct answers within the answered
* questionnaire ({@link Double})
*/
public double getCorrectAnswersPercentage() {
int numberOfAnsweredAnswers = getAnswers().length;
int numberOfNoCorrectAnswers = getNumberOfNoCorrectAnswers();
double numberOfCorrectQuestions = (double) getNumberOfCorrectAnswers();
double correctPercentage = (numberOfCorrectQuestions / (numberOfAnsweredAnswers - numberOfNoCorrectAnswers)) * 100;
return correctPercentage;
}
/**
* The following method returns the percentage of incorrect answers within
* the answered questionnaire.
*
* @return the percentage of the incorrect answers within the answered
* questionnaire ({@link Double})
*/
public double getIncorrectAnswersPercentage() {
int numberOfAnsweredAnswers = getAnswers().length;
int numberOfNoCorrectAnswers = getNumberOfNoCorrectAnswers();
double numberOfIncorrectQuestions = (double) getNumberOfIncorrectAnswers();
double incorrectPercentage = (numberOfIncorrectQuestions / (numberOfAnsweredAnswers - numberOfNoCorrectAnswers)) * 100;
return incorrectPercentage;
}
/**
* The following method gets the number of incorrect answers. Can be useful
* if we don't want this parameter given as a percentage
*
* @return the number of incorrect answers ({@link Integer})
*/
public int getNumberOfIncorrectAnswers() {
return checkResults(INCORRECT_ANSWERS);
}
/**
* The following method gets the number of correct answers. Can be useful if
* we don't want this parameter given as a percentage
*
* @return the number of correct answers ({@link Integer})
*/
public int getNumberOfCorrectAnswers() {
return checkResults(CORRECT_ANSWERS);
}
/**
* The following method gets the number of questions within the
* questionnaire that don't have a correct answer associated.
*
* @return number of questions with no-correct/correct answer associated (
* {@link Integer})
*/
public int getNumberOfNoCorrectAnswers() {
return checkResults(NO_CORRECT_ANSWERS);
}
/**
* The following method checks the answers given by the user, and returns
* the number of correct/incorrect answers, depending on the parameter
*
* @param correctResults
* . If true, the method returns the number of correct answers.
* If false, the method returns the number of incorrect answers
* @return number of correct or incorrect answers ({@link Integer})
* @return null if the parameter is different from CORRECT_ANSWERS,
* INCORRECT_ANSWERS or NO_CORRECT_ANSWERS.
*
*/
private int checkResults(int correctResults) {
Answer[] answers = getAnswers();
int correctAnswers = 0;
int incorrectAnswers = 0;
int noCorrectAnswers = 0;
for (int i = 0; i < answers.length; i++) {
Question question = answers[i].getAssociatedQuestion();
if (question.isHasCorrectAnswer()) {
if (answers[i].isCorrectAnswer() == Answer.CORRECT_ANSWER)
correctAnswers++;
else
incorrectAnswers++;
} else {
noCorrectAnswers++;
}
}
switch (correctResults) {
case CORRECT_ANSWERS:
return correctAnswers;
case INCORRECT_ANSWERS:
return incorrectAnswers;
case NO_CORRECT_ANSWERS:
return noCorrectAnswers;
default:
return -1;
}
}
/**
* The following method gets the incorrect answers given by the user, within
* an answered questionnaire.
*
* @return an array filled with the incorrect answers ({@link Answer})
* @see Answer
*/
public Answer[] getIncorrectAnswers() {
Answer[] answers = getAnswers();
Answer[] incorrectAnswers = new Answer[getNumberOfIncorrectAnswers()];
int index = 0;
for (int i = 0; i < answers.length; i++) {
Question question = answers[i].getAssociatedQuestion();
if (question.isHasCorrectAnswer()
&& !(answers[i].isCorrectAnswer() == Answer.CORRECT_ANSWER)) {
incorrectAnswers[index] = answers[i];
index++;
}
}
return incorrectAnswers;
}
/**
* The following method gets the correct answers given by the user, within
* an answered questionnaire.
*
* @return an array filled with the correct answers ({@link Answer})
* @see Answer
*/
public Answer[] getCorrectAnswers() {
Answer[] answers = getAnswers();
Answer[] correctAnswers = new Answer[getNumberOfCorrectAnswers()];
int index = 0;
for (int i = 0; i < answers.length; i++) {
Question question = answers[i].getAssociatedQuestion();
if (question.isHasCorrectAnswer()
&& answers[i].isCorrectAnswer() == Answer.CORRECT_ANSWER) {
correctAnswers[index] = answers[i];
index++;
}
}
return correctAnswers;
}
/**
* The following method gets the no-correct answers, that is all the answers
* that have no correct answer associated, given by the user, within an
* answered questionnaire.
*
* @return an array filled with the no-correct answers ({@link Answer})
* @see Answer
*/
public Answer[] getNoCorrectAnswers() {
Answer[] answers = getAnswers();
Answer[] noCorrectAnswers = new Answer[getNumberOfNoCorrectAnswers()];
int index = 0;
for (int i = 0; i < answers.length; i++) {
Question question = answers[i].getAssociatedQuestion();
if (!(question.isHasCorrectAnswer())) {
noCorrectAnswers[index] = answers[i];
index++;
}
}
return noCorrectAnswers;
}
/**
* The following method gets the associated questions associated to the
* answers array given, within an answered questionnaire.
*
* @return an array filled with the questions, associated to the answers
* array given ({@link Question})
* @see Question
* @see Answer
*/
public Question[] getAssociatedQuestions(Answer[] answers) {
Question[] questions = new Question[answers.length];
for (int i = 0; i < answers.length; i++) {
questions[i] = answers[i].getAssociatedQuestion();
}
return questions;
}
/**
* The following method generates a map with all the answered questions.
* This map contains all the questions that have been answered.
*
* @return map ({@link Map})
* @see Answer
* @see Question
*/
private Map generateAnswerMap() {
Map map = new HashMap();
Answer[] answers = getAnswers();
for (int i = 0; i < answers.length; i++) {
map.put(answers[i].getAssociatedQuestion(), answers[i]);
}
return map;
}
/**
* The following method returns the set of questions that are candidates to
* be next question
*
* @return candidates to be next question({@link ArrayList})
* @see Question
*/
private ArrayList getCandidates() {
Question[] allQuestions = getAssociatedQuestionnaire().getQuestions();
ArrayList candidates = new ArrayList(Arrays.asList(allQuestions));
Map answerMap = generateAnswerMap();
for (int i = 0; i < allQuestions.length; i++) {
if (answerMap.containsKey(allQuestions[i])) { // if the question has
// been already
// answered
candidates.remove(allQuestions[i]); // the question can not be a
// candidate
// because it has been answered before.
} else {
if (allQuestions[i] instanceof ConditionalQuestion) { // if the
// question
// is
// Conditional
// type
ConditionalQuestion cq = (ConditionalQuestion) allQuestions[i];
Question depends = cq.getDependsOn();
if (!(answerMap.containsKey(depends)) || // the cq is not a
// candidate if the
// question
// to which is related to, haven't appeared
(cq.getTriggerAnswer() != ((Answer) answerMap
.get(depends)).getAnswerContent())) // or
// the
// depending
// question
// has
// appeared
// but the answer doesn't match with the expected
// trigger
candidates.remove(allQuestions[i]);
}
}
}
return candidates;
}
/**
* The following method returns the next question of a questionnaire
*
* @return questionnaire's next question ({@link Question})
* @return null if there is not next question, that is, we have given all
* the questions that composes the questionnaire, based on the
* answers given by the examined user.
* @see Question
*/
public Question nextQuestion() {
ArrayList possibleQuestions = getCandidates();
if (possibleQuestions.size() != 0) {
if (getAssociatedQuestionnaire().isOrderedQuestions())
return (Question) possibleQuestions.get(0);
else {
Random rndm = new Random();
int number = rndm.nextInt(possibleQuestions.size());
return (Question) possibleQuestions.get(number);
}
} else
return null;
}
/**
* The following method calculates the completeness of a questionnaire,
* given in percentage.
*
* @return the completed percentage of the questionnaire ({@link Double})
*/
public double calculateCompleteness() {
int numberOfAnswers = getAnswers().length;
return (double) ((numberOfAnswers) * 100)
/ (numberOfAnswers + getCandidates().size());
}
/**
* The following method stores the answer given by the user for a specific
* question.
*
* @param q
* ({@link Question})
* @param answerContent
* , just one Resource ({@link Object})
* @see Answer
*/
public void answeredToQuestion(Question q, Object answerContent) {
Answer a = new Answer(answerContent, q);
addAnswer(a);
}
/**
* The following method stores the answer given by the user for a specific
* question.
*
* @param q
* ({@link Question})
* @param answerContent
* , more than one Resource ({@link Object})
* @see Answer
*/
public void answeredToQuestion(Question q, Object[] answerContent) {
Answer a = new Answer(answerContent, q);
addAnswer(a);
}
}