Example #1
0
def ccx_invite(request, course, ccx=None):
    """
    Invite users to new ccx
    """
    if not ccx:
        raise Http404

    action = request.POST.get("enrollment-button")
    identifiers_raw = request.POST.get("student-ids")
    identifiers = _split_input_list(identifiers_raw)
    auto_enroll = True if "auto-enroll" in request.POST else False
    email_students = True if "email-students" in request.POST else False
    for identifier in identifiers:
        user = None
        email = None
        try:
            user = get_student_from_identifier(identifier)
        except User.DoesNotExist:
            email = identifier
        else:
            email = user.email
        try:
            validate_email(email)
            course_key = CCXLocator.from_course_locator(course.id, ccx.id)
            email_params = get_email_params(course, auto_enroll, course_key=course_key, display_name=ccx.display_name)
            if action == "Enroll":
                enroll_email(
                    course_key, email, auto_enroll=auto_enroll, email_students=email_students, email_params=email_params
                )
            if action == "Unenroll":
                unenroll_email(course_key, email, email_students=email_students, email_params=email_params)
        except ValidationError:
            log.info("Invalid user name or email when trying to invite students: %s", email)
    url = reverse("ccx_coach_dashboard", kwargs={"course_id": CCXLocator.from_course_locator(course.id, ccx.id)})
    return redirect(url)
Example #2
0
def ccx_student_management(request, course, ccx=None):
    """Manage the enrollment of individual students in a CCX
    """
    if not ccx:
        raise Http404

    action = request.POST.get('student-action', None)
    student_id = request.POST.get('student-id', '')
    user = email = None
    try:
        user = get_student_from_identifier(student_id)
    except User.DoesNotExist:
        email = student_id
    else:
        email = user.email

    course_key = CCXLocator.from_course_locator(course.id, ccx.id)
    try:
        validate_email(email)
        if action == 'add':
            # by decree, no emails sent to students added this way
            # by decree, any students added this way are auto_enrolled
            enroll_email(course_key, email, auto_enroll=True, email_students=False)
        elif action == 'revoke':
            unenroll_email(course_key, email, email_students=False)
    except ValidationError:
        log.info('Invalid user name or email when trying to enroll student: %s', email)

    url = reverse(
        'ccx_coach_dashboard',
        kwargs={'course_id': course_key}
    )
    return redirect(url)
Example #3
0
def ccx_student_management(request, course, ccx=None):
    """Manage the enrollment of individual students in a CCX
    """
    if not ccx:
        raise Http404

    action = request.POST.get("student-action", None)
    student_id = request.POST.get("student-id", "")
    user = email = None
    error_message = ""
    course_key = CCXLocator.from_course_locator(course.id, ccx.id)
    try:
        user = get_student_from_identifier(student_id)
    except User.DoesNotExist:
        email = student_id
        error_message = validate_student_email(email)
        if email and not error_message:
            error_message = _('Could not find a user with name or email "{email}" ').format(email=email)
    else:
        email = user.email
        error_message = validate_student_email(email)

    if error_message is None:
        if action == "add":
            # by decree, no emails sent to students added this way
            # by decree, any students added this way are auto_enrolled
            enroll_email(course_key, email, auto_enroll=True, email_students=False)
        elif action == "revoke":
            unenroll_email(course_key, email, email_students=False)
    else:
        messages.error(request, error_message)

    url = reverse("ccx_coach_dashboard", kwargs={"course_id": course_key})
    return redirect(url)
Example #4
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 #5
0
def _ccx_students_enrrolling_center(action, identifiers, email_students, course_key, email_params):
    """
    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

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

    if action == "Enroll" or action == "add":
        ccx_course_overview = CourseOverview.get_from_id(course_key)
        for identifier in identifiers:
            if CourseEnrollment.objects.is_course_full(ccx_course_overview):
                error = "The course is full: the limit is {0}".format(
                    ccx_course_overview.max_student_enrollments_allowed
                )
                log.info("%s", error)
                errors.append(error)
                break
            try:
                email = get_valid_student_email(identifier)
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue
            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_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 #6
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 #7
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,
                )
    def test_unenroll_notenrolled(self):
        before_ideal = SettableEnrollmentState(user=True,
                                               enrollment=False,
                                               allowed=False,
                                               auto_enroll=False)

        after_ideal = SettableEnrollmentState(user=True,
                                              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 reverse_add_master_course_staff_to_ccx(master_course, ccx_key, display_name):
    """
    Remove staff of ccx.

    Arguments:
        master_course (CourseDescriptorWithMixins): Master course instance
        ccx_key (CCXLocator): CCX course key
        display_name (str): ccx display name for email
    """
    list_staff = list_with_level(master_course, 'staff')
    list_instructor = list_with_level(master_course, 'instructor')

    with ccx_course(ccx_key) as course_ccx:
        email_params = get_email_params(course_ccx, auto_enroll=True, course_key=ccx_key, display_name=display_name)
        for staff in list_staff:
            # allow 'staff' access on ccx to staff of master course
            revoke_access(course_ccx, staff, 'staff')

            # Enroll the staff in the ccx
            unenroll_email(
                course_id=ccx_key,
                student_email=staff.email,
                email_students=True,
                email_params=email_params,
            )

        for instructor in list_instructor:
            # allow 'instructor' access on ccx to instructor of master course
            revoke_access(course_ccx, instructor, 'instructor')

            # Enroll the instructor in the ccx
            unenroll_email(
                course_id=ccx_key,
                student_email=instructor.email,
                email_students=True,
                email_params=email_params,
            )
Example #10
0
def ccx_student_management(request, course, ccx=None):
    """Manage the enrollment of individual students in a CCX
    """
    if not ccx:
        raise Http404

    action = request.POST.get('student-action', None)
    student_id = request.POST.get('student-id', '')
    user = email = None
    error_message = ""
    course_key = CCXLocator.from_course_locator(course.id, ccx.id)
    try:
        user = get_student_from_identifier(student_id)
    except User.DoesNotExist:
        email = student_id
        error_message = validate_student_email(email)
        if email and not error_message:
            error_message = _(
                'Could not find a user with name or email "{email}" '
            ).format(email=email)
    else:
        email = user.email
        error_message = validate_student_email(email)

    if error_message is None:
        if action == 'add':
            # by decree, no emails sent to students added this way
            # by decree, any students added this way are auto_enrolled
            enroll_email(course_key, email, auto_enroll=True, email_students=False)
        elif action == 'revoke':
            unenroll_email(course_key, email, email_students=False)
    else:
        messages.error(request, error_message)

    url = reverse('ccx_coach_dashboard', kwargs={'course_id': course_key})
    return redirect(url)
Example #11
0
def students_update_enrollment(request, course_id):
    """
    Enroll or unenroll students by email.
    Requires staff access.

    Query Parameters:
    - action in ['enroll', 'unenroll']
    - identifiers is string containing a list of emails and/or usernames separated by anything split_input_list can handle.
    - auto_enroll is a boolean (defaults to false)
        If auto_enroll is false, students will be allowed to enroll.
        If auto_enroll is true, students will be enrolled as soon as they register.
    - email_students is a boolean (defaults to false)
        If email_students is true, students will be sent email notification
        If email_students is false, students will not be sent email notification

    Returns an analog to this JSON structure: {
        "action": "enroll",
        "auto_enroll": false,
        "results": [
            {
                "email": "*****@*****.**",
                "before": {
                    "enrollment": false,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                },
                "after": {
                    "enrollment": true,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                }
            }
        ]
    }
    """
    course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)

    action = request.GET.get('action')
    identifiers_raw = request.GET.get('identifiers')
    identifiers = _split_input_list(identifiers_raw)
    auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]
    email_students = request.GET.get('email_students') in ['true', 'True', True]

    email_params = {}
    if email_students:
        course = get_course_by_id(course_id)
        email_params = get_email_params(course, auto_enroll)

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

        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 = enroll_email(course_id, email, auto_enroll, email_students, email_params)
            elif action == 'unenroll':
                before, after = unenroll_email(course_id, email, email_students, email_params)
            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=W0703
            # catch and log any exceptions
            # so that one error doesn't cause a 500.
            log.exception("Error while #{}ing student")
            log.exception(exc)
            results.append({
                'identifier': identifier,
                'error': True,
            })

        else:
            results.append({
                'identifier': identifier,
                'before': before.to_dict(),
                'after': after.to_dict(),
            })

    response_payload = {
        'action': action,
        'results': results,
        'auto_enroll': auto_enroll,
    }
    return JsonResponse(response_payload)
Example #12
0
def students_update_enrollment(request, course_id):
    """
Enroll or unenroll students by email.
Requires staff access.

Query Parameters:
- action in ['enroll', 'unenroll']
- emails is string containing a list of emails separated by anything split_input_list can handle.
- auto_enroll is a boolean (defaults to false)
If auto_enroll is false, students will be allowed to enroll.
If auto_enroll is true, students will be enrolled as soon as they register.
- email_students is a boolean (defaults to false)
If email_students is true, students will be sent email notification
If email_students is false, students will not be sent email notification

Returns an analog to this JSON structure: {
"action": "enroll",
"auto_enroll": false,
"results": [
{
"email": "*****@*****.**",
"before": {
"enrollment": false,
"auto_enroll": false,
"user": true,
"allowed": false
},
"after": {
"enrollment": true,
"auto_enroll": false,
"user": true,
"allowed": false
}
}
]
}
"""

    action = request.GET.get('action')
    emails_raw = request.GET.get('emails')
    emails = _split_input_list(emails_raw)
    auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]
    email_students = request.GET.get('email_students') in ['true', 'True', True]

    email_params = {}
    if email_students:
        course = get_course_by_id(course_id)
        email_params = get_email_params(course, auto_enroll)

    results = []
    for email in emails:
        try:
            if action == 'enroll':
                before, after = enroll_email(course_id, email, auto_enroll, email_students, email_params)
            elif action == 'unenroll':
                before, after = unenroll_email(course_id, email, email_students, email_params)
            else:
                return HttpResponseBadRequest("Unrecognized action '{}'".format(action))

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

    response_payload = {
        'action': action,
        'results': results,
        'auto_enroll': auto_enroll,
    }
    return JsonResponse(response_payload)
Example #13
0
def students_update_enrollment(request, course_id):
    """
    Enroll or unenroll students by email.
    Requires staff access.

    Query Parameters:
    - action in ['enroll', 'unenroll']
    - emails is string containing a list of emails separated by anything split_input_list can handle.
    - auto_enroll is a boolean (defaults to false)
        If auto_enroll is false, students will be allowed to enroll.
        If auto_enroll is true, students will be enroled as soon as they register.

    Returns an analog to this JSON structure: {
        "action": "enroll",
        "auto_enroll": false,
        "results": [
            {
                "email": "*****@*****.**",
                "before": {
                    "enrollment": false,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                },
                "after": {
                    "enrollment": true,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                }
            }
        ]
    }
    """
    action = request.GET.get('action')
    emails_raw = request.GET.get('emails')
    emails = _split_input_list(emails_raw)
    auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]

    results = []
    for email in emails:
        try:
            if action == 'enroll':
                before, after = enroll_email(course_id, email, auto_enroll)
            elif action == 'unenroll':
                before, after = unenroll_email(course_id, email)
            else:
                return HttpResponseBadRequest(
                    "Unrecognized action '{}'".format(action))

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

    response_payload = {
        'action': action,
        'results': results,
        'auto_enroll': auto_enroll,
    }
    return JsonResponse(response_payload)
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 #15
0
def students_update_enrollment(request, course_id):
    """
    Enroll or unenroll students by email.
    Requires staff access.

    Query Parameters:
    - action in ['enroll', 'unenroll']
    - identifiers is string containing a list of emails and/or usernames separated by anything split_input_list can handle.
    - auto_enroll is a boolean (defaults to false)
        If auto_enroll is false, students will be allowed to enroll.
        If auto_enroll is true, students will be enrolled as soon as they register.
    - email_students is a boolean (defaults to false)
        If email_students is true, students will be sent email notification
        If email_students is false, students will not be sent email notification

    Returns an analog to this JSON structure: {
        "action": "enroll",
        "auto_enroll": false,
        "results": [
            {
                "email": "*****@*****.**",
                "before": {
                    "enrollment": false,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                },
                "after": {
                    "enrollment": true,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                }
            }
        ]
    }
    """

    action = request.GET.get('action')
    identifiers_raw = request.GET.get('identifiers')
    identifiers = _split_input_list(identifiers_raw)
    auto_enroll = request.GET.get('auto_enroll') in ['true', 'True', True]
    email_students = request.GET.get('email_students') in [
        'true', 'True', True
    ]

    email_params = {}
    if email_students:
        course = get_course_by_id(course_id)
        email_params = get_email_params(course, auto_enroll)

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

        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 = enroll_email(course_id, email, auto_enroll,
                                             email_students, email_params)
            elif action == 'unenroll':
                before, after = unenroll_email(course_id, email,
                                               email_students, email_params)
            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=W0703
            # catch and log any exceptions
            # so that one error doesn't cause a 500.
            log.exception("Error while #{}ing student")
            log.exception(exc)
            results.append({
                'identifier': identifier,
                'error': True,
            })

        else:
            results.append({
                'identifier': identifier,
                'before': before.to_dict(),
                'after': after.to_dict(),
            })

    response_payload = {
        'action': action,
        'results': results,
        'auto_enroll': auto_enroll,
    }
    return JsonResponse(response_payload)
Example #16
0
def students_update_enrollment(request, course_id):
    """
    Enroll or unenroll students by email.
    Requires staff access.

    Query Parameters:
    - action in ['enroll', 'unenroll']
    - emails is string containing a list of emails separated by anything split_input_list can handle.
    - auto_enroll is a boolean (defaults to false)
        If auto_enroll is false, students will be allowed to enroll.
        If auto_enroll is true, students will be enrolled as soon as they register.
    - email_students is a boolean (defaults to false)
        If email_students is true, students will be sent email notification
        If email_students is false, students will not be sent email notification

    Returns an analog to this JSON structure: {
        "action": "enroll",
        "auto_enroll": false,
        "results": [
            {
                "email": "*****@*****.**",
                "before": {
                    "enrollment": false,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                },
                "after": {
                    "enrollment": true,
                    "auto_enroll": false,
                    "user": true,
                    "allowed": false
                }
            }
        ]
    }
    """

    action = request.GET.get("action")
    emails_raw = request.GET.get("emails")
    emails = _split_input_list(emails_raw)
    auto_enroll = request.GET.get("auto_enroll") in ["true", "True", True]
    email_students = request.GET.get("email_students") in ["true", "True", True]

    email_params = {}
    if email_students:
        course = get_course_by_id(course_id)
        email_params = get_email_params(course, auto_enroll)

    results = []
    for email in emails:
        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)
        except ValidationError:
            # Flag this email as an error if invalid, but continue checking
            # the remaining in the list
            results.append({"email": email, "error": True, "invalidEmail": True})
            continue

        try:
            if action == "enroll":
                before, after = enroll_email(course_id, email, auto_enroll, email_students, email_params)
            elif action == "unenroll":
                before, after = unenroll_email(course_id, email, email_students, email_params)
            else:
                return HttpResponseBadRequest(strip_tags("Unrecognized action '{}'".format(action)))

            results.append({"email": email, "before": before.to_dict(), "after": after.to_dict()})
        # catch and log any exceptions
        # so that one error doesn't cause a 500.
        except Exception as exc:  # pylint: disable=W0703
            log.exception("Error while #{}ing student")
            log.exception(exc)
            results.append({"email": email, "error": True, "invalidEmail": False})

    response_payload = {"action": action, "results": results, "auto_enroll": auto_enroll}
    return JsonResponse(response_payload)