Index: lams_central/src/java/org/lamsfoundation/lams/webservice/SPEnrolmentServlet.java =================================================================== diff -u -r0957abe7df0238b85edabcf3a4e37d41d4f0140e -rf9ff8c07a58545fb546081544721330ae53cbb86 --- lams_central/src/java/org/lamsfoundation/lams/webservice/SPEnrolmentServlet.java (.../SPEnrolmentServlet.java) (revision 0957abe7df0238b85edabcf3a4e37d41d4f0140e) +++ lams_central/src/java/org/lamsfoundation/lams/webservice/SPEnrolmentServlet.java (.../SPEnrolmentServlet.java) (revision f9ff8c07a58545fb546081544721330ae53cbb86) @@ -78,6 +78,21 @@ * @author Marcin Cieslak */ public class SPEnrolmentServlet extends HttpServlet { + // provisioning behaves differently when processing different flavours of files + private static enum Mode { + LEARNER("learner"), STAFF("staff"), MANAGER("manager"); + + private String role; + + private Mode(String role) { + this.role = role; + } + + private String getRole() { + return role; + } + }; + private static final long serialVersionUID = -5348322697437185394L; private static final Logger logger = Logger.getLogger(SPEnrolmentServlet.class); @@ -86,7 +101,6 @@ private static final String FILE_INPUT_PARAM = "file-input"; private static final String DELIMITER = "\\|"; private static final String INTEGRATED_SERVER_NAME = "saml"; - private static final String ROLE_STAFF = "staff"; private static ILessonService lessonService = null; private static IUserManagementService userManagementService = null; @@ -120,9 +134,15 @@ .collect(Collectors.toList())) // filter out malformed rows .filter(row -> row.size() == 7).collect(Collectors.toList()); - // it is easier to detect whether we process staff or learners just once + if (lines.isEmpty()) { + throw new ServletException("File is empty"); + } + + // it is easier to detect whether we process managers or staff or learners just once // than for each user - they do not come together anyway - boolean isStaffMode = !lines.isEmpty() && lines.get(0).get(6).equals(ROLE_STAFF); + String roleColumn = lines.get(0).get(6); + final Mode mode = roleColumn.equals(Mode.STAFF.getRole()) ? Mode.STAFF + : (roleColumn.equals(Mode.MANAGER.getRole()) ? Mode.MANAGER : Mode.LEARNER); // map of course code (ID) -> course name ConcurrentMap courses = lines.parallelStream().unordered().collect( @@ -132,17 +152,9 @@ // for learner email is login, for staff it is a different ID in email format ConcurrentMap users = lines.parallelStream().unordered() .collect(Collectors.toConcurrentMap( - elem -> elem.get(6).equals(ROLE_STAFF) ? elem.get(3) : elem.get(5), + elem -> mode == Mode.STAFF || mode == Mode.MANAGER ? elem.get(3) : elem.get(5), elem -> new String[] { elem.get(5), elem.get(4) }, (elem1, elem2) -> elem1)); - // map of course code -> subcourse code -> user logins - Map>> mappings = lines.stream() - .collect(Collectors.groupingBy(elem -> elem.get(0), LinkedHashMap::new, - Collectors.groupingBy(elem -> elem.get(2), LinkedHashMap::new, - Collectors.mapping( - elem -> elem.get(6).equals(ROLE_STAFF) ? elem.get(3) : elem.get(5), - Collectors.toList())))); - // map of user login -> user ID Map userIDs = new HashMap<>(); @@ -162,16 +174,63 @@ .findByPropertyValues(ExtUserUseridMap.class, "extUsername", allExistingUsers.keySet()) .parallelStream().filter(e -> e.getExtServer().getSid().equals(extServerSid)) .collect(Collectors.toConcurrentMap(ExtUserUseridMap::getExtUsername, e -> e)); - // create users and ext users + // CREATE USERS and ext users Set allUsersParsed = createUsers(extServer, creatorId, users, userIDs, allExistingUsers, allExistingExtUsers); + // map of user login -> course ID -> role IDs + // for all users which are present in the output file + Map>> allExistingRoles = userManagementService + .findByPropertyValues(UserOrganisation.class, "user.userId", + allExistingUsers.values().parallelStream() + .collect(Collectors.mapping(User::getUserId, Collectors.toSet()))) + .stream() + .collect(Collectors.groupingBy(uo -> uo.getUser().getLogin(), + Collectors.toMap( + userOrganisation -> userOrganisation.getOrganisation().getOrganisationId(), + userOrganisation -> userOrganisation.getUserOrganisationRoles().stream() + .map(userOrganisationRole -> userOrganisationRole.getRole().getRoleId()) + .collect(Collectors.toSet())))); // load all organisations from DB which are present in the output file, by code // map of code -> organisation Map allExistingOrganisations = userManagementService .findByPropertyValues(Organisation.class, "code", courses.keySet()).parallelStream() .collect(Collectors.toConcurrentMap(Organisation::getCode, o -> o)); + // When setting group managers, just process courses, not subcourses and lessons + if (mode == Mode.MANAGER) { + // map of course code -> user logins + Map> mappings = lines.stream() + .collect(Collectors.groupingBy(elem -> elem.get(0), LinkedHashMap::new, + Collectors.mapping(elem -> elem.get(3), Collectors.toList()))); + + AtomicInteger mappingsProcessed = new AtomicInteger(); + logger.info("Processing courses and assigments"); + for (String courseCode : courses.keySet()) { + Organisation course = allExistingOrganisations.get(courseCode); + + assignManagers(course, creatorId, mappings, allUsersParsed, userIDs, allExistingRoles, + allExistingUsers, mappingsProcessed); + + logger.info("Processed " + mappingsProcessed.get() + " entries"); + } + logger.info("SP enrolments provisioning completed successfully"); + + // END OF GROUP MANAGER PROCESSING! + return; + } + + // START OF LEARNER / STAFF PROCESSING + + // map of course code -> subcourse code -> user logins + Map>> mappings = lines.stream() + .collect( + Collectors.groupingBy(elem -> elem.get(0), LinkedHashMap::new, + Collectors.groupingBy(elem -> elem.get(2), LinkedHashMap::new, + Collectors.mapping( + elem -> mode == Mode.STAFF ? elem.get(3) : elem.get(5), + Collectors.toList())))); + // prepare codes of all suborganisations disregarding which organisation is their parent Set allExistingSubOrganisationCodes = mappings.values().stream() .flatMap(e -> e.keySet().stream()).collect(Collectors.toSet()); @@ -196,20 +255,6 @@ .parallelStream().filter(e -> e.getExtServer().getSid().equals(extServerSid)) .collect(Collectors.toConcurrentMap(e -> e.getOrganisation().getOrganisationId(), e -> e)); - // map of user login -> course ID -> role IDs - // for all users which are present in the output file - Map>> allExistingRoles = userManagementService - .findByPropertyValues(UserOrganisation.class, "user.userId", - allExistingUsers.values().parallelStream() - .collect(Collectors.mapping(User::getUserId, Collectors.toSet()))) - .stream() - .collect(Collectors.groupingBy(uo -> uo.getUser().getLogin(), - Collectors.toMap( - userOrganisation -> userOrganisation.getOrganisation().getOrganisationId(), - userOrganisation -> userOrganisation.getUserOrganisationRoles().stream() - .map(userOrganisationRole -> userOrganisationRole.getRole().getRoleId()) - .collect(Collectors.toSet())))); - Set allUsersinCourses = new HashSet<>(); // go through each course AtomicInteger mappingsProcessed = new AtomicInteger(); @@ -219,11 +264,10 @@ // create or get existing course Organisation course = getCourse(courseCode, courseEntry.getValue(), extServer, creatorId, rootOrganisation, allExistingOrganisations, allExistingExtCourses); - // collect learners from all subcourses of this course - Set allUsersInSubcourses = assignUsers(course, mappings, extServer, creatorId, allUsersParsed, - userIDs, allExistingRoles, allExistingSubcourses, allExistingExtCourses, allExistingUsers, - isStaffMode, mappingsProcessed); + Set allUsersInSubcourses = assignLearnersOrStaff(course, mappings, extServer, creatorId, + allUsersParsed, userIDs, allExistingRoles, allExistingSubcourses, allExistingExtCourses, + allExistingUsers, mode == Mode.STAFF, mappingsProcessed); allUsersinCourses.addAll(allUsersInSubcourses); logger.info("Processed " + mappingsProcessed.get() + " entries"); @@ -236,7 +280,7 @@ // make a flat set of roles from all subcourses Set roles = userManagementService.getRolesForUser(user.getUserId()).values().stream() .collect(HashSet::new, Set::addAll, Set::addAll); - if (isStaffMode) { + if (mode == Mode.STAFF) { // check if the user is learner in any course roles.remove(Role.ROLE_MONITOR); roles.remove(Role.ROLE_AUTHOR); @@ -255,7 +299,6 @@ } } - logger.info("SP enrolments provisioning completed successfully"); } catch (Exception e) { logger.error("Error while provisioning SP enrolments", e); } finally { @@ -375,7 +418,7 @@ } @SuppressWarnings("unchecked") - private Set assignUsers(Organisation course, Map>> mappings, + private Set assignLearnersOrStaff(Organisation course, Map>> mappings, ExtServer extServer, Integer creatorId, Set allUsersParsed, Map userIDs, Map>> allExistingRoles, Map> allExistingSubcourses, @@ -527,6 +570,74 @@ return allUsersInSubcourses; } + @SuppressWarnings("unchecked") + private void assignManagers(Organisation course, Integer creatorId, Map> mappings, + Set allUsersParsed, Map userIDs, + Map>> allExistingRoles, Map allExistingUsers, + AtomicInteger mappingsProcessed) throws UserInfoValidationException { + String courseCode = course.getCode(); + Integer courseId = course.getOrganisationId(); + + // get existing managers for given course + Collection courseUsers = userManagementService.getUsersFromOrganisationByRole(courseId, + Role.GROUP_MANAGER, true); + + // go through each user + for (String login : mappings.get(courseCode)) { + mappingsProcessed.incrementAndGet(); + + // check if the user is already a manager is the course + // if so, there is nothing to do + boolean userAlreadyAssigned = false; + Integer userId = userIDs.get(login); + Iterator courseUserIterator = courseUsers.iterator(); + while (courseUserIterator.hasNext()) { + if (userId.equals(courseUserIterator.next().getUserId())) { + courseUserIterator.remove(); + userAlreadyAssigned = true; + break; + } + } + if (userAlreadyAssigned) { + continue; + } + + // the user is not a manager yet, so assign him the role + Map> existingCoursesRoles = allExistingRoles.get(login); + Set existingCourseRoles = existingCoursesRoles == null ? null + : allExistingRoles.get(login).get(courseId); + if (existingCourseRoles == null) { + existingCourseRoles = new HashSet<>(); + } + existingCourseRoles.add(Role.ROLE_GROUP_MANAGER); + + User user = allExistingUsers.get(login); + userManagementService.setRolesForUserOrganisation(user, course, + existingCourseRoles.stream().map(String::valueOf).collect(Collectors.toList()), false); + + String message = "Group manager \"" + login + "\" added to course"; + logger.info(message); + logEventService.logEvent(LogEvent.TYPE_USER_ORG_ADMIN, creatorId, null, null, null, + "SPEnrolment: " + message); + } + + // user is a group manager, but he should not; remove him role from course + for (User user : courseUsers) { + Map> existingCoursesRoles = allExistingRoles.get(user.getLogin()); + Set existingSubcourseRoles = existingCoursesRoles == null ? null + : existingCoursesRoles.get(courseId); + if (existingSubcourseRoles != null) { + existingSubcourseRoles.remove(Role.ROLE_GROUP_MANAGER); + userManagementService.setRolesForUserOrganisation(user.getUserId(), courseId, existingSubcourseRoles); + + String message = "Group manager \"" + user.getLogin() + "\" removed from course " + courseId; + logger.info(message); + logEventService.logEvent(LogEvent.TYPE_USER_ORG_ADMIN, creatorId, null, null, null, + "SPEnrolment: " + message); + } + } + } + @Override public void init() throws ServletException { lessonService = (ILessonService) WebApplicationContextUtils