Example #1
0
    def delete(self, request, course_id):
        """
        Unenroll a user from a course; requires staff access

        **Example Request**
            DELETE /api/enrollment/v1/roster/course-v1:foo+bar+foobar
            {
                'email': '*****@*****.**',
                'email_students': false,
                'auto_enroll': true
            }
        """
        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    'message': u'Invalid or missing course_id',
                },
            )
        if not user_has_role(request.user, CourseStaffRole(course_key)):
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    'message':
                    u'User does not have permission to update enrollment for [{course_id}].'
                    .format(course_id=course_id, ),
                },
            )
        email = request.data.get('email')
        try:
            validate_email(email)
        except ValidationError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    'message': u'Invalid email address',
                },
            )
        email_students = request.data.get('email_students',
                                          False) in ['true', 'True', True]
        auto_enroll = request.data.get('auto_enroll',
                                       False) in ['true', 'True', True]
        email_params = {}
        language = None
        if email_students:
            course = get_course_by_id(course_key)
            email_params = get_email_params(course, auto_enroll)
            if User.objects.filter(email=email).exists():
                user = User.objects.get(email=email)
                language = get_user_email_language(user)
        unenroll_email(course_key,
                       email,
                       email_students,
                       email_params,
                       language=language)
        return Response(status=status.HTTP_204_NO_CONTENT)
Example #2
0
def ccx_students_enrolling_center(action, identifiers, email_students, course_key, email_params, coach):
    """
    Function to enroll/add or unenroll/revoke students.

    This function exists for backwards compatibility: in CCX there are
    two different views to manage students that used to implement
    a different logic. Now the logic has been reconciled at the point that
    this function can be used by both.
    The two different views can be merged after some UI refactoring.

    Arguments:
        action (str): type of action to perform (add, Enroll, revoke, Unenroll)
        identifiers (list): list of students username/email
        email_students (bool): Flag to send an email to students
        course_key (CCXLocator): a CCX course key
        email_params (dict): dictionary of settings for the email to be sent
        coach (User): ccx coach

    Returns:
        list: list of error
    """
    errors = []

    if action == 'Enroll' or action == 'add':
        ccx_course_overview = CourseOverview.get_from_id(course_key)
        course_locator = course_key.to_course_locator()
        staff = CourseStaffRole(course_locator).users_with_role()
        admins = CourseInstructorRole(course_locator).users_with_role()

        for identifier in identifiers:
            must_enroll = False
            try:
                email, student = get_valid_student_with_email(identifier)
                if student:
                    must_enroll = student in staff or student in admins or student == coach
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue

            if CourseEnrollment.objects.is_course_full(ccx_course_overview) and not must_enroll:
                error = _('The course is full: the limit is {max_student_enrollments_allowed}').format(
                    max_student_enrollments_allowed=ccx_course_overview.max_student_enrollments_allowed)
                log.info("%s", error)
                errors.append(error)
                break
            enroll_email(course_key, email, auto_enroll=True, email_students=email_students, email_params=email_params)
    elif action == 'Unenroll' or action == 'revoke':
        for identifier in identifiers:
            try:
                email, __ = get_valid_student_with_email(identifier)
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue
            unenroll_email(course_key, email, email_students=email_students, email_params=email_params)
    return errors
Example #3
0
def ccx_students_enrolling_center(action, identifiers, email_students, course_key, email_params, coach):
    """
    Function to enroll or unenroll/revoke students.

    Arguments:
        action (str): type of action to perform (Enroll, Unenroll/revoke)
        identifiers (list): list of students username/email
        email_students (bool): Flag to send an email to students
        course_key (CCXLocator): a CCX course key
        email_params (dict): dictionary of settings for the email to be sent
        coach (User): ccx coach

    Returns:
        list: list of error
    """
    errors = []

    if action == 'Enroll':
        ccx_course_overview = CourseOverview.get_from_id(course_key)
        course_locator = course_key.to_course_locator()
        staff = CourseStaffRole(course_locator).users_with_role()
        admins = CourseInstructorRole(course_locator).users_with_role()

        for identifier in identifiers:
            must_enroll = False
            try:
                email, student = get_valid_student_with_email(identifier)
                if student:
                    must_enroll = student in staff or student in admins or student == coach
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue

            if CourseEnrollment.objects.is_course_full(ccx_course_overview) and not must_enroll:
                error = _('The course is full: the limit is {max_student_enrollments_allowed}').format(
                    max_student_enrollments_allowed=ccx_course_overview.max_student_enrollments_allowed)
                log.info("%s", error)
                errors.append(error)
                break
            enroll_email(course_key, email, auto_enroll=True, email_students=email_students, email_params=email_params)
    elif action == 'Unenroll' or action == 'revoke':
        for identifier in identifiers:
            try:
                email, __ = get_valid_student_with_email(identifier)
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue
            unenroll_email(course_key, email, email_students=email_students, email_params=email_params)
    return errors
Example #4
0
def remove_master_course_staff_from_ccx(master_course,
                                        ccx_key,
                                        display_name,
                                        send_email=True):
    """
    Remove staff and instructor roles on ccx to all the staff and instructors members of master course.

    Arguments:
        master_course (CourseBlockWithMixins): Master course instance.
        ccx_key (CCXLocator): CCX course key.
        display_name (str): ccx display name for email.
        send_email (bool): flag to switch on or off email to the users on revoke access.

    """
    list_staff = list_with_level(master_course.id, 'staff')
    list_instructor = list_with_level(master_course.id, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        list_staff_ccx = list_with_level(course_ccx.id, 'staff')
        list_instructor_ccx = list_with_level(course_ccx.id, 'instructor')
        email_params = get_email_params(course_ccx,
                                        auto_enroll=True,
                                        course_key=ccx_key,
                                        display_name=display_name)
        for staff in list_staff:
            if staff in list_staff_ccx:
                # revoke 'staff' access on ccx.
                revoke_access(course_ccx, staff, 'staff')

                # Unenroll the staff on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=staff.email,
                    email_students=send_email,
                    email_params=email_params,
                )

        for instructor in list_instructor:
            if instructor in list_instructor_ccx:
                # revoke 'instructor' access on ccx.
                revoke_access(course_ccx, instructor, 'instructor')

                # Unenroll the instructor on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=instructor.email,
                    email_students=send_email,
                    email_params=email_params,
                )
Example #5
0
    def delete(self, request, course_id):
        """
        Unenroll a user from a course; requires staff access

        **Example Request**
            DELETE /api/enrollment/v1/roster/course-v1:foo+bar+foobar
            {
                'email': '*****@*****.**',
                'email_students': false,
                'auto_enroll': true
            }
        """
        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    'message': u'Invalid or missing course_id',
                },
            )
        if not user_has_role(request.user, CourseStaffRole(course_key)):
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    'message': u'User does not have permission to update enrollment for [{course_id}].'.format(
                        course_id=course_id,
                    ),
                },
            )
        email = request.data.get('email')
        try:
            validate_email(email)
        except ValidationError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    'message': u'Invalid email address',
                },
            )

        email_students, auto_enroll, email_params, language = self.api_params_helper(request, course_key, email)
        unenroll_email(
            course_key, email, email_students, email_params, language=language
        )
        return Response(status=status.HTTP_204_NO_CONTENT)
Example #6
0
def remove_master_course_staff_from_ccx(master_course, ccx_key, display_name, send_email=True):
    """
    Remove staff and instructor roles on ccx to all the staff and instructors members of master course.

    Arguments:
        master_course (CourseDescriptorWithMixins): Master course instance.
        ccx_key (CCXLocator): CCX course key.
        display_name (str): ccx display name for email.
        send_email (bool): flag to switch on or off email to the users on revoke access.

    """
    list_staff = list_with_level(master_course, 'staff')
    list_instructor = list_with_level(master_course, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        list_staff_ccx = list_with_level(course_ccx, 'staff')
        list_instructor_ccx = list_with_level(course_ccx, 'instructor')
        email_params = get_email_params(course_ccx, auto_enroll=True, course_key=ccx_key, display_name=display_name)
        for staff in list_staff:
            if staff in list_staff_ccx:
                # revoke 'staff' access on ccx.
                revoke_access(course_ccx, staff, 'staff')

                # Unenroll the staff on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=staff.email,
                    email_students=send_email,
                    email_params=email_params,
                )

        for instructor in list_instructor:
            if instructor in list_instructor_ccx:
                # revoke 'instructor' access on ccx.
                revoke_access(course_ccx, instructor, 'instructor')

                # Unenroll the instructor on ccx.
                unenroll_email(
                    course_id=ccx_key,
                    student_email=instructor.email,
                    email_students=send_email,
                    email_params=email_params,
                )
Example #7
0
    def test_unenroll_norecord(self):
        before_ideal = SettableEnrollmentState(user=False,
                                               enrollment=False,
                                               allowed=False,
                                               auto_enroll=False)

        after_ideal = SettableEnrollmentState(user=False,
                                              enrollment=False,
                                              allowed=False,
                                              auto_enroll=False)

        action = lambda email: unenroll_email(self.course_key, email)

        return self._run_state_change_test(before_ideal, after_ideal, action)
Example #8
0
    def test_unenroll_norecord(self):
        before_ideal = SettableEnrollmentState(
            user=False,
            enrollment=False,
            allowed=False,
            auto_enroll=False
        )

        after_ideal = SettableEnrollmentState(
            user=False,
            enrollment=False,
            allowed=False,
            auto_enroll=False
        )

        action = lambda email: unenroll_email(self.course_key, email)

        return self._run_state_change_test(before_ideal, after_ideal, action)
Example #9
0
    def post(self, request, *args, **kwargs):

        course_id = request.POST.get('course_id', False)
        try:
            course_id = SlashSeparatedCourseKey.from_deprecated_string(
                course_id)
        except Exception:
            course_id = None

        if not course_id:
            self.msg = u"课程ID错误"
            context = {'msg': self.msg, 'datatable': self.make_datatable()}
            return render_to_response(self.template_name, context)
        elif not request.POST.get('identifiers'):
            self.msg = u"邮箱用户名错误"
            context = {'msg': self.msg, 'datatable': self.make_datatable()}
            return render_to_response(self.template_name, context)

        action = request.POST.get('action')
        identifiers_raw = request.POST.get('identifiers')
        identifiers = _split_input_list(identifiers_raw)
        auto_enroll = _get_boolean_param(request, 'auto_enroll')
        email_students = _get_boolean_param(request, 'email_students')
        is_white_label = CourseMode.is_white_label(course_id)
        reason = request.POST.get('reason')

        if is_white_label:
            if not reason:
                self.msg = "400"
                context = {'msg': self.msg, 'datatable': self.make_datatable()}
                return render_to_response(self.template_name, context)

        enrollment_obj = None
        state_transition = DEFAULT_TRANSITION_STATE

        email_params = {}
        if email_students:
            course = get_course_by_id(course_id)
            email_params = get_email_params(course,
                                            auto_enroll,
                                            secure=request.is_secure())

        results = []
        for identifier in identifiers:
            # First try to get a user object from the identifer
            user = None
            email = None
            language = None
            try:
                user = get_student_from_identifier(identifier)
            except User.DoesNotExist:
                email = identifier
            else:
                email = user.email
                language = get_user_email_language(user)

            try:
                # Use django.core.validators.validate_email to check email address
                # validity (obviously, cannot check if email actually /exists/,
                # simply that it is plausibly valid)
                validate_email(email)  # Raises ValidationError if invalid
                if action == 'enroll':
                    before, after, enrollment_obj = enroll_email(
                        course_id,
                        email,
                        auto_enroll,
                        email_students,
                        email_params,
                        language=language)
                    before_enrollment = before.to_dict()['enrollment']
                    before_user_registered = before.to_dict()['user']
                    before_allowed = before.to_dict()['allowed']
                    after_enrollment = after.to_dict()['enrollment']
                    after_allowed = after.to_dict()['allowed']

                    if before_user_registered:
                        if after_enrollment:
                            if before_enrollment:
                                state_transition = ENROLLED_TO_ENROLLED
                            else:
                                if before_allowed:
                                    state_transition = ALLOWEDTOENROLL_TO_ENROLLED
                                else:
                                    state_transition = UNENROLLED_TO_ENROLLED
                    else:
                        if after_allowed:
                            state_transition = UNENROLLED_TO_ALLOWEDTOENROLL

                elif action == 'unenroll':
                    before, after = unenroll_email(course_id,
                                                   email,
                                                   email_students,
                                                   email_params,
                                                   language=language)
                    before_enrollment = before.to_dict()['enrollment']
                    before_allowed = before.to_dict()['allowed']
                    enrollment_obj = CourseEnrollment.get_enrollment(
                        user, course_id)

                    if before_enrollment:
                        state_transition = ENROLLED_TO_UNENROLLED
                    else:
                        if before_allowed:
                            state_transition = ALLOWEDTOENROLL_TO_UNENROLLED
                        else:
                            state_transition = UNENROLLED_TO_UNENROLLED

                else:
                    return HttpResponseBadRequest(
                        strip_tags("Unrecognized action '{}'".format(action)))

            except ValidationError:
                # Flag this email as an error if invalid, but continue checking
                # the remaining in the list
                results.append({
                    'identifier': identifier,
                    'invalidIdentifier': True,
                })

            except Exception as exc:  # pylint: disable=broad-except
                # catch and log any exceptions
                # so that one error doesn't cause a 500.
                log.exception(u"Error while #{}ing student")
                log.exception(exc)
                results.append({
                    'identifier': identifier,
                    'error': True,
                })

            else:
                ManualEnrollmentAudit.create_manual_enrollment_audit(
                    request.user, email, state_transition, reason,
                    enrollment_obj)
                results.append({
                    'identifier': identifier,
                    'before': before.to_dict(),
                    'after': after.to_dict(),
                })

        invalid_id = []
        valid_id = []

        for result in results:
            if ('error' in result) or ('invalidIdentifier' in result):
                invalid_id.append(result['identifier'])
            else:
                valid_id.append(result['identifier'])

        invalid_message = [
            "{} 无效 <br>".format(i.encode('utf-8')) for i in invalid_id
        ]
        valid_message = []

        action = "选课" if action == "enroll" else "弃选"

        for i in valid_id:
            if action == "弃选":
                valid_message.append("{0}  {1} 成功 <br>".format(i, action))
                continue
            if email_students:
                valid_message.append("{0}  {1} 成功,并向他发送电子邮件 <br>".format(
                    i, action))
            else:
                valid_message.append("{0}  {1} 成功<br>".format(i, action))
        invalid_message.extend(valid_message)

        self.msg = "".join(invalid_message)

        context = {'msg': self.msg, 'datatable': self.make_datatable()}
        return render_to_response(self.template_name, context)
Example #10
0
def ccx_students_enrolling_center(action, identifiers, email_students,
                                  course_key, email_params, coach):
    """
    Function to enroll/add or unenroll/revoke students.

    This function exists for backwards compatibility: in CCX there are
    two different views to manage students that used to implement
    a different logic. Now the logic has been reconciled at the point that
    this function can be used by both.
    The two different views can be merged after some UI refactoring.

    Arguments:
        action (str): type of action to perform (add, Enroll, revoke, Unenroll)
        identifiers (list): list of students username/email
        email_students (bool): Flag to send an email to students
        course_key (CCXLocator): a CCX course key
        email_params (dict): dictionary of settings for the email to be sent
        coach (User): ccx coach

    Returns:
        list: list of error
    """
    errors = []

    if action == 'Enroll' or action == 'add':
        ccx_course_overview = CourseOverview.get_from_id(course_key)
        course_locator = course_key.to_course_locator()
        staff = CourseStaffRole(course_locator).users_with_role()
        admins = CourseInstructorRole(course_locator).users_with_role()

        for identifier in identifiers:
            must_enroll = False
            try:
                email, student = get_valid_student_with_email(identifier)
                if student:
                    must_enroll = student in staff or student in admins or student == coach
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue

            if CourseEnrollment.objects.is_course_full(
                    ccx_course_overview) and not must_enroll:
                error = _(
                    'The course is full: the limit is {max_student_enrollments_allowed}'
                ).format(max_student_enrollments_allowed=ccx_course_overview.
                         max_student_enrollments_allowed)
                log.info("%s", error)
                errors.append(error)
                break
            enroll_email(course_key,
                         email,
                         auto_enroll=True,
                         email_students=email_students,
                         email_params=email_params)
    elif action == 'Unenroll' or action == 'revoke':
        for identifier in identifiers:
            try:
                email, __ = get_valid_student_with_email(identifier)
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue
            unenroll_email(course_key,
                           email,
                           email_students=email_students,
                           email_params=email_params)
    return errors