Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r50e336123ddaab4628f5f94f795340c8e845c6df -r897ad3c3694440281f178d7834a9233d62c4e874 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 50e336123ddaab4628f5f94f795340c8e845c6df) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 897ad3c3694440281f178d7834a9233d62c4e874) @@ -58,6 +58,8 @@ import org.lamsfoundation.lams.contentrepository.client.IToolContentHandler; import org.lamsfoundation.lams.events.IEventNotificationService; import org.lamsfoundation.lams.learning.service.ILearnerService; +import org.lamsfoundation.lams.learningdesign.Grouping; +import org.lamsfoundation.lams.learningdesign.ToolActivity; import org.lamsfoundation.lams.learningdesign.service.ExportToolContentException; import org.lamsfoundation.lams.learningdesign.service.IExportToolContentService; import org.lamsfoundation.lams.learningdesign.service.ImportToolContentException; @@ -3660,6 +3662,13 @@ } @Override + public Grouping getGrouping(long toolContentId) { + ToolActivity toolActivity = (ToolActivity) userManagementService + .findByProperty(ToolActivity.class, "toolContentId", toolContentId).get(0); + return toolActivity.getApplyGrouping() ? toolActivity.getGrouping() : null; + } + + @Override public List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString) { Lesson lesson = lessonService.getLessonByToolContentId(toolContentId); return lessonService.getLessonLearners(lesson.getLessonId(), searchString, null, null, true); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java =================================================================== diff -u -r50e336123ddaab4628f5f94f795340c8e845c6df -r897ad3c3694440281f178d7834a9233d62c4e874 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 50e336123ddaab4628f5f94f795340c8e845c6df) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 897ad3c3694440281f178d7834a9233d62c4e874) @@ -31,6 +31,7 @@ import java.util.Optional; import java.util.Set; +import org.lamsfoundation.lams.learningdesign.Grouping; import org.lamsfoundation.lams.notebook.model.NotebookEntry; import org.lamsfoundation.lams.tool.assessment.dto.AssessmentResultDTO; import org.lamsfoundation.lams.tool.assessment.dto.AssessmentUserDTO; @@ -540,5 +541,7 @@ Collection getAllGroupUsers(Long toolSessionId); + Grouping getGrouping(long toolContentId); + List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString); } \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r50e336123ddaab4628f5f94f795340c8e845c6df -r897ad3c3694440281f178d7834a9233d62c4e874 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 50e336123ddaab4628f5f94f795340c8e845c6df) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 897ad3c3694440281f178d7834a9233d62c4e874) @@ -31,13 +31,15 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Date; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TimeZone; +import java.util.TreeMap; import java.util.TreeSet; import java.util.function.Function; import java.util.stream.Collectors; @@ -51,6 +53,8 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.learningdesign.Group; +import org.lamsfoundation.lams.learningdesign.Grouping; import org.lamsfoundation.lams.qb.dto.QbStatsActivityDTO; import org.lamsfoundation.lams.qb.model.QbQuestion; import org.lamsfoundation.lams.qb.service.IQbService; @@ -113,6 +117,9 @@ public class MonitoringController { public static Logger log = Logger.getLogger(MonitoringController.class); + private static final Comparator USER_NAME_COMPARATOR = Comparator.comparing(User::getFirstName) + .thenComparing(User::getLastName).thenComparing(User::getLogin); + @Autowired @Qualifier("laasseAssessmentService") private IAssessmentService service; @@ -847,14 +854,37 @@ Map timeLimitAdjustments = assessment.getTimeLimitAdjustments(); List users = service.getPossibleIndividualTimeLimitUsers(toolContentId, searchString); + Grouping grouping = service.getGrouping(toolContentId); ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode(); + String groupLabel = service.getMessage("monitoring.label.group") + " \""; + if (grouping != null) { + Set groups = grouping.getGroups(); + for (Group group : groups) { + if (!group.getUsers().isEmpty() && group.getGroupName().contains(searchString.toLowerCase())) { + ObjectNode groupJSON = JsonNodeFactory.instance.objectNode(); + groupJSON.put("label", groupLabel + group.getGroupName() + "\""); + groupJSON.put("value", "group-" + group.getGroupId()); + responseJSON.add(groupJSON); + } + } + } + for (User user : users) { if (!timeLimitAdjustments.containsKey(user.getUserId())) { // this format is required by jQuery UI autocomplete ObjectNode userJSON = JsonNodeFactory.instance.objectNode(); - userJSON.put("value", user.getUserId()); - userJSON.put("label", user.getFirstName() + " " + user.getLastName() + " (" + user.getLogin() + ")"); + userJSON.put("value", "user-" + user.getUserId()); + + String name = user.getFirstName() + " " + user.getLastName() + " (" + user.getLogin() + ")"; + if (grouping != null) { + Group group = grouping.getGroupBy(user); + if (group != null) { + name += " - " + group.getGroupName(); + } + } + + userJSON.put("label", name); responseJSON.add(userJSON); } } @@ -867,19 +897,47 @@ @RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId) { Assessment assessment = service.getAssessmentByContentId(toolContentId); Map timeLimitAdjustments = assessment.getTimeLimitAdjustments(); - - // find User objects based on their userIDs and sort by names + Grouping grouping = service.getGrouping(toolContentId); + // find User objects based on their userIDs and sort by name List users = assessment.getTimeLimitAdjustments().keySet().stream() - .map(userId -> userManagementService.getUserById(userId)).sorted(Comparator - .comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getLogin)) + .map(userId -> userManagementService.getUserById(userId)).sorted(USER_NAME_COMPARATOR) .collect(Collectors.toList()); + if (grouping != null) { + // Make a map group -> its users who have a time limit set + // key are sorted by group name, users in each group are sorted by name + List groupedUsers = grouping.getGroups().stream() + .collect(Collectors.toMap(Group::getGroupName, group -> { + return group.getUsers().stream() + .filter(user -> timeLimitAdjustments.containsKey(user.getUserId())) + .collect(Collectors.toCollection(() -> new TreeSet<>(USER_NAME_COMPARATOR))); + }, (s1, s2) -> { + s1.addAll(s2); + return s1; + }, TreeMap::new)).values().stream().flatMap(Set::stream).collect(Collectors.toList()); + + // from general user list remove grouped users + users.removeAll(groupedUsers); + // at the end of list, add remaining, not yet grouped users + groupedUsers.addAll(users); + users = groupedUsers; + } + ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode(); for (User user : users) { ObjectNode userJSON = JsonNodeFactory.instance.objectNode(); userJSON.put("userId", user.getUserId()); - userJSON.put("name", user.getFirstName() + " " + user.getLastName() + " (" + user.getLogin() + ")"); userJSON.put("adjustment", timeLimitAdjustments.get(user.getUserId().intValue())); + + String name = user.getFirstName() + " " + user.getLastName() + " (" + user.getLogin() + ")"; + if (grouping != null) { + Group group = grouping.getGroupBy(user); + if (group != null && !group.isNull()) { + name += " - " + group.getGroupName(); + } + } + userJSON.put("name", name); + responseJSON.add(userJSON); } return responseJSON.toString(); @@ -889,14 +947,31 @@ @ResponseStatus(HttpStatus.OK) public void updateIndividualTimeLimit( @RequestParam(name = AssessmentConstants.PARAM_TOOL_CONTENT_ID) long toolContentId, - @RequestParam int userId, @RequestParam(required = false) Integer adjustment) { + @RequestParam String itemId, @RequestParam(required = false) Integer adjustment) { Assessment assessment = service.getAssessmentByContentId(toolContentId); Map timeLimitAdjustments = assessment.getTimeLimitAdjustments(); - if (adjustment == null) { - timeLimitAdjustments.remove(userId); + Set userIds = null; + + // itemId can user- or group- + String[] itemIdParts = itemId.split("-"); + if (itemIdParts[0].equalsIgnoreCase("group")) { + // add all users from a group, except for ones who are already added + Group group = (Group) userManagementService.findById(Group.class, Long.valueOf(itemIdParts[1])); + userIds = group.getUsers().stream().map(User::getUserId) + .filter(userId -> !timeLimitAdjustments.containsKey(userId)).collect(Collectors.toSet()); } else { - timeLimitAdjustments.put(userId, adjustment); + // adjust for a single user + userIds = new HashSet<>(); + userIds.add(Integer.valueOf(itemIdParts[1])); } + + for (Integer userId : userIds) { + if (adjustment == null) { + timeLimitAdjustments.remove(userId); + } else { + timeLimitAdjustments.put(userId, adjustment); + } + } service.saveOrUpdateAssessment(assessment); } Index: lams_tool_assessment/web/pages/monitoring/summary.jsp =================================================================== diff -u -r50e336123ddaab4628f5f94f795340c8e845c6df -r897ad3c3694440281f178d7834a9233d62c4e874 --- lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 50e336123ddaab4628f5f94f795340c8e845c6df) +++ lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 897ad3c3694440281f178d7834a9233d62c4e874) @@ -431,13 +431,13 @@ // disable individual time adjustment if (toggle === false) { - updateIndividualTimeLimitOnServer(userId); + updateIndividualTimeLimitOnServer('user-' + userId); return; } var existingAdjustment = +$('.individual-time-limit-value', row).text(), newAdjustment = existingAdjustment + adjust; - updateIndividualTimeLimitOnServer(userId, newAdjustment); + updateIndividualTimeLimitOnServer('user-' + userId, newAdjustment); return; } } @@ -568,7 +568,7 @@ 'delay' : 700, 'minLength' : 3, 'select' : function(event, ui){ - // userUid and default 0 adjustment + // user ID or group ID, and default 0 adjustment updateIndividualTimeLimitOnServer(ui.item.value, 0); // clear search field @@ -586,14 +586,15 @@ } - function updateIndividualTimeLimitOnServer(userId, adjustment) { + function updateIndividualTimeLimitOnServer(itemId, adjustment) { $.ajax({ 'url' : '', 'type': 'post', 'cache' : 'false', 'data': { 'toolContentID' : '${assessment.contentId}', - 'userId' : userId, + // itemId can user- or group- + 'itemId' : itemId, 'adjustment' : adjustment, '' : '' },