def verify_canvas_course(self, course_id):
        """
        Verify that the Canvas course still exists, has a correct sis_id, and
        contains a UW Group section.
        """
        try:
            valid_adhoc_course_sis_id(course_id)
            (prefix, canvas_course_id) = course_id.split('_')
            canvas_course = get_course_by_id(canvas_course_id)
        except CoursePolicyException:
            canvas_course = get_course_by_sis_id(course_id)

        if canvas_course.sis_course_id is None:
            update_course_sis_id(canvas_course.course_id, course_id)

        group_section_id = group_section_sis_id(course_id)
        try:
            section = get_section_by_sis_id(group_section_id)
        except DataFailureException as err:
            if err.status == 404:
                self.data.add(
                    SectionCSV(section_id=group_section_id,
                               course_id=course_id,
                               name=group_section_name()))
            else:
                raise
    def _process(self, member):
        section_id = group_section_sis_id(member.course_id)

        status = Enrollment.DELETED_STATUS if (
            member.is_deleted) else Enrollment.ACTIVE_STATUS

        if member.is_eppn():
            member.login = member.name

        try:
            self.add_group_enrollment_data(member, section_id, member.role,
                                           status)

        except Exception as err:
            self.logger.info("Skip group member %s (%s)" % (member.name, err))
    def all_course_enrollments(self, course_id):
        group_section_id = group_section_sis_id(course_id)
        sis_enrollments = {}
        group_enrollments = set()
        for enr in get_enrollments_for_course_by_sis_id(course_id):
            # Split the enrollments into sis and group enrollments,
            # discarding enrollments for adhoc sections
            try:
                valid_academic_section_sis_id(enr.sis_section_id)
                sis_enrollments[enr.login_id.lower()] = enr.sis_section_id

            except CoursePolicyException:
                if enr.sis_section_id == group_section_id:
                    group_enrollments.add(SetMember(enr.login_id, enr.role))

        return (sis_enrollments, group_enrollments)
    def _process(self, course_id):
        try:
            self._verify_canvas_course(course_id)
        except DataFailureException as err:
            if err.status == 404:
                Group.objects.deprioritize_course(course_id)
                self.logger.info("Drop group sync for deleted course %s" % (course_id))
            else:
                self._requeue_course(course_id, err)
            return

        group_section_id = group_section_sis_id(course_id)
        self.data.add(SectionCSV(section_id=group_section_id, course_id=course_id, name=group_section_name()))

        # Get the enrollments for academic courses from Canvas, excluding
        # ad-hoc and group sections
        try:
            valid_academic_course_sis_id(course_id)
            if course_id not in self.cached_course_enrollments:
                canvas_enrollments = get_sis_enrollments_for_course(course_id)
                self.cached_course_enrollments[course_id] = canvas_enrollments

        except CoursePolicyException:
            self.cached_course_enrollments[course_id] = []
        except DataFailureException as err:
            self._requeue_course(course_id, err)
            return

        current_members = []
        for group in Group.objects.get_active_by_course(course_id):
            try:
                current_members.extend(self._get_current_members(group))

            except DataFailureException as err:
                self._requeue_course(course_id, err)
                return

            # skip on any group policy exception
            except GroupPolicyException as err:
                self.logger.info("Skip group %s (%s)" % (group.group_id, err))

        cached_members = CourseMember.objects.get_by_course(course_id)
        for member in cached_members:
            match = next((m for m in current_members if m == member), None)

            if match is None:
                if not self.delta or not member.is_deleted:
                    try:
                        self.add_group_enrollment_data(
                            member, group_section_id, member.role, status=Enrollment.DELETED_STATUS
                        )
                    except Exception as err:
                        self.logger.info("Skip group member %s (%s)" % (member.name, err))

                member.deactivate()

        for member in current_members:
            match = next((m for m in cached_members if m == member), None)

            # new or previously removed member
            if not self.delta or match is None or match.is_deleted:
                try:
                    self.add_group_enrollment_data(
                        member, group_section_id, member.role, status=Enrollment.ACTIVE_STATUS
                    )
                except Exception as err:
                    self.logger.info("Skip group member %s (%s)" % (member.name, err))

                if match is None:
                    member.save()
                elif match.is_deleted:
                    match.activate()
    def _process(self, course_id):
        group_section_id = group_section_sis_id(course_id)

        try:
            self.verify_canvas_course(course_id)
        except DataFailureException as err:
            if err.status == 404:
                Group.objects.deprioritize_course(course_id)
                self.logger.info(
                    "Drop group sync for deleted course {}".format(course_id))
            else:
                self._requeue_course(course_id, err)
            return

        try:
            # Get the enrollments for SIS and Group sections in this course
            (sis_enrollments,
             group_enrollments) = self.all_course_enrollments(course_id)

            # Build a flattened set of current group memberships from GWS
            current_members = self.all_group_memberships(course_id)

        except DataFailureException as err:
            self._requeue_course(course_id, err)
            return

        # Remove enrollments not in the current group membership from
        # the groups section
        for member in (group_enrollments - current_members):
            try:
                self.add_group_enrollment_data(member.login,
                                               group_section_id,
                                               member.role,
                                               status=ENROLLMENT_DELETED)
            except DataFailureException as err:
                self.logger.info("Skip remove group member {}: {}".format(
                    member.login, err))

        # Add group members not already enrolled to the groups section,
        # unless the user already has an sis enrollment in the course
        for member in (current_members - group_enrollments):
            if member.login in sis_enrollments:
                self.logger.info(
                    "Skip add group member {} (present in {})".format(
                        member.login, sis_enrollments[member.login]))
                continue

            try:
                self.add_group_enrollment_data(member.login,
                                               group_section_id,
                                               member.role,
                                               status=ENROLLMENT_ACTIVE)
            except DataFailureException as err:
                self.logger.info("Skip add group member {}: {}".format(
                    member.login, err))

        # Remove any existing group enrollments that also have an
        # sis enrollment in the course
        for member in group_enrollments:
            if member.login in sis_enrollments:
                self.add_group_enrollment_data(member.login,
                                               group_section_id,
                                               member.role,
                                               status=ENROLLMENT_DELETED)
                self.logger.info(
                    "Remove group enrollment {} (present in {})".format(
                        member.login, sis_enrollments[member.login]))