Exemplo n.º 1
0
    def test_update_enrollment(self):
        # Add fake course enrollment information to the fake data API
        fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit'])
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
        get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
        self.assertEquals(result, get_result)

        result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='honor')
        self.assertEquals('honor', result['mode'])

        result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='verified')
        self.assertEquals('verified', result['mode'])
Exemplo n.º 2
0
def enroll_user_in_review_course_if_needed(user, current_course):
    '''
    If the user is not enrolled in the review version of the course,
    they are unable to see any of the problems. This ensures they
    are enrolled so they can see review problems.

    Parameters:
        user (User): the current user interacting with the review XBlock
        current_course (CourseLocator): The course the learner is currently in
    '''
    enrollment_course_id = ENROLLMENT_COURSE_MAPPING[str(current_course)]
    enrollment_status = get_enrollment(user.username, enrollment_course_id)
    if not enrollment_status:
        add_enrollment(user.username, enrollment_course_id)
    elif not enrollment_status['is_active']:
        update_enrollment(user.username, enrollment_course_id, is_active=True)
Exemplo n.º 3
0
 def post(self, request, username_or_email):
     """Allows support staff to alter a user's enrollment."""
     try:
         user = User.objects.get(Q(username=username_or_email) | Q(email=username_or_email))
         course_id = request.data['course_id']
         course_key = CourseKey.from_string(course_id)
         old_mode = request.data['old_mode']
         new_mode = request.data['new_mode']
         reason = request.data['reason']
         enrollment = CourseEnrollment.objects.get(user=user, course_id=course_key)
         if enrollment.mode != old_mode:
             return HttpResponseBadRequest(u'User {username} is not enrolled with mode {old_mode}.'.format(
                 username=user.username,
                 old_mode=old_mode
             ))
         if new_mode == CourseMode.CREDIT_MODE:
             return HttpResponseBadRequest(u'Enrollment cannot be changed to credit mode.')
     except KeyError as err:
         return HttpResponseBadRequest(u'The field {} is required.'.format(text_type(err)))
     except InvalidKeyError:
         return HttpResponseBadRequest(u'Could not parse course key.')
     except (CourseEnrollment.DoesNotExist, User.DoesNotExist):
         return HttpResponseBadRequest(
             u'Could not find enrollment for user {username} in course {course}.'.format(
                 username=username_or_email,
                 course=unicode(course_key)
             )
         )
     try:
         # Wrapped in a transaction so that we can be sure the
         # ManualEnrollmentAudit record is always created correctly.
         with transaction.atomic():
             update_enrollment(user.username, course_id, mode=new_mode, include_expired=True)
             manual_enrollment = ManualEnrollmentAudit.create_manual_enrollment_audit(
                 request.user,
                 enrollment.user.email,
                 ENROLLED_TO_ENROLLED,
                 reason=reason,
                 enrollment=enrollment
             )
             return JsonResponse(ManualEnrollmentSerializer(instance=manual_enrollment).data)
     except CourseModeNotFoundError as err:
         return HttpResponseBadRequest(text_type(err))
Exemplo n.º 4
0
    def test_send_to_track(self):
        """
        Make sure email sent to a registration track goes there.
        """
        CourseMode.objects.create(mode_slug='test', course_id=self.course.id)
        for student in self.students:
            update_enrollment(student, unicode(self.course.id), 'test')
        test_email = {
            'action': 'Send email',
            'send_to': '["track:test"]',
            'subject': 'test subject for test track',
            'message': 'test message for test track',
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        self.assertItemsEqual(
            [e.to[0] for e in mail.outbox],
            [s.email for s in self.students]
        )
Exemplo n.º 5
0
    def test_send_to_track_other_enrollments(self):
        """
        Failing test for EDUCATOR-217: verifies that emails are only sent to
        users in a specific track if they're in that track in the course the
        email is being sent from.
        """
        # Create a mode and designate an enrolled user to be placed in that mode
        CourseMode.objects.create(mode_slug='test_mode',
                                  course_id=self.course.id)
        test_mode_student = self.students[0]
        update_enrollment(test_mode_student, unicode(self.course.id),
                          'test_mode')

        # Take another user already enrolled in the course, then enroll them in
        # another course but in that same test mode
        test_mode_student_other_course = self.students[1]
        other_course = CourseFactory.create()
        CourseMode.objects.create(mode_slug='test_mode',
                                  course_id=other_course.id)
        CourseEnrollmentFactory.create(user=test_mode_student_other_course,
                                       course_id=other_course.id)
        update_enrollment(test_mode_student_other_course,
                          unicode(other_course.id), 'test_mode')

        # Send the emails...
        test_email = {
            'action': 'Send email',
            'send_to': '["track:test_mode"]',
            'subject': 'test subject for test_mode track',
            'message': 'test message for test_mode track',
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        # Only the the student in the test mode in the course the email was
        # sent from should receive an email
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].to[0], test_mode_student.email)
Exemplo n.º 6
0
    def test_send_to_track_other_enrollments(self):
        """
        Failing test for EDUCATOR-217: verifies that emails are only sent to
        users in a specific track if they're in that track in the course the
        email is being sent from.
        """
        # Create a mode and designate an enrolled user to be placed in that mode
        CourseMode.objects.create(mode_slug='test_mode', course_id=self.course.id)
        test_mode_student = self.students[0]
        update_enrollment(test_mode_student, unicode(self.course.id), 'test_mode')

        # Take another user already enrolled in the course, then enroll them in
        # another course but in that same test mode
        test_mode_student_other_course = self.students[1]
        other_course = CourseFactory.create()
        CourseMode.objects.create(mode_slug='test_mode', course_id=other_course.id)
        CourseEnrollmentFactory.create(
            user=test_mode_student_other_course,
            course_id=other_course.id
        )
        update_enrollment(test_mode_student_other_course, unicode(other_course.id), 'test_mode')

        # Send the emails...
        test_email = {
            'action': 'Send email',
            'send_to': '["track:test_mode"]',
            'subject': 'test subject for test_mode track',
            'message': 'test message for test_mode track',
        }
        response = self.client.post(self.send_mail_url, test_email)
        self.assertEquals(json.loads(response.content), self.success_content)

        # Only the the student in the test mode in the course the email was
        # sent from should receive an email
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].to[0], test_mode_student.email)
Exemplo n.º 7
0
    def assert_update_enrollment(self, mode, is_active=True, include_expired=False):
        """ Dry method for updating enrollment."""

        result = api.update_enrollment(
            self.USERNAME, self.COURSE_ID, mode=mode, is_active=is_active, include_expired=include_expired
        )
        self.assertEquals(mode, result['mode'])
        self.assertIsNotNone(result)
        self.assertEquals(result['student'], self.USERNAME)
        self.assertEquals(result['course']['course_id'], self.COURSE_ID)
        self.assertEquals(result['mode'], mode)

        if is_active:
            self.assertTrue(result['is_active'])
        else:
            self.assertFalse(result['is_active'])
Exemplo n.º 8
0
    def test_unenroll(self, course_modes, mode):
        # Add a fake course enrollment information to the fake data API
        fake_data_api.add_course(self.COURSE_ID, course_modes=course_modes)
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode=mode)
        self.assertIsNotNone(result)
        self.assertEquals(result['student'], self.USERNAME)
        self.assertEquals(result['course']['course_id'], self.COURSE_ID)
        self.assertEquals(result['mode'], mode)
        self.assertTrue(result['is_active'])

        result = api.update_enrollment(self.USERNAME, self.COURSE_ID, mode=mode, is_active=False)
        self.assertIsNotNone(result)
        self.assertEquals(result['student'], self.USERNAME)
        self.assertEquals(result['course']['course_id'], self.COURSE_ID)
        self.assertEquals(result['mode'], mode)
        self.assertFalse(result['is_active'])
Exemplo n.º 9
0
def get_course_enrollment(request, course_id=None):
    """Create, read, or update enrollment information for a student.

    HTTP Endpoint for all CRUD operations for a student course enrollment. Allows creation, reading, and
    updates of the current enrollment for a particular course.

    Args:
        request (Request): To get current course enrollment information, a GET request will return
            information for the current user and the specified course. A POST request will create a
            new course enrollment for the current user. If 'mode' or 'deactivate' are found in the
            POST parameters, the mode can be modified, or the enrollment can be deactivated.
        course_id (str): URI element specifying the course location. Enrollment information will be
            returned, created, or updated for this particular course.

    Return:
        A JSON serialized representation of the course enrollment. If this is a new or modified enrollment,
        the returned enrollment will reflect all changes.

    """
    try:
        if 'mode' in request.DATA:
            return Response(
                api.update_enrollment(request.user.username, course_id,
                                      request.DATA['mode']))
        elif 'deactivate' in request.DATA:
            return Response(
                api.deactivate_enrollment(request.user.username, course_id))
        elif course_id and request.method == 'POST':
            return Response(
                api.add_enrollment(request.user.username, course_id))
        else:
            return Response(
                api.get_enrollment(request.user.username, course_id))
    except api.CourseModeNotFoundError as error:
        return Response(status=status.HTTP_400_BAD_REQUEST, data=error.data)
    except NonExistentCourseError:
        return Response(status=status.HTTP_400_BAD_REQUEST)
    except api.EnrollmentNotFoundError:
        return Response(status=status.HTTP_400_BAD_REQUEST)
    except CourseEnrollmentException:
        return Response(status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 10
0
    def assert_update_enrollment(self,
                                 mode,
                                 is_active=True,
                                 include_expired=False):
        """ Dry method for updating enrollment."""

        result = api.update_enrollment(self.USERNAME,
                                       self.COURSE_ID,
                                       mode=mode,
                                       is_active=is_active,
                                       include_expired=include_expired)
        self.assertEquals(mode, result['mode'])
        self.assertIsNotNone(result)
        self.assertEquals(result['student'], self.USERNAME)
        self.assertEquals(result['course']['course_id'], self.COURSE_ID)
        self.assertEquals(result['mode'], mode)

        if is_active:
            self.assertTrue(result['is_active'])
        else:
            self.assertFalse(result['is_active'])
Exemplo n.º 11
0
    def test_update_enrollment_attributes(self):
        # Add fake course enrollment information to the fake data API
        fake_data_api.add_course(self.COURSE_ID, course_modes=['honor', 'verified', 'audit', 'credit'])
        # Enroll in the course and verify the URL we get sent to
        result = api.add_enrollment(self.USERNAME, self.COURSE_ID, mode='audit')
        get_result = api.get_enrollment(self.USERNAME, self.COURSE_ID)
        self.assertEquals(result, get_result)

        enrollment_attributes = [
            {
                "namespace": "credit",
                "name": "provider_id",
                "value": "hogwarts",
            }
        ]

        result = api.update_enrollment(
            self.USERNAME, self.COURSE_ID, mode='credit', enrollment_attributes=enrollment_attributes
        )
        self.assertEquals('credit', result['mode'])
        attributes = api.get_enrollment_attributes(self.USERNAME, self.COURSE_ID)
        self.assertEquals(enrollment_attributes[0], attributes[0])
Exemplo n.º 12
0
def get_course_enrollment(request, course_id=None):
    """Create, read, or update enrollment information for a student.

    HTTP Endpoint for all CRUD operations for a student course enrollment. Allows creation, reading, and
    updates of the current enrollment for a particular course.

    Args:
        request (Request): To get current course enrollment information, a GET request will return
            information for the current user and the specified course. A POST request will create a
            new course enrollment for the current user. If 'mode' or 'deactivate' are found in the
            POST parameters, the mode can be modified, or the enrollment can be deactivated.
        course_id (str): URI element specifying the course location. Enrollment information will be
            returned, created, or updated for this particular course.

    Return:
        A JSON serialized representation of the course enrollment. If this is a new or modified enrollment,
        the returned enrollment will reflect all changes.

    """
    try:
        if 'mode' in request.DATA:
            return Response(api.update_enrollment(request.user.username, course_id, request.DATA['mode']))
        elif 'deactivate' in request.DATA:
            return Response(api.deactivate_enrollment(request.user.username, course_id))
        elif course_id and request.method == 'POST':
            return Response(api.add_enrollment(request.user.username, course_id))
        else:
            return Response(api.get_enrollment(request.user.username, course_id))
    except api.CourseModeNotFoundError as error:
        return Response(status=status.HTTP_400_BAD_REQUEST, data=error.data)
    except NonExistentCourseError:
        return Response(status=status.HTTP_400_BAD_REQUEST)
    except api.EnrollmentNotFoundError:
        return Response(status=status.HTTP_400_BAD_REQUEST)
    except CourseEnrollmentException:
        return Response(status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 13
0
    def post(self, request):
        """
            Enrolls the currently logged in user in a course.
        """
        # Get the User, Course ID, and Mode from the request.
        user = request.DATA.get('user', request.user.username)

        if 'course_details' not in request.DATA or 'course_id' not in request.DATA[
                'course_details']:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"Course ID must be specified to create a new enrollment."
                })
        course_id = request.DATA['course_details']['course_id']
        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"No course '{course_id}' found for enrollment".format(
                        course_id=course_id)
                })

        mode = request.DATA.get('mode', CourseMode.HONOR)

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not user:
            user = request.user.username
        if user != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode != CourseMode.HONOR and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message":
                    u"User does not have permission to create enrollment with mode [{mode}]."
                    .format(mode=mode)
                })

        # Check whether any country access rules block the user from enrollment
        # We do this at the view level (rather than the Python API level)
        # because this check requires information about the HTTP request.
        redirect_url = embargo_api.redirect_if_blocked(
            course_id, user=user, ip_address=get_ip(request), url=request.path)
        if redirect_url:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message":
                    (u"Users from this location cannot access the course '{course_id}'."
                     ).format(course_id=course_id),
                    "user_message_url":
                    redirect_url
                })

        try:
            # Check if the user is currently enrolled, and if it is the same as the current enrolled mode. We do not
            # have to check if it is inactive or not, because if it is, we are still upgrading if the mode is different,
            # and either path will re-activate the enrollment.
            #
            # Only server-to-server calls will currently be allowed to modify the mode for existing enrollments. All
            # other requests will go through add_enrollment(), which will allow creating of new enrollments, and
            # re-activating enrollments
            enrollment = api.get_enrollment(user, unicode(course_id))
            if has_api_key_permissions and enrollment and enrollment[
                    'mode'] != mode:
                response = api.update_enrollment(user,
                                                 unicode(course_id),
                                                 mode=mode)
            else:
                response = api.add_enrollment(user,
                                              unicode(course_id),
                                              mode=mode)
            email_opt_in = request.DATA.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    (u"The course mode '{mode}' is not available for course '{course_id}'."
                     ).format(mode="honor", course_id=course_id),
                    "course_details":
                    error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    u"No course '{course_id}' found for enrollment".format(
                        course_id=course_id)
                })
        except CourseEnrollmentExistsError as error:
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message":
                    (u"An error occurred while creating the new course enrollment for user "
                     u"'{user}' in course '{course_id}'").format(
                         user=user, course_id=course_id)
                })
Exemplo n.º 14
0
    def post(self, request):
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.

        username = request.data.get('user', request.user.username)
        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.data.get('mode')

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode not in (CourseMode.AUDIT, CourseMode.HONOR, None) and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            enrollment_attributes = request.data.get('enrollment_attributes')
            enrollment = api.get_enrollment(username, unicode(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            missing_attrs = []
            if enrollment_attributes:
                actual_attrs = [
                    u"{namespace}:{name}".format(**attr)
                    for attr in enrollment_attributes
                ]
                missing_attrs = set(REQUIRED_ATTRIBUTES.get(mode, [])) - set(actual_attrs)
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                if len(missing_attrs) > 0:
                    msg = u"Missing enrollment attributes: requested mode={} required attributes={}".format(
                        mode, REQUIRED_ATTRIBUTES.get(mode)
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})

                response = api.update_enrollment(
                    username,
                    unicode(course_id),
                    mode=mode,
                    is_active=is_active,
                    enrollment_attributes=enrollment_attributes
                )
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)

            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            log.info('The user [%s] has already been enrolled in course run [%s].', username, course_id)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The [{mode}] course mode is expired or otherwise unavailable for course run [{course_id}]."
                    ).format(mode=mode, course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            log.warning('An enrollment already exists for user [%s] in course run [%s].', username, course_id)
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            log.exception("An error occurred while creating the new course enrollment for user "
                          "[%s] in course run [%s]", username, course_id)
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
        finally:
            # Assumes that the ecommerce service uses an API key to authenticate.
            if has_api_key_permissions:
                current_enrollment = api.get_enrollment(username, unicode(course_id))
                audit_log(
                    'enrollment_change_requested',
                    course_id=unicode(course_id),
                    requested_mode=mode,
                    actual_mode=current_enrollment['mode'] if current_enrollment else None,
                    requested_activation=is_active,
                    actual_activation=current_enrollment['is_active'] if current_enrollment else None,
                    user_id=user.id
                )
Exemplo n.º 15
0
    def post(self, request):
        """
            Enrolls the currently logged in user in a course.
        """
        # Get the User, Course ID, and Mode from the request.
        username = request.DATA.get('user', request.user.username)
        course_id = request.DATA.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.DATA.get('mode', CourseMode.HONOR)

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode != CourseMode.HONOR and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        # Check whether any country access rules block the user from enrollment
        # We do this at the view level (rather than the Python API level)
        # because this check requires information about the HTTP request.
        redirect_url = embargo_api.redirect_if_blocked(
            course_id, user=user, ip_address=get_ip(request), url=request.path)
        if redirect_url:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": (
                        u"Users from this location cannot access the course '{course_id}'."
                    ).format(course_id=course_id),
                    "user_message_url": request.build_absolute_uri(redirect_url)
                }
            )

        try:
            # Check if the user is currently enrolled, and if it is the same as the current enrolled mode. We do not
            # have to check if it is inactive or not, because if it is, we are still upgrading if the mode is different,
            # and either path will re-activate the enrollment.
            #
            # Only server-to-server calls will currently be allowed to modify the mode for existing enrollments. All
            # other requests will go through add_enrollment(), which will allow creating of new enrollments, and
            # re-activating enrollments
            enrollment = api.get_enrollment(username, unicode(course_id))
            if has_api_key_permissions and enrollment and enrollment['mode'] != mode:
                response = api.update_enrollment(username, unicode(course_id), mode=mode)
            else:
                response = api.add_enrollment(username, unicode(course_id), mode=mode)
            email_opt_in = request.DATA.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)
            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The course mode '{mode}' is not available for course '{course_id}'."
                    ).format(mode="honor", course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )
Exemplo n.º 16
0
 def test_unenroll_not_enrolled_in_course(self):
     # Add a fake course enrollment information to the fake data API
     fake_data_api.add_course(self.COURSE_ID, course_modes=['honor'])
     api.update_enrollment(self.USERNAME, self.COURSE_ID, mode='honor', is_active=False)
Exemplo n.º 17
0
    def post(self, request):
        """
        Enrolls the list of users in a verified course mode.
        """
        # Get the users, Course ID, and Mode from the request.

        users = request.data.get('users', [])

        if len(users) == 0:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Users must be specified to create a new enrollment."}
            )

        course_id = request.data.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)}
            )

        # use verified course mode by default
        mode = request.data.get('mode', CourseMode.VERIFIED)

        bad_users = []
        list_users = []
        for username in users:
            try:
                user = User.objects.get(username=username)
                list_users.append(user)
            except ObjectDoesNotExist:
                bad_users.append(username)

        if len(bad_users) > 0:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={'message': u'Users: {} does not exist.'.format(', '.join(bad_users))}
            )

        for user in list_users:
            embargo_response = embargo_api.get_embargo_response(request, course_key, user)

            if embargo_response:
                return embargo_response

        current_username = None
        try:
            is_active = request.data.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={'message': u"'{value}' is an invalid enrollment activation status.".format(value=is_active)}
                )

            enrollment_attributes = request.data.get('enrollment_attributes')
            errors = False
            already_paid = []  # list of users with verified enrollment
            not_enrolled = []  # list of not enrolled yet or unenrolled users
            for username in users:
                current_username = username
                enrollment = api.get_enrollment(username, unicode(course_key))
                if not enrollment:
                    not_enrolled.append(username)
                elif enrollment['is_active'] is not True:
                    not_enrolled.append(username)
                elif enrollment['mode'] == CourseMode.VERIFIED:
                    already_paid.append(username)
            msg_paid = u""
            msg_not_enrolled = u""
            if len(already_paid) > 0:
                msg_paid = u'Users: {} already paid for course.'.format(', '.join(already_paid))
                errors = True
            if len(not_enrolled) > 0:
                msg_not_enrolled = u'Users: {} not enrolled for course.'.format(', '.join(not_enrolled))
                errors = True
            if errors:
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"message": (u"'{course_id}'\n:{msg_paid}\n{msg_not_enrolled}").format(
                        course_id=course_id,
                        msg_paid=msg_paid,
                        msg_not_enrolled=msg_not_enrolled
                    ),
                    })

            # update for cohorts
            cohort_exists = is_cohort_exists(course_key, VERIFIED)
            if not cohort_exists:
                cohort = add_cohort(course_key, VERIFIED, 'manual')
            else:
                cohort = get_cohort_by_name(course_key, VERIFIED)

            for username in users:
                current_username = username
                api.update_enrollment(username, unicode(course_key), mode=mode, is_active=is_active)
                user = User.objects.get(username=username)
                course_cohorts = CourseUserGroup.objects.filter(
                    course_id=cohort.course_id,
                    users__id=user.id,
                    group_type=CourseUserGroup.COHORT
                )

                add_user_into_verified_cohort(course_cohorts, cohort, user)

            email_opt_in = request.data.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_key.org
                for username in users:
                    update_email_opt_in(username, org, email_opt_in)

            return Response(
                status=status.HTTP_200_OK,
                data={
                    "message": u"Success for course '{course_id}'.".format(course_id=course_id)
                })
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The course mode '{mode}' is not available for course '{course_id}'."
                    ).format(mode="verified", course_id=course_id),
                    "course_details": error.data
                })
        except CourseEnrollmentExistsError as error:
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=current_username, course_id=course_id)
                }
            )
Exemplo n.º 18
0
    def post(self, request):
        """
        Enrolls the list of users in a verified course mode.
        """
        # Get the users, Course ID, and Mode from the request.

        users = request.DATA.get('users', [])

        if len(users) == 0:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Users must be specified to create a new enrollment."}
            )

        course_id = request.DATA.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        # use verified course mode by default
        mode = request.DATA.get('mode', CourseMode.VERIFIED)

        bad_users = []
        list_users = []
        for username in users:
            try:
                user = User.objects.get(username=username)
                list_users.append(user)
            except ObjectDoesNotExist:
                bad_users.append(username)

        if len(bad_users) > 0:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={'message': u'Users: {} does not exist.'.format(', '.join(bad_users))}
            )

        for user in list_users:
            embargo_response = embargo_api.get_embargo_response(request, course_id, user)

            if embargo_response:
                return embargo_response

        current_username = None
        try:
            is_active = request.DATA.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            enrollment_attributes = request.DATA.get('enrollment_attributes')
            errors = False
            already_paid = []  # list of users with verified enrollment
            not_enrolled = []  # list of not enrolled yet or unenrolled users
            for username in users:
                current_username = username
                enrollment = api.get_enrollment(username, unicode(course_id))
                if not enrollment:
                    not_enrolled.append(username)
                elif enrollment['is_active'] is not True:
                    not_enrolled.append(username)
                elif enrollment['mode'] == CourseMode.VERIFIED:
                    already_paid.append(username)
            msg_paid = u""
            msg_not_enrolled = u""
            if len(already_paid) > 0:
                msg_paid = u'Users: {} already paid for course.'.format(', '.join(already_paid))
                errors = True
            if len(not_enrolled) > 0:
                msg_not_enrolled = u'Users: {} not enrolled for course.'.format(', '.join(not_enrolled))
                errors = True
            if errors:
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"message": (u"'{course_id}'\n:{msg_paid}\n{msg_not_enrolled}").format(
                        course_id=course_id,
                        msg_paid=msg_paid,
                        msg_not_enrolled=msg_not_enrolled
                    ),
                    })

            for username in users:
                current_username = username
                response = api.update_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)

            email_opt_in = request.DATA.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                for username in users:
                    update_email_opt_in(username, org, email_opt_in)

            return Response(
                status=status.HTTP_200_OK,
                data={
                    "message": u"Success for course '{course_id}'.".format(course_id=course_id)
                })
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The course mode '{mode}' is not available for course '{course_id}'."
                    ).format(mode="verified", course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=current_username, course_id=course_id)
                }
            )
Exemplo n.º 19
0
    def post(self, request):
        """Enrolls the currently logged-in user in a course.

        Server-to-server calls may deactivate or modify the mode of existing enrollments. All other requests
        go through `add_enrollment()`, which allows creation of new and reactivation of old enrollments.
        """
        # Get the User, Course ID, and Mode from the request.
        username = request.DATA.get('user', request.user.username)
        course_id = request.DATA.get('course_details', {}).get('course_id')

        if not course_id:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"message": u"Course ID must be specified to create a new enrollment."}
            )

        try:
            course_id = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )

        mode = request.DATA.get('mode', CourseMode.HONOR)

        has_api_key_permissions = self.has_api_key_permissions(request)

        # Check that the user specified is either the same user, or this is a server-to-server request.
        if not username:
            username = request.user.username
        if username != request.user.username and not has_api_key_permissions:
            # Return a 404 instead of a 403 (Unauthorized). If one user is looking up
            # other users, do not let them deduce the existence of an enrollment.
            return Response(status=status.HTTP_404_NOT_FOUND)

        if mode != CourseMode.HONOR and not has_api_key_permissions:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={
                    "message": u"User does not have permission to create enrollment with mode [{mode}].".format(
                        mode=mode
                    )
                }
            )

        try:
            # Lookup the user, instead of using request.user, since request.user may not match the username POSTed.
            user = User.objects.get(username=username)
        except ObjectDoesNotExist:
            return Response(
                status=status.HTTP_406_NOT_ACCEPTABLE,
                data={
                    'message': u'The user {} does not exist.'.format(username)
                }
            )

        embargo_response = embargo_api.get_embargo_response(request, course_id, user)

        if embargo_response:
            return embargo_response

        try:
            is_active = request.DATA.get('is_active')
            # Check if the requested activation status is None or a Boolean
            if is_active is not None and not isinstance(is_active, bool):
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={
                        'message': (u"'{value}' is an invalid enrollment activation status.").format(value=is_active)
                    }
                )

            enrollment = api.get_enrollment(username, unicode(course_id))
            mode_changed = enrollment and mode is not None and enrollment['mode'] != mode
            active_changed = enrollment and is_active is not None and enrollment['is_active'] != is_active
            if has_api_key_permissions and (mode_changed or active_changed):
                if mode_changed and active_changed and not is_active:
                    # if the requester wanted to deactivate but specified the wrong mode, fail
                    # the request (on the assumption that the requester had outdated information
                    # about the currently active enrollment).
                    msg = u"Enrollment mode mismatch: active mode={}, requested mode={}. Won't deactivate.".format(
                        enrollment["mode"], mode
                    )
                    log.warning(msg)
                    return Response(status=status.HTTP_400_BAD_REQUEST, data={"message": msg})
                response = api.update_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)
            else:
                # Will reactivate inactive enrollments.
                response = api.add_enrollment(username, unicode(course_id), mode=mode, is_active=is_active)

            email_opt_in = request.DATA.get('email_opt_in', None)
            if email_opt_in is not None:
                org = course_id.org
                update_email_opt_in(request.user, org, email_opt_in)

            return Response(response)
        except CourseModeNotFoundError as error:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"The course mode '{mode}' is not available for course '{course_id}'."
                    ).format(mode="honor", course_id=course_id),
                    "course_details": error.data
                })
        except CourseNotFoundError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": u"No course '{course_id}' found for enrollment".format(course_id=course_id)
                }
            )
        except CourseEnrollmentExistsError as error:
            return Response(data=error.enrollment)
        except CourseEnrollmentError:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={
                    "message": (
                        u"An error occurred while creating the new course enrollment for user "
                        u"'{username}' in course '{course_id}'"
                    ).format(username=username, course_id=course_id)
                }
            )