Index: lams_build/lib/lams/lams.jar =================================================================== diff -u -r6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4 -ra7411c3e567e0a5fce58aff5703e7b0a956803a0 Binary files differ Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java =================================================================== diff -u -r6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4 -ra7411c3e567e0a5fce58aff5703e7b0a956803a0 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java (.../ILearnerProgressDAO.java) (revision 6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/ILearnerProgressDAO.java (.../ILearnerProgressDAO.java) (revision a7411c3e567e0a5fce58aff5703e7b0a956803a0) @@ -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 -r6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4 -ra7411c3e567e0a5fce58aff5703e7b0a956803a0 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java (.../LearnerProgressDAO.java) (revision 6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LearnerProgressDAO.java (.../LearnerProgressDAO.java) (revision a7411c3e567e0a5fce58aff5703e7b0a956803a0) @@ -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.HibernateException; import org.hibernate.Query; @@ -80,14 +79,17 @@ private final 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 final 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 " + " p.currentActivity.id IN (:activityIds)"; + private final static String LOAD_LEARNERS_BY_LESSON = "FROM LearnerProgress prog WHERE prog.lesson.id = :lessonId"; + @Override public LearnerProgress getLearnerProgress(Long learnerProgressId) { return (LearnerProgress) getHibernateTemplate().get(LearnerProgress.class, learnerProgressId); @@ -144,21 +146,14 @@ @Override public Object doInHibernate(Session session) throws HibernateException { Query query = session.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) hibernateTemplate.get(User.class, userId.intValue())); - } - return learners; + return query.list(); } }); } @@ -208,6 +203,50 @@ @SuppressWarnings("unchecked") @Override + public List getLearnersByLesson(final Long lessonId, String searchPhrase, boolean orderByCompletion, + final Integer limit, final Integer offset) { + final String queryText = LearnerProgressDAO.buildLearnersByLessonQuery(false, searchPhrase, orderByCompletion); + + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + + return (List) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + Query query = session.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(final Long lessonId) { HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); @@ -307,6 +346,19 @@ } @Override + public Integer getNumUsersByLesson(final Long lessonId, String searchPhrase) { + final String queryText = buildLearnersByLessonQuery(true, searchPhrase, null); + HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); + return (Integer) hibernateTemplate.execute(new HibernateCallback() { + @Override + public Object doInHibernate(Session session) throws HibernateException { + Object value = session.createQuery(queryText).setLong("lessonId", lessonId.longValue()).uniqueResult(); + return ((Number) value).intValue(); + } + }); + } + + @Override public Integer getNumUsersCompletedLesson(final Long lessonId) { HibernateTemplate hibernateTemplate = new HibernateTemplate(this.getSessionFactory()); return (Integer) hibernateTemplate.execute(new HibernateCallback() { Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java =================================================================== diff -u -r6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4 -ra7411c3e567e0a5fce58aff5703e7b0a956803a0 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision 6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringService.java (.../IMonitoringService.java) (revision a7411c3e567e0a5fce58aff5703e7b0a956803a0) @@ -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 -r6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4 -ra7411c3e567e0a5fce58aff5703e7b0a956803a0 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision a7411c3e567e0a5fce58aff5703e7b0a956803a0) @@ -2375,6 +2375,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 -r6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4 -ra7411c3e567e0a5fce58aff5703e7b0a956803a0 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision 6a93b1b03ef0e2e65d7376991ad3ccca9cd6aef4) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision a7411c3e567e0a5fce58aff5703e7b0a956803a0) @@ -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;