From c2fd4826a05af5a271934f83af71822727be4dc1 Mon Sep 17 00:00:00 2001 From: Christoph Thelen <christoph.thelen@mni.thm.de> Date: Wed, 15 Oct 2014 16:27:59 +0200 Subject: [PATCH] Added request methods that return session list info --- .../arsnova/controller/SessionController.java | 35 ++++ .../java/de/thm/arsnova/dao/CouchDBDao.java | 149 ++++++++++++++++-- .../java/de/thm/arsnova/dao/IDatabaseDao.java | 7 + .../java/de/thm/arsnova/entities/Session.java | 2 +- .../de/thm/arsnova/entities/SessionInfo.java | 115 ++++++++++++++ .../thm/arsnova/services/ISessionService.java | 5 + .../thm/arsnova/services/SessionService.java | 49 +++++- .../de/thm/arsnova/dao/StubDatabaseDao.java | 19 +++ 8 files changed, 363 insertions(+), 18 deletions(-) create mode 100644 src/main/java/de/thm/arsnova/entities/SessionInfo.java diff --git a/src/main/java/de/thm/arsnova/controller/SessionController.java b/src/main/java/de/thm/arsnova/controller/SessionController.java index ee59ec72..f8b22256 100644 --- a/src/main/java/de/thm/arsnova/controller/SessionController.java +++ b/src/main/java/de/thm/arsnova/controller/SessionController.java @@ -43,11 +43,14 @@ import org.springframework.web.bind.annotation.RestController; import de.thm.arsnova.connector.model.Course; import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.exceptions.UnauthorizedException; import de.thm.arsnova.services.ISessionService; import de.thm.arsnova.services.IUserService; import de.thm.arsnova.services.SessionService.SessionNameComparator; +import de.thm.arsnova.services.SessionService.SessionInfoNameComparator; import de.thm.arsnova.services.SessionService.SessionShortNameComparator; +import de.thm.arsnova.services.SessionService.SessionInfoShortNameComparator; import de.thm.arsnova.web.DeprecatedApi; @RestController @@ -154,6 +157,38 @@ public class SessionController extends AbstractController { return sessions; } + /** + * Returns a list of my own sessions with only the necessary information like name, keyword, or counters. + * @param statusOnly The flag that has to be set in order to get this shortened list. + * @param response + * @return + */ + @RequestMapping(value = "/", method = RequestMethod.GET, params = "statusonly=true") + public final List<SessionInfo> getMySessions( + @RequestParam(value = "visitedonly", defaultValue = "false") final boolean visitedOnly, + @RequestParam(value = "sortby", defaultValue = "name") final String sortby, + final HttpServletResponse response + ) { + List<SessionInfo> sessions; + if (!visitedOnly) { + sessions = sessionService.getMySessionsInfo(); + } else { + sessions = sessionService.getMyVisitedSessionsInfo(); + } + + if (sessions == null || sessions.isEmpty()) { + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + return null; + } + + if (sortby != null && sortby.equals("shortname")) { + Collections.sort(sessions, new SessionInfoShortNameComparator()); + } else { + Collections.sort(sessions, new SessionInfoNameComparator()); + } + return sessions; + } + @RequestMapping(value = "/{sessionkey}/lock", method = RequestMethod.POST) public final Session lockSession( @PathVariable final String sessionkey, diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java index 6fcc51c6..e4152ad2 100644 --- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java +++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java @@ -26,7 +26,9 @@ import java.util.AbstractMap; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import net.sf.ezmorph.Morpher; import net.sf.ezmorph.MorpherRegistry; @@ -35,6 +37,7 @@ import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.util.JSONUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -57,6 +60,7 @@ import de.thm.arsnova.entities.LoggedIn; import de.thm.arsnova.entities.PossibleAnswer; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.entities.User; import de.thm.arsnova.entities.VisitedSession; import de.thm.arsnova.exceptions.NotFoundException; @@ -126,6 +130,98 @@ public class CouchDBDao implements IDatabaseDao { return result; } + @Override + public final List<SessionInfo> getMySessionsInfo(final User user) { + final List<Session> sessions = this.getMySessions(user); + return getInfosForSessions(sessions); + } + + private List<SessionInfo> getInfosForSessions(final List<Session> sessions) { + final ExtendedView questionCountView = new ExtendedView("skill_question/count_by_session"); + final ExtendedView answerCountView = new ExtendedView("skill_question/count_answers_by_session"); + final ExtendedView interposedCountView = new ExtendedView("interposed_question/count_by_session_reading"); + final ExtendedView interposedCountUserView = new ExtendedView("interposed_question/count_by_session_reading_for_creator"); + questionCountView.setSessionIdKeys(sessions); + questionCountView.setGroup(true); + answerCountView.setSessionIdKeys(sessions); + answerCountView.setGroup(true); + List<String> interposedQueryKeys = new ArrayList<String>(); + for (Session s : sessions) { + interposedQueryKeys.add("[\"" + s.get_id() + "\",\"unread\"]"); + } + try { + interposedCountView.setKeys(URLEncoder.encode("["+StringUtils.join(interposedQueryKeys, ",")+"]", "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + interposedCountView.setGroup(true); + return getSessionInfoData(sessions, questionCountView, answerCountView, interposedCountView); + } + + private List<SessionInfo> getInfosForVisitedSessions(final List<Session> sessions, final User user) { + final ExtendedView questionCountView = new ExtendedView("skill_question/count_by_session"); + final ExtendedView answerCountView = new ExtendedView("skill_question/count_answers_by_session"); + final ExtendedView interposedCountUserView = new ExtendedView("interposed_question/count_by_session_reading_for_creator"); + questionCountView.setSessionIdKeys(sessions); + questionCountView.setGroup(true); + answerCountView.setSessionIdKeys(sessions); + answerCountView.setGroup(true); + List<String> interposedQueryKeys = new ArrayList<String>(); + for (Session s : sessions) { + interposedQueryKeys.add("[\"" + s.get_id() + "\",\"" + user.getUsername() + "\",\"unread\"]"); + } + try { + interposedCountUserView.setKeys(URLEncoder.encode("["+StringUtils.join(interposedQueryKeys, ",")+"]", "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + interposedCountUserView.setGroup(true); + return getSessionInfoData(sessions, questionCountView, answerCountView, interposedCountUserView); + } + + private List<SessionInfo> getSessionInfoData(final List<Session> sessions, + final ExtendedView questionCountView, + final ExtendedView answerCountView, + final ExtendedView interposedCountView) { + final ViewResults questionCountViewResults = getDatabase().view(questionCountView); + final ViewResults answerCountViewResults = getDatabase().view(answerCountView); + final ViewResults interposedCountViewResults = getDatabase().view(interposedCountView); + + Map<String, Integer> questionCountMap = new HashMap<String, Integer>(); + for (final Document d : questionCountViewResults.getResults()) { + questionCountMap.put(d.getString("key"), d.getInt("value")); + } + Map<String, Integer> answerCountMap = new HashMap<String, Integer>(); + for (final Document d : answerCountViewResults.getResults()) { + answerCountMap.put(d.getString("key"), d.getInt("value")); + } + Map<String, Integer> interposedCountMap = new HashMap<String, Integer>(); + for (final Document d : interposedCountViewResults.getResults()) { + interposedCountMap.put(d.getJSONArray("key").getString(0), d.getInt("value")); + } + List<SessionInfo> sessionInfos = new ArrayList<SessionInfo>(); + for (Session session : sessions) { + int numQuestions = 0; + int numAnswers = 0; + int numInterposed = 0; + if (questionCountMap.containsKey(session.get_id())) { + numQuestions = questionCountMap.get(session.get_id()); + } + if (answerCountMap.containsKey(session.get_id())) { + numAnswers = answerCountMap.get(session.get_id()); + } + if (interposedCountMap.containsKey(session.get_id())) { + numInterposed = interposedCountMap.get(session.get_id()); + } + SessionInfo info = new SessionInfo(session); + info.setNumQuestions(numQuestions); + info.setNumAnswers(numAnswers); + info.setNumInterposed(numInterposed); + sessionInfos.add(info); + } + return sessionInfos; + } + @Override public final List<Question> getSkillQuestions(final User user, final Session session) { String viewName; @@ -885,22 +981,22 @@ public class CouchDBDao implements IDatabaseDao { allSessions.addAll(visitedSessions); } } - // Do these sessions still exist? + // Filter sessions that don't exist anymore, also filter my own sessions final List<Session> result = new ArrayList<Session>(); - final List<Session> deletedSessions = new ArrayList<Session>(); + final List<Session> filteredSessions = new ArrayList<Session>(); for (final Session s : allSessions) { try { final Session session = getSessionFromKeyword(s.getKeyword()); - if (session != null) { + if (session != null && !session.isCreator(user)) { result.add(session); } else { - deletedSessions.add(s); + filteredSessions.add(s); } } catch (final NotFoundException e) { - deletedSessions.add(s); + filteredSessions.add(s); } } - if (deletedSessions.isEmpty()) { + if (filteredSessions.isEmpty()) { return result; } // Update document to remove sessions that don't exist anymore @@ -928,6 +1024,12 @@ public class CouchDBDao implements IDatabaseDao { return result; } + @Override + public List<SessionInfo> getMyVisitedSessionsInfo(final User user) { + List<Session> sessions = this.getMyVisitedSessions(user); + return this.getInfosForVisitedSessions(sessions, user); + } + @Override public Answer saveAnswer(final Answer answer, final User user) { try { @@ -1007,6 +1109,12 @@ public class CouchDBDao implements IDatabaseDao { return result; } + @Override + public List<SessionInfo> getCourseSessionsInfo(final List<Course> courses) { + List<Session> sessions = this.getCourseSessions(courses); + return getInfosForSessions(sessions); + } + private static class ExtendedView extends View { private String keys; @@ -1020,18 +1128,30 @@ public class CouchDBDao implements IDatabaseDao { } public void setCourseIdKeys(final List<Course> courses) { - if (courses.isEmpty()) { + List<String> courseIds = new ArrayList<String>(); + for (Course c : courses) { + courseIds.add(c.getId()); + } + setKeyList(courseIds); + } + + public void setSessionIdKeys(final List<Session> sessions) { + List<String> sessionIds = new ArrayList<String>(); + for (Session s : sessions) { + sessionIds.add(s.get_id()); + } + setKeyList(sessionIds); + } + + public void setKeyList(final List<String> keylist) { + if (keylist.isEmpty()) { keys = "[]"; return; } final StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < courses.size() - 1; i++) { - sb.append("\"" + courses.get(i).getId() + "\","); - } - sb.append("\"" + courses.get(courses.size() - 1).getId() + "\""); - sb.append("]"); + // generates: ["<key>","<key>","<key>",...] + sb.append("[\"" + StringUtils.join(keylist, "\",\"") + "\"]"); try { setKeys(URLEncoder.encode(sb.toString(), "UTF-8")); } catch (final UnsupportedEncodingException e) { @@ -1046,10 +1166,9 @@ public class CouchDBDao implements IDatabaseDao { query.append(super.getQueryString()); } if (keys != null) { - if (query.toString().isEmpty()) { + if (!query.toString().isEmpty()) { query.append("&"); } - query.append("keys=" + keys); } diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java index 6287c0b2..84b9e16f 100644 --- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java +++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java @@ -30,6 +30,7 @@ import de.thm.arsnova.entities.InterposedReadingCount; import de.thm.arsnova.entities.LoggedIn; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.entities.User; public interface IDatabaseDao { @@ -170,4 +171,10 @@ public interface IDatabaseDao { int getLearningProgress(Session session); SimpleEntry<Integer, Integer> getMyLearningProgress(Session session, User user); + + List<SessionInfo> getMySessionsInfo(User user); + + List<SessionInfo> getCourseSessionsInfo(List<Course> myCourses); + + List<SessionInfo> getMyVisitedSessionsInfo(User currentUser); } diff --git a/src/main/java/de/thm/arsnova/entities/Session.java b/src/main/java/de/thm/arsnova/entities/Session.java index ff44e399..3e62ed48 100644 --- a/src/main/java/de/thm/arsnova/entities/Session.java +++ b/src/main/java/de/thm/arsnova/entities/Session.java @@ -148,6 +148,6 @@ public class Session implements Serializable { @Override public String toString() { - return "Session [keyword=" + keyword+ ", type=" + type + "]"; + return "Session [keyword=" + keyword+ ", type=" + type + ", creator=" + creator + "]"; } } diff --git a/src/main/java/de/thm/arsnova/entities/SessionInfo.java b/src/main/java/de/thm/arsnova/entities/SessionInfo.java new file mode 100644 index 00000000..b1a0be9c --- /dev/null +++ b/src/main/java/de/thm/arsnova/entities/SessionInfo.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 THM webMedia + * + * This file is part of ARSnova. + * + * ARSnova 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 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.entities; + +import java.util.ArrayList; +import java.util.List; + +public class SessionInfo { + + private String name; + private String shortName; + private String keyword; + private boolean active; + private String courseType; + + private int numQuestions; + private int numAnswers; + private int numInterposed; + + public SessionInfo(Session session) { + this.name = session.getName(); + this.shortName = session.getShortName(); + this.keyword = session.getKeyword(); + this.active = session.isActive(); + this.courseType = session.getCourseType(); + } + + public static List<SessionInfo> fromSessionList(List<Session> sessions) { + List<SessionInfo> infos = new ArrayList<SessionInfo>(); + for (Session s : sessions) { + infos.add(new SessionInfo(s)); + } + return infos; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getShortName() { + return shortName; + } + + public void setShortName(String shortName) { + this.shortName = shortName; + } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getCourseType() { + return courseType; + } + + public void setCourseType(String courseType) { + this.courseType = courseType; + } + + public int getNumQuestions() { + return numQuestions; + } + + public void setNumQuestions(int numQuestions) { + this.numQuestions = numQuestions; + } + + public int getNumAnswers() { + return numAnswers; + } + + public void setNumAnswers(int numAnswers) { + this.numAnswers = numAnswers; + } + + public int getNumInterposed() { + return numInterposed; + } + + public void setNumInterposed(int numInterposed) { + this.numInterposed = numInterposed; + } +} diff --git a/src/main/java/de/thm/arsnova/services/ISessionService.java b/src/main/java/de/thm/arsnova/services/ISessionService.java index 86f82373..a215b57a 100644 --- a/src/main/java/de/thm/arsnova/services/ISessionService.java +++ b/src/main/java/de/thm/arsnova/services/ISessionService.java @@ -25,6 +25,7 @@ import java.util.UUID; import de.thm.arsnova.connector.model.Course; import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.SessionInfo; public interface ISessionService { Session getSession(String keyword); @@ -54,4 +55,8 @@ public interface ISessionService { int getLearningProgress(String sessionkey); SimpleEntry<Integer, Integer> getMyLearningProgress(String sessionkey); + + List<SessionInfo> getMySessionsInfo(); + + 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 0af3b5fb..c25b5d64 100644 --- a/src/main/java/de/thm/arsnova/services/SessionService.java +++ b/src/main/java/de/thm/arsnova/services/SessionService.java @@ -34,9 +34,11 @@ import org.springframework.stereotype.Service; import de.thm.arsnova.connector.client.ConnectorClient; import de.thm.arsnova.connector.model.Course; +import de.thm.arsnova.connector.model.Courses; import de.thm.arsnova.dao.IDatabaseDao; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.entities.User; import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NotFoundException; @@ -54,6 +56,15 @@ public class SessionService implements ISessionService { } } + public static class SessionInfoNameComparator implements Comparator<SessionInfo>, Serializable { + private static final long serialVersionUID = 1L; + + @Override + public int compare(final SessionInfo session1, final SessionInfo session2) { + return session1.getName().compareToIgnoreCase(session2.getName()); + } + } + public static class SessionShortNameComparator implements Comparator<Session>, Serializable { private static final long serialVersionUID = 1L; @@ -63,6 +74,15 @@ public class SessionService implements ISessionService { } } + public static class SessionInfoShortNameComparator implements Comparator<SessionInfo>, Serializable { + private static final long serialVersionUID = 1L; + + @Override + public int compare(final SessionInfo session1, final SessionInfo session2) { + return session1.getShortName().compareToIgnoreCase(session2.getShortName()); + } + } + @Autowired private IDatabaseDao databaseDao; @@ -128,14 +148,12 @@ public class SessionService implements ISessionService { throw new ForbiddenException(); } } - if (connectorClient != null && session.isCourseSession()) { final String courseid = session.getCourseId(); if (!connectorClient.getMembership(userService.getCurrentUser().getUsername(), courseid).isMember()) { throw new ForbiddenException(); } } - return session; } @@ -151,6 +169,11 @@ public class SessionService implements ISessionService { connectorClient.getCourses(userService.getCurrentUser().getUsername()).getCourse() ); + return combineSessions(mySessions, courseSessions); + } + + private List<Session> combineSessions(final List<Session> mySessions, + final List<Session> courseSessions) { final Map<String, Session> allAvailableSessions = new HashMap<String, Session>(); for (final Session session : mySessions) { @@ -162,12 +185,34 @@ public class SessionService implements ISessionService { return new ArrayList<Session>(allAvailableSessions.values()); } + @Override + @PreAuthorize("isAuthenticated()") + public final List<SessionInfo> getMySessionsInfo() { + final User user = userService.getCurrentUser(); + final List<SessionInfo> mySessions = databaseDao.getMySessionsInfo(user); + if (connectorClient == null) { + return mySessions; + } + + final List<Course> myCourses = connectorClient.getCourses(user.getUsername()).getCourse(); + final List<SessionInfo> courseSessions = databaseDao.getCourseSessionsInfo(myCourses); + // assume that my sessions and my course sessions are distinct + mySessions.addAll(courseSessions); + return mySessions; + } + @Override @PreAuthorize("isAuthenticated()") public final List<Session> getMyVisitedSessions() { return databaseDao.getMyVisitedSessions(userService.getCurrentUser()); } + @Override + @PreAuthorize("isAuthenticated()") + public final List<SessionInfo> getMyVisitedSessionsInfo() { + return databaseDao.getMyVisitedSessionsInfo(userService.getCurrentUser()); + } + @Override @PreAuthorize("isAuthenticated()") public final Session saveSession(final Session session) { diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java index d279fa8d..ec428ad3 100644 --- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java +++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java @@ -33,6 +33,7 @@ import de.thm.arsnova.entities.InterposedReadingCount; import de.thm.arsnova.entities.LoggedIn; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.entities.User; import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NoContentException; @@ -523,4 +524,22 @@ public class StubDatabaseDao implements IDatabaseDao { // TODO Auto-generated method stub return null; } + + @Override + public List<SessionInfo> getMySessionsInfo(User user) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List<SessionInfo> getCourseSessionsInfo(List<Course> myCourses) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List<SessionInfo> getMyVisitedSessionsInfo(User currentUser) { + // TODO Auto-generated method stub + return null; + } } -- GitLab