Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java =================================================================== diff -u -r114eaf5eb3133a3914d1debacf3e80b8da99b7ce -r8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision 114eaf5eb3133a3914d1debacf3e80b8da99b7ce) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringAction.java (.../MonitoringAction.java) (revision 8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90) @@ -34,6 +34,7 @@ import java.util.Date; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -65,6 +66,7 @@ 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; @@ -1054,6 +1056,7 @@ return null; } Long branchingActivityId = WebUtil.readLongParam(request, "branchingActivityID", true); + Integer searchedLearnerId = WebUtil.readIntParam(request, "searchedLearnerId", true); Lesson lesson = getLessonService().getLesson(lessonId); LearningDesign learningDesign = lesson.getLearningDesign(); @@ -1125,6 +1128,12 @@ responseJSON.put("contributeActivities", new JSONArray(gson.toJson(contributeActivities))); } + // check if the searched learner has started the lesson + LearnerProgress searchedLearnerProgress = null; + if (searchedLearnerId != null) { + searchedLearnerProgress = getLessonService().getUserProgressForLesson(searchedLearnerId, lessonId); + } + JSONArray activitiesJSON = new JSONArray(); for (Activity activity : activities) { Long activityId = activity.getActivityId(); @@ -1190,6 +1199,14 @@ learnerCount = getMonitoringService().getCountLearnersCurrentActivity(activity); } + if ((searchedLearnerProgress != null) && (searchedLearnerProgress.getCurrentActivity() != null) + && activity.getActivityId() + .equals(searchedLearnerProgress.getCurrentActivity().getActivityId())) { + // put the searched learner in front + latestLearners = MonitoringAction.insertSearchedLearner(searchedLearnerProgress.getUser(), + latestLearners, MonitoringAction.LATEST_LEARNER_PROGRESS_ACTIVITY_DISPLAY_LIMIT); + } + // parse learners into JSON format if (!latestLearners.isEmpty()) { JSONArray learnersJSON = new JSONArray(); @@ -1209,18 +1226,25 @@ // find learners who completed the lesson List completedLearners = getMonitoringService().getLearnersLatestCompleted(lessonId, MonitoringAction.LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT, null); - for (User learner : completedLearners) { - JSONObject learnerJSON = WebUtil.userToJSON(learner); - // no more details are needed for learners who completed the lesson - responseJSON.append("completedLearners", learnerJSON); - } Integer completedLearnerCount = null; if (completedLearners.size() < MonitoringAction.LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT) { completedLearnerCount = completedLearners.size(); } else { completedLearnerCount = getMonitoringService().getCountLearnersCompletedLesson(lessonId); } responseJSON.put("completedLearnerCount", completedLearnerCount); + + if ((searchedLearnerProgress != null) && searchedLearnerProgress.isComplete()) { + // put the searched learner in front + completedLearners = MonitoringAction.insertSearchedLearner(searchedLearnerProgress.getUser(), + completedLearners, MonitoringAction.LATEST_LEARNER_PROGRESS_LESSON_DISPLAY_LIMIT); + } + for (User learner : completedLearners) { + JSONObject learnerJSON = WebUtil.userToJSON(learner); + // no more details are needed for learners who completed the lesson + responseJSON.append("completedLearners", learnerJSON); + } + responseJSON.put("numberPossibleLearners", getLessonService().getCountLessonLearners(lessonId)); // on first fetch get transitions metadata so Monitoring can set their SVG elems IDs @@ -1244,7 +1268,7 @@ } /** - * Gives suggestions when a Monitor searches for a Learner in Learners tab. + * Gives suggestions when a Monitor searches for a Learner in Sequence and Learners tabs. */ public ActionForward autocompleteMonitoringLearners(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { @@ -1255,17 +1279,16 @@ JSONArray responseJSON = new JSONArray(); for (User learner : learners) { JSONObject learnerJSON = new JSONObject(); - String fullName = learner.getFirstName() + " " + learner.getLastName() + " "; - // it looks better with the braces - learnerJSON.put("label", fullName + "(" + learner.getLogin() + ")"); - // it requires no braces for proper search - learnerJSON.put("value", fullName + learner.getLogin()); + learnerJSON.put("label", learner.getFirstName() + " " + learner.getLastName() + " " + learner.getLogin()); + learnerJSON.put("value", learner.getUserId()); + responseJSON.put(learnerJSON); } - + response.setContentType("application/json;charset=utf-8"); response.getWriter().print(responseJSON); return null; + } public ActionForward releaseGate(ActionMapping mapping, ActionForm form, HttpServletRequest request, @@ -1558,4 +1581,18 @@ } return result; } + + + /** + * Puts the searched learner in front of other learners in the list. + */ + private static List insertSearchedLearner(User searchedLearner, List latestLearners, int limit) { + latestLearners.remove(searchedLearner); + LinkedList updatedLatestLearners = new LinkedList(latestLearners); + updatedLatestLearners.addFirst(searchedLearner); + if (updatedLatestLearners.size() > limit) { + updatedLatestLearners.removeLast(); + } + return updatedLatestLearners; + } } \ No newline at end of file Index: lams_monitoring/web/css/monitorLesson.css =================================================================== diff -u -re37003cb9c64c5dffbcf46d5fd6bb274ef86f3ea -r8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90 --- lams_monitoring/web/css/monitorLesson.css (.../monitorLesson.css) (revision e37003cb9c64c5dffbcf46d5fd6bb274ef86f3ea) +++ lams_monitoring/web/css/monitorLesson.css (.../monitorLesson.css) (revision 8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90) @@ -191,6 +191,28 @@ display: none; } +.topButtonsContainer #sequenceSearchPhrase { + float: right; + margin: 6px 5px 5px 0; +} + +.topButtonsContainer #sequenceSearchPhraseLabel { + float: right; + margin: 9px 7px 5px 0; +} + +.topButtonsContainer #sequenceSearchPhraseClear { + float: right; + cursor: pointer; + margin: 9px 25px 5px 0; + visibility: hidden; +} + +img#sequenceSearchedLearnerHighlighter { + position: absolute; + display: none; +} + div#completedLearnersContainer { padding: 4px 0px 3px 5px; border-top: thin dotted #2E6E9E; Index: lams_monitoring/web/includes/javascript/monitorLesson.js =================================================================== diff -u -r114eaf5eb3133a3914d1debacf3e80b8da99b7ce -r8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90 --- lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 114eaf5eb3133a3914d1debacf3e80b8da99b7ce) +++ lams_monitoring/web/includes/javascript/monitorLesson.js (.../monitorLesson.js) (revision 8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90) @@ -11,6 +11,8 @@ sequenceBranchingId = null, // info box show timeout sequenceInfoTimeout = 10000, +// which learner was selected in the search box + sequenceSearchedLearner = null, // how learners in pop up lists are currently sorted sortOrderAsc = { learnerGroup : false, @@ -638,8 +640,8 @@ // make sure there is only one selected learner if (selectedLearner.length == 1) { // go to "force complete" mode, similar to draggin user to an activity - var activityId = $(this).dialog('option', 'activityId'); - var dropArea = sequenceCanvas.add('#completedLearnersContainer'); + var activityId = $(this).dialog('option', 'activityId'), + dropArea = sequenceCanvas.add('#completedLearnersContainer'); dropArea.css('cursor', 'url(' + LAMS_URL + 'images/icons/user.png),pointer') .one('click', function(event) { @@ -755,6 +757,20 @@ } ] }); + + // search for users with the term the Monitor entered + $("#sequenceSearchPhrase").autocomplete( { + 'source' : LAMS_URL + "monitoring/monitoring.do?method=autocompleteMonitoringLearners&lessonID=" + lessonId, + 'delay' : 700, + 'select' : function(event, ui){ + // put the learner first name, last name and login into the box + $(this).val(ui.item.label); + // mark the learner's ID and make him highlighted after the refresh + sequenceSearchedLearner = ui.item.value; + updateSequenceTab(); + return false; + } + }); } @@ -823,7 +839,8 @@ 'method' : 'getLessonProgress', 'lessonID' : lessonId, 'branchingActivityID' : sequenceBranchingId, - 'getTransitions' : sequenceCanvasFirstFetch + 'getTransitions' : sequenceCanvasFirstFetch, + 'searchedLearnerId' : sequenceSearchedLearner }, success : function(response) { if (sequenceCanvasFirstFetch) { @@ -909,7 +926,7 @@ activityGroup.css('cursor', 'pointer'); dblTap(activityGroup, dblClickFunction); } - }); + }); sequenceRefreshInProgress = false; } @@ -1107,7 +1124,8 @@ 'y' : coord.y, 'height' : 16, 'width' : 16, - 'xlink:href' : LAMS_URL + 'images/icons/user.png', + 'xlink:href' : LAMS_URL + 'images/icons/' + + (learner.id == sequenceSearchedLearner ? 'user_online.png' : 'user.png'), 'style' : 'cursor : pointer' }, null, appendTarget); appendXMLElement('title', null, learnerDisplayName, element); @@ -1176,6 +1194,13 @@ openPopUp(url, "LearnActivity", 600, 800, true); }); } + + if (learner.id == sequenceSearchedLearner){ + // do it here instead of addActivityIcons() + // as in that method the icons are added to the document yet + // and they have no offset for calculations + highlightSearchedLearner(learnerIcon); + } }); } @@ -1213,10 +1238,12 @@ // create learner icons, along with handlers $.each(learners, function(learnerIndex, learner){ // make an icon for each learner - $('').attr({ - 'src' : LAMS_URL + 'images/icons/user.png', + var icon = $('').attr({ + 'src' : LAMS_URL + 'images/icons/' + + (learner.id == sequenceSearchedLearner ? 'user_online.png' : 'user.png'), + 'style' : 'cursor : pointer', 'title' : getLearnerDisplayName(learner) - }).css('cursor', 'pointer') + }) // drag learners to force complete activities .draggable({ 'appendTo' : '#tabSequence', @@ -1238,6 +1265,10 @@ } }) .appendTo(iconsContainer); + + if (learner.id == sequenceSearchedLearner){ + highlightSearchedLearner(icon); + } }); // show a group icon @@ -1314,6 +1345,50 @@ /** + * Shows where the searched learner is. + */ +function highlightSearchedLearner(icon) { + // show the "clear" button + $('#sequenceSearchPhraseClear').css('visibility', 'visible'); + + var highlighter = $('#sequenceSearchedLearnerHighlighter').offset({ + 'top' : icon.offset().top - 25, + 'left' : icon.offset().left - 4 + }); + + // blink only after the search, not after subsequent refreshes + if (!highlighter.is(':visible')) { + highlighter.show(); + toggleInterval = setInterval(function(){ + highlighter.toggle(); + }, 500); + + setTimeout(function(){ + clearInterval(toggleInterval); + // make sure that search box was not cleared during blinking + if (sequenceSearchedLearner) { + highlighter.show(); + } else { + highlighter.hide(); + } + }, 3000); + } +} + + +/** + * Cancels the performed search. + */ +function sequenceClearSearchPhrase(){ + $('#sequenceSearchPhrase').val(''); + $('#sequenceSearchPhraseClear').css('visibility', 'hidden'); + $('#sequenceSearchedLearnerHighlighter').hide(); + sequenceSearchedLearner = null; + updateSequenceTab(); +} + + +/** * Shows Edit Class dialog for class manipulation. */ function showClassDialog(){ @@ -1502,6 +1577,7 @@ }); } + //********** LEARNERS TAB FUNCTIONS ********** /** @@ -1513,7 +1589,10 @@ 'source' : LAMS_URL + "monitoring/monitoring.do?method=autocompleteMonitoringLearners&lessonID=" + lessonId, 'delay' : 700, 'select' : function(event, ui){ - loadLearnerProgressPage(1, ui.item.value); + // learner's ID in ui.item.value is not used here + $(this).val(ui.item.label); + loadLearnerProgressPage(1, ui.item.label); + return false; } }) // run the real search when the Monitor presses Enter @@ -1648,7 +1727,6 @@ var isProgressSorted = $('#orderByCompletionCheckbox:checked').length > 0; // either go to the given page or refresh the current one pageNumber = pageNumber || learnerProgressCurrentPageNumber; - // either get the phrase for the parameter or check what was entered in the box learnersSearchPhrase = learnersSearchPhrase || $('#learnersSearchPhrase').val(); if (learnersSearchPhrase && learnersSearchPhrase.trim() == ''){ learnersSearchPhrase = null; @@ -1697,7 +1775,6 @@ } - /** * Refreshes the existing progress bars. */ @@ -1714,6 +1791,7 @@ learnersRefreshInProgress = false; } + /** * Clears previous run search for phrase. */ Index: lams_monitoring/web/monitor.jsp =================================================================== diff -u -r114eaf5eb3133a3914d1debacf3e80b8da99b7ce -r8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90 --- lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 114eaf5eb3133a3914d1debacf3e80b8da99b7ce) +++ lams_monitoring/web/monitor.jsp (.../monitor.jsp) (revision 8a6062e3e66be7bbb7b8d0c5c8d9b8d3c66baf90) @@ -411,10 +411,10 @@ - - + + @@ -430,13 +430,22 @@ onClick="javascript:closeBranchingSequence()"> + + +
+
@@ -447,7 +456,7 @@ -
+