def get_user_program_info(user, edx_client): """ Provides a detailed serialization all of a User's enrolled Programs with enrollment/grade info Args: user (User): A User edx_client (EdxApi): An EdxApi instance Returns: list: Enrolled Program information """ # update cache # NOTE: this part can be moved to an asynchronous task if edx_client is not None: try: for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired( user, edx_client, cache_type) except InvalidCredentialStored: # this needs to raise in order to force the user re-login raise except: # pylint: disable=bare-except log.exception('Impossible to refresh edX cache') response_data = { "programs": [], "is_edx_data_fresh": CachedEdxDataApi.are_all_caches_fresh(user) } all_programs = (Program.objects.filter( live=True, programenrollment__user=user).prefetch_related( 'course_set__courserun_set')) for program in all_programs: mmtrack_info = get_mmtrack(user, program) response_data['programs'].append(get_info_for_program(mmtrack_info)) return response_data
def get_user_program_info(user, edx_client): """ Provides a detailed serialization all of a User's enrolled Programs with enrollment/grade info Args: user (User): A User edx_client (EdxApi): An EdxApi instance Returns: list: Enrolled Program information """ # update cache # NOTE: this part can be moved to an asynchronous task if edx_client is not None: try: for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired(user, edx_client, cache_type) except InvalidCredentialStored: # this needs to raise in order to force the user re-login raise except: # pylint: disable=bare-except log.exception('Impossible to refresh edX cache') response_data = { "programs": [], "is_edx_data_fresh": CachedEdxDataApi.are_all_caches_fresh(user) } all_programs = ( Program.objects.filter(live=True, programenrollment__user=user).prefetch_related('course_set__courserun_set') ) for program in all_programs: mmtrack_info = get_mmtrack(user, program) response_data['programs'].append(get_info_for_program(mmtrack_info)) return response_data
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 test_update_cache_if_expired_http_errors(self, status_code, mock_enr): """ Test for update_cache_if_expired in case a backend function raises an HTTPError """ def raise_http_error(*args, **kwargs): # pylint: disable=unused-argument """Mock function to raise an exception""" error = HTTPError() error.response = MagicMock() error.response.status_code = status_code raise error mock_enr.side_effect = raise_http_error if status_code in (400, 401): with self.assertRaises(InvalidCredentialStored): CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, CachedEdxDataApi.ENROLLMENT) else: with self.assertRaises(HTTPError): CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, CachedEdxDataApi.ENROLLMENT)
def test_update_cache_if_expired(self, mock_enr, mock_cert, mock_grade): """Test for update_cache_if_expired""" all_mocks = ( mock_enr, mock_cert, mock_grade, ) with self.assertRaises(ValueError): CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, 'footype') # if there is no entry in the UserCacheRefreshTime the cache is not fresh and needs to be refreshed for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: # the following is possible only because a mocked function is called assert UserCacheRefreshTime.objects.filter( user=self.user).exists() is False CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, cache_type) for mock_func in all_mocks: assert mock_func.called is True mock_func.reset_mock() # if we create a fresh entry in the UserCacheRefreshTime, no update is called now = now_in_utc() user_cache = UserCacheRefreshTimeFactory.create( user=self.user, enrollment=now, certificate=now, current_grade=now, ) for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, cache_type) for mock_func in all_mocks: assert mock_func.called is False mock_func.reset_mock() # moving back the last access time, the functions are called again yesterday = now - timedelta(days=1) user_cache.enrollment = yesterday user_cache.certificate = yesterday user_cache.current_grade = yesterday user_cache.save() for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, cache_type) for mock_func in all_mocks: assert mock_func.called is True
def test_update_cache_if_expired_http_errors(self, status_code, mock_enr): """ Test for update_cache_if_expired in case a backend function raises an HTTPError """ def raise_http_error(*args, **kwargs): # pylint: disable=unused-argument """Mock function to raise an exception""" error = HTTPError() error.response = MagicMock() error.response.status_code = status_code raise error mock_enr.side_effect = raise_http_error if status_code in (400, 401): with self.assertRaises(InvalidCredentialStored): CachedEdxDataApi.update_cache_if_expired( self.user, self.edx_client, CachedEdxDataApi.ENROLLMENT) else: with self.assertRaises(HTTPError): CachedEdxDataApi.update_cache_if_expired( self.user, self.edx_client, CachedEdxDataApi.ENROLLMENT)
def test_update_cache_if_expired(self, mock_enr, mock_cert, mock_grade): """Test for update_cache_if_expired""" all_mocks = (mock_enr, mock_cert, mock_grade, ) with self.assertRaises(ValueError): CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, 'footype') # if there is no entry in the UserCacheRefreshTime the cache is not fresh and needs to be refreshed for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: # the following is possible only because a mocked function is called assert UserCacheRefreshTime.objects.filter(user=self.user).exists() is False CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, cache_type) for mock_func in all_mocks: assert mock_func.called is True mock_func.reset_mock() # if we create a fresh entry in the UserCacheRefreshTime, no update is called now = now_in_utc() user_cache = UserCacheRefreshTimeFactory.create( user=self.user, enrollment=now, certificate=now, current_grade=now, ) for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, cache_type) for mock_func in all_mocks: assert mock_func.called is False mock_func.reset_mock() # moving back the last access time, the functions are called again yesterday = now - timedelta(days=1) user_cache.enrollment = yesterday user_cache.certificate = yesterday user_cache.current_grade = yesterday user_cache.save() for cache_type in CachedEdxDataApi.SUPPORTED_CACHES: CachedEdxDataApi.update_cache_if_expired(self.user, self.edx_client, cache_type) for mock_func in all_mocks: assert mock_func.called is True