diff --git a/src/main/java/de/thm/arsnova/controller/SessionController.java b/src/main/java/de/thm/arsnova/controller/SessionController.java index 3383c2908a6a04f857be77b28ed371e7c74442b1..890e9f337f0e5dbbb928f1f114f75001901218af 100644 --- a/src/main/java/de/thm/arsnova/controller/SessionController.java +++ b/src/main/java/de/thm/arsnova/controller/SessionController.java @@ -21,8 +21,6 @@ import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; - import javax.servlet.http.HttpServletResponse; import net.sf.json.JSONObject; @@ -101,8 +99,8 @@ public class SessionController extends AbstractController { session.setName(session.getName() + appendix); session.setShortName(session.getShortName() + appendix); } - } - + } + final Session newSession = sessionService.saveSession(session); if (newSession == null) { @@ -195,7 +193,7 @@ public class SessionController extends AbstractController { final HttpServletResponse response ) { List<SessionInfo> sessions = sessionService.getMyPublicPoolSessionsInfo(); - + if (sessions == null || sessions.isEmpty()) { response.setStatus(HttpServletResponse.SC_NO_CONTENT); return null; @@ -203,13 +201,13 @@ public class SessionController extends AbstractController { return sessions; } - + @RequestMapping(value = "/publicpool", method = RequestMethod.GET) public final List<Session> getPublicPoolSessions( final HttpServletResponse response ) { List<Session> sessions = sessionService.getPublicPoolSessions(); - + if (sessions == null || sessions.isEmpty()) { response.setStatus(HttpServletResponse.SC_NO_CONTENT); return null; @@ -234,17 +232,19 @@ public class SessionController extends AbstractController { @RequestMapping(value = "/{sessionkey}/learningprogress", method = RequestMethod.GET) public final int learningProgress( @PathVariable final String sessionkey, + @RequestParam(value = "type", defaultValue = "questions") final String progressType, final HttpServletResponse response ) { - return sessionService.getLearningProgress(sessionkey); + return sessionService.getLearningProgress(sessionkey, progressType); } @RequestMapping(value = "/{sessionkey}/mylearningprogress", method = RequestMethod.GET) public final JSONObject myLearningProgress( @PathVariable final String sessionkey, + @RequestParam(value = "type", defaultValue = "questions") final String progressType, final HttpServletResponse response ) { - final SimpleEntry<Integer, Integer> result = sessionService.getMyLearningProgress(sessionkey); + final SimpleEntry<Integer, Integer> result = sessionService.getMyLearningProgress(sessionkey, progressType); final JSONObject json = new JSONObject(); json.put("myprogress", result.getKey()); json.put("courseprogress", result.getValue()); diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java index c2eb003d82b031a8d52eabf855ef16e3af928137..3f2a8b3b703150f8bfd406575e2ff3bb950ea4b0 100644 --- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java +++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java @@ -18,15 +18,12 @@ package de.thm.arsnova.dao; import java.io.IOException; -import java.util.AbstractMap; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; 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; @@ -50,6 +47,7 @@ import com.fourspaces.couchdb.View; import com.fourspaces.couchdb.ViewResults; import de.thm.arsnova.connector.model.Course; +import de.thm.arsnova.domain.CourseScore; import de.thm.arsnova.entities.Answer; import de.thm.arsnova.entities.DbUser; import de.thm.arsnova.entities.InterposedQuestion; @@ -127,7 +125,7 @@ public class CouchDBDao implements IDatabaseDao { } return result; } - + @Override public final List<Session> getPublicPoolSessions() { final NovaView view = new NovaView("session/public_pool_by_subject"); @@ -135,7 +133,7 @@ public class CouchDBDao implements IDatabaseDao { final ViewResults sessions = getDatabase().view(view); final List<Session> result = new ArrayList<Session>(); - + for (final Document d : sessions.getResults()) { final Session session = (Session) JSONObject.toBean( d.getJSONObject().getJSONObject("value"), @@ -146,13 +144,13 @@ public class CouchDBDao implements IDatabaseDao { } return result; } - + @Override public final List<Session> getMyPublicPoolSessions(final User user) { final NovaView view = new NovaView("session/public_pool_by_creator"); view.setStartKeyArray(user.getUsername()); view.setEndKeyArray(user.getUsername(), "{}"); - + final ViewResults sessions = getDatabase().view(view); final List<Session> result = new ArrayList<Session>(); @@ -168,13 +166,13 @@ public class CouchDBDao implements IDatabaseDao { } return result; } - + @Override public final List<SessionInfo> getMyPublicPoolSessionsInfo(final User user) { final List<Session> sessions = this.getMyPublicPoolSessions(user); return getInfosForSessions(sessions); } - + @Override public final List<SessionInfo> getMySessionsInfo(final User user) { final List<Session> sessions = this.getMySessions(user); @@ -186,7 +184,7 @@ public class CouchDBDao implements IDatabaseDao { final ExtendedView answerCountView = new ExtendedView("skill_question/count_answers_by_session"); final ExtendedView interposedCountView = new ExtendedView("interposed_question/count_by_session"); final ExtendedView unredInterposedCountView = new ExtendedView("interposed_question/count_by_session_reading"); - + interposedCountView.setSessionIdKeys(sessions); interposedCountView.setGroup(true); questionCountView.setSessionIdKeys(sessions); @@ -278,7 +276,7 @@ public class CouchDBDao implements IDatabaseDao { private List<SessionInfo> getSessionInfoData(final List<Session> sessions, final ExtendedView questionCountView, final ExtendedView answerCountView, - final ExtendedView interposedCountView, + final ExtendedView interposedCountView, final ExtendedView unredInterposedCountView) { final ViewResults questionCountViewResults = getDatabase().view(questionCountView); final ViewResults answerCountViewResults = getDatabase().view(answerCountView); @@ -301,7 +299,7 @@ public class CouchDBDao implements IDatabaseDao { for (final Document d : unredInterposedCountViewResults.getResults()) { unredInterposedCountMap.put(d.getJSONArray("key").getString(0), d.getInt("value")); } - + List<SessionInfo> sessionInfos = new ArrayList<SessionInfo>(); for (Session session : sessions) { int numQuestions = 0; @@ -320,7 +318,7 @@ public class CouchDBDao implements IDatabaseDao { if (unredInterposedCountMap.containsKey(session.get_id())) { numUnredInterposed = unredInterposedCountMap.get(session.get_id()); } - + SessionInfo info = new SessionInfo(session); info.setNumQuestions(numQuestions); info.setNumAnswers(numAnswers); @@ -811,7 +809,7 @@ public class CouchDBDao implements IDatabaseDao { if (results.getResults().size() == 0) { return 0; } - + return results.getJSONArray("rows").optJSONObject(0).optInt("value"); } @@ -1565,9 +1563,7 @@ public class CouchDBDao implements IDatabaseDao { } @Override - 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... + public CourseScore getLearningProgress(final Session session) { 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()); @@ -1578,98 +1574,28 @@ public class CouchDBDao implements IDatabaseDao { final List<Document> maximumValueResult = getDatabase().view(maximumValueView).getResults(); final List<Document> answerSumResult = getDatabase().view(answerSumView).getResults(); + CourseScore courseScore = new CourseScore(); + // no results found if (maximumValueResult.isEmpty() || answerSumResult.isEmpty()) { - return 0; + return courseScore; } - // collect mapping (questionId -> value) - Map<String, Integer> questionValues = new HashMap<String, Integer>(); + // collect mapping (questionId -> max value) for (Document d : maximumValueResult) { - questionValues.put(d.getJSONArray("key").getString(1), d.getInt("value")); + String questionId = d.getJSONArray("key").getString(1); + int questionScore = d.getInt("value"); + courseScore.add(questionId, questionScore); } // collect mapping (questionId -> (user -> value)) - Map<String, Map<String, Integer>> answerValues = new HashMap<String, Map<String, Integer>>(); for (Document d : answerSumResult) { + String username = d.getJSONArray("key").getString(1); 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++; - } - } + int userscore = value.getInt("score"); + courseScore.add(questionId, username, userscore); } - - 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_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); - } - - // 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 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); + return courseScore; } @Override diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java index 802240158f02a93e8f234b88ff56704e5470068d..3807436a32ae3f5fa004215da177a355e810f346 100644 --- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java +++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java @@ -17,12 +17,9 @@ */ package de.thm.arsnova.dao; -import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; import java.util.List; -import java.util.Map; - import de.thm.arsnova.connector.model.Course; +import de.thm.arsnova.domain.CourseScore; import de.thm.arsnova.entities.Answer; import de.thm.arsnova.entities.DbUser; import de.thm.arsnova.entities.InterposedQuestion; @@ -39,9 +36,9 @@ public interface IDatabaseDao { Session getSession(String keyword); List<Session> getMySessions(User user); - + List<Session> getPublicPoolSessions(); - + List<Session> getMyPublicPoolSessions(User user); Session saveSession(User user, Session session); @@ -75,7 +72,7 @@ public interface IDatabaseDao { List<Answer> getAnswers(String questionId, int piRound); int getAnswerCount(Question question, int piRound); - + int getAbstentionAnswerCount(String questionId); List<Answer> getFreetextAnswers(String questionId); @@ -161,7 +158,7 @@ public interface IDatabaseDao { void deleteAllInterposedQuestions(Session session); void deleteAllInterposedQuestions(Session session, User user); - + void publishQuestions(Session session, boolean publish, List<Question> questions); void publishAllQuestions(Session session, boolean publish); @@ -174,14 +171,12 @@ public interface IDatabaseDao { boolean deleteUser(DbUser dbUser); - int getLearningProgress(Session session); - - SimpleEntry<Integer, Integer> getMyLearningProgress(Session session, User user); + CourseScore getLearningProgress(Session session); List<SessionInfo> getMySessionsInfo(User user); List<SessionInfo> getMyPublicPoolSessionsInfo(final User user); - + List<SessionInfo> getMyVisitedSessionsInfo(User currentUser); void deleteAllPreparationAnswers(Session session); diff --git a/src/main/java/de/thm/arsnova/domain/CourseScore.java b/src/main/java/de/thm/arsnova/domain/CourseScore.java new file mode 100644 index 0000000000000000000000000000000000000000..1373028512330dcfbef98de082fe4d6e43634b14 --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/CourseScore.java @@ -0,0 +1,80 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend 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. + * + * ARSnova Backend 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.domain; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class CourseScore implements Iterable<QuestionScore> { + + private final Map<String, QuestionScore> scores = new HashMap<String, QuestionScore>(); + + public void add(String questionId, int questionScore) { + scores.put(questionId, new QuestionScore(questionId, questionScore)); + } + + /** + * @pre questionId has been added before. + * @param questionId + * @param username + * @param userscore + */ + public void add(String questionId, String username, int userscore) { + if (!scores.containsKey(questionId)) { + throw new IllegalArgumentException("Invalid argument questionId"); + } + QuestionScore questionScore = scores.get(questionId); + questionScore.add(username, userscore); + } + + public int getMaximumScore() { + int score = 0; + for (QuestionScore questionScore : this) { + score += questionScore.getMaximum(); + } + return score; + } + + public int getTotalUserScore() { + int score = 0; + for (QuestionScore questionScore : this) { + score += questionScore.getTotalUserScore(); + } + return score; + } + + public int getTotalUserCount() { + Set<String> users = new HashSet<String>(); + for (QuestionScore questionScore : this) { + questionScore.collectUsers(users); + } + return users.size(); + } + + public int getQuestionCount() { + return scores.size(); + } + + @Override + public Iterator<QuestionScore> iterator() { + return this.scores.values().iterator(); + } +} diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgress.java b/src/main/java/de/thm/arsnova/domain/LearningProgress.java new file mode 100644 index 0000000000000000000000000000000000000000..e8ccfbfedc80275bfaedfff91cf00cfbab1aa76b --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/LearningProgress.java @@ -0,0 +1,30 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend 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. + * + * ARSnova Backend 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.domain; + +import java.util.AbstractMap.SimpleEntry; + +import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.User; + +public interface LearningProgress { + + public int getCourseProgress(Session session); + + public SimpleEntry<Integer,Integer> getMyProgress(Session session, User user); +} diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java b/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0f819d410e0b95b018ce3cb38f1f16713d8b3907 --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java @@ -0,0 +1,22 @@ +package de.thm.arsnova.domain; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import de.thm.arsnova.dao.IDatabaseDao; + +@Component +public class LearningProgressFactory { + + @Autowired + private IDatabaseDao databaseDao; + + public LearningProgress createFromType(String progressType) { + if (progressType.equals("questions")) { + return new QuestionBasedLearningProgress(databaseDao); + } else { + return new PointBasedLearningProgress(databaseDao); + } + } + +} diff --git a/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java b/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java new file mode 100644 index 0000000000000000000000000000000000000000..be968bff83b08dd4a97caf20e734aba3121dcf37 --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/PointBasedLearningProgress.java @@ -0,0 +1,70 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend 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. + * + * ARSnova Backend 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.domain; + +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; + +import de.thm.arsnova.dao.IDatabaseDao; +import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.User; + +public class PointBasedLearningProgress implements LearningProgress { + + private IDatabaseDao databaseDao; + + public PointBasedLearningProgress(IDatabaseDao dao) { + this.databaseDao = dao; + } + + @Override + public int getCourseProgress(Session session) { + CourseScore courseScore = databaseDao.getLearningProgress(session); + return calculateCourseScore(courseScore); + } + + private int calculateCourseScore(CourseScore courseScore) { + final double courseMaximumValue = courseScore.getMaximumScore(); + final double userTotalValue = courseScore.getTotalUserScore(); + final double numUsers = courseScore.getTotalUserCount(); + if (courseMaximumValue == 0 || numUsers == 0) { + return 0; + } + final double courseAverageValue = userTotalValue / numUsers; + final double courseProgress = courseAverageValue / courseMaximumValue; + return (int)Math.min(100, Math.round(courseProgress * 100)); + } + + @Override + public SimpleEntry<Integer, Integer> getMyProgress(Session session, User user) { + CourseScore courseScore = databaseDao.getLearningProgress(session); + int courseProgress = calculateCourseScore(courseScore); + + final double courseMaximumValue = courseScore.getMaximumScore(); + final double userTotalValue = courseScore.getTotalUserScore(); + + if (courseMaximumValue == 0) { + return new AbstractMap.SimpleEntry<Integer, Integer>(0, courseProgress); + } + final double myProgress = userTotalValue / courseMaximumValue; + final int myLearningProgress = (int)Math.min(100, Math.round(myProgress*100)); + + return new AbstractMap.SimpleEntry<Integer, Integer>(myLearningProgress, courseProgress); + } + +} diff --git a/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java b/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java new file mode 100644 index 0000000000000000000000000000000000000000..9fcc56ce4874579326ab6eecc516a37996c2d707 --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/QuestionBasedLearningProgress.java @@ -0,0 +1,100 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend 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. + * + * ARSnova Backend 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.domain; + +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; + +import de.thm.arsnova.dao.IDatabaseDao; +import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.User; + +public class QuestionBasedLearningProgress implements LearningProgress { + + private IDatabaseDao databaseDao; + + public QuestionBasedLearningProgress(IDatabaseDao dao) { + this.databaseDao = dao; + } + + @Override + public int getCourseProgress(Session session) { + CourseScore courseScore = databaseDao.getLearningProgress(session); + return calculateCourseProgress(courseScore); + } + + private int calculateCourseProgress(CourseScore courseScore) { + int numQuestionsCorrect = numQuestionsCorrectForCourse(courseScore); + int numUsers = courseScore.getTotalUserCount(); + final double correctQuestionsOnAverage = (double)numQuestionsCorrect / (double)numUsers; + final double myLearningProgress = correctQuestionsOnAverage / courseScore.getQuestionCount(); + // calculate percent, cap results to 100 + return (int) Math.min(100, Math.round(myLearningProgress*100)); + } + + private int numQuestionsCorrectForCourse(CourseScore courseScore) { + // a question is seen as "correct" if and only if all participants have answered it correctly + int numQuestionsCorrect = 0; + for (QuestionScore questionScore : courseScore) { + int requiredScore = questionScore.getMaximum(); + boolean allCorrect = true; + for (UserScore userScore : questionScore) { + if (!userScore.hasScore(requiredScore)) { + allCorrect = false; + break; + } + } + if (allCorrect) { + numQuestionsCorrect++; + } + } + return numQuestionsCorrect; + } + + @Override + public SimpleEntry<Integer, Integer> getMyProgress(Session session, User user) { + CourseScore courseScore = databaseDao.getLearningProgress(session); + + int courseProgress = calculateCourseProgress(courseScore); + + int numQuestionsCorrect = numQuestionsCorrectForUser(user, courseScore); + final double myLearningProgress = numQuestionsCorrect / (double)(courseScore.getQuestionCount()); + // 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); + } + + private int numQuestionsCorrectForUser(User user, CourseScore courseScore) { + // 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 (QuestionScore questionScore : courseScore) { + int requiredScore = questionScore.getMaximum(); + for (UserScore userScore : questionScore) { + if (!userScore.isUser(user)) { + continue; + } + if (userScore.hasScore(requiredScore)) { + numQuestionsCorrect++; + } + } + } + return numQuestionsCorrect; + } + +} diff --git a/src/main/java/de/thm/arsnova/domain/QuestionScore.java b/src/main/java/de/thm/arsnova/domain/QuestionScore.java new file mode 100644 index 0000000000000000000000000000000000000000..3aceb3e97b79a7351cdc946569b7a96477f1cdce --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/QuestionScore.java @@ -0,0 +1,68 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend 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. + * + * ARSnova Backend 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.domain; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class QuestionScore implements Iterable<UserScore> { + + private String questionId; + + private int maximumScore; + + private List<UserScore> userScores = new ArrayList<UserScore>(); + + public QuestionScore(String questionId, int maximumScore) { + this.questionId = questionId; + this.maximumScore = maximumScore; + } + + public int getMaximum() { + return this.maximumScore; + } + + @Override + public Iterator<UserScore> iterator() { + return this.userScores.iterator(); + } + + public void add(String username, int userscore) { + userScores.add(new UserScore(username, userscore)); + } + + public int getTotalUserScore() { + int totalScore = 0; + for (UserScore score : userScores) { + totalScore += score.getScore(); + } + return totalScore; + } + + public int getUserCount() { + return userScores.size(); + } + + public void collectUsers(Set<String> users) { + for (UserScore score : userScores) { + users.add(score.getUsername()); + } + } +} diff --git a/src/main/java/de/thm/arsnova/domain/UserScore.java b/src/main/java/de/thm/arsnova/domain/UserScore.java new file mode 100644 index 0000000000000000000000000000000000000000..282fd06e7e73692997fee753e597b91e0c84cfde --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/UserScore.java @@ -0,0 +1,48 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend 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. + * + * ARSnova Backend 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.domain; + +import de.thm.arsnova.entities.User; + +public class UserScore { + + private String username; + + private int score; + + public UserScore(String username, int score) { + this.username = username; + this.score = score; + } + + public boolean hasScore(int score) { + return this.score == score; + } + + public int getScore() { + return score; + } + + public boolean isUser(User user) { + return user.getUsername().equals(username); + } + + public String getUsername() { + return username; + } +} diff --git a/src/main/java/de/thm/arsnova/domain/package-info.java b/src/main/java/de/thm/arsnova/domain/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..ee2d2f1de66118173509e1ba7b8f66e6acf2343f --- /dev/null +++ b/src/main/java/de/thm/arsnova/domain/package-info.java @@ -0,0 +1 @@ +package de.thm.arsnova.domain; \ No newline at end of file diff --git a/src/main/java/de/thm/arsnova/services/ISessionService.java b/src/main/java/de/thm/arsnova/services/ISessionService.java index 0ded048f9016e43d164ac42e5338eab31422806f..8d223c64c192af75cb1f8b1a8d31275d56a34ddc 100644 --- a/src/main/java/de/thm/arsnova/services/ISessionService.java +++ b/src/main/java/de/thm/arsnova/services/ISessionService.java @@ -18,9 +18,7 @@ package de.thm.arsnova.services; import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.UUID; import de.thm.arsnova.connector.model.Course; @@ -37,7 +35,7 @@ public interface ISessionService { String generateKeyword(); List<Session> getMySessions(); - + List<Session> getPublicPoolSessions(); List<Session> getMyVisitedSessions(); @@ -54,12 +52,12 @@ public interface ISessionService { void deleteSession(String sessionkey); - int getLearningProgress(String sessionkey); + int getLearningProgress(String sessionkey, String progressType); - SimpleEntry<Integer, Integer> getMyLearningProgress(String sessionkey); + SimpleEntry<Integer, Integer> getMyLearningProgress(String sessionkey, String progressType); List<SessionInfo> getMySessionsInfo(); - + List<SessionInfo> getMyPublicPoolSessionsInfo(); List<SessionInfo> getMyVisitedSessionsInfo(); diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java index 37b9f8b4cadebe3d43dd7e1110d1668c5df3826e..1f29a67cb4987dbf705cea45ae107aa0892e0f7e 100644 --- a/src/main/java/de/thm/arsnova/services/SessionService.java +++ b/src/main/java/de/thm/arsnova/services/SessionService.java @@ -19,17 +19,14 @@ package de.thm.arsnova.services; import java.io.Serializable; import java.util.AbstractMap.SimpleEntry; -import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.UUID; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +34,8 @@ import de.thm.arsnova.ImageUtils; import de.thm.arsnova.connector.client.ConnectorClient; import de.thm.arsnova.connector.model.Course; import de.thm.arsnova.dao.IDatabaseDao; +import de.thm.arsnova.domain.LearningProgress; +import de.thm.arsnova.domain.LearningProgressFactory; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.SessionInfo; @@ -94,12 +93,15 @@ public class SessionService implements ISessionService { @Autowired private ARSnovaSocketIOServer socketIoServer; + @Autowired + private LearningProgressFactory learningProgressFactory; + @Autowired(required = false) private ConnectorClient connectorClient; - + @Value("${pp.logofilesize_b}") private int uploadFileSizeByte; - + public static final Logger LOGGER = LoggerFactory.getLogger(SessionService.class); public void setDatabaseDao(final IDatabaseDao newDatabaseDao) { @@ -164,13 +166,13 @@ public class SessionService implements ISessionService { public final List<Session> getMySessions() { return databaseDao.getMySessions(userService.getCurrentUser()); } - + @Override @PreAuthorize("isAuthenticated()") public final List<Session> getPublicPoolSessions() { return databaseDao.getPublicPoolSessions(); } - + @Override @PreAuthorize("isAuthenticated()") public final List<SessionInfo> getMyPublicPoolSessionsInfo() { @@ -221,7 +223,7 @@ public class SessionService implements ISessionService { throw new BadRequestException(); } } - + return databaseDao.saveSession(userService.getCurrentUser(), session); } @@ -286,16 +288,18 @@ public class SessionService implements ISessionService { @Override @PreAuthorize("isAuthenticated()") - public int getLearningProgress(final String sessionkey) { + public int getLearningProgress(final String sessionkey, final String progressType) { final Session session = databaseDao.getSession(sessionkey); - return databaseDao.getLearningProgress(session); + LearningProgress learningProgress = learningProgressFactory.createFromType(progressType); + return learningProgress.getCourseProgress(session); } @Override @PreAuthorize("isAuthenticated()") - public SimpleEntry<Integer, Integer> getMyLearningProgress(final String sessionkey) { + public SimpleEntry<Integer, Integer> getMyLearningProgress(final String sessionkey, final String progressType) { final Session session = databaseDao.getSession(sessionkey); final User user = userService.getCurrentUser(); - return databaseDao.getMyLearningProgress(session, user); + LearningProgress learningProgress = learningProgressFactory.createFromType(progressType); + return learningProgress.getMyProgress(session, user); } } diff --git a/src/main/webapp/WEB-INF/spring/spring-main.xml b/src/main/webapp/WEB-INF/spring/spring-main.xml index b9bedd607523757fe09fd2554f21851a8eaa0b7f..e5141f7570cf970dfb379f5dc75f9ab957286c4e 100644 --- a/src/main/webapp/WEB-INF/spring/spring-main.xml +++ b/src/main/webapp/WEB-INF/spring/spring-main.xml @@ -23,7 +23,7 @@ <property name="fileEncoding" value="UTF-8" /> </bean> - <context:component-scan base-package="de.thm.arsnova.dao,de.thm.arsnova.events,de.thm.arsnova.security,de.thm.arsnova.services,de.thm.arsnova.config" /> + <context:component-scan base-package="de.thm.arsnova.dao,de.thm.arsnova.events,de.thm.arsnova.security,de.thm.arsnova.services,de.thm.arsnova.config,de.thm.arsnova.domain" /> <context:annotation-config /> diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java index 8a09d652ccb10b0d08070a0080287798ab24256a..6d21ab41f2bfc9113bfab7eca7fbef43c4b16f49 100644 --- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java +++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java @@ -18,13 +18,13 @@ */ package de.thm.arsnova.dao; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import de.thm.arsnova.connector.model.Course; +import de.thm.arsnova.domain.CourseScore; import de.thm.arsnova.entities.Answer; import de.thm.arsnova.entities.DbUser; import de.thm.arsnova.entities.Feedback; @@ -193,19 +193,19 @@ public class StubDatabaseDao implements IDatabaseDao { // TODO Auto-generated method stub return null; } - + @Override public List<Session> getPublicPoolSessions() { // TODO Auto-generated method stub return null; } - + @Override public List<Session> getMyPublicPoolSessions(User user) { // TODO Auto-generated method stub return null; } - + @Override public List<SessionInfo> getMyPublicPoolSessionsInfo(final User user) { // TODO Auto-generated method stub @@ -477,7 +477,7 @@ public class StubDatabaseDao implements IDatabaseDao { public void deleteAllInterposedQuestions(Session session) { // TODO Auto-generated method stub } - + @Override public void publishQuestions(Session session, boolean publish, List<Question> questions) { // TODO Auto-generated method stub @@ -509,13 +509,7 @@ public class StubDatabaseDao implements IDatabaseDao { } @Override - public int getLearningProgress(Session session) { - // TODO Auto-generated method stub - return 0; - } - - @Override - public SimpleEntry<Integer, Integer> getMyLearningProgress(Session session, User user) { + public CourseScore getLearningProgress(Session session) { // TODO Auto-generated method stub return null; }