Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r62b97a5e0fd88110e023f00793a983713296f9b6 -r8993cac8ce6dcb8d8ccb24c1cb387d44c0847199 Binary files differ Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java =================================================================== diff -u -r62b97a5e0fd88110e023f00793a983713296f9b6 -r8993cac8ce6dcb8d8ccb24c1cb387d44c0847199 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java (.../ILearnerProgressDAO.java) (revision 62b97a5e0fd88110e023f00793a983713296f9b6) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java (.../ILearnerProgressDAO.java) (revision 8993cac8ce6dcb8d8ccb24c1cb387d44c0847199) @@ -102,6 +102,14 @@ List getLearnersLatestCompletedForLesson(Long lessonId, Integer limit, Integer offset); /** + * Get learners whose first name, last name or login match any of the tokens from search phrase. Sorts either by + * name or, if orderByCompletion is set, by most progressed first. Used mainly by Learners tab in Monitoring + * interface. + */ + List getLearnersByLesson(Long lessonId, String searchPhrase, boolean orderByCompletion, Integer limit, + Integer offset); + + /** * Get all the learner progress records for a lesson where the progress is marked as completed. * * @param lessonId @@ -163,6 +171,11 @@ Integer getNumUsersCompletedActivity(Activity activity); /** + * Get number of learners whose first name, last name or login match any of the tokens from search phrase. + */ + Integer getNumUsersByLesson(Long lessonId, String searchPhrase); + + /** * Get number of learners who finished the given lesson. */ Integer getNumUsersCompletedLesson(Long lessonId); Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java =================================================================== diff -u -r62b97a5e0fd88110e023f00793a983713296f9b6 -r8993cac8ce6dcb8d8ccb24c1cb387d44c0847199 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java (.../LearnerProgressDAO.java) (revision 62b97a5e0fd88110e023f00793a983713296f9b6) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java (.../LearnerProgressDAO.java) (revision 8993cac8ce6dcb8d8ccb24c1cb387d44c0847199) @@ -23,10 +23,9 @@ /* $$Id$$ */ package org.lamsfoundation.lams.lesson.dao.hibernate; -import java.math.BigInteger; -import java.util.LinkedList; import java.util.List; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.hibernate.Query; import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO; @@ -46,45 +45,48 @@ protected Logger log = Logger.getLogger(LearnerProgressDAO.class); - private final static String LOAD_PROGRESS_BY_LEARNER = "from LearnerProgress p where p.user.id = :learnerId and p.lesson.id = :lessonId"; + private static String LOAD_PROGRESS_BY_LEARNER = "from LearnerProgress p where p.user.id = :learnerId and p.lesson.id = :lessonId"; - private final static String LOAD_PROGRESS_REFFERING_TO_ACTIVITY = "from LearnerProgress p where p.previousActivity = :activity or p.currentActivity = :activity or p.nextActivity = :activity "; + private static String LOAD_PROGRESS_REFFERING_TO_ACTIVITY = "from LearnerProgress p where p.previousActivity = :activity or p.currentActivity = :activity or p.nextActivity = :activity "; - private final static String LOAD_COMPLETED_PROGRESS_BY_LESSON = "from LearnerProgress p where p.lessonComplete > 0 and p.lesson.id = :lessonId"; + private static String LOAD_COMPLETED_PROGRESS_BY_LESSON = "from LearnerProgress p where p.lessonComplete > 0 and p.lesson.id = :lessonId"; - private final static String LOAD_LEARNERS_LATEST_COMPLETED_BY_LESSON = "SELECT p.user FROM LearnerProgress p WHERE " + private static String LOAD_LEARNERS_LATEST_COMPLETED_BY_LESSON = "SELECT p.user FROM LearnerProgress p WHERE " + "p.lessonComplete > 0 and p.lesson.id = :lessonId ORDER BY p.finishDate DESC"; - private final static String COUNT_COMPLETED_PROGRESS_BY_LESSON = "select count(*) from LearnerProgress p " + private static String COUNT_COMPLETED_PROGRESS_BY_LESSON = "select count(*) from LearnerProgress p " + " where p.lessonComplete > 0 and p.lesson.id = :lessonId"; - private final static String COUNT_ATTEMPTED_ACTIVITY = "select count(*) from LearnerProgress prog, " + private static String COUNT_ATTEMPTED_ACTIVITY = "select count(*) from LearnerProgress prog, " + " Activity act join prog.attemptedActivities attAct " + " where act.id = :activityId and " + " index(attAct) = act"; - private final static String COUNT_COMPLETED_ACTIVITY = "select count(*) from LearnerProgress prog, " + private static String COUNT_COMPLETED_ACTIVITY = "select count(*) from LearnerProgress prog, " + " Activity act join prog.completedActivities compAct " + " where act.id = :activityId and " + " index(compAct) = act"; - private final static String COUNT_CURRENT_ACTIVITY = "select count(*) from LearnerProgress prog WHERE " + private static String COUNT_CURRENT_ACTIVITY = "select count(*) from LearnerProgress prog WHERE " + " prog.currentActivity = :activity"; - private final static String LOAD_PROGRESS_BY_LESSON = "from LearnerProgress p " + private static String LOAD_PROGRESS_BY_LESSON = "from LearnerProgress p " + " where p.lesson.id = :lessonId order by p.user.lastName, p.user.firstName, p.user.userId"; - private final static String LOAD_PROGRESS_BY_LESSON_AND_USER_IDS = "from LearnerProgress p " + private static String LOAD_PROGRESS_BY_LESSON_AND_USER_IDS = "from LearnerProgress p " + " where p.lesson.id = :lessonId AND p.user.userId IN (:userIds) order by p.user.lastName, p.user.firstName, p.user.userId"; - private final static String LOAD_PROGRESSES_BY_LESSON_LIST = "FROM LearnerProgress progress WHERE " + private static String LOAD_PROGRESSES_BY_LESSON_LIST = "FROM LearnerProgress progress WHERE " + " progress.lesson.lessonId IN (:lessonIds)"; - private final static String LOAD_LEARNERS_LATEST_BY_ACTIVITY = "SELECT prog.user_id FROM lams_learner_progress AS prog " + private static String LOAD_LEARNERS_LATEST_BY_ACTIVITY = "SELECT u.* FROM lams_learner_progress AS prog " + "JOIN lams_progress_attempted AS att USING (learner_progress_id) " + + "JOIN lams_user AS u USING (user_id) " + "WHERE prog.current_activity_id = :activityId AND att.activity_id = :activityId " + "ORDER BY att.start_date_time DESC"; - private final static String LOAD_LEARNERS_BY_ACTIVITIES = "SELECT p.user FROM LearnerProgress p WHERE " + private static String LOAD_LEARNERS_BY_ACTIVITIES = "SELECT p.user FROM LearnerProgress p WHERE " + " p.currentActivity.id IN (:activityIds)"; + private static String LOAD_LEARNERS_BY_LESSON = "FROM LearnerProgress prog WHERE prog.lesson.id = :lessonId"; + @Override public LearnerProgress getLearnerProgress(Long learnerProgressId) { return (LearnerProgress) getSession().get(LearnerProgress.class, learnerProgressId); @@ -101,7 +103,7 @@ } @Override - public LearnerProgress getLearnerProgressByLearner(final Integer learnerId, final Long lessonId) { + public LearnerProgress getLearnerProgressByLearner(Integer learnerId, Long lessonId) { return (LearnerProgress) getSession().createQuery(LearnerProgressDAO.LOAD_PROGRESS_BY_LEARNER) .setInteger("learnerId", learnerId).setLong("lessonId", lessonId).uniqueResult(); @@ -114,35 +116,28 @@ @SuppressWarnings("unchecked") @Override - public List getLearnerProgressReferringToActivity(final Activity activity) { + public List getLearnerProgressReferringToActivity(Activity activity) { return getSession().createQuery(LearnerProgressDAO.LOAD_PROGRESS_REFFERING_TO_ACTIVITY) .setEntity("activity", activity).list(); } @SuppressWarnings("unchecked") @Override - public List getLearnersLatestByActivity(final Long activityId, final Integer limit, final Integer offset) { + public List getLearnersLatestByActivity(Long activityId, Integer limit, Integer offset) { Query query = getSession().createSQLQuery(LearnerProgressDAO.LOAD_LEARNERS_LATEST_BY_ACTIVITY) - .setLong("activityId", activityId); + .addEntity(User.class).setLong("activityId", activityId); if (limit != null) { query.setMaxResults(limit); } if (offset != null) { query.setFirstResult(offset); } - // first query fetches only progress IDs - List result = query.list(); - // fetch user objects and return them - List learners = new LinkedList(); - for (BigInteger userId : result) { - learners.add((User) getSession().get(User.class, userId.intValue())); - } - return learners; + return query.list(); } @SuppressWarnings("unchecked") @Override - public List getLearnersByActivities(final Long[] activityIds, final Integer limit, final Integer offset) { + public List getLearnersByActivities(Long[] activityIds, Integer limit, Integer offset) { Query query = getSession().createQuery(LearnerProgressDAO.LOAD_LEARNERS_BY_ACTIVITIES) .setParameterList("activityIds", activityIds); if (limit != null) { @@ -156,8 +151,7 @@ @SuppressWarnings("unchecked") @Override - public List getLearnersLatestCompletedForLesson(final Long lessonId, final Integer limit, - final Integer offset) { + public List getLearnersLatestCompletedForLesson(Long lessonId, Integer limit, Integer offset) { Query query = getSession().createQuery(LearnerProgressDAO.LOAD_LEARNERS_LATEST_COMPLETED_BY_LESSON) .setLong("lessonId", lessonId); if (limit != null) { @@ -171,65 +165,109 @@ @SuppressWarnings("unchecked") @Override - public List getCompletedLearnerProgressForLesson(final Long lessonId) { + public List getLearnersByLesson(Long lessonId, String searchPhrase, boolean orderByCompletion, Integer limit, + Integer offset) { + String queryText = LearnerProgressDAO.buildLearnersByLessonQuery(false, searchPhrase, orderByCompletion); + + Query query = getSession().createQuery(queryText).setLong("lessonId", lessonId); + if (limit != null) { + query.setMaxResults(limit); + } + if (offset != null) { + query.setFirstResult(offset); + } + return query.list(); + } + + private static String buildLearnersByLessonQuery(boolean count, String searchPhrase, Boolean orderByCompletion) { + StringBuilder queryText = new StringBuilder("SELECT ").append(count ? "COUNT(*) " : "prog.user ") + .append(LearnerProgressDAO.LOAD_LEARNERS_BY_LESSON); + if (!StringUtils.isBlank(searchPhrase)) { + String[] tokens = searchPhrase.trim().split("\\s+"); + for (String token : tokens) { + queryText.append(" AND (prog.user.firstName LIKE '%").append(token) + .append("%' OR prog.user.lastName LIKE '%").append(token) + .append("%' OR prog.user.login LIKE '%").append(token).append("%')"); + } + } + if (!count && (orderByCompletion != null)) { + queryText.append(" ORDER BY"); + if (orderByCompletion) { + queryText.append(" prog.lessonComplete DESC, prog.completedActivities.size DESC,"); + } + queryText.append(" prog.user.firstName ASC, prog.user.lastName ASC"); + } + return queryText.toString(); + } + + @SuppressWarnings("unchecked") + @Override + public List getCompletedLearnerProgressForLesson(Long lessonId) { return getSession().createQuery(LearnerProgressDAO.LOAD_COMPLETED_PROGRESS_BY_LESSON) .setLong("lessonId", lessonId).list(); } @SuppressWarnings("unchecked") @Override - public List getLearnerProgressForLesson(final Long lessonId) { + public List getLearnerProgressForLesson(Long lessonId) { return getSession().createQuery(LearnerProgressDAO.LOAD_PROGRESS_BY_LESSON).setLong("lessonId", lessonId) .list(); } @SuppressWarnings("unchecked") @Override - public List getLearnerProgressForLesson(final Long lessonId, final List userIds) { + public List getLearnerProgressForLesson(Long lessonId, List userIds) { return getSession().createQuery(LearnerProgressDAO.LOAD_PROGRESS_BY_LESSON_AND_USER_IDS) .setLong("lessonId", lessonId).setParameterList("userIds", userIds).list(); } @SuppressWarnings("unchecked") @Override - public List getLearnerProgressForLessons(final List lessonIds) { + public List getLearnerProgressForLessons(List lessonIds) { return getSession().createQuery(LearnerProgressDAO.LOAD_PROGRESSES_BY_LESSON_LIST) .setParameterList("lessonIds", lessonIds).list(); } @Override @SuppressWarnings("unchecked") - public List getLearnersHaveAttemptedActivity(final Activity activity) { + public List getLearnersHaveAttemptedActivity(Activity activity) { List learners = getSession().getNamedQuery("usersAttemptedActivity") .setLong("activityId", activity.getActivityId().longValue()).list(); return learners; } @Override - public Integer getNumUsersAttemptedActivity(final Activity activity) { + public Integer getNumUsersAttemptedActivity(Activity activity) { Object value = getSession().createQuery(LearnerProgressDAO.COUNT_ATTEMPTED_ACTIVITY) .setLong("activityId", activity.getActivityId().longValue()).uniqueResult(); Integer attempted = new Integer(((Number) value).intValue()); return new Integer(attempted.intValue() + getNumUsersCompletedActivity(activity).intValue()); } @Override - public Integer getNumUsersCompletedActivity(final Activity activity) { + public Integer getNumUsersCompletedActivity(Activity activity) { Object value = getSession().createQuery(LearnerProgressDAO.COUNT_COMPLETED_ACTIVITY) .setLong("activityId", activity.getActivityId().longValue()).uniqueResult(); return new Integer(((Number) value).intValue()); } @Override - public Integer getNumUsersCompletedLesson(final Long lessonId) { + public Integer getNumUsersByLesson(Long lessonId, String searchPhrase) { + String queryText = LearnerProgressDAO.buildLearnersByLessonQuery(true, searchPhrase, null); + Object value = getSession().createQuery(queryText).setLong("lessonId", lessonId.longValue()).uniqueResult(); + return ((Number) value).intValue(); + } + + @Override + public Integer getNumUsersCompletedLesson(Long lessonId) { Object value = getSession().createQuery(LearnerProgressDAO.COUNT_COMPLETED_PROGRESS_BY_LESSON) .setLong("lessonId", lessonId).uniqueResult(); return ((Number) value).intValue(); } @Override - public Integer getNumUsersCurrentActivity(final Activity activity) { + public Integer getNumUsersCurrentActivity(Activity activity) { Object value = getSession().createQuery(LearnerProgressDAO.COUNT_CURRENT_ACTIVITY) .setEntity("activity", activity).uniqueResult(); return ((Number) value).intValue(); Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java =================================================================== diff -u -r62b97a5e0fd88110e023f00793a983713296f9b6 -r8993cac8ce6dcb8d8ccb24c1cb387d44c0847199 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 62b97a5e0fd88110e023f00793a983713296f9b6) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 8993cac8ce6dcb8d8ccb24c1cb387d44c0847199) @@ -686,6 +686,14 @@ List getLearnersLatestCompleted(Long lessonId, Integer limit, Integer offset); /** + * Get learners whose first name, last name or login match any of the tokens from search phrase. Sorts either by + * name or, if orderByCompletion is set, by most progressed first. Used mainly by Learners tab in Monitoring + * interface. + */ + List getLearnersFromProgress(Long lessonId, String searchPhrase, boolean orderByCompletion, Integer limit, + Integer offset); + + /** * Get learners who most recently entered the activity. */ List getLearnersLatestByActivity(Long activityId, Integer limit, Integer offset); @@ -696,6 +704,11 @@ List getLearnersByActivities(Long[] activityIds, Integer limit, Integer offset); /** + * Get number of learners whose first name, last name or login match any of the tokens from search phrase. + */ + Integer getCountLearnersFromProgress(Long lessonId, String searchPhrase); + + /** * Get number of learners who are at the given activity at the moment. */ Integer getCountLearnersCurrentActivity(Activity activity); Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r62b97a5e0fd88110e023f00793a983713296f9b6 -r8993cac8ce6dcb8d8ccb24c1cb387d44c0847199 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 62b97a5e0fd88110e023f00793a983713296f9b6) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 8993cac8ce6dcb8d8ccb24c1cb387d44c0847199) @@ -2370,6 +2370,17 @@ } @Override + public List getLearnersFromProgress(Long lessonId, String searchPhrase, boolean orderByCompletion, + Integer limit, Integer offset) { + return learnerProgressDAO.getLearnersByLesson(lessonId, searchPhrase, orderByCompletion, limit, offset); + } + + @Override + public Integer getCountLearnersFromProgress(Long lessonId, String searchPhrase) { + return learnerProgressDAO.getNumUsersByLesson(lessonId, searchPhrase); + } + + @Override public Integer getCountLearnersCurrentActivity(Activity activity) { return learnerProgressDAO.getNumUsersCurrentActivity(activity); } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java =================================================================== diff -u -rc3bc7ef2d33cf63ec5800f7577c1bc2d3993d521 -r8993cac8ce6dcb8d8ccb24c1cb387d44c0847199 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision c3bc7ef2d33cf63ec5800f7577c1bc2d3993d521) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision 8993cac8ce6dcb8d8ccb24c1cb387d44c0847199) @@ -31,11 +31,9 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -48,7 +46,6 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.commons.lang.StringUtils; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; @@ -68,12 +65,9 @@ import org.lamsfoundation.lams.learningdesign.SequenceActivity; import org.lamsfoundation.lams.learningdesign.Transition; import org.lamsfoundation.lams.learningdesign.exception.LearningDesignException; -import org.lamsfoundation.lams.lesson.LearnerProgress; import org.lamsfoundation.lams.lesson.Lesson; import org.lamsfoundation.lams.lesson.dto.LessonDetailsDTO; import org.lamsfoundation.lams.lesson.service.ILessonService; -import org.lamsfoundation.lams.lesson.util.LearnerProgressComparator; -import org.lamsfoundation.lams.lesson.util.LearnerProgressNameComparator; import org.lamsfoundation.lams.monitoring.MonitoringConstants; import org.lamsfoundation.lams.monitoring.dto.ContributeActivityDTO; import org.lamsfoundation.lams.monitoring.service.IMonitoringService; @@ -976,7 +970,6 @@ /** * Gets users whose progress bars will be displayed in Learner tab in Monitor. */ - @SuppressWarnings("unchecked") public ActionForward getLearnerProgressPage(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws JSONException, IOException { long lessonId = WebUtil.readLongParam(request, AttributeNames.PARAM_LESSON_ID); @@ -985,51 +978,19 @@ if (pageNumber == null) { pageNumber = 1; } + // are the learners sorted by the most completed first? boolean isProgressSorted = WebUtil.readBooleanParam(request, "isProgressSorted", false); + List learners = getMonitoringService().getLearnersFromProgress(lessonId, searchPhrase, isProgressSorted, + 10, (pageNumber - 1) * 10); JSONObject responseJSON = new JSONObject(); - Lesson lesson = getLessonService().getLesson(lessonId); - List learnerProgresses = new ArrayList(lesson.getLearnerProgresses()); - // sort either by user's name or his progress - Collections.sort(learnerProgresses, - isProgressSorted ? new LearnerProgressComparator() : new LearnerProgressNameComparator()); - - if (!StringUtils.isBlank(searchPhrase)) { - // get only users whose names match the given phrase - Set searchResult = new LinkedHashSet(); - - // check if there are several search phrases in the query - String[] searchPhrases = searchPhrase.split(";"); - for (int searchPhraseIndex = 0; searchPhraseIndex < searchPhrases.length; searchPhraseIndex++) { - searchPhrases[searchPhraseIndex] = searchPhrases[searchPhraseIndex].trim().toLowerCase(); - } - - for (LearnerProgress learnerProgress : learnerProgresses) { - User learner = learnerProgress.getUser(); - StringBuilder learnerDisplayName = new StringBuilder(learner.getFirstName().toLowerCase()).append(" ") - .append(learner.getLastName().toLowerCase()).append(" ") - .append(learner.getLogin().toLowerCase()); - for (String searchPhrasePiece : searchPhrases) { - if (!StringUtils.isBlank(searchPhrasePiece) - && (learnerDisplayName.indexOf(searchPhrasePiece) != -1)) { - searchResult.add(learnerProgress); - } - } - } - - learnerProgresses.clear(); - learnerProgresses.addAll(searchResult); + for (User learner : learners) { + responseJSON.append("learners", WebUtil.userToJSON(learner)); } - // batch size is 10 - int toIndex = Math.min(pageNumber * 10, learnerProgresses.size()); - int fromIndex = Math.min((pageNumber - 1) * 10, Math.max(toIndex - 10, 0)); - // get just the requested chunk - for (LearnerProgress learnerProgress : learnerProgresses.subList(fromIndex, toIndex)) { - responseJSON.append("learners", WebUtil.userToJSON(learnerProgress.getUser())); - } - - responseJSON.put("numberActiveLearners", learnerProgresses.size()); + // get all possible learners matching the given phrase, if any; used for max page number + responseJSON.put("numberActiveLearners", + getMonitoringService().getCountLearnersFromProgress(lessonId, searchPhrase)); response.setContentType("application/json;charset=utf-8"); response.getWriter().print(responseJSON.toString()); return null;