/******************************************************************************* * 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); } }