diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6529fee16a3..8fe85966e11 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,10 @@ Change Log Unreleased ~~~~~~~~~~ +[4.0.2] - 2021-09-28 +~~~~~~~~~~~~~~~~~~~~~ +* Batch of refactorings to use format strings/lazy string formatting for logging calls + [4.0.1] - 2021-09-21 ~~~~~~~~~~~~~~~~~~~~~ * Bug fix for student onboarding statuses by course. If learner has multiple attempts, return non-reset attempt status if possible. diff --git a/edx_proctoring/__init__.py b/edx_proctoring/__init__.py index 161fd8ee38f..f13bc439424 100644 --- a/edx_proctoring/__init__.py +++ b/edx_proctoring/__init__.py @@ -3,6 +3,6 @@ """ # Be sure to update the version number in edx_proctoring/package.json -__version__ = '4.0.1' +__version__ = '4.0.2' default_app_config = 'edx_proctoring.apps.EdxProctoringConfig' # pylint: disable=invalid-name diff --git a/edx_proctoring/backends/rest.py b/edx_proctoring/backends/rest.py index d84dcaf42eb..7fc93bdd217 100644 --- a/edx_proctoring/backends/rest.py +++ b/edx_proctoring/backends/rest.py @@ -115,21 +115,16 @@ def get_javascript(self): except WebpackBundleLookupError: warnings.warn( - 'Could not find webpack bundle for proctoring backend {package}.' - ' Check whether webpack is configured to build such a bundle'.format( - package=package - ) + f'Could not find webpack bundle for proctoring backend {package}.' + ' Check whether webpack is configured to build such a bundle' ) except BaseWebpackLoaderException: warnings.warn( - 'Could not find webpack bundle for proctoring backend {package}.'.format( - package=package - ) + f'Could not find webpack bundle for proctoring backend {package}.' ) except IOError as err: warnings.warn( - 'Webpack stats file corresponding to WebWorkers not found: {}' - .format(str(err)) + f'Webpack stats file corresponding to WebWorkers not found: {str(err)}' ) # if the Javascript URL is not an absolute URL (i.e. doesn't have a scheme), prepend @@ -189,9 +184,8 @@ def register_exam_attempt(self, exam, context): # attempt code isn't needed in this API payload.pop('attempt_code', False) log.debug( - 'Creating exam attempt for exam_id={exam_id} (external_id={external_id}) at {url}'.format( - exam_id=exam['id'], external_id=exam['external_id'], url=url - ) + 'Creating exam attempt for exam_id=%(exam_id)i (external_id=%(external_id)s) at %(url)s', + {'exam_id': exam['id'], 'external_id': exam['external_id'], 'url': url} ) response = self.session.post(url, json=payload) if response.status_code != 200: @@ -272,7 +266,10 @@ def on_exam_saved(self, exam): url = self.exam_url.format(exam_id=external_id) else: url = self.create_exam_url - log.info('Saving exam_id={exam_id} to {url}'.format(exam_id=exam['id'], url=url)) + log.info( + 'Saving exam_id=%(exam_id)i to %(url)s', + {'exam_id': exam['id'], 'url': url} + ) response = None try: response = self.session.post(url, json=exam) @@ -284,9 +281,8 @@ def on_exam_saved(self, exam): else: content = None log.exception( - 'Failed to save exam_id={exam_id} to {url}. Response: {content}'.format( - exam_id=exam['id'], url=url, content=content - ) + 'Failed to save exam_id=%(exam_id)i to %(url)s. Response: %(content)s', + {'exam_id': exam['id'], 'url': url, 'content': content} ) data = {} return data.get('id') @@ -317,10 +313,9 @@ def get_instructor_url(self, course_id, user, exam_id=None, attempt_id=None, sho url = self.instructor_url.format(client_id=self.client_id, jwt=encoded) log.debug( - 'Created instructor url for course_id={course_id} exam_id={exam_id} ' - 'attempt_id={attempt_id}'.format( - course_id=course_id, exam_id=exam_id, attempt_id=attempt_id - ) + ('Created instructor url for course_id=%(course_id)s exam_id=%(exam_id)i ' + 'attempt_id=%(attempt_id)s'), + {'course_id': course_id, 'exam_id': exam_id, 'attempt_id': attempt_id} ) return url @@ -362,7 +357,7 @@ def _get_language_headers(self): default_lang = settings.LANGUAGE_CODE lang_header = default_lang if current_lang and current_lang != default_lang: - lang_header = '{};{}'.format(current_lang, default_lang) + lang_header = f'{current_lang};{default_lang}' return {'Accept-Language': lang_header} def _make_attempt_request(self, exam, attempt, method='POST', status=None, **payload): diff --git a/edx_proctoring/backends/software_secure.py b/edx_proctoring/backends/software_secure.py index 3c0d195b761..47c7dfc31ad 100644 --- a/edx_proctoring/backends/software_secure.py +++ b/edx_proctoring/backends/software_secure.py @@ -80,12 +80,8 @@ def register_exam_attempt(self, exam, context): if status not in [200, 201]: err_msg = ( - 'Could not register attempt_code={attempt_code}. ' - 'HTTP Status code was {status_code} and response was {response}.'.format( - attempt_code=attempt_code, - status_code=status, - response=response - ) + f'Could not register attempt_code={attempt_code}. ' + f'HTTP Status code was {status} and response was {response}.' ) log.error(err_msg) raise BackendProviderCannotRegisterAttempt(err_msg, status) @@ -141,13 +137,9 @@ def on_review_callback(self, attempt, payload): ) if not match: err_msg = ( - 'Found attempt_code {attempt_code}, but the recorded external_id did not ' - 'match the ssiRecordLocator that had been recorded previously. Has {existing} ' - 'but received {received}!'.format( - attempt_code=attempt['attempt_code'], - existing=attempt['external_id'], - received=received_id - ) + f"Found attempt_code {attempt['attempt_code']}, but the recorded external_id did not " + f"match the ssiRecordLocator that had been recorded previously. Has {attempt['external_id']} " + f'but received {received_id}!' ) raise ProctoredExamSuspiciousLookup(err_msg) @@ -156,9 +148,7 @@ def on_review_callback(self, attempt, payload): del payload['videoReviewLink'] log_msg = ( - 'Received callback from SoftwareSecure with review data: {payload}'.format( - payload=payload - ) + f'Received callback from SoftwareSecure with review data: {payload}' ) log.info(log_msg) SoftwareSecureReviewStatus.validate(payload['reviewStatus']) @@ -228,24 +218,18 @@ def _get_payload(self, exam, context): review_policy = context.get('review_policy', constants.DEFAULT_SOFTWARE_SECURE_REVIEW_POLICY) review_policy_exception = context.get('review_policy_exception') scheme = 'https' if getattr(settings, 'HTTPS', 'on') == 'on' else 'http' - callback_url = '{scheme}://{hostname}{path}'.format( - scheme=scheme, - hostname=settings.SITE_NAME, - path=reverse( - 'edx_proctoring:anonymous.proctoring_launch_callback.start_exam', - args=[attempt_code] - ) + path = reverse( + 'edx_proctoring:anonymous.proctoring_launch_callback.start_exam', + args=[attempt_code] ) + callback_url = f'{scheme}://{settings.SITE_NAME}{path}' # compile the notes to the reviewer # this is a combination of the Exam Policy which is for all students # combined with any exceptions granted to the particular student reviewer_notes = review_policy if review_policy_exception: - reviewer_notes = '{notes}; {exception}'.format( - notes=reviewer_notes, - exception=review_policy_exception - ) + reviewer_notes = f'{reviewer_notes}; {review_policy_exception}' (first_name, last_name) = self._split_fullname(full_name) @@ -353,8 +337,8 @@ def _sign_doc(self, body_json, method, headers, date): message = method_string + headers_str + body_str log_msg = ( - 'About to send payload to SoftwareSecure: examCode={examCode}, courseID={courseID}'. - format(examCode=body_json.get('examCode'), courseID=body_json.get('orgExtra').get('courseID')) + f"About to send payload to SoftwareSecure: examCode={body_json.get('examCode')}, " + f"courseID={body_json.get('orgExtra').get('courseID')}" ) log.info(log_msg) diff --git a/edx_proctoring/management/commands/set_attempt_status.py b/edx_proctoring/management/commands/set_attempt_status.py index 9e5e76ae649..d9e7363dbb2 100644 --- a/edx_proctoring/management/commands/set_attempt_status.py +++ b/edx_proctoring/management/commands/set_attempt_status.py @@ -40,15 +40,12 @@ def handle(self, *args, **options): msg = ( 'Running management command to update ' - 'attempt {attempt_id} status to {to_status}'.format( - attempt_id=attempt_id, - to_status=to_status - ) + f'attempt {attempt_id} status to {to_status}' ) self.stdout.write(msg) if not ProctoredExamStudentAttemptStatus.is_valid_status(to_status): - raise CommandError('{to_status} is not a valid attempt status!'.format(to_status=to_status)) + raise CommandError(f'{to_status} is not a valid attempt status!') update_attempt_status(attempt_id, to_status) diff --git a/edx_proctoring/management/commands/set_is_attempt_active.py b/edx_proctoring/management/commands/set_is_attempt_active.py index b0f14b4d690..e338caa79a1 100644 --- a/edx_proctoring/management/commands/set_is_attempt_active.py +++ b/edx_proctoring/management/commands/set_is_attempt_active.py @@ -60,7 +60,7 @@ def handle(self, *args, **options): self.check_and_update(archived_review, batch_size, sleep_time, only_update_archives=True) if self.update_attempt_codes: - log.info('Updating {} reviews'.format(len(self.update_attempt_codes))) + log.info('Updating %i reviews', len(self.update_attempt_codes)) self.bulk_update(self.update_attempt_codes, False) def check_and_update(self, review_object, size, sleep_time, only_update_archives=False): @@ -72,10 +72,10 @@ def check_and_update(self, review_object, size, sleep_time, only_update_archives self.distinct_attempt_codes.add(review_object.attempt_code) self.update_attempt_codes.append(review_object.attempt_code) self.update_field_count += 1 - log.info('Adding review {} to be updated'.format(review_object.id)) + log.info('Adding review %i to be updated', review_object.id) if self.update_field_count == size: - log.info('Updating {} reviews'.format(size)) + log.info('Updating %i reviews', size) self.bulk_update(self.update_attempt_codes, only_update_archives) self.update_field_count = 0 self.update_attempt_codes = [] diff --git a/edx_proctoring/management/commands/update_attempt_status_from_review.py b/edx_proctoring/management/commands/update_attempt_status_from_review.py index 8c453420cad..4760c290729 100644 --- a/edx_proctoring/management/commands/update_attempt_status_from_review.py +++ b/edx_proctoring/management/commands/update_attempt_status_from_review.py @@ -68,10 +68,11 @@ def handle(self, *args, **options): for review in reviews_in_date_range: review_id = review.id attempt_code = review.attempt_code - log.info('Saving review_id={review_id} for corresponding attempt_code={attempt_code}'.format( - review_id=review_id, - attempt_code=attempt_code - )) + log.info( + 'Saving review_id=%i for corresponding attempt_code=%s', + review_id, + attempt_code + ) review.save() review_count += 1 diff --git a/edx_proctoring/views.py b/edx_proctoring/views.py index a2112d39883..9b32fafc053 100644 --- a/edx_proctoring/views.py +++ b/edx_proctoring/views.py @@ -562,10 +562,11 @@ def get(self, request): # pylint: disable=too-many-statements if not onboarding_exams: LOG.info( - 'User user_id={user_id} has no accessible onboarding exams in course course_id={course_id}'.format( - user_id=user.id, - course_id=course_id, - ) + 'User user_id=%(user_id)s has no accessible onboarding exams in course course_id=%(course_id)s', + { + 'user_id': user.id, + 'course_id': course_id, + } ) return Response( status=404, @@ -603,18 +604,18 @@ def get(self, request): # pylint: disable=too-many-statements onboarding_profile_data = backend.get_onboarding_profile_info(course_id, user_id=obs_user_id) except BackendProviderOnboardingProfilesException as exc: # if backend raises exception, log message and return data from onboarding exam attempt - log_message = ( - 'Failed to use backend onboarding status API endpoint for user_id={user_id}' - 'in course_id={course_id} because backend failed to respond. Onboarding status' + LOG.warning( + 'Failed to use backend onboarding status API endpoint for user_id=%(user_id)s' + 'in course_id=%(course_id)s because backend failed to respond. Onboarding status' 'will be determined by the user\'s onboarding attempts. ' - 'Status: {status}, Response: {response}.'.format( - user_id=user.id, - course_id=course_id, - response=str(exc), - status=exc.http_status, - ) + 'Status: %(status)s, Response: %(response)s.', + { + 'user_id': user.id, + 'course_id': course_id, + 'response': str(exc), + 'status': exc.http_status, + } ) - LOG.warning(log_message) return Response(data) if onboarding_profile_data is None: @@ -632,26 +633,27 @@ def get(self, request): # pylint: disable=too-many-statements expiration_date = onboarding_profile_data['expiration_date'] if readable_status != data['onboarding_status']: - log_message = ( - 'Backend onboarding status API endpoint info for user_id={user_id}' - 'in course_id={course_id} differs from system info. Endpoint onboarding attempt' - 'has status={endpoint_status}. System onboarding attempt has status={system_status}.'.format( - user_id=user.id, - course_id=course_id, - endpoint_status=readable_status, - system_status=data['onboarding_status'] - ) + LOG.error( + 'Backend onboarding status API endpoint info for user_id=%(user_id)s' + 'in course_id=%(course_id)s differs from system info. Endpoint onboarding attempt' + 'has status=%(endpoint_status)s. System onboarding attempt has status=%(system_status)s.', + { + 'user_id': user.id, + 'course_id': course_id, + 'endpoint_status': readable_status, + 'system_status': data['onboarding_status'], + } ) - LOG.error(log_message) data['onboarding_status'] = readable_status data['expiration_date'] = expiration_date LOG.info( - 'Used backend onboarding status API endpoint to retrieve user_id={user_id}' - 'in course_id={course_id}'.format( - user_id=user.id, - course_id=course_id - ) + 'Used backend onboarding status API endpoint to retrieve user_id=%(user_id)s' + 'in course_id=%(course_id)s', + { + 'user_id': user.id, + 'course_id': course_id, + } ) return Response(data) @@ -743,20 +745,20 @@ def get(self, request, course_id): if api_response_error: # if backend raises exception, log message and return data from onboarding exam attempt - log_message = ( - 'Failed to use backend onboarding status API endpoint for course_id={course_id}' - 'with query parameters text_search: {text_search_filter}, status: {status_filters}' + LOG.warning( + 'Failed to use backend onboarding status API endpoint for course_id=%(course_id)s' + 'with query parameters text_search: %(text_search_filter)s, status: %(status_filters)s' 'because backend failed to respond. Onboarding status' 'will be determined by the users\'s onboarding attempts. ' - 'Status: {status}, Response: {response}.'.format( - course_id=course_id, - text_search_filter=text_search, - status_filters=status_filters, - response=str(api_response_error), - status=api_response_error.http_status, - ) + 'Status: %(status)s, Response: %(response)s.', + { + 'course_id': course_id, + 'text_search_filter': text_search, + 'status_filters': status_filters, + 'response': str(api_response_error), + 'status': api_response_error.http_status, + } ) - LOG.warning(log_message) return Response( status=status.HTTP_503_SERVICE_UNAVAILABLE, @@ -764,11 +766,12 @@ def get(self, request, course_id): ) LOG.info( - 'Backend onboarding API returned {num_profiles} from the proctoring provider ' - 'for course {course_id}.'.format( - num_profiles=len(onboarding_profile_info), - course_id=course_id, - ) + 'Backend onboarding API returned %(num_profiles)s from the proctoring provider ' + 'for course %(course_id)s.', + { + 'num_profiles': len(onboarding_profile_info), + 'course_id': course_id, + } ) obscured_user_ids_to_users = {obscured_user_id(user.id, onboarding_exam.backend): user for user in users} @@ -795,16 +798,16 @@ def get(self, request, course_id): del obscured_user_ids_to_users[obscured_id] if missing_user_ids: - log_message = ( + LOG.warning( 'Users are present in response whose obscured user IDs do not exist in list of learners ' - 'enrolled in course {course_id} with proctoring eligible enrollments. There are a total ' - 'of {num_missing_ids} of these. A sample of these IDs is {obs_id_sample}.' - ).format( - course_id=course_id, - num_missing_ids=len(missing_user_ids), - obs_id_sample=missing_user_ids[0:5], + 'enrolled in course %(course_id)s with proctoring eligible enrollments. There are a total ' + 'of %(num_missing_ids)s of these. A sample of these IDs is %(obs_id_sample)s.', + { + 'course_id': course_id, + 'num_missing_ids': len(missing_user_ids), + 'obs_id_sample': missing_user_ids[0:5], + } ) - LOG.warning(log_message) if status_filters is None or 'not_started' in status_filters: for (obscured_id, user) in obscured_user_ids_to_users.items(): @@ -1052,10 +1055,11 @@ def _get_onboarding_info(self, backend, course_id, status_filters): onboarding_profile_kwargs['status'] = onboarding_status_filter_string LOG.info( - 'Onboarding profile kwargs for course {course_id} are {onboarding_kwargs}.'.format( - course_id=course_id, - onboarding_kwargs=onboarding_profile_kwargs, - ) + 'Onboarding profile kwargs for course %(course_id)s are %(onboarding_kwargs)s.', + { + 'course_id': course_id, + 'onboarding_kwargs': onboarding_profile_kwargs, + } ) try: request = backend.get_onboarding_profile_info( @@ -1113,15 +1117,15 @@ def get(self, request, attempt_id): if not attempt: err_msg = ( - 'Attempted to get attempt_id={attempt_id} but this attempt does not ' - 'exist.'.format(attempt_id=attempt_id) + f'Attempted to get attempt_id={attempt_id} but this attempt does not ' + 'exist.' ) raise StudentExamAttemptDoesNotExistsException(err_msg) # make sure the the attempt belongs to the calling user_id if attempt['user']['id'] != user_id: err_msg = ( - 'user_id={user_id} attempted to get attempt_id={attempt_id} but ' - 'does not have authorization.'.format(user_id=user_id, attempt_id=attempt_id) + f'user_id={user_id} attempted to get attempt_id={attempt_id} but ' + 'does not have authorization.' ) raise ProctoredExamPermissionDenied(err_msg) @@ -1158,9 +1162,7 @@ def put(self, request, attempt_id): if not attempt: err_msg = ( - 'Attempted to update attempt_id={attempt_id} but it does not exist.'.format( - attempt_id=attempt_id - ) + f'Attempted to update attempt_id={attempt_id} but it does not exist.' ) raise StudentExamAttemptDoesNotExistsException(err_msg) @@ -1169,13 +1171,8 @@ def put(self, request, attempt_id): detail = request.data.get('detail') err_msg = ( - 'user_id={user_id} attempted to update attempt_id={attempt_id} in ' - 'course_id={course_id} but does not have access to it. (action={action})'.format( - user_id=user_id, - attempt_id=attempt_id, - course_id={course_id}, - action={action}, - ) + f'user_id={user_id} attempted to update attempt_id={attempt_id} in ' + f'course_id={course_id} but does not have access to it. (action={action})' ) # only allow a staff user to change another user's exam attempt status via the 'mark_ready_to_resume' action @@ -1236,15 +1233,16 @@ def put(self, request, attempt_id): exam_attempt_id = False LOG.warning( 'Browser JS reported problem with proctoring desktop application. Error detail: ' - '{error_detail}. Did block user: {should_block_user}. user_id={user_id}, ' - 'course_id={course_id}, exam_id={exam_id}, attempt_id={attempt_id}'.format( - should_block_user=should_block_user, - user_id=user_id, - course_id=course_id, - exam_id=attempt['proctored_exam']['id'], - attempt_id=attempt_id, - error_detail=detail - ) + '%(error_detail)s. Did block user: %(should_block_user)s. user_id=%(user_id)s, ' + 'course_id=%(course_id)s, exam_id=%(exam_id)s, attempt_id=%(attempt_id)s', + { + 'should_block_user': should_block_user, + 'user_id': user_id, + 'course_id': course_id, + 'exam_id': attempt['proctored_exam']['id'], + 'attempt_id': attempt_id, + 'error_detail': detail, + } ) elif action == 'decline': exam_attempt_id = update_attempt_status( @@ -1269,8 +1267,8 @@ def delete(self, request, attempt_id): # pylint: disable=unused-argument if not attempt: err_msg = ( - 'Attempted to delete attempt_id={attempt_id} but this attempt does not ' - 'exist.'.format(attempt_id=attempt_id) + f'Attempted to delete attempt_id={attempt_id} but this attempt does not ' + 'exist.' ) raise StudentExamAttemptDoesNotExistsException(err_msg) @@ -1362,12 +1360,8 @@ def post(self, request): # because student can attempt the practice after the due date if not exam.get("is_practice_exam") and is_exam_passed_due(exam, request.user): raise ProctoredExamPermissionDenied( - 'user_id={user_id} attempted to create an attempt for expired exam ' - 'with exam_id={exam_id} in course_id={course_id}'.format( - user_id=user_id, - exam_id=exam_id, - course_id=exam['course_id'], - ) + f'user_id={user_id} attempted to create an attempt for expired exam ' + f"with exam_id={exam_id} in course_id={exam['course_id']}" ) exam_attempt_id = create_exam_attempt( @@ -1712,13 +1706,9 @@ def put(self, request, attempt_id): # pylint: disable=unused-argument # make sure the the attempt belongs to the calling user_id if attempt and attempt['user']['id'] != request.user.id: err_msg = ( - 'user_id={user_id} attempted to update attempt_id={attempt_id} ' - 'review status to acknowledged in exam_id={exam_id}, but does not ' - 'have authorization.'.format( - user_id=request.user.id, - attempt_id=attempt_id, - exam_id=attempt['proctored_exam']['id'], - ) + f'user_id={request.user.id} attempted to update attempt_id={attempt_id} ' + f"review status to acknowledged in exam_id={attempt['proctored_exam']['id']}, but does not " + 'have authorization.' ) raise ProctoredExamPermissionDenied(err_msg) @@ -1739,7 +1729,8 @@ def post(self, request, external_id): # pylint: disable=unused-argument attempt = get_exam_attempt_by_external_id(external_id) if not attempt: LOG.warning( - 'Attempt cannot be found by external_id={external_id}'.format(external_id=external_id) + 'Attempt cannot be found by external_id=%(external_id)s', + {'external_id': external_id} ) return Response(data='You have entered an exam code that is not valid.', status=404) if attempt['status'] in [ProctoredExamStudentAttemptStatus.created, @@ -1770,22 +1761,18 @@ def make_review(self, attempt, data, backend=None): if review: if not constants.ALLOW_REVIEW_UPDATES: err_msg = ( - 'We already have a review submitted regarding attempt_code={attempt_code}. ' - 'We do not allow for updates! (backend={backend})'.format( - attempt_code=attempt_code, backend=backend - ) + f'We already have a review submitted regarding attempt_code={attempt_code}. ' + f'We do not allow for updates! (backend={backend})' ) raise ProctoredExamReviewAlreadyExists(err_msg) # we allow updates - warn_msg = ( - 'We already have a review submitted from our proctoring provider regarding ' - 'attempt_code={attempt_code}. We have been configured to allow for ' - 'updates and will continue... (backend={backend})'.format( - attempt_code=attempt_code, backend=backend - ) + LOG.warning( + ('We already have a review submitted from our proctoring provider regarding ' + 'attempt_code=%(attempt_code)s. We have been configured to allow for ' + 'updates and will continue... (backend=%(backend)s)'), + {'attempt_code': attempt_code, 'backend': backend} ) - LOG.warning(warn_msg) else: # this is first time we've received this attempt_code, so # make a new record in the review table @@ -1839,10 +1826,9 @@ def make_review(self, attempt, data, backend=None): course_id = attempt['proctored_exam']['course_id'] exam_id = attempt['proctored_exam']['id'] review_url = request.build_absolute_uri( - '{}?attempt={}'.format( - reverse('edx_proctoring:instructor_dashboard_exam', args=[course_id, exam_id]), - attempt['external_id'] - )) + f"{reverse('edx_proctoring:instructor_dashboard_exam', args=[course_id, exam_id])}" + f"?attempt={attempt['external_id']}" + ) instructor_service.send_support_notification( course_id=attempt['proctored_exam']['course_id'], exam_name=attempt['proctored_exam']['exam_name'], @@ -1864,8 +1850,8 @@ def post(self, request, external_id): attempt = get_exam_attempt_by_external_id(external_id) if attempt is None: err_msg = ( - 'Attempted to access exam attempt with external_id={external_id} ' - 'but it does not exist.'.format(external_id=external_id) + f'Attempted to access exam attempt with external_id={external_id} ' + 'but it does not exist.' ) raise StudentExamAttemptDoesNotExistsException(err_msg) if request.user.has_perm('edx_proctoring.can_review_attempt', attempt): @@ -1926,9 +1912,7 @@ def post(self, request): attempt_obj, is_archived = locate_attempt_by_attempt_code(attempt_code) if not attempt_obj: # still can't find, error out - err_msg = ( - 'Could not locate attempt_code={attempt_code}'.format(attempt_code=attempt_code) - ) + err_msg = f'Could not locate attempt_code={attempt_code}' raise StudentExamAttemptDoesNotExistsException(err_msg) serialized = ProctoredExamStudentAttemptSerializer(attempt_obj).data serialized['is_archived'] = is_archived @@ -1976,10 +1960,9 @@ def get(self, request, course_id, exam_id=None): # In this case, what are we supposed to do?! # It should not be possible to get in this state, because # course teams will be prevented from updating the backend after the course start date - error_message = "Multiple backends for course %r %r != %r" % ( - course_id, - existing_backend_name, - exam_backend_name + error_message = ( + f'Multiple backends for course {course_id} ' + f'{existing_backend_name} != {exam_backend_name}' ) return Response(data=error_message, status=400) existing_backend_name = exam_backend_name @@ -2043,18 +2026,20 @@ def post(self, request, user_id, **kwargs): # pylint: disable=unused-argument continue backend_user_id = obscured_user_id(user_id, backend_name) LOG.info( - 'Retiring user_id={user_id} from backend={backend}'.format( - user_id=user_id, backend=backend_name - ) + 'Retiring user_id=%(user_id)s from backend=%(backend)s', + {'user_id': user_id, 'backend': backend_name} ) try: result = get_backend_provider(name=backend_name).retire_user(backend_user_id) except ProctoredBaseException: LOG.exception( - 'Failed to delete user_id={user_id} (backend_user_id={backend_user_id}) from ' - 'backend={backend}'.format( - user_id=user_id, backend_user_id=backend_user_id, backend=backend_name - ) + 'Failed to delete user_id=%(user_id)s (backend_user_id=%(backend_user_id)s) from ' + 'backend=%(backend)s', + { + 'user_id': user_id, + 'backend_user_id': backend_user_id, + 'backend': backend_name + } ) result = False if result is not None: diff --git a/package.json b/package.json index 9b9bd5b254e..6f0bdcbf37c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@edx/edx-proctoring", "//": "Note that the version format is slightly different than that of the Python version when using prereleases.", - "version": "4.0.1", + "version": "4.0.2", "main": "edx_proctoring/static/index.js", "scripts": { "test": "gulp test"