diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java index 706577a92c5d9d8d7ae5b64d0af91a8db4ef886e..5750bcdfda370d471d41675a9fd8db81fc08fc15 100644 --- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java +++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import net.sf.ezmorph.Morpher; @@ -1438,7 +1439,7 @@ public class CouchDBDao implements IDatabaseDao { final List<Question> questions = getQuestions(new NovaView("skill_question/by_session"), session); publishQuestions(session, publish, questions); } - + @Override public void publishQuestions(final Session session, final boolean publish, List<Question> questions) { for (final Question q : questions) { @@ -1486,61 +1487,108 @@ public class CouchDBDao implements IDatabaseDao { public int getLearningProgress(final Session session) { // Note: we have to use this many views because our CouchDB version does not support // advanced features like summing over lists. Thus, we have to do it all by ourselves... - final NovaView maximumValueView = new NovaView("learning_progress_maximum_value/max"); - final NovaView answerSumView = new NovaView("learning_progress_user_values/sum"); - final NovaView answerDocumentCountView = new NovaView("learning_progress_course_answers/count"); - maximumValueView.setKey(session.get_id()); + final NovaView maximumValueView = new NovaView("learning_progress/maximum_value_of_question"); + final NovaView answerSumView = new NovaView("learning_progress/question_value_achieved_for_user"); + maximumValueView.setStartKeyArray(session.get_id()); + maximumValueView.setEndKeyArray(session.get_id(), "{}"); answerSumView.setStartKeyArray(session.get_id()); answerSumView.setEndKeyArray(session.get_id(), "{}"); - answerDocumentCountView.setStartKeyArray(session.get_id()); - answerDocumentCountView.setEndKeyArray(session.get_id(), "{}"); - answerDocumentCountView.setGroup(true); final List<Document> maximumValueResult = getDatabase().view(maximumValueView).getResults(); final List<Document> answerSumResult = getDatabase().view(answerSumView).getResults(); - final List<Document> answerDocumentCountResult = getDatabase().view(answerDocumentCountView).getResults(); - if (maximumValueResult.isEmpty() || answerSumResult.isEmpty() || answerDocumentCountResult.isEmpty()) { + // no results found + if (maximumValueResult.isEmpty() || answerSumResult.isEmpty()) { return 0; } - final double courseMaximumValue = maximumValueResult.get(0).getInt("value"); - final double userTotalValue = answerSumResult.get(0).getInt("value"); - final double numUsers = answerDocumentCountResult.size(); - if (courseMaximumValue == 0 || numUsers == 0) { - return 0; + // collect mapping (questionId -> value) + Map<String, Integer> questionValues = new HashMap<String, Integer>(); + for (Document d : maximumValueResult) { + questionValues.put(d.getJSONArray("key").getString(1), d.getInt("value")); } - final double courseAverageValue = userTotalValue / numUsers; - final double courseProgress = courseAverageValue / courseMaximumValue; - return (int)Math.min(100, Math.round(courseProgress * 100)); + // collect mapping (questionId -> (user -> value)) + Map<String, Map<String, Integer>> answerValues = new HashMap<String, Map<String, Integer>>(); + for (Document d : answerSumResult) { + JSONObject value = d.getJSONObject("value"); + String questionId = value.getString("questionId"); + if (!answerValues.containsKey(questionId)) { + answerValues.put(questionId, new HashMap<String, Integer>()); + } + Map<String, Integer> userValues = answerValues.get(questionId); + userValues.put(d.getJSONArray("key").getString(1), value.getInt("score")); + answerValues.put(questionId, userValues); + } + + // a question is seen as "correct" if and only if all participants have answered it correctly + int numQuestionsCorrect = 0; + for (Entry<String, Integer> entry : questionValues.entrySet()) { + if (answerValues.containsKey(entry.getKey())) { + Map<String, Integer> userValues = answerValues.get(entry.getKey()); + int requiredValue = entry.getValue(); + boolean allCorrect = true; + for (Entry<String, Integer> userEntry : userValues.entrySet()) { + // did this particular participant answer it correctly, i.e., has the required points? + if (userEntry.getValue() != requiredValue) { + allCorrect = false; + break; + } + } + if (allCorrect) { + numQuestionsCorrect++; + } + } + } + + final double myLearningProgress = (double)numQuestionsCorrect / (questionValues.size()); + // calculate percent, cap results to 100 + return (int) Math.min(100, Math.round(myLearningProgress*100)); } @Override public SimpleEntry<Integer,Integer> getMyLearningProgress(final Session session, final User user) { final int courseProgress = getLearningProgress(session); - final NovaView maximumValueView = new NovaView("learning_progress_maximum_value/max"); - final NovaView answerSumView = new NovaView("learning_progress_user_values/sum"); - maximumValueView.setKey(session.get_id()); + final NovaView maximumValueView = new NovaView("learning_progress/maximum_value_of_question"); + final NovaView answerSumView = new NovaView("learning_progress/question_value_achieved_for_user"); + maximumValueView.setStartKeyArray(session.get_id()); + maximumValueView.setEndKeyArray(session.get_id(), "{}"); answerSumView.setKey(session.get_id(), user.getUsername()); final List<Document> maximumValueResult = getDatabase().view(maximumValueView).getResults(); final List<Document> answerSumResult = getDatabase().view(answerSumView).getResults(); + // no results found if (maximumValueResult.isEmpty() || answerSumResult.isEmpty()) { return new AbstractMap.SimpleEntry<Integer, Integer>(0, courseProgress); } - final double courseMaximumValue = maximumValueResult.get(0).getInt("value"); - final double userTotalValue = answerSumResult.get(0).getInt("value"); - - if (courseMaximumValue == 0) { - return new AbstractMap.SimpleEntry<Integer, Integer>(0, courseProgress); + // collect mappings (questionId -> value) + Map<String, Integer> questionValues = new HashMap<String, Integer>(); + for (Document d : maximumValueResult) { + questionValues.put(d.getJSONArray("key").getString(1), d.getInt("value")); + } + Map<String, Integer> answerValues = new HashMap<String, Integer>(); + for (Document d : answerSumResult) { + JSONObject value = d.getJSONObject("value"); + answerValues.put(value.getString("questionId"), value.getInt("score")); + } + // compare user's values to the maximum number for each question to determine the answers' correctness + // mapping (questionId -> 1 if correct, 0 if incorrect) + int numQuestionsCorrect = 0; + for (Entry<String, Integer> entry : questionValues.entrySet()) { + if (answerValues.containsKey(entry.getKey())) { + int answeredValue = answerValues.get(entry.getKey()); + if (answeredValue == entry.getValue()) { + numQuestionsCorrect++; + } + } } - final double myProgress = userTotalValue / courseMaximumValue; - final int myLearningProgress = (int)Math.min(100, Math.round(myProgress*100)); - return new AbstractMap.SimpleEntry<Integer, Integer>(myLearningProgress, courseProgress); + final double myLearningProgress = (double)numQuestionsCorrect / (double)(questionValues.size()); + // calculate percent, cap results to 100 + final int percentage = (int) Math.min(100, Math.round(myLearningProgress*100)); + return new AbstractMap.SimpleEntry<Integer, Integer>(percentage, courseProgress); } @Override