def test_api_call_with_retries_with_all_errors(self): """api_call_with_retries returns None when no valid Response is found after the maximum number of attempts.""" full_url: str = '/'.join( [self.api_handler.base_url, self.get_scores_url]) num_attempts: int = 4 resp_mocks: list[MagicMock] = [ MagicMock(spec=Response, status_code=504, text=json.dumps({'message': 'Gateway Timeout'}), url=full_url) for i in range(num_attempts + 1) ] with patch.object(ApiUtil, 'api_call', autospec=True) as mock_api_call: mock_api_call.side_effect = resp_mocks response: Union[MagicMock, None] = api_call_with_retries( self.api_handler, self.get_scores_url, CANVAS_SCOPE, 'GET', self.canvas_params, max_req_attempts=num_attempts) self.assertEqual(mock_api_call.call_count, 4) mock_api_call.assert_called_with(self.api_handler, self.get_scores_url, CANVAS_SCOPE, 'GET', self.canvas_params) self.assertEqual(response, None)
def get_sub_dicts_for_exam(self, page_size: int = 50) -> list[dict[str, Any]]: """ Gets the graded submissions for the exam using paging. :param page_size: How many results from Canvas to include per page :type page_size: int, optional (default is 50) :return: List of submission dictionaries from Canvas returned based on the URL and parameters :rtype: List of dictionaries with string keys """ get_subs_url: str = f'{CANVAS_URL_BEGIN}/courses/{self.exam.course_id}/students/submissions' canvas_params: dict[str, Any] = { 'student_ids[]': 'all', 'assignment_ids[]': str(self.exam.assignment_id), 'per_page': page_size, 'include[]': 'user', 'graded_since': self.sub_time_filter.strftime(ISO8601_FORMAT) } more_pages: bool = True page_num: int = 1 sub_dicts: list[dict[str, Any]] = [] next_params: dict[str, Any] = canvas_params LOGGER.debug(f'Params for first request: {next_params}') while more_pages: LOGGER.debug(f'Page number {page_num}') response: Union[Response, None] = api_call_with_retries( self.api_handler, get_subs_url, CANVAS_SCOPE, 'GET', next_params, MAX_REQ_ATTEMPTS) if response is None: LOGGER.info( 'api_call_with_retries failed to get a response; no more data will be collected' ) more_pages = False else: sub_dicts += json.loads(response.text) page_info: Union[None, dict[ str, Any]] = self.api_handler.get_next_page(response) if not page_info: more_pages = False else: LOGGER.debug(f'Params for next page: {page_info}') next_params = page_info page_num += 1 sub_dicts_with_scores: list[dict[str, Any]] = list( filter((lambda x: x['score'] is not None), sub_dicts)) filter_diff: int = len(sub_dicts) - len(sub_dicts_with_scores) if filter_diff > 0: LOGGER.info( f'Discarded {filter_diff} Canvas submission(s) with no score(s)' ) LOGGER.info( f'Gathered {len(sub_dicts_with_scores)} submission(s) from Canvas') LOGGER.debug(sub_dicts_with_scores) return sub_dicts_with_scores
def test_api_call_with_retries_when_no_errors(self): """api_call_with_retries returns Response object when a valid Response is found.""" with patch.object(ApiUtil, 'api_call', autospec=True) as mock_api_call: mock_api_call.return_value = MagicMock( spec=Response, status_code=200, text=json.dumps(self.canvas_potions_val_subs), url='/'.join([self.api_handler.base_url, self.get_scores_url])) response = api_call_with_retries(self.api_handler, self.get_scores_url, CANVAS_SCOPE, 'GET', self.canvas_params) self.assertEqual(mock_api_call.call_count, 1) mock_api_call.assert_called_with(self.api_handler, self.get_scores_url, CANVAS_SCOPE, 'GET', self.canvas_params) self.assertTrue(response.ok) self.assertEqual(json.loads(response.text), self.canvas_potions_val_subs)