Index: lams_central/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r76a9b6401f527e6b1701261cebe945a9658c22a7 -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 76a9b6401f527e6b1701261cebe945a9658c22a7) +++ lams_central/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -711,7 +711,8 @@ label.upload.group.spreadsheet = Upload Finished Spreadsheet error.file.required = Grouped Spreadsheet Required error.file.wrong.format = Grouped Spreadsheet in the wrong format. Please upload a file based on the given template. -label.import.successful = Group Import successful. {0} learners allocated to groups, {1} learners skipped. +label.import.successful = Group Import successful. {0} learners allocated to groups. +label.import.successful.skipped = {0} learners skipped, including: label.import.warning.replace.groups = Importing this file will replace the existing groups. Do you want to import this file? filename.create.grouping.template = create-grouping-template spreadsheet.column.login = Login @@ -1160,4 +1161,4 @@ label.questions.choice.taxonomy.level.6 = Create authoring.fla.page.menu.new.ai = Use AI Wizard authoring.learning.design.ai.wizard = Learning Design AI Wizard -label.grouping.return.to.monitoring = Return to monitoring +label.grouping.return.to.monitoring = Return to monitoring \ No newline at end of file Index: lams_central/conf/language/lams/ApplicationResources_en_AU.properties =================================================================== diff -u -rfcbdee228838943b3a5dea3114c3a7ae33da583b -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision fcbdee228838943b3a5dea3114c3a7ae33da583b) +++ lams_central/conf/language/lams/ApplicationResources_en_AU.properties (.../ApplicationResources_en_AU.properties) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -711,7 +711,8 @@ label.upload.group.spreadsheet = Upload Finished Spreadsheet error.file.required = Grouped Spreadsheet Required error.file.wrong.format = Grouped Spreadsheet in the wrong format. Please upload a file based on the given template. -label.import.successful = Group Import successful. {0} learners allocated to groups, {1} learners skipped. +label.import.successful = Group Import successful. {0} learners allocated to groups. +label.import.successful.skipped = {0} learners skipped, including: label.import.warning.replace.groups = Importing this file will replace the existing groups. Do you want to import this file? filename.create.grouping.template = create-grouping-template spreadsheet.column.login = Login @@ -1132,6 +1133,7 @@ label.grouping.general.instructions.line1 = Choose a course grouping to use in this lesson or create a new one. You are working with copies, so any changes made here have no influence on the original course groupings. Grouping with zero groups are not listed here. Once a grouping is chosen, the only way to come back to the grouping list is to manually remove all groups. label.grouping.general.instructions.line2 = Place the lesson participants in their groups. Initially you can add and remove learners, but once a groups is used (that is, a participant starts an activity that uses the grouping) you will not be able to remove learners from it. Even if a group is in use you can still add learners. The changes are saved immediately. label.grouping.general.instructions.branching = This grouping activity is used for branching. Groups can therefore not be added or deleted. +label.grouping.return.to.monitoring = Return to monitoring label.branching.general.instructions = Place the lesson participants in their branches. Initially you can add and remove learners, but once a participant starts one of the branches then you will not be able to remove learners from any branches. If you try to remove someone from a branch and they will not remove then check their progress - if they start using the branch while you are on this screen you will not get any errors but you will not be able to remove them from the branch. You will still be able to add learners to branches. label.grouping.popup.viewmode.message = You are presently in group view mode. Groups can not be modified. label.tab.grouping = Groups @@ -1159,4 +1161,4 @@ label.questions.choice.taxonomy.level.6 = Create authoring.fla.page.menu.new.ai = Use AI Wizard authoring.learning.design.ai.wizard = Learning Design AI Wizard -label.grouping.return.to.monitoring = Return to monitoring +label.grouping.return.to.monitoring = Return to monitoring \ No newline at end of file Index: lams_central/web/groups.jsp =================================================================== diff -u -r81c45e6f3d16b4413e771896c62cbcf821684138 -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_central/web/groups.jsp (.../groups.jsp) (revision 81c45e6f3d16b4413e771896c62cbcf821684138) +++ lams_central/web/groups.jsp (.../groups.jsp) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -92,12 +92,11 @@ ERROR_FILE_WRONG_FORMAT_LABEL : "${ERROR_FILE_WRONG_FORMAT_VAR}", - - - - + LABEL_IMPORT_SUCCESSFUL_LABEL : "${LABEL_IMPORT_SUCCESSFUL_VAR}", + + LABEL_IMPORT_SUCCESSFUL_SKIPPED_LABEL : "${LABEL_IMPORT_SUCCESSFUL_SKIPPED_VAR}" }; Index: lams_central/web/includes/javascript/groups.js =================================================================== diff -u -r81c45e6f3d16b4413e771896c62cbcf821684138 -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_central/web/includes/javascript/groups.js (.../groups.js) (revision 81c45e6f3d16b4413e771896c62cbcf821684138) +++ lams_central/web/includes/javascript/groups.js (.../groups.js) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -662,7 +662,13 @@ alert(LABELS.GENERAL_ERROR_LABEL); } } else { - var msg = LABELS.LABEL_IMPORT_SUCCESSFUL_LABEL.replace("%1", response.added).replace("%2", response.skipped); + var msg = LABELS.LABEL_IMPORT_SUCCESSFUL_LABEL.replace("{0}", response.added); + if (response.skipped) { + msg += '\n'+ LABELS.LABEL_IMPORT_SUCCESSFUL_SKIPPED_LABEL.replace("{0}", response.skipped.length); + for (let i = 0; i < response.skipped.length && i < 10; i++) { + msg += '\n' + response.skipped[i]; + } + } alert(msg); reloadIframe(returnedGroupingId); } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringFullService.java =================================================================== diff -u -r11403ed5085a73344f4cea2b7d3677115c6f2150 -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringFullService.java (.../IMonitoringFullService.java) (revision 11403ed5085a73344f4cea2b7d3677115c6f2150) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/IMonitoringFullService.java (.../IMonitoringFullService.java) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -250,7 +250,7 @@ * Add learners to a group based on their logins. Doesn't necessarily check if the user is already in another * group. */ - abstract int addUsersToGroupByLogins(Long activityID, String groupName, Set logins, + abstract List addUsersToGroupByLogins(Long activityID, String groupName, Set logins, boolean forceChosenGrouping) throws LessonServiceException; /** Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java =================================================================== diff -u -r11403ed5085a73344f4cea2b7d3677115c6f2150 -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 11403ed5085a73344f4cea2b7d3677115c6f2150) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/service/MonitoringService.java (.../MonitoringService.java) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -143,8 +143,8 @@ * * @author Jacky Fang * @author Manpreet Minhas - * @since 2/02/2005 * @version 1.1 + * @since 2/02/2005 */ public class MonitoringService implements IMonitoringFullService { @@ -213,9 +213,10 @@ // --------------------------------------------------------------------- // Inversion of Control Methods - Method injection // --------------------------------------------------------------------- + /** * @param messageService - * the i18n Service bean. + * the i18n Service bean. */ public void setMessageService(MessageService messageService) { this.messageService = messageService; @@ -228,7 +229,7 @@ /** * @param userManagementService - * The userManagementService to set. + * The userManagementService to set. */ public void setUserManagementService(IUserManagementService userManagementService) { this.userManagementService = userManagementService; @@ -240,22 +241,20 @@ /** * @param learningDesignDAO - * The learningDesignDAO to set. + * The learningDesignDAO to set. */ public void setLearningDesignDAO(ILearningDesignDAO learningDesignDAO) { this.learningDesignDAO = learningDesignDAO; } /** - * * @param learnerService */ public void setLearnerService(ILearnerService learnerService) { this.learnerService = learnerService; } /** - * * @param lessonService */ public void setLessonService(ILessonService lessonService) { @@ -264,31 +263,31 @@ /** * @param authoringService - * The authoringService to set. + * The authoringService to set. */ public void setAuthoringService(IAuthoringService authoringService) { this.authoringService = authoringService; } /** * @param lessonClassDAO - * The lessonClassDAO to set. + * The lessonClassDAO to set. */ public void setLessonClassDAO(ILessonClassDAO lessonClassDAO) { this.lessonClassDAO = lessonClassDAO; } /** * @param lessonDAO - * The lessonDAO to set. + * The lessonDAO to set. */ public void setLessonDAO(ILessonDAO lessonDAO) { this.lessonDAO = lessonDAO; } /** * @param learnerProgressDAO - * The learnerProgressDAO to set. + * The learnerProgressDAO to set. */ public void setLearnerProgressDAO(ILearnerProgressDAO learnerProgressDAO) { this.learnerProgressDAO = learnerProgressDAO; @@ -328,23 +327,23 @@ /** * @param lamsToolService - * The lamsToolService to set. + * The lamsToolService to set. */ public void setLamsCoreToolService(ILamsCoreToolService lamsToolService) { lamsCoreToolService = lamsToolService; } /** * @param activityDAO - * The activityDAO to set. + * The activityDAO to set. */ public void setActivityDAO(IActivityDAO activityDAO) { this.activityDAO = activityDAO; } /** * @param scheduler - * The scheduler to set. + * The scheduler to set. */ public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; @@ -463,8 +462,9 @@ enableLessonNotifications, forceLearnerRestart, allowLearnerRestart, gradebookOnComplete, scheduledNumberDaysToLessonFinish, precedingLesson); logLessonGeneralChange(LogEvent.TYPE_TEACHER_LESSON_CREATE, user != null ? user.getUserId() : null, - lesson != null ? lesson.getLessonId() : null, MonitoringService.AUDIT_LESSON_CREATED_KEY, new Object[] { - lessonName, copiedLearningDesign.getTitle(), copiedLearningDesign.getLearningDesignId() }); + lesson != null ? lesson.getLessonId() : null, MonitoringService.AUDIT_LESSON_CREATED_KEY, + new Object[] { lessonName, copiedLearningDesign.getTitle(), + copiedLearningDesign.getLearningDesignId() }); return lesson; } @@ -509,8 +509,8 @@ if (requestedLesson.isLessonStarted()) { // can't schedule it as it is already started. If the UI is correct, // this should never happen. - MonitoringService.log - .error("Lesson for id=" + lessonId + " has been started. Unable to schedule lesson start."); + MonitoringService.log.error( + "Lesson for id=" + lessonId + " has been started. Unable to schedule lesson start."); return; } @@ -536,9 +536,10 @@ if (!alreadyScheduled) { // setup the message for scheduling job JobDetail startLessonJob = JobBuilder.newJob(StartScheduleLessonJob.class) - .withIdentity("startLessonOnSchedule:" + lessonId) - .withDescription(requestedLesson.getLessonName() + ":" - + (requestedLesson.getUser() == null ? "" : requestedLesson.getUser().getFullName())) + .withIdentity("startLessonOnSchedule:" + lessonId).withDescription( + requestedLesson.getLessonName() + ":" + (requestedLesson.getUser() == null + ? "" + : requestedLesson.getUser().getFullName())) .usingJobData(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)) .usingJobData(MonitoringConstants.KEY_USER_ID, new Integer(userId)).build(); @@ -586,8 +587,8 @@ } /** - * Set up the job to end the lesson on endDate. EndDate is assumed to be converted to the LAMS's default - * timezone. If endDate == null then remove any existing scheduling. + * Set up the job to end the lesson on endDate. EndDate is assumed to be converted to the LAMS's default timezone. + * If endDate == null then remove any existing scheduling. */ private void finishLessonOnScheduleAsServerDate(Lesson requestedLesson, Date endDate, Integer userId) { @@ -612,9 +613,9 @@ } else { // setup the message for scheduling job finishLessonJob = JobBuilder.newJob(FinishScheduleLessonJob.class) - .withIdentity("finishLessonOnSchedule:" + lessonId) - .withDescription(requestedLesson.getLessonName() + ":" - + (requestedLesson.getUser() == null ? "" + .withIdentity("finishLessonOnSchedule:" + lessonId).withDescription( + requestedLesson.getLessonName() + ":" + (requestedLesson.getUser() == null + ? "" : requestedLesson.getUser().getFullName())) .usingJobData(MonitoringConstants.KEY_LESSON_ID, new Long(lessonId)) .usingJobData(MonitoringConstants.KEY_USER_ID, new Integer(userId)).build(); @@ -628,14 +629,14 @@ if (alreadyScheduled) { scheduler.rescheduleJob(finishLessonTrigger.getKey(), finishLessonTrigger); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log - .debug("Finish lesson [" + lessonId + "] job has been rescheduled to " + endDate); + MonitoringService.log.debug( + "Finish lesson [" + lessonId + "] job has been rescheduled to " + endDate); } } else { scheduler.scheduleJob(finishLessonJob, finishLessonTrigger); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log - .debug("Finish lesson [" + lessonId + "] job has been scheduled to " + endDate); + MonitoringService.log.debug( + "Finish lesson [" + lessonId + "] job has been scheduled to " + endDate); } } } else { @@ -670,7 +671,8 @@ Date endDate = null; if (scheduledNumberDaysToLessonFinish > 0) { // calculate finish date - Date startDate = (requestedLesson.getStartDateTime() != null) ? requestedLesson.getStartDateTime() + Date startDate = (requestedLesson.getStartDateTime() != null) + ? requestedLesson.getStartDateTime() : requestedLesson.getScheduleStartDate(); if (startDate == null) { throw new MonitoringServiceException("Lesson with id=" + lessonId @@ -749,8 +751,8 @@ // if it is schedule gate, we need to initialize the sheduler for it. if (activity.getActivityTypeId().intValue() == Activity.SCHEDULE_GATE_ACTIVITY_TYPE) { - ScheduleGateActivity gateActivity = (ScheduleGateActivity) activityDAO - .getActivityByActivityId(activity.getActivityId()); + ScheduleGateActivity gateActivity = (ScheduleGateActivity) activityDAO.getActivityByActivityId( + activity.getActivityId()); // do not run the scheduler if the gate is basen on user completing previous activity if (!Boolean.TRUE.equals(gateActivity.getGateActivityCompletionBased())) { activity = runGateScheduler(gateActivity, lessonStartTime, lessonName); @@ -789,12 +791,12 @@ *
  • 3. start the scheduling job
  • * * @param scheduleGate - * the gate that needs to be scheduled. + * the gate that needs to be scheduled. * @param schedulingStartTime - * the time on which the gate open should be based if an offset is used. For starting a lesson, this is - * the lessonStartTime. For live edit, it is now. + * the time on which the gate open should be based if an offset is used. For starting a lesson, this is the + * lessonStartTime. For live edit, it is now. * @param lessonName - * the name lesson incorporating this gate - used for the description of the Quartz job. Optional. + * the name lesson incorporating this gate - used for the description of the Quartz job. Optional. * @returns An updated gate, that should be saved by the calling code. */ private ScheduleGateActivity runGateScheduler(ScheduleGateActivity scheduleGate, Date schedulingStartTime, @@ -814,9 +816,10 @@ .usingJobData("gateId", scheduleGate.getActivityId()).build(); // start the scheduling job - boolean startGateOnly = ((scheduleGate.getGateStartTimeOffset() == null) - && (scheduleGate.getGateEndTimeOffset() == null)) - || ((scheduleGate.getGateStartTimeOffset() != null) && (scheduleGate.getGateEndTimeOffset() == null)); + boolean startGateOnly = + ((scheduleGate.getGateStartTimeOffset() == null) && (scheduleGate.getGateEndTimeOffset() == null)) || ( + (scheduleGate.getGateStartTimeOffset() != null) && (scheduleGate.getGateEndTimeOffset() + == null)); String openGateTriggerName = "openGateTrigger:" + scheduleGate.getActivityId(); String closeGateTriggerName = "closeGateTrigger:" + scheduleGate.getActivityId(); @@ -828,8 +831,7 @@ } catch (SchedulerException e) { MonitoringService.log.error( "Error occurred at [runGateScheduler] - fail to start scheduling. Error while fetching Quartz trigger \"" - + openGateTriggerName + "\"", - e); + + openGateTriggerName + "\"", e); } try { @@ -860,8 +862,7 @@ } catch (SchedulerException e) { MonitoringService.log.error( "Error occurred at [runGateScheduler] - fail to start scheduling. Error while setting up gate open/close triggers. Open Gate Trigger name \"" - + openGateTriggerName + "\"", - e); + + openGateTriggerName + "\"", e); } if (MonitoringService.log.isDebugEnabled()) { MonitoringService.log.debug("Scheduler for Gate " + scheduleGate.getActivityId() + " started..."); @@ -887,8 +888,8 @@ openGate(gateId, userId); } else { - ScheduleGateActivity gate = (ScheduleGateActivity) activityDAO - .getActivityByActivityId(gateActivity.getActivityId()); + ScheduleGateActivity gate = (ScheduleGateActivity) activityDAO.getActivityByActivityId( + gateActivity.getActivityId()); // work out new offset in minutes from lesson start time to the given date Lesson lesson = learnerService.getLessonByActivity(gate); @@ -944,9 +945,9 @@ } } else { log.error(new StringBuilder( - "Unable to reschedule gate opening time as we are missing the lesson starting time. Schedule gate ") - .append(gate).append(" lesson ").append(lesson.getLessonId()).append(" status ") - .append(lesson.getLessonStateId()).toString()); + "Unable to reschedule gate opening time as we are missing the lesson starting time. Schedule gate ").append( + gate).append(" lesson ").append(lesson.getLessonId()).append(" status ") + .append(lesson.getLessonStateId()).toString()); } } } @@ -985,8 +986,8 @@ public void suspendLesson(long lessonId, Integer userId, boolean clearScheduleDetails) { securityService.ensureLessonMonitor(lessonId, userId, "suspend lesson"); Lesson lesson = lessonDAO.getLesson(new Long(lessonId)); - if (!Lesson.SUSPENDED_STATE.equals(lesson.getLessonStateId()) - && !Lesson.REMOVED_STATE.equals(lesson.getLessonStateId())) { + if (!Lesson.SUSPENDED_STATE.equals(lesson.getLessonStateId()) && !Lesson.REMOVED_STATE.equals( + lesson.getLessonStateId())) { setLessonState(lesson, Lesson.SUSPENDED_STATE, userId); } if (clearScheduleDetails) { @@ -1179,8 +1180,8 @@ lamsCoreToolService.notifyToolToDeleteContent(toolActivity); } catch (ToolException e) { if (log.isDebugEnabled()) { - log.debug("Tried to remove content of a non-existent tool: " - + toolActivity.getTool().getToolDisplayName()); + log.debug("Tried to remove content of a non-existent tool: " + toolActivity.getTool() + .getToolDisplayName()); } } // possible nonthreadsafe access to session!!! @@ -1349,11 +1350,10 @@ // check if the target activity or its parents were completed // if yes, we move user backward, otherwise forward - if ((learnerProgress != null) && (learnerProgress.getCompletedActivities().containsKey(stopActivity) - || ((parentActivity != null) && (learnerProgress.getCompletedActivities() - .containsKey(parentActivity) + if ((learnerProgress != null) && (learnerProgress.getCompletedActivities().containsKey(stopActivity) || ( + (parentActivity != null) && (learnerProgress.getCompletedActivities().containsKey(parentActivity) || ((parentActivity.getParentActivity() != null) && learnerProgress.getCompletedActivities() - .containsKey(parentActivity.getParentActivity())))))) { + .containsKey(parentActivity.getParentActivity())))))) { return forceUncompleteActivity(learnerProgress, stopActivity, removeLearnerContent); } @@ -1392,7 +1392,8 @@ String stopReason = forceCompleteActivity(learner, lessonId, learnerProgress, currentActivity, stopPreviousActivity, new ArrayList()); - return stopReason != null ? stopReason + return stopReason != null + ? stopReason : messageService.getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_STOPPED_UNEXPECTEDLY); } @@ -1430,16 +1431,17 @@ try { lessonService.performGrouping(lessonId, groupActivity, learner); } catch (LessonServiceException e) { - MonitoringService.log.error("Force complete failed. Learner " + learner + " lessonId " - + lessonId + " processing activity " + activity, e); + MonitoringService.log.error( + "Force complete failed. Learner " + learner + " lessonId " + lessonId + + " processing activity " + activity, e); stopReason = messageService.getMessage( MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_GROUPING_ERROR, new Object[] { activity.getTitle() }); } learnerService.completeActivity(learner.getUserId(), activity, progress.getLearnerProgressId()); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log - .debug("Grouping activity [" + activity.getActivityId() + "] is completed."); + MonitoringService.log.debug( + "Grouping activity [" + activity.getActivityId() + "] is completed."); } } else { // except random grouping, stop here @@ -1450,8 +1452,8 @@ // if group already exist learnerService.completeActivity(learner.getUserId(), activity, progress.getLearnerProgressId()); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log - .debug("Grouping activity [" + activity.getActivityId() + "] is completed."); + MonitoringService.log.debug( + "Grouping activity [" + activity.getActivityId() + "] is completed."); } } @@ -1545,11 +1547,11 @@ Activity nextActivity = learnerProgress.getNextActivity(); // now where? - if ((nextActivity == null) - || ((activity != null) && nextActivity.getActivityId().equals(activity.getActivityId()))) { + if ((nextActivity == null) || ((activity != null) && nextActivity.getActivityId() + .equals(activity.getActivityId()))) { // looks like we have reached the end of the sequence? - stopReason = messageService - .getMessage(MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END); + stopReason = messageService.getMessage( + MonitoringService.FORCE_COMPLETE_STOP_MESSAGE_COMPLETED_TO_END); } else if (touchedActivityIds.contains(nextActivity.getActivityId())) { // processed this one before. Better cut at this point or we // will end up in a loop. @@ -1648,8 +1650,8 @@ groupings.add(childActivity); // get real instance instead of a lazy loaded proxy - Activity sequenceChildActivity = ((SequenceActivity) activityDAO - .getActivityByActivityId(childActivity.getActivityId())).getDefaultActivity(); + Activity sequenceChildActivity = ((SequenceActivity) activityDAO.getActivityByActivityId( + childActivity.getActivityId())).getDefaultActivity(); List sequenceChildActivities = new LinkedList<>(); while (sequenceChildActivity != null) { sequenceChildActivities.add(sequenceChildActivity); @@ -1725,14 +1727,14 @@ learnerProgress.getAttemptedActivities().put(targetActivity, completedActivityProgress.getStartDate()); if (targetParentActivity != null) { // set parent as attempted - learnerProgress.getAttemptedActivities().put(targetParentActivity, - completedActivityProgress.getStartDate()); + learnerProgress.getAttemptedActivities() + .put(targetParentActivity, completedActivityProgress.getStartDate()); targetParentActivity = targetActivity.getParentActivity(); if (targetParentActivity != null) { // if target was part of branch, then immediate parent was Sequence // and parent's parent is Branching - learnerProgress.getAttemptedActivities().put(targetParentActivity, - completedActivityProgress.getStartDate()); + learnerProgress.getAttemptedActivities() + .put(targetParentActivity, completedActivityProgress.getStartDate()); } } @@ -1857,8 +1859,8 @@ Set lessons = org.getLessons(); for (Lesson les : lessons) { Activity firstActivity = les.getLearningDesign().getFirstActivity(); - List usersStartedFirstActivity = learnerProgressDAO - .getLearnersAttemptedOrCompletedActivity(firstActivity); + List usersStartedFirstActivity = learnerProgressDAO.getLearnersAttemptedOrCompletedActivity( + firstActivity); usersStartedAtLest1Lesson.addAll(usersStartedFirstActivity); } @@ -1901,8 +1903,8 @@ Set sortedUsers = new TreeSet<>(new Comparator() { @Override public int compare(User usr0, User usr1) { - return ((usr0.getLastName() + usr0.getFirstName() + usr0.getLogin()) - .compareTo(usr1.getLastName() + usr1.getFirstName() + usr1.getLogin())); + return ((usr0.getLastName() + usr0.getFirstName() + usr0.getLogin()).compareTo( + usr1.getLastName() + usr1.getFirstName() + usr1.getLogin())); } }); sortedUsers.addAll(users); @@ -1960,8 +1962,8 @@ row = sheet.initRow(); row.addCell(messageService.getMessage("email.notifications.archived.messages.list.sent.count"), true); - row.addCell(notification.getRecipients().size() + " " - + messageService.getMessage("email.notifications.archived.messages.list.learners"), false); + row.addCell(notification.getRecipients().size() + " " + messageService.getMessage( + "email.notifications.archived.messages.list.learners"), false); // get all recipient objects, sorted by name List recipients = getArchivedEmailNotificationRecipients(emailNotificationUid, null, null); @@ -1979,15 +1981,15 @@ * Returns list of users who has already finished specified lesson. * * @param lessonId - * specified lesson + * specified lesson * @return */ @Override public List getUsersCompletedLesson(Long lessonId, Integer limit, Integer offset, boolean orderAscending) { List usersCompletedLesson = new LinkedList<>(); - List completedLearnerProgresses = learnerProgressDAO - .getCompletedLearnerProgressForLesson(lessonId, limit, offset, orderAscending); + List completedLearnerProgresses = learnerProgressDAO.getCompletedLearnerProgressForLesson( + lessonId, limit, offset, orderAscending); for (LearnerProgress learnerProgress : completedLearnerProgresses) { usersCompletedLesson.add(learnerProgress.getUser()); } @@ -2017,8 +2019,8 @@ MonitoringService.log.error(error); throw new MonitoringServiceException(error); } else if (!activity.isGroupingActivity()) { - String error = "Activity should have been GroupingActivity but was a different kind of activity. " - + activity; + String error = + "Activity should have been GroupingActivity but was a different kind of activity. " + activity; MonitoringService.log.error(error); throw new MonitoringServiceException(error); } @@ -2050,8 +2052,9 @@ String url = null; if ((activity == null) || (learner == null)) { - MonitoringService.log.error("getLearnerActivityURL activity or user missing. Activity ID " + activityID - + " activity " + activity + " userID " + learnerUserID + " user " + learner); + MonitoringService.log.error( + "getLearnerActivityURL activity or user missing. Activity ID " + activityID + " activity " + + activity + " userID " + learnerUserID + " user " + learner); } else if (activity.isToolActivity()) { url = lamsCoreToolService.getToolLearnerProgressURL(lessonID, activity, learner); } else if (activity.isOptionsActivity() || activity.isParallelActivity()) { @@ -2085,15 +2088,16 @@ // --------------------------------------------------------------------- // Helper Methods - create lesson // --------------------------------------------------------------------- + /** * Create a new lesson and setup all the staffs and learners who will be participating this less. * * @param organisation - * the organization this lesson belongs to. + * the organization this lesson belongs to. * @param organizationUsers - * a list of learner will be in this new lessons. + * a list of learner will be in this new lessons. * @param staffs - * a list of staffs who will be in charge of this lesson. + * a list of staffs who will be in charge of this lesson. * @param newLesson */ private LessonClass createLessonClass(Organisation organisation, String learnerGroupName, @@ -2120,18 +2124,16 @@ * Setup a new lesson object without class and insert it into the database. * * @param lessonName - * the name of the lesson + * the name of the lesson * @param lessonDescription - * the description of the lesson. + * the description of the lesson. * @param user - * user the user who want to create this lesson. + * user the user who want to create this lesson. * @param copiedLearningDesign - * the copied learning design + * the copied learning design * @param enableLessonNotifications - * enable "email notifications" link for the current lesson - * + * enable "email notifications" link for the current lesson * @return the lesson object without class. - * */ private Lesson createNewLesson(String lessonName, String lessonDescription, User user, LearningDesign copiedLearningDesign, Boolean enableLessonIntro, Boolean displayDesignImage, @@ -2156,7 +2158,7 @@ * Setup the empty lesson class according to the run-time learning design copy. * * @param copiedLearningDesign - * the run-time learning design instance. + * the run-time learning design instance. * @return the new empty lesson class. */ private LessonClass createNewLessonClass(LearningDesign copiedLearningDesign) { @@ -2212,23 +2214,24 @@ Set learners = lesson.getAllLearners(); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log - .debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + " learners."); + MonitoringService.log.debug( + "getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + " learners."); } Iterator iter = grouping.getGroups().iterator(); while (iter.hasNext()) { Group group = (Group) iter.next(); learners.removeAll(group.getUsers()); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("getClassMembersNotGrouped: Group " + group.getGroupId() + " has " - + group.getUsers().size() + " members."); + MonitoringService.log.debug( + "getClassMembersNotGrouped: Group " + group.getGroupId() + " has " + group.getUsers().size() + + " members."); } } if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log - .debug("getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + " learners."); + MonitoringService.log.debug( + "getClassMembersNotGrouped: Lesson " + lessonID + " has " + learners.size() + " learners."); } SortedSet sortedUsers = new TreeSet(new LastNameAlphabeticComparator()); @@ -2283,9 +2286,8 @@ // Is this grouping used for branching. If it is, must honour the // groups // set in authoring or some groups won't have a branch. - if ((grouping.getMaxNumberOfGroups() != null) && (grouping.getMaxNumberOfGroups() > 0) - && (grouping.getGroups() != null) - && (grouping.getGroups().size() >= grouping.getMaxNumberOfGroups())) { + if ((grouping.getMaxNumberOfGroups() != null) && (grouping.getMaxNumberOfGroups() > 0) && ( + grouping.getGroups() != null) && (grouping.getGroups().size() >= grouping.getMaxNumberOfGroups())) { boolean usedForBranching = grouping.isUsedForBranching(); if (!usedForBranching) { MonitoringService.log.info("Setting max number of groups to null for grouping " + grouping @@ -2299,8 +2301,9 @@ "Request made to add a group which would be more than the max number of groups for the grouping " + grouping + ". This grouping is used for branching so we can't increase the max group number."); - throw new MonitoringServiceException("Cannot increase the number of groups for the grouping " - + grouping + " as this grouping is used for a branching activity."); + throw new MonitoringServiceException( + "Cannot increase the number of groups for the grouping " + grouping + + " as this grouping is used for a branching activity."); } } } @@ -2346,15 +2349,16 @@ } @Override - public int addUsersToGroupByLogins(Long activityID, String groupName, Set logins, boolean forceChosenGrouping) - throws LessonServiceException { + public List addUsersToGroupByLogins(Long activityID, String groupName, Set logins, + boolean forceChosenGrouping) throws LessonServiceException { - ArrayList learners = new ArrayList<>(); + List learners = new ArrayList<>(); for (String login : logins) { User learner = userManagementService.getUserByLogin(login); if (learner == null) { - MonitoringService.log.warn("Unable to add learner " + login + " for group in related to activity " - + activityID + " as learner cannot be found."); + MonitoringService.log.warn( + "Unable to add learner " + login + " for group in related to activity " + activityID + + " as learner cannot be found."); } else { learners.add(learner); } @@ -2393,7 +2397,7 @@ lessonService.performGrouping(grouping, group.getGroupId(), learners, forceChosenGrouping); } - return learners.size(); + return learners; } @SuppressWarnings("unchecked") @@ -2432,9 +2436,9 @@ * Branching. Don't use for Group Based Branching as there could be more than one group for the branch. * * @param sequenceActivityID - * Activity id of the sequenceActivity representing this branch + * Activity id of the sequenceActivity representing this branch * @param learnerIDs - * the IDS of the learners to be added. + * the IDS of the learners to be added. */ private void removeUsersFromBranch(Long sequenceActivityID, String learnerIDs[]) throws LessonServiceException { @@ -2470,8 +2474,9 @@ Activity parentActivity = branch.getParentActivity(); if ((parentActivity == null) || !parentActivity.isBranchingActivity()) { - String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " - + branch + " parent activity was " + parentActivity; + String error = + "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " + branch + + " parent activity was " + parentActivity; MonitoringService.log.error(error); throw new MonitoringServiceException(error); } @@ -2519,8 +2524,9 @@ Activity parentActivity = branch.getParentActivity(); if ((parentActivity == null) || !parentActivity.isBranchingActivity()) { - String error = "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " - + branch + " parent activity was " + parentActivity; + String error = + "addUsersToBranch: Branching activity missing or not a branching activity. Branch was " + branch + + " parent activity was " + parentActivity; MonitoringService.log.error(error); throw new MonitoringServiceException(error); } @@ -2562,8 +2568,9 @@ public boolean isActivityAttempted(Activity activity) { Integer numAttempted = lessonService.getCountLearnersHaveAttemptedOrCompletedActivity(activity); if (MonitoringService.log.isDebugEnabled()) { - MonitoringService.log.debug("isActivityAttempted: num attempts for activity " + activity.getActivityId() - + " is " + numAttempted); + MonitoringService.log.debug( + "isActivityAttempted: num attempts for activity " + activity.getActivityId() + " is " + + numAttempted); } return (numAttempted != null) && (numAttempted.intValue() > 0); } @@ -2779,7 +2786,8 @@ } String title = activity.getTitle(); // null for gates if (title == null) { - title = activity.isGateActivity() ? getMessageService().getMessage("label.gate.title") + title = activity.isGateActivity() + ? getMessageService().getMessage("label.gate.title") : getMessageService().getMessage("label.unknown"); } @@ -2857,8 +2865,8 @@ this.startLesson(newLesson.getLessonId(), creatorId); if (log) { - StringBuilder logMessageBuilder = new StringBuilder("cloned lesson \"") - .append(lesson.getLessonName()).append("\" from organisation \"") + StringBuilder logMessageBuilder = new StringBuilder("cloned lesson \"").append( + lesson.getLessonName()).append("\" from organisation \"") .append(lesson.getOrganisation().getName()).append("\" to \"").append(group.getName()) .append("\""); AuditLogFilter.log(AuditLogFilter.LESSON_CLONE_ACTION, logMessageBuilder); Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingUploadController.java =================================================================== diff -u -r11403ed5085a73344f4cea2b7d3677115c6f2150 -r50ce17b539bba75dc92c9799db20a66fa118e788 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingUploadController.java (.../GroupingUploadController.java) (revision 11403ed5085a73344f4cea2b7d3677115c6f2150) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/GroupingUploadController.java (.../GroupingUploadController.java) (revision 50ce17b539bba75dc92c9799db20a66fa118e788) @@ -50,6 +50,7 @@ import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.FileUtil; +import org.lamsfoundation.lams.util.JsonUtil; import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.WebUtil; import org.lamsfoundation.lams.util.excel.ExcelRow; @@ -81,6 +82,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.Vector; +import java.util.stream.Collectors; /** * The action servlet that provides the support for the AJAX based Chosen Grouping upload from File @@ -320,27 +322,28 @@ } Map> groups = new HashMap<>(); - int totalUsersSkipped = parseGroupSpreadsheet(fileElements, orgGroupingId, groups); + Set usersSkipped = parseGroupSpreadsheet(fileElements, orgGroupingId, groups); int totalUsersAdded = 0; List orgGroups = new LinkedList<>(); for (Map.Entry> groupEntry : groups.entrySet()) { String groupName = groupEntry.getKey(); // just overwrite existing groups; they will be updated if already exist Set learners = new HashSet<>(); + for (String login : groupEntry.getValue()) { User learner = userManagementService.getUserByLogin(login); if (learner == null) { log.warn("Unable to add learner " + login + " for group in related to grouping " + orgGroupingId + " as learner cannot be found."); - totalUsersSkipped++; + usersSkipped.add(login); //Check user is a part of the organisation } else if (!securityService.hasOrgRole(organisation.getOrganisationId(), learner.getUserId(), new String[] { Role.GROUP_MANAGER, Role.LEARNER, Role.MONITOR, Role.AUTHOR }, "be added to grouping", true)) { - totalUsersSkipped++; + usersSkipped.add(login); } else { totalUsersAdded++; @@ -359,15 +362,14 @@ } userManagementService.saveOrganisationGrouping(orgGrouping, orgGroups); - return createResponseJSON(false, null, true, orgGrouping.getGroupingId(), totalUsersAdded, totalUsersSkipped); + return createResponseJSON(false, null, true, orgGrouping.getGroupingId(), totalUsersAdded, usersSkipped); } /** Clean out and reuse any existing groups */ private ObjectNode saveLessonGrouping(Long lessonId, Long activityId, MultipartFile fileElements) throws IOException { - int totalUsersSkipped = 0; int totalUsersAdded = 0; // Lesson grouping case so clean out and reuse any existing groups @@ -382,12 +384,13 @@ existingGroupNames.add(group.getGroupName()); if (!group.mayBeDeleted()) { String error = messageService.getMessage("error.groups.upload.locked"); - return createResponseJSON(true, error, true, grouping.getGroupingId(), 0, 0); + return createResponseJSON(true, error, true, grouping.getGroupingId(), 0, null); } } Map> groups = new HashMap<>(); - totalUsersSkipped = parseGroupSpreadsheet(fileElements, grouping.getGroupingId(), groups); + Set usersSkipped = parseGroupSpreadsheet(fileElements, grouping.getGroupingId(), groups); + Set allUsers = groups.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()); // if branching must use the already specified groups or cannot match to a branch! if (activity.isChosenBranchingActivity()) { @@ -399,7 +402,7 @@ } String error = messageService.getMessage("error.branching.upload.must.use.existing.groups", new String[] { groupNamesStrBlder.toString() }); - return createResponseJSON(true, error.toString(), false, grouping.getGroupingId(), 0, 0); + return createResponseJSON(true, error.toString(), false, grouping.getGroupingId(), 0, null); } } } @@ -414,12 +417,10 @@ User learner = userManagementService.getUserByLogin(login); if (learner == null) { log.warn("Unable to add learner " + login + " to lesson grouping as learner cannot be found."); - totalUsersSkipped++; iter.remove(); } else if (!securityService.isLessonLearner(lessonId, learner.getUserId(), "be added to grouping", true)) { - totalUsersSkipped++; iter.remove(); } } @@ -441,17 +442,19 @@ // Now put in the new users groupings for (Map.Entry> groupEntry : groups.entrySet()) { - int added = monitoringService.addUsersToGroupByLogins(activityId, groupEntry.getKey(), - groupEntry.getValue(), true); - totalUsersAdded += added; - totalUsersSkipped += groupEntry.getValue().size() - added; + Set usersToAdd = groupEntry.getValue(); + List addedUsers = monitoringService.addUsersToGroupByLogins(activityId, groupEntry.getKey(), + usersToAdd, true); + totalUsersAdded += addedUsers.size(); + allUsers.removeAll(addedUsers.stream().map(User::getLogin).collect(Collectors.toSet())); } + usersSkipped.addAll(allUsers); - return createResponseJSON(false, null, true, grouping.getGroupingId(), totalUsersAdded, totalUsersSkipped); + return createResponseJSON(false, null, true, grouping.getGroupingId(), totalUsersAdded, usersSkipped); } private ObjectNode createResponseJSON(boolean isError, String errorMessage, boolean reload, Long groupingId, - int totalUsersAdded, int totalUsersSkipped) { + int totalUsersAdded, Collection usersSkipped) throws IOException { ObjectNode responseJSON = JsonNodeFactory.instance.objectNode(); if (isError) { responseJSON.put("result", "FAIL"); @@ -460,7 +463,9 @@ } else { responseJSON.put("result", "OK"); responseJSON.put("added", totalUsersAdded); - responseJSON.put("skipped", totalUsersSkipped); + if (usersSkipped != null && !usersSkipped.isEmpty()) { + responseJSON.set("skipped", JsonUtil.readArray(usersSkipped)); + } } responseJSON.put("groupingId", groupingId); return responseJSON; @@ -477,37 +482,35 @@ return null; } - public int parseGroupSpreadsheet(MultipartFile fileItem, Long groupingID, Map> groups) + public Set parseGroupSpreadsheet(MultipartFile fileItem, Long groupingID, Map> groups) throws IOException { POIFSFileSystem fs = new POIFSFileSystem(fileItem.getInputStream()); HSSFWorkbook wb = new HSSFWorkbook(fs); HSSFSheet sheet = wb.getSheetAt(0); int startRow = sheet.getFirstRowNum(); int endRow = sheet.getLastRowNum(); - int skipped = 0; + Set usersSkipped = new TreeSet<>(); Set allUsers = new HashSet<>(); for (int i = startRow + 1; i < (endRow + 1); i++) { HSSFRow row = sheet.getRow(i); String login = parseStringCell(row.getCell(0)); if (StringUtils.isBlank(login)) { - skipped++; GroupingUploadController.log.warn( "Unable to add learner for group related to grouping " + groupingID + " as login is missing."); continue; } boolean alreadyExists = !allUsers.add(login); if (alreadyExists) { - skipped++; GroupingUploadController.log.warn( "Skipping duplicate row for learner " + login + " for group related to grouping " + groupingID); continue; } String groupName = row.getLastCellNum() > 3 ? parseStringCell(row.getCell(3)) : null; if (groupName == null || groupName.length() == 0) { - skipped++; + usersSkipped.add(login); GroupingUploadController.log.warn( "Unable to add learner " + login + " for group in related to grouping " + groupingID + " as group name is missing."); @@ -520,7 +523,7 @@ } users.add(login); } - return skipped; + return usersSkipped; } private UserDTO getUserDTO() {