Exemple #1
0
def test_enrollments():
    """
    Enrolls the user in a course and then pulls down the enrollments for the user.
    This assumes that the course in the edX instance is available for enrollment.
    """
    api = EdxApi({
        'access_token': ACCESS_TOKEN,
        'api_key': API_KEY
    },
                 base_url=BASE_URL)

    # the enrollment will be done manually until
    # a client to enroll the student is implemented
    requester = api.get_requester()
    requester.post(urljoin(BASE_URL, '/api/enrollment/v1/enrollment'),
                   json={
                       "course_details": {
                           "course_id": 'course-v1:edX+DemoX+Demo_Course'
                       }
                   })

    enrollments = api.enrollments.get_student_enrollments()

    enrolled_courses = [
        enrolled_course.course_details.course_id
        for enrolled_course in enrollments.enrolled_courses
    ]

    assert 'course-v1:edX+DemoX+Demo_Course' in enrolled_courses
Exemple #2
0
def test_get_certificate_404_error():
    """
    Asserts that a 404 returned from EDX will be silenced for get_student_certificates
    """

    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    username = '******'
    course_key = 'course-v1:edX+DemoX+Demo_Course'

    certificates = api.certificates
    old_requester_get = certificates.requester.get

    def mocked_get(url, *args, **kwargs):
        """
        Return an error for specific URLs
        """
        if '/api/certificates/v0/certificates/' in url:
            return FakeErroredResponse(status_code=404)
        return old_requester_get(url, *args, **kwargs)

    with patch.object(certificates.requester, 'get', autospec=True) as get:
        get.side_effect = mocked_get
        with pytest.raises(HTTPError):
            certificates.get_student_certificate(username, course_key)

        # Note no error here, just empty list
        certs = certificates.get_student_certificates(username)
        assert not certs.all_courses_certs
Exemple #3
0
def test_course_structure():
    """
    Pull down the course structure and validate it has the correct entries.
    This test assumes that the used user can access the course.
    """
    api = EdxApi({
        'access_token': ACCESS_TOKEN,
        'api_key': API_KEY
    },
                 base_url=BASE_URL)
    structure = api.course_structure.course_blocks(
        'course-v1:edX+DemoX+Demo_Course', 'staff')

    titles = [
        block.title for block in structure.root.children if block.visible
    ]

    assert titles == [
        'Introduction',
        'Example Week 1: Getting Started',
        'Example Week 2: Get Interactive',
        'Example Week 3: Be Social',
        'About Exams and Certificates',
        'holding section',
    ]
Exemple #4
0
 def setUpClass(cls):
     edx_base_url = "http://edx.example.com"
     cls.api_url = urljoin(edx_base_url, EmailSettings.api_url)
     cls.client = EdxApi({"access_token": "foobar"}, cls.api_url)
     cls.email_settings = cls.client.email_settings
     cls.json = {"success": "true"}
     cls.course_id = "course_id"
    def get(self, request, username, *args, **kargs):  # pylint: disable=unused-argument
        """
        Returns information needed to display the user
        dashboard for all the programs the user is enrolled in.
        """
        user = get_object_or_404(User,
                                 social_auth__uid=username,
                                 social_auth__provider=EdxOrgOAuth2.name)

        # get the credentials for the current user for edX
        edx_client = None
        if user == request.user:
            user_social = get_social_auth(request.user)
            try:
                utils.refresh_user_token(user_social)
            except utils.InvalidCredentialStored as exc:
                return Response(status=exc.http_status_code,
                                data={'error': str(exc)})
            except:  # pylint: disable=bare-except
                log.exception(
                    'Impossible to refresh user credentials in dashboard view')
            # create an instance of the client to query edX
            edx_client = EdxApi(user_social.extra_data,
                                settings.EDXORG_BASE_URL)

        try:
            program_dashboard = get_user_program_info(user, edx_client)
        except utils.InvalidCredentialStored as exc:
            log.exception(
                'Access token for user %s is fresh but invalid; forcing login.',
                user.username)
            return Response(status=exc.http_status_code,
                            data={'error': str(exc)})
        return Response(status=status.HTTP_200_OK, data=program_dashboard)
Exemple #6
0
    def get(self, request, *args, **kargs):  # pylint: disable=unused-argument, no-self-use
        """
        Returns information needed to display the user dashboard for a program.
        """

        # get the credentials for the current user for edX
        user_social = request.user.social_auth.get(provider=EdxOrgOAuth2.name)
        try:
            utils.refresh_user_token(user_social)
        except utils.InvalidCredentialStored as exc:
            return Response(
                status=exc.http_status_code,
                data={'error': str(exc)}
            )

        # create an instance of the client to query edX
        edx_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL)
        # get an enrollments client for the student
        enrollments = get_student_enrollments(request.user, edx_client)
        # get a certificates client for the student
        certificates = get_student_certificates(request.user, edx_client)

        response_data = []
        for program in Program.objects.filter(live=True):
            response_data.append(get_info_for_program(program, enrollments, certificates))
        return Response(response_data)
Exemple #7
0
    def post(self, request):
        """
        Audit enrolls the user in a course in edx
        """
        course_id = request.data.get('course_id')
        if course_id is None:
            raise ValidationError('course id missing in the request')
        # get the credentials for the current user for edX
        user_social = get_social_auth(request.user)
        try:
            utils.refresh_user_token(user_social)
        except utils.InvalidCredentialStored as exc:
            log.error(
                "Error while refreshing credentials for user %s",
                get_social_username(request.user),
            )
            return Response(
                status=exc.http_status_code,
                data={'error': str(exc)}
            )

        # create an instance of the client to query edX
        edx_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL)

        try:
            enrollment = edx_client.enrollments.create_audit_student_enrollment(course_id)
        except HTTPError as exc:
            if exc.response.status_code == status.HTTP_400_BAD_REQUEST:
                raise PossiblyImproperlyConfigured(
                    'Got a 400 status code from edX server while trying to create '
                    'audit enrollment. This might happen if the course is improperly '
                    'configured on MicroMasters. Course key '
                    '{course_key}, edX user "{edX_user}"'.format(
                        edX_user=get_social_username(request.user),
                        course_key=course_id,
                    )
                )
            log.error(
                "Http error from edX while creating audit enrollment for course key %s for edX user %s",
                course_id,
                get_social_username(request.user),
            )
            return Response(
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                data={'error': str(exc)}
            )
        except Exception as exc:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for edX user %s",
                course_id,
                get_social_username(request.user),
            )
            return Response(
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
                data={'error': str(exc)}
            )
        CachedEdxDataApi.update_cached_enrollment(request.user, enrollment, enrollment.course_id, index_user=True)
        return Response(
            data=enrollment.json
        )
Exemple #8
0
    def setUpClass(cls):
        with open(
                os.path.join(os.path.dirname(__file__),
                             'fixtures/user_enrollments.json')) as file_obj:
            cls.enrollments_json = json.loads(file_obj.read())

        with open(
                os.path.join(os.path.dirname(__file__),
                             'fixtures/enrollments_list.json')) as file_obj:
            cls.enrollments_list_json = json.loads(file_obj.read())

        cls.enrollment_responses = [
            {
                'json': cls.enrollments_json[0],
                'status_code': 200
            },
            {
                'json': cls.enrollments_json[1],
                'status_code': 200
            },
        ]

        base_edx_url = 'http://edx.example.com'
        cls.enrollment_url = urljoin(base_edx_url,
                                     CourseEnrollments.enrollment_url)
        cls.client = EdxApi({'access_token': 'foobar'}, cls.enrollment_url)
        cls.enrollment_client = cls.client.enrollments
Exemple #9
0
def get_edx_api_client(user,
                       ttl_in_seconds=OPENEDX_AUTH_DEFAULT_TTL_IN_SECONDS):
    """
    Gets an edx api client instance for the user

    Args:
        user (users.models.User): A user object
        ttl_in_seconds (int): number of seconds the auth credentials for this client should still be valid

    Returns:
         EdxApi: edx api client instance
    """
    try:
        auth = get_valid_edx_api_auth(user, ttl_in_seconds=ttl_in_seconds)
    except OpenEdxApiAuth.DoesNotExist:
        raise NoEdxApiAuthError(
            "{} does not have an associated OpenEdxApiAuth".format(str(user)))
    return EdxApi(
        {
            "access_token": auth.access_token,
            "api_key": settings.OPENEDX_API_KEY
        },
        settings.OPENEDX_API_BASE_URL,
        timeout=settings.EDX_API_CLIENT_TIMEOUT,
    )
Exemple #10
0
def test_user_name_update():
    """
    Asserts that update user's name api updates the full name of the user correctly.
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    user_name = 'Test Name'
    updated_user = api.user_info.update_user_name('staff', user_name)
    assert updated_user.username == 'staff'
    assert updated_user.name == user_name
Exemple #11
0
def test_user_info_timeout():
    """
    assert timeout exception on user_info
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    user_info = api.user_info
    with patch.object(user_info.requester, 'get', autospec=True) as get:
        get.side_effect = mocked_timeout
        with pytest.raises(Timeout):
            user_info.get_user_info()
Exemple #12
0
def test_course_details():
    """
    Pull down the course details.
    This test assumes that the used user is not anonymous.
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    details = api.course_detail.get_detail('course-v1:edX+DemoX+Demo_Course')

    assert details.course_id == "course-v1:edX+DemoX+Demo_Course"
    assert details.name == "edX Demonstration Course"
Exemple #13
0
def test_user_info():
    """
    Gets information about the logged in user
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    info = api.user_info.get_user_info()
    assert info.username == 'staff'
    assert info.name == ''
    assert info.email == '*****@*****.**'
    assert isinstance(info.user_id, int)
Exemple #14
0
def enroll_user_on_success(order):
    """
    Enroll user after they made a successful purchase.

    Args:
        order (Order): An order to be fulfilled

    Returns:
         None
    """
    user_social = get_social_auth(order.user)
    enrollments_client = EdxApi(user_social.extra_data,
                                settings.EDXORG_BASE_URL).enrollments
    existing_enrollments = enrollments_client.get_student_enrollments()

    exceptions = []
    enrollments = []
    for line in order.line_set.all():
        course_key = line.course_key
        try:
            if not existing_enrollments.is_enrolled_in(course_key):
                enrollments.append(
                    enrollments_client.create_audit_student_enrollment(
                        course_key))
        except Exception as ex:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for user %s",
                course_key,
                get_social_username(order.user),
            )
            exceptions.append(ex)

    for enrollment in enrollments:
        CachedEdxDataApi.update_cached_enrollment(
            order.user,
            enrollment,
            enrollment.course_id,
            index_user=True,
        )

    if exceptions:
        raise EcommerceEdxApiException(exceptions)
Exemple #15
0
def test_get_current_grade_timeout():
    """
    assert timeout exception on get current grade.
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    current_grades = api.current_grades
    with patch.object(current_grades.requester, 'get', autospec=True) as get:
        get.side_effect = mocked_timeout
        with pytest.raises(Timeout):
            current_grades.get_student_current_grade(
                'staff', 'course-v1:edX+DemoX+Demo_Course')
Exemple #16
0
def test_course_details_timeout():
    """
    assert timeout exception on get course_detail
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    course_detail = api.course_detail

    with patch.object(course_detail._requester, 'get', autospec=True) as get:  # pylint: disable=protected-access
        get.side_effect = mocked_timeout
        with pytest.raises(Timeout):
            course_detail.get_detail('course-v1:edX+DemoX+Demo_Course')
Exemple #17
0
def test_create_ccx():
    """
    Creates a CCX for the demo course. This course *MUST* have ccx enabled in
    the advanced settings.
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    ccx_id = api.ccx.create(
        'course-v1:edX+DemoX+Demo_Course', '*****@*****.**', 100,
        'My CCX from test_create_ccx integration test via edx-api-client.')

    assert ccx_id is not None
    assert '@' in ccx_id  # follows ccx format
Exemple #18
0
def enroll_user_on_success(order):
    """
    Enroll user after they made a successful purchase.

    Args:
        order (Order): An order to be fulfilled

    Returns:
         None
    """
    user_social = get_social_auth(order.user)
    enrollments_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL).enrollments
    existing_enrollments = enrollments_client.get_student_enrollments()

    exceptions = []
    enrollments = []
    for line in order.line_set.all():
        course_key = line.course_key
        try:
            if not existing_enrollments.is_enrolled_in(course_key):
                enrollments.append(enrollments_client.create_audit_student_enrollment(course_key))
        except Exception as ex:  # pylint: disable=broad-except
            log.exception(
                "Error creating audit enrollment for course key %s for user %s",
                course_key,
                get_social_username(order.user),
            )
            exceptions.append(ex)

    for enrollment in enrollments:
        CachedEdxDataApi.update_cached_enrollment(
            order.user,
            enrollment,
            enrollment.course_id,
            index_user=True,
        )

    if exceptions:
        raise EcommerceEdxApiException(exceptions)
Exemple #19
0
    def setUp(self):
        super(GradesApiTestCase, self).setUp()

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    "../enrollments/fixtures/user_enrollments.json",
                )) as file:  # pylint: disable=redefined-builtin
            self.enrollment_data = json.load(file)
        self.enrollment_url = urljoin(
            "https://edx.example.com",
            enrollments.CourseEnrollments.enrollment_url)
        self.client = EdxApi({"access_token": "opensesame"},
                             "https://edx.example.com")
Exemple #20
0
def test_create_enrollment_timeout():
    """
     Asserts request timeout error on enrollment api
    """
    api = EdxApi({
        'access_token': ACCESS_TOKEN,
        'api_key': API_KEY
    },
                 base_url=BASE_URL)
    enrollments = api.enrollments
    with patch.object(enrollments.requester, 'post', autospec=True) as post:
        post.side_effect = mocked_timeout
        with pytest.raises(Timeout):
            enrollments.create_student_enrollment(
                ENROLLMENT_CREATION_COURSE_ID)
Exemple #21
0
def test_create_audit_enrollment():
    """
    Integration test to enroll the user in a course with `audit` mode
    """
    api = EdxApi({
        'access_token': ACCESS_TOKEN,
        'api_key': API_KEY
    },
                 base_url=BASE_URL)
    enrollment = api.enrollments.create_student_enrollment(
        course_id=ENROLLMENT_CREATION_COURSE_ID,
        mode=ENROLLMENT_MODE_AUDIT,
        username="******")
    assert enrollment.course_id == ENROLLMENT_CREATION_COURSE_ID
    assert enrollment.mode == ENROLLMENT_MODE_AUDIT
Exemple #22
0
def test_get_certificate():
    """
    Gets the certificate for the demo course.
    See this module docstring for the code to run to create one
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    certificate = api.certificates.get_student_certificate(
        'staff', 'course-v1:edX+DemoX+Demo_Course')
    assert certificate.username == 'staff'
    assert certificate.is_verified is True

    certificates = api.certificates.get_student_certificates('staff')
    assert len(certificates.all_courses_certs) >= 1
    assert 'course-v1:edX+DemoX+Demo_Course' in certificates.all_courses_certs
    assert 'course-v1:edX+DemoX+Demo_Course' in certificates.all_courses_verified_certs
Exemple #23
0
def test_deactivate_enrollment():
    """
    Integration test to enroll then deactivate a user in a course
    """
    api = EdxApi({
        'access_token': ACCESS_TOKEN,
        'api_key': API_KEY
    },
                 base_url=BASE_URL)
    api.enrollments.create_student_enrollment(
        course_id=ENROLLMENT_CREATION_COURSE_ID, mode=ENROLLMENT_MODE_AUDIT)
    deactivated_enrollment = api.enrollments.deactivate_enrollment(
        course_id=ENROLLMENT_CREATION_COURSE_ID)
    assert deactivated_enrollment.course_id == ENROLLMENT_CREATION_COURSE_ID
    assert deactivated_enrollment.is_active is False
Exemple #24
0
def test_get_timeout_error_ccx():
    """
    Asserts request timeout error on ccx
    """

    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    ccx = api.ccx

    with patch.object(ccx.requester, 'post', autospec=True) as post:
        post.side_effect = mocked_timeout
        with pytest.raises(Timeout):
            ccx.create(
                'course-v1:edX+DemoX+Demo_Course', '*****@*****.**', 100,
                'My CCX from test_create_ccx integration test via edx-api-client.'
            )
Exemple #25
0
    def user_data(self, access_token, *args, **kwargs):
        """
        Loads user data from service.

        This is the function that has to pull the data from edx

        Args:
            access_token (str): the OAUTH access token

        Returns:
            dict: a dictionary containing user information
                coming from the remote service.
        """
        edx_client = EdxApi({'access_token': access_token}, self.EDXORG_BASE_URL)
        info = edx_client.user_info.get_user_info()
        return {'name': info.name, 'username': info.username, 'email': info.email}
Exemple #26
0
def test_get_certificate_timeout_error():
    """
    Asserts request timeout error on certificate apis
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    username = '******'
    course_key = 'course-v1:edX+DemoX+Demo_Course'
    certificates = api.certificates

    with patch.object(certificates.requester, 'get', autospec=True) as get:
        get.side_effect = mocked_timeout
        with pytest.raises(Timeout):
            certificates.get_student_certificate(username, course_key)

        with pytest.raises(Timeout):
            certificates.get_student_certificates(username)
Exemple #27
0
def refresh_user_data(user_id):
    """
    Refresh the edx cache data for a user.

    Note that this function will not raise an exception on error, instead the errors are logged.

    Args:
        user_id (int): The user id
    """
    # pylint: disable=bare-except
    try:
        user = User.objects.get(pk=user_id)
    except:
        log.exception('edX data refresh task: unable to get user "%s"',
                      user_id)
        return

    # get the credentials for the current user for edX
    try:
        user_social = get_social_auth(user)
    except:
        log.exception('user "%s" does not have edX credentials', user.username)
        return

    try:
        utils.refresh_user_token(user_social)
    except:
        save_cache_update_failure(user_id)
        log.exception("Unable to refresh token for student %s", user.username)
        return

    try:
        edx_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL)
    except:
        log.exception("Unable to create an edX client object for student %s",
                      user.username)
        return

    for cache_type in CachedEdxDataApi.SUPPORTED_CACHES:
        try:
            CachedEdxDataApi.update_cache_if_expired(user, edx_client,
                                                     cache_type)
        except:
            save_cache_update_failure(user_id)
            log.exception("Unable to refresh cache %s for student %s",
                          cache_type, user.username)
            continue
    def update_all_cached_grade_data(cls, user):
        """
        Updates only certificates and Current grade.
        Used before a final grade freeze.

        Args:
            user (django.contrib.auth.models.User): A user
        Returns:
            None
        """
        # get the credentials for the current user for edX
        user_social = get_social_auth(user)
        utils.refresh_user_token(user_social)
        # create an instance of the client to query edX
        edx_client = EdxApi(user_social.extra_data, settings.EDXORG_BASE_URL)
        cls.update_cached_certificates(user, edx_client)
        cls.update_cached_current_grades(user, edx_client)
Exemple #29
0
def test_get_current_grade():
    """
    Gets the user current grade.
    If an user is enrolled in a course she has a current grade (probably with percent == 0.0)
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    course_grade = api.current_grades.get_student_current_grade(
        'staff', 'course-v1:edX+DemoX+Demo_Course')
    assert course_grade.username == 'staff'

    student_grades = api.current_grades.get_student_current_grades('staff')
    assert len(student_grades.all_course_ids) >= 1
    assert 'course-v1:edX+DemoX+Demo_Course' in student_grades.all_course_ids

    course_grades = api.current_grades.get_course_current_grades(
        'course-v1:edX+DemoX+Demo_Course')
    assert len(course_grades.all_usernames) >= 1
    assert 'honor' in course_grades.all_usernames
Exemple #30
0
def get_edx_api_service_client():
    """
    Gets an edx api client instance for the service worker user

    Returns:
         EdxApi: edx api service worker client instance
    """
    if settings.OPENEDX_SERVICE_WORKER_API_TOKEN is None:
        raise ImproperlyConfigured(
            "OPENEDX_SERVICE_WORKER_API_TOKEN is not set")

    edx_client = EdxApi(
        {
            "access_token": settings.OPENEDX_SERVICE_WORKER_API_TOKEN,
            "api_key": settings.OPENEDX_API_KEY,
        },
        settings.OPENEDX_API_BASE_URL,
        timeout=settings.EDX_API_CLIENT_TIMEOUT,
    )

    return edx_client
Exemple #31
0
def test_enrollments_list():
    """
    Enrolls the user in a course and then pulls down the enrollments for the user.
    This assumes that the course in the edX instance is available for enrollment.
    """
    api = EdxApi({'access_token': ACCESS_TOKEN}, base_url=BASE_URL)
    enrollments = api.enrollments.get_enrollments()

    cnt = 0
    for enrollment in enrollments:
        assert enrollment.course_id
        assert enrollment.created
        assert enrollment.mode
        assert enrollment.is_active
        assert enrollment.user
        if enrollment.mode != ENROLLMENT_MODE_VERIFIED:
            assert not enrollment.is_verified
        else:
            assert enrollment.is_verified
        cnt += 1

    assert cnt >= 2