From 7324435d1e443b6d908ede00d780c07d73e75277 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sun, 8 Dec 2024 20:14:44 +0000 Subject: [PATCH] Manually fix/ignore ruff linting errors --- app/assets.py | 2 +- .../assessments/models/applicants_response.py | 6 +- .../assessments/models/round_summary.py | 2 +- app/blueprints/assessments/routes.py | 19 ++- app/blueprints/assessments/status.py | 5 +- app/blueprints/scoring/helpers.py | 4 +- app/blueprints/scoring/routes.py | 2 +- app/blueprints/services/aws.py | 6 +- app/blueprints/services/data_services.py | 130 ++++++++++++------ app/blueprints/services/models/question.py | 2 +- app/blueprints/tagging/routes.py | 24 +++- .../themes/deprecated_theme_mapper.py | 10 +- app/error_handlers.py | 12 +- create_app.py | 4 +- tests/api_data/example_application_answers.py | 2 + tests/api_data/test_data.py | 1 + tests/test_deprecated_theme_mapper.py | 4 +- tests/test_jinja_macros.py | 2 +- tests/test_validation.py | 2 +- 19 files changed, 151 insertions(+), 88 deletions(-) diff --git a/app/assets.py b/app/assets.py index cc53194a..2dfd3c99 100644 --- a/app/assets.py +++ b/app/assets.py @@ -66,4 +66,4 @@ def compile_static_assets(assets, flask_app): f" ({flask_app.static_folder}). Please check the static folder" " exists and expected static files exist at the relative paths," f" defined here in the contents of the bundle object -> {e}." - ) + ) from e diff --git a/app/blueprints/assessments/models/applicants_response.py b/app/blueprints/assessments/models/applicants_response.py index e1da114e..ffc61d9d 100644 --- a/app/blueprints/assessments/models/applicants_response.py +++ b/app/blueprints/assessments/models/applicants_response.py @@ -214,7 +214,7 @@ def _convert_to_month_year(input_date): return str(date_object.strftime("%B %Y")) -def _ui_component_from_factory(item: dict, application_id: str): +def _ui_component_from_factory(item: dict, application_id: str): # noqa: C901 """ :param item: dict A dictionary representing the UI component to create. It must contain the following keys: @@ -439,14 +439,14 @@ def _get_item_by_presentation_type_index(response: list[dict], presentation_type for idx, item in enumerate((item for item in response if item["presentation_type"] == presentation_type)) if idx == index ) - except StopIteration: + except StopIteration as e: raise ValueError( "Could not find item with presentation_type:" f" {presentation_type} at index: {index}\nThis probably means" " there is an uneven number of 'heading', 'description' and" " 'amount' items\nThere should be an equal number of each of" " these items" - ) + ) from e def _convert_checkbox_items( diff --git a/app/blueprints/assessments/models/round_summary.py b/app/blueprints/assessments/models/round_summary.py index a8e625b4..25293bcc 100644 --- a/app/blueprints/assessments/models/round_summary.py +++ b/app/blueprints/assessments/models/round_summary.py @@ -139,7 +139,7 @@ def _populate_live_round_stats(round_id_to_summary_map, live_rounds, fund) -> di return round_id_to_summary_map -def create_round_summaries(fund: Fund, filters: LandingFilters) -> list[RoundSummary]: +def create_round_summaries(fund: Fund, filters: LandingFilters) -> list[RoundSummary]: # noqa: C901 """Get all the round stats in a fund.""" access_controller = AssessmentAccessController(fund.short_name) diff --git a/app/blueprints/assessments/routes.py b/app/blueprints/assessments/routes.py index e3c34f3c..2f27da58 100644 --- a/app/blueprints/assessments/routes.py +++ b/app/blueprints/assessments/routes.py @@ -269,7 +269,8 @@ def _get_fund_dashboard_data(fund: Fund, round: Round, request): if users_not_mapped: current_app.logger.warning( "The following users were assigned applications but could not be" - f"found in the account store: {users_not_mapped}" + "found in the account store: {users_not_mapped}", + extra=dict(users_not_mapped=users_not_mapped), ) tags_in_application_map, tag_option_groups = get_tag_map_and_tag_options( @@ -803,7 +804,7 @@ def assessor_comments(fund_short_name: str, round_short_name: str): ) @check_access_fund_short_name_round_sn @check_access_fund_short_name_round_sn(roles_required=[Config.LEAD_ASSESSOR]) -def assignment_overview(fund_short_name: str, round_short_name: str): +def assignment_overview(fund_short_name: str, round_short_name: str): # noqa: C901 # Options to navigate back in the flow to change selections if "change_users" in request.form: return redirect( @@ -1010,7 +1011,8 @@ def assignment_overview(fund_short_name: str, round_short_name: str): if future.result() is None: user_id, application_id = future_assignments[future].split(",") current_app.logger.error( - f"Could not create assignment for user {user_id} and application {application_id}" + "Could not create assignment for user {user_id} and application {application_id}", + extra=dict(user_id=user_id, application_id=application_id), ) return redirect( @@ -1136,14 +1138,14 @@ def display_sub_criteria( """ Page showing sub criteria and themes for an application """ - current_app.logger.info(f"Processing GET to {request.path}.") + current_app.logger.info("Processing GET to {request_path}.", extra=dict(request_path=request.path)) sub_criteria = get_sub_criteria(application_id, sub_criteria_id) theme_id = request.args.get("theme_id", sub_criteria.themes[0].id) comment_form = CommentsForm() try: current_theme: Theme = next(iter(t for t in sub_criteria.themes if t.id == theme_id)) except StopIteration: - current_app.logger.warning("Unknown theme ID requested: " + theme_id) + current_app.logger.warning("Unknown theme ID requested: {theme_id}", extra=dict(theme_id=theme_id)) abort(404) add_comment_argument = request.args.get("add_comment") == "1" if add_comment_argument and comment_form.validate_on_submit(): @@ -1257,7 +1259,9 @@ def display_sub_criteria( @assessment_bp.route("/application//export", methods=["GET"]) @check_access_application_id(roles_required=[Config.LEAD_ASSESSOR]) def generate_doc_list_for_download(application_id): - current_app.logger.info(f"Generating docs for application id {application_id}") + current_app.logger.info( + "Generating docs for application id {application_id}", extra=dict(application_id=application_id) + ) state = get_state_for_tasklist_banner(application_id) short_id = state.short_id[-6:] flags_list = get_flags(application_id) @@ -1300,7 +1304,8 @@ def generate_doc_list_for_download(application_id): @check_access_application_id(roles_required=[Config.LEAD_ASSESSOR]) def download_application_answers(application_id: str, short_id: str, file_type: str): current_app.logger.info( - f"Generating application Q+A download for application {application_id} in {file_type} format" + "Generating application Q+A download for application {application_id} in {file_type} format", + extra=dict(application_id=application_id, file_type=file_type), ) application_json = get_application_json(application_id) application_json_blob = application_json["jsonb_blob"] diff --git a/app/blueprints/assessments/status.py b/app/blueprints/assessments/status.py index e52f97b7..f34a8801 100644 --- a/app/blueprints/assessments/status.py +++ b/app/blueprints/assessments/status.py @@ -55,4 +55,7 @@ def update_ar_status_to_qa_completed(application_id, user_id): current_app.logger.info("The application status has been updated to QA_COMPLETE") return response else: - current_app.logger.error(f"Could not create qa_complete record for application {application_id}") + current_app.logger.error( + "Could not create qa_complete record for application {application_id}", + extra=dict(application_id=application_id), + ) diff --git a/app/blueprints/scoring/helpers.py b/app/blueprints/scoring/helpers.py index 0920c4bc..4b4ab261 100644 --- a/app/blueprints/scoring/helpers.py +++ b/app/blueprints/scoring/helpers.py @@ -19,7 +19,9 @@ def get_scoring_class(round_id): } scoring_form_class = class_mapping[scoring_system] except KeyError: - current_app.logger.error(f"Scoring system '{scoring_system}' not found.") + current_app.logger.error( + "Scoring system '{scoring_system}' not found.", extra=dict(scoring_system=scoring_system) + ) abort( 500, description=f"Scoring system '{scoring_system}' for round {round_id} has not been configured.", diff --git a/app/blueprints/scoring/routes.py b/app/blueprints/scoring/routes.py index 5dafce0d..5d293c39 100644 --- a/app/blueprints/scoring/routes.py +++ b/app/blueprints/scoring/routes.py @@ -48,7 +48,7 @@ def score( is_rescore = rescore_form.validate_on_submit() if not is_rescore and request.method == "POST": if score_form.validate_on_submit(): - current_app.logger.info(f"Processing POST to {request.path}.") + current_app.logger.info("Processing POST to {request_path}.", extra=dict(rqeuest_path=request.path)) score = int(score_form.score.data) user_id = g.account_id justification = score_form.justification.data diff --git a/app/blueprints/services/aws.py b/app/blueprints/services/aws.py index dfad49ec..ba3c26b9 100644 --- a/app/blueprints/services/aws.py +++ b/app/blueprints/services/aws.py @@ -31,7 +31,9 @@ def get_file_for_download_from_aws(file_name: str, application_id: str): prefixed_file_name = application_id + "/" + file_name try: - current_app.logger.info(f"Retrieving file {prefixed_file_name} from AWS") + current_app.logger.info( + "Retrieving file {prefixed_file_name} from AWS", extra=dict(prefixed_file_name=prefixed_file_name) + ) obj = _S3_CLIENT.get_object(Bucket=Config.AWS_BUCKET_NAME, Key=prefixed_file_name) mimetype = obj["ResponseMetadata"]["HTTPHeaders"]["content-type"] @@ -40,7 +42,7 @@ def get_file_for_download_from_aws(file_name: str, application_id: str): return data, mimetype except ClientError as e: current_app.logger.error(e) - raise Exception(e) + raise Exception(e) from e def list_files_in_folder(prefix): diff --git a/app/blueprints/services/data_services.py b/app/blueprints/services/data_services.py index de99b903..2eeda8b8 100644 --- a/app/blueprints/services/data_services.py +++ b/app/blueprints/services/data_services.py @@ -1,4 +1,3 @@ -import traceback from copy import deepcopy from functools import lru_cache from typing import Collection, Dict, List, Optional, Set, Union @@ -27,10 +26,13 @@ def get_data(endpoint: str, payload: Dict = None): try: if payload: - current_app.logger.info(f"Fetching data from '{endpoint}', with payload: {payload}.") + current_app.logger.info( + "Fetching data from '{endpoint}', with payload: {payload}.", + extra=dict(endpoint=endpoint, payload=payload), + ) response = requests.get(endpoint, payload) else: - current_app.logger.info(f"Fetching data from '{endpoint}'.") + current_app.logger.info("Fetching data from '{endpoint}'.", extra=dict(endpoint=endpoint)) response = requests.get(endpoint) if response.status_code == 200: if "application/json" == response.headers["Content-Type"]: @@ -38,24 +40,28 @@ def get_data(endpoint: str, payload: Dict = None): else: return response.content elif response.status_code in [204, 404]: - current_app.logger.warning(f"Request successful but no resources returned for endpoint '{endpoint}'.") + current_app.logger.warning( + "Request successful but no resources returned for endpoint '{endpoint}'.", extra=dict(endpoint=endpoint) + ) else: - current_app.logger.error(f"Could not get data for endpoint '{endpoint}' ") + current_app.logger.error("Could not get data for endpoint '{endpoint}' ", extra=dict(endpoint=endpoint)) except requests.exceptions.RequestException as e: - stack_trace = traceback.format_exc() - current_app.logger.error(f"{e}\n{stack_trace}") + current_app.logger.exception("{e}", extra=dict(e=str(e))) def get_assessment_progress(application_metadata, fund_id, round_id): application_ids_list = {"application_ids": [x.get("application_id") for x in application_metadata]} endpoint_url = Config.ASSESSMENT_PROGRESS_ENDPOINT.format(fund_id=fund_id, round_id=round_id) current_app.logger.info( - f"Fetching assessment progress from '{endpoint_url}', with json payload: {application_ids_list}." + "Fetching assessment progress from '{endpoint_url}', with json payload: {application_ids_list}.", + extra=dict(endpoint_url=endpoint_url, application_ids_list=application_ids_list), ) response = requests.post(endpoint_url, json=application_ids_list) if not response.ok: - current_app.logger.error(f"Could not get assessment progress for endpoint '{endpoint_url}'") + current_app.logger.error( + "Could not get assessment progress for endpoint '{endpoint_url}'", extra=dict(endpoint_url=endpoint_url) + ) return application_metadata response_json = response.json() @@ -77,7 +83,10 @@ def call_search_applications(params: dict | str): def get_application_assignments(application_id, only_active=False): application_assignments_endpoint = Config.ASSESSMENT_ASSIGNED_USERS_ENDPOINT.format(application_id=application_id) - current_app.logger.info(f"Endpoint '{application_assignments_endpoint}'.") + current_app.logger.info( + "Endpoint '{application_assignments_endpoint}'.", + extra=dict(application_assignments_endpoint=application_assignments_endpoint), + ) query_params = {"active": "true"} if only_active else {} response = get_data(application_assignments_endpoint, query_params) @@ -90,7 +99,7 @@ def get_application_overviews(fund_id, round_id, search_params): ) + Config.APPLICATION_OVERVIEW_ENDPOINT_FUND_ROUND_PARAMS.format( fund_id=fund_id, round_id=round_id, params=urlencode(search_params) ) - current_app.logger.info(f"Endpoint '{overviews_endpoint}'.") + current_app.logger.info("Endpoint '{overviews_endpoint}'.", extra=dict(overviews_endpoint=overviews_endpoint)) overviews_response = get_data(overviews_endpoint) return overviews_response @@ -114,7 +123,7 @@ def get_users_for_fund( users_for_fund = (Config.ACCOUNT_STORE_API_HOST) + Config.USER_FUND_ENDPOINT.format(fund_short_name=fund_short_name) - current_app.logger.info(f"Endpoint '{users_for_fund}'.") + current_app.logger.info("Endpoint '{users_for_fund}'.", extra=dict(users_for_fund=users_for_fund)) users_for_fund_response = get_data(users_for_fund, query_params) return users_for_fund_response @@ -128,11 +137,13 @@ def get_tags_for_fund_round(fund_id, round_id, search_params: dict | None = None ) response = get_data(endpoint) if response is not None: - current_app.logger.info(f"tags returned: {len(response)}") + current_app.logger.info("tags returned: {len}", extra=dict(len=len(response))) result = [Tag.from_dict(item) for item in response] return result else: - current_app.logger.info(f"No tags found for fund {fund_id}, round {round_id}.") + current_app.logger.info( + "No tags found for fund {fund_id}, round {round_id}.", extra=dict(fund_id=fund_id, round_id=round_id) + ) return [] @@ -140,11 +151,14 @@ def get_tag_for_fund_round(fund_id, round_id, tag_id) -> Tag: endpoint = Config.ASSESSMENT_TAG_ENDPOINT.format(fund_id=fund_id, round_id=round_id, tag_id=tag_id) response = get_data(endpoint) if response is not None: - current_app.logger.info(f"tag returned: {len(response)}") + current_app.logger.info("tag returned: {len}", extra=dict(len=len(response))) result = Tag.from_dict(response) return result else: - current_app.logger.info(f"No tag found for fund {fund_id}, round {round_id}, tag_id {tag_id}.") + current_app.logger.info( + "No tag found for fund {fund_id}, round {round_id}, tag_id {tag_id}.", + extra=dict(fund_id=fund_id, round_id=round_id, tag_id=tag_id), + ) return None @@ -159,12 +173,14 @@ def update_tags(fund_id, round_id, tags) -> bool: for tag in tags ] - current_app.logger.info(f"Requesting update for of the following tags: {payload}") + current_app.logger.info("Requesting update for of the following tags: {payload}", extra=dict(payload=payload)) response = requests.put(endpoint, json=payload) was_successful = response.ok if not was_successful: - current_app.logger.error(f"Update associated tags failed, code: {response.status_code}.") + current_app.logger.error( + "Update associated tags failed, code: {status_code}.", extra=dict(status_code=response.status_code) + ) return was_successful @@ -172,7 +188,7 @@ def get_tag_types() -> List[TagType]: endpoint = Config.ASSESSMENT_TAG_TYPES_ENDPOINT response = get_data(endpoint) if response is not None: - current_app.logger.info(f"tags returned: {len(response)}") + current_app.logger.info("tags returned: {len}", extra=dict(len=len(response))) result = [TagType.from_dict(item) for item in response] return result else: @@ -182,11 +198,14 @@ def get_tag_types() -> List[TagType]: def post_new_tag_for_fund_round(fund_id, round_id, tag) -> bool: endpoint = Config.ASSESSMENT_TAGS_ENDPOINT.format(fund_id=fund_id, round_id=round_id, params=None) - current_app.logger.info(f"Posting the following tag: {tag}for fund {fund_id} and round {round_id}.") + current_app.logger.info( + "Posting the following tag: {tag}for fund {fund_id} and round {round_id}.", + extra=dict(tag=tag, fund_id=fund_id, round_id=round_id), + ) response = requests.post(endpoint, json=tag) tag_created = response.ok if not tag_created: - current_app.logger.error(f"Post tag failed, code: {response.status_code}.") + current_app.logger.error("Post tag failed, code: {status_code}.", extra=dict(status_code=response.status_code)) return tag_created @@ -195,11 +214,13 @@ def get_associated_tags_for_application(application_id) -> List[Tag]: endpoint = Config.ASSESSMENT_ASSOCIATE_TAGS_ENDPOINT.format(application_id=application_id) result = get_data(endpoint) if result: - current_app.logger.info(f"tags returned: {len(result)}") + current_app.logger.info("tags returned: {len}", extra=dict(len=len(result))) result = [AssociatedTag.from_dict(item) for item in result if item["associated"] is True] return result else: - current_app.logger.info(f"No associated tags found for application: {application_id}.") + current_app.logger.info( + "No associated tags found for application: {application_id}.", extra=dict(application_id=application_id) + ) return None @@ -210,7 +231,9 @@ def get_all_associated_tags_for_application(application_id) -> List[Tag]: result = [AssociatedTag.from_dict(item) for item in result] return result else: - current_app.logger.info(f"No associated tags found for application: {application_id}.") + current_app.logger.info( + "No associated tags found for application: {application_id}.", extra=dict(application_id=application_id) + ) return [] @@ -219,12 +242,15 @@ def update_associated_tags(application_id, tags) -> bool: payload = [{"id": tag["tag_id"], "user_id": tag["user_id"]} for tag in tags] current_app.logger.info( - f"Requesting the following tags: {payload} associate with application_id '{application_id}'" + "Requesting the following tags: {payload} associate with application_id '{application_id}'", + extra=dict(payload=payload, application_id=application_id), ) response = requests.put(endpoint, json=payload) was_successful = response.ok if not was_successful: - current_app.logger.error(f"Update associated tags failed, code: {response.status_code}.") + current_app.logger.error( + "Update associated tags failed, code: {status_code}.", extra=dict(status_code=response.status_code) + ) return was_successful @@ -335,11 +361,14 @@ def get_score_and_justification(application_id, sub_criteria_id=None, score_hist } score_response = get_data(score_url, score_params) if score_response: - current_app.logger.info(f"Response from Assessment Store: '{score_response}'.") + current_app.logger.info( + "Response from Assessment Store: '{score_response}'.", extra=dict(score_response=score_response) + ) else: current_app.logger.info( - f"No scores found for application: {application_id}, sub_criteria_id: {sub_criteria_id}" + "No scores found for application: {application_id}, sub_criteria_id: {sub_criteria_id}", + extra=dict(application_id=application_id, sub_criteria_id=sub_criteria_id), ) return score_response @@ -374,7 +403,7 @@ def submit_score_and_justification(score, justification, application_id, user_id } url = Config.ASSESSMENT_SCORES_ENDPOINT response = requests.post(url, json=data_dict) - current_app.logger.info(f"Response from Assessment Store: '{response.json()}'.") + current_app.logger.info("Response from Assessment Store: '{data}'.", extra=dict(data=response.json())) return response.ok @@ -402,7 +431,9 @@ def get_assessments_stats(fund_id: str, round_ids: Collection[str], search_param assessments_stats_endpoint = (Config.ASSESSMENT_STORE_API_HOST) + Config.ASSESSMENTS_STATS_ENDPOINT.format( fund_id=fund_id, params=urlencode(search_params) ) - current_app.logger.info(f"Endpoint '{assessments_stats_endpoint}'.") + current_app.logger.info( + "Endpoint '{assessments_stats_endpoint}'.", extra=dict(assessments_stats_endpoint=assessments_stats_endpoint) + ) response = requests.post(assessments_stats_endpoint, json={"round_ids": list(round_ids)}) return response.json() @@ -463,9 +494,10 @@ def get_sub_criteria(application_id, sub_criteria_id): if sub_criteria_response and "id" in sub_criteria_response: return SubCriteria.from_filtered_dict(sub_criteria_response) else: - msg = f"sub_criteria: '{sub_criteria_id}' not found." - current_app.logger.warning(msg) - abort(404, description=msg) + current_app.logger.warning( + "sub_criteria: '{sub_criteria_id}' not found.", extra=dict(sub_criteria_id=sub_criteria_id) + ) + abort(404, description=f"sub_criteria: '{sub_criteria_id}' not found.") def get_sub_criteria_banner_state(application_id: str): @@ -479,9 +511,10 @@ def get_sub_criteria_banner_state(application_id: str): if banner: return Banner.from_filtered_dict(banner) else: - msg = f"banner_state: '{application_id}' not found." - current_app.logger.warning(msg) - abort(404, description=msg) + current_app.logger.warning( + "banner_state: '{application_id}' not found.", extra=dict(application_id=application_id) + ) + abort(404, description=f"banner_state: '{application_id}' not found.") def get_flag(flag_id: str) -> Optional[Flag]: @@ -489,8 +522,7 @@ def get_flag(flag_id: str) -> Optional[Flag]: if flag: return Flag.from_dict(flag) else: - msg = f"flag for id: '{flag_id}' not found." - current_app.logger.warning(msg) + current_app.logger.warning("flag for id: '{flag_id}' not found.", extra=dict(flag_id=flag_id)) return None @@ -617,7 +649,8 @@ def get_comments( if not comment_response or len(comment_response) == 0: current_app.logger.info( - f"No comments found for application: {application_id}, sub_criteria_id: {sub_criteria_id}" + "No comments found for application: {application_id}, sub_criteria_id: {sub_criteria_id}", + extra=dict(application_id=application_id, sub_criteria_id=sub_criteria_id), ) return None else: @@ -690,7 +723,7 @@ def submit_comment( url = Config.ASSESSMENT_COMMENT_ENDPOINT response = requests.put(url, json=data_dict) - current_app.logger.info(f"Response from Assessment Store: '{response.json()}'.") + current_app.logger.info("Response from Assessment Store: '{data}'.", extra=dict(data=response.json())) return response.ok @@ -731,7 +764,7 @@ def update_tag(fund_id: str, round_id: str, updated_tag: Dict) -> Tag: if response.status_code == 200: return response.json()[0] - current_app.logger.error(f"Unable to update tag: {updated_tag}") + current_app.logger.error("Unable to update tag: {updated_tag}", extra=dict(updated_tag=updated_tag)) return None @@ -740,7 +773,9 @@ def get_applicant_export(fund_id, round_id, report_type): fund_id=fund_id, round_id=round_id, report_type=report_type ) - current_app.logger.info(f"Endpoint '{applicant_export_endpoint}'.") + current_app.logger.info( + "Endpoint '{applicant_export_endpoint}'.", extra=dict(applicant_export_endpoint=applicant_export_endpoint) + ) applicant_export_response = get_data(applicant_export_endpoint) return applicant_export_response @@ -751,7 +786,9 @@ def get_applicant_feedback_and_survey_report(fund_id, round_id, status_only): fund_id=fund_id, round_id=round_id, status_only=status_only ) - current_app.logger.info(f"Endpoint '{applicant_feedback_endpoint}'.") + current_app.logger.info( + "Endpoint '{applicant_feedback_endpoint}'.", extra=dict(applicant_feedback_endpoint=applicant_feedback_endpoint) + ) response = get_data(applicant_feedback_endpoint) return response @@ -759,7 +796,7 @@ def get_applicant_feedback_and_survey_report(fund_id, round_id, status_only): def get_scoring_system(round_id: str) -> List[Flag]: scoring_endpoint = Config.ASSESSMENT_SCORING_SYSTEM_ENDPOINT.format(round_id=round_id) - current_app.logger.info(f"Calling endpoint '{scoring_endpoint}'.") + current_app.logger.info("Calling endpoint '{scoring_endpoint}'.", extra=dict(scoring_endpoint=scoring_endpoint)) scoring_system = get_data(scoring_endpoint)["scoring_system"] return scoring_system @@ -795,7 +832,10 @@ def assign_user_to_assessment( else: response = requests.post(assignment_endpoint, json=json_body) if not response.ok: - current_app.logger.error(f"Could not get assign user {user_id} to application {application_id}") + current_app.logger.error( + "Could not get assign user {user_id} to application {application_id}", + extra=dict(user_id=user_id, application_id=application_id), + ) return None return response.json() diff --git a/app/blueprints/services/models/question.py b/app/blueprints/services/models/question.py index 9668f4b6..778d2bb6 100644 --- a/app/blueprints/services/models/question.py +++ b/app/blueprints/services/models/question.py @@ -31,7 +31,7 @@ def as_structured_question(self): for question_field in self.fields: answer = question_field.answer try: - if type(ast.literal_eval(answer)) == list: + if type(ast.literal_eval(answer)) is list: answers_per_question_field[question_field.title] = ast.literal_eval(answer) continue except (ValueError, SyntaxError): diff --git a/app/blueprints/tagging/routes.py b/app/blueprints/tagging/routes.py index 0add68f2..3d9edc99 100644 --- a/app/blueprints/tagging/routes.py +++ b/app/blueprints/tagging/routes.py @@ -207,7 +207,7 @@ def create_tag(fund_id, round_id): return redirect(url_for("tagging_bp.create_tag", fund_id=fund_id, round_id=round_id)) elif request.method == "POST": - current_app.logger.info(f"Tag creation form failed validation: {new_tag_form.errors}") + current_app.logger.info("Tag creation form failed validation: {errors}", extra=dict(errors=new_tag_form.errors)) errors = new_tag_form.errors return render_template( @@ -244,7 +244,9 @@ def deactivate_tag(fund_id, round_id, tag_id): "round_id": round_id, } if deactivate_tag_form.validate_on_submit(): - current_app.logger.info(f"Tag deactivation form validated, deactivating tag_id: {tag_id}.") + current_app.logger.info( + "Tag deactivation form validated, deactivating tag_id: {tag_id}.", extra=dict(tag_id=tag_id) + ) tag_update_to_deactivate = [{"id": tag_id, "active": False}] tag_deactivated = update_tags(fund_id, round_id, tag_update_to_deactivate) if tag_deactivated: @@ -258,7 +260,9 @@ def deactivate_tag(fund_id, round_id, tag_id): flash(TAG_DEACTIVATE_ERROR_MESSAGE) elif request.method == "POST": - current_app.logger.info(f"Tag deactivation form failed validation: {deactivate_tag_form.errors}") + current_app.logger.info( + "Tag deactivation form failed validation: {errors}", extra=dict(errors=deactivate_tag_form.errors) + ) flash(TAG_DEACTIVATE_ERROR_MESSAGE) return render_template( "deactivate_tag.html", @@ -293,7 +297,9 @@ def reactivate_tag(fund_id, round_id, tag_id): "round_id": round_id, } if reactivate_tag_form.validate_on_submit(): - current_app.logger.info(f"Tag reactivation form validated, reactivating tag_id: {tag_id}.") + current_app.logger.info( + "Tag reactivation form validated, reactivating tag_id: {tag_id}.", extra=dict(tag_id=tag_id) + ) tag_to_reactivate = [{"id": tag_id, "active": True}] tag_reactivated = update_tags(fund_id, round_id, tag_to_reactivate) if tag_reactivated: @@ -306,7 +312,9 @@ def reactivate_tag(fund_id, round_id, tag_id): ) flash(TAG_REACTIVATE_ERROR_MESSAGE) elif request.method == "POST": - current_app.logger.info(f"Tag reactivation form failed validation: {reactivate_tag_form.errors}") + current_app.logger.info( + "Tag reactivation form failed validation: {errors}", extra=dict(errors=reactivate_tag_form.errors) + ) flash(TAG_REACTIVATE_ERROR_MESSAGE) return render_template( "reactivate_tag.html", @@ -325,7 +333,7 @@ def edit_tag(fund_id, round_id, tag_id): fund_round = get_fund_round(fund_id, round_id) tag = get_tag(fund_id, round_id, tag_id) if request.method == "GET": - current_app.logger.info(f"Loading edit tag page for id {tag_id}") + current_app.logger.info("Loading edit tag page for id {tag_id}", extra=dict(tag_id=tag_id)) elif request.method == "POST": current_app.logger.info("In edit tag post") @@ -343,7 +351,9 @@ def edit_tag(fund_id, round_id, tag_id): else: flash("An error occurred and your changes were not saved. Please try again later.") else: - current_app.logger.info(f"Edit tag form failed validation: {edit_tag_form.errors}") + current_app.logger.info( + "Edit tag form failed validation: {errors}", extra=dict(errors=edit_tag_form.errors) + ) flash(FLAG_ERROR_MESSAGE) return render_template( diff --git a/app/blueprints/themes/deprecated_theme_mapper.py b/app/blueprints/themes/deprecated_theme_mapper.py index a54c53e6..8f674b37 100644 --- a/app/blueprints/themes/deprecated_theme_mapper.py +++ b/app/blueprints/themes/deprecated_theme_mapper.py @@ -28,7 +28,7 @@ def map_application_with_sub_criteria_themes_fields( else: map_single_field_answer(theme, questions) except TypeError: - current_app.logger.error(f"Incorrect theme id -> {theme_id}") + current_app.logger.error("Incorrect theme id -> {theme_id}", extra=dict(theme_id=theme_id)) return f"Incorrect theme id -> {theme_id}" convert_boolean_values(themes_fields) @@ -98,7 +98,7 @@ def get_themes_fields(sub_criterias, theme_id) -> str | Any: if theme_id == theme.get("id") ][0] except IndexError: - current_app.logger.error(f"Incorrect theme id -> {theme_id}") + current_app.logger.error("Incorrect theme id -> {theme_id}", extra=dict(theme_id=theme_id)) return f"Incorrect theme id -> {theme_id}" @@ -158,10 +158,12 @@ def deprecated_sort_add_another_component_contents( amount_answer = [amount.rsplit(": ", 1)[1] for amount in theme["answer"]] theme["answer"] = amount_answer except (KeyError, IndexError): - current_app.logger.debug(f"Answer not provided for field_id: {field['field_id']}") + current_app.logger.debug( + "Answer not provided for field_id: {field_id}", extra=dict(field_id=field["field_id"]) + ) -def format_add_another_component_contents( +def format_add_another_component_contents( # noqa: C901 themes_fields: list[dict], ) -> list[dict]: for field in themes_fields: diff --git a/app/error_handlers.py b/app/error_handlers.py index 026c2a83..0f6dab18 100644 --- a/app/error_handlers.py +++ b/app/error_handlers.py @@ -1,5 +1,3 @@ -import traceback - from flask import current_app, render_template, request from app.blueprints.assessments.routes import assessment_bp @@ -10,7 +8,7 @@ def not_found(error): - current_app.logger.info(f"Encountered 404 against url {request.path}") + current_app.logger.info("Encountered 404 against url {request_path}", extra=dict(request_path=request.path)) return render_template("404.html"), 404 @@ -22,9 +20,7 @@ def forbidden(error): else error.description ) - error_message = f"Encountered 403: {error}" - stack_trace = traceback.format_exc() - current_app.logger.info(f"{error_message}\n{stack_trace}") + current_app.logger.info("Encountered 403: {error}", extra=dict(error=str(error))) return ( render_template("403.html", error_description=error.description), 403, @@ -46,7 +42,5 @@ def error_503(error): @scoring_bp.errorhandler(500) @scoring_bp.errorhandler(Exception) def internal_server_error(error): - error_message = f"Encountered 500: {error}" - stack_trace = traceback.format_exc() - current_app.logger.error(f"{error_message}\n{stack_trace}") + current_app.logger.exception("Encountered 500: {error}", extra=dict(error=str(error))) return render_template("500.html"), 500 diff --git a/create_app.py b/create_app.py index 80649f76..ddb9ddc2 100644 --- a/create_app.py +++ b/create_app.py @@ -139,8 +139,8 @@ def check_for_maintenance(): request.path.endswith("js") or request.path.endswith("css") or request.path.endswith("/healthcheck") ): current_app.logger.warning( - f"""Application is in the Maintenance mode - reach url: {request.url}""" + "Application is in the Maintenance mode reach url: {request_url}", + extra=dict(request_url=request.url), ) return ( render_template( diff --git a/tests/api_data/example_application_answers.py b/tests/api_data/example_application_answers.py index cf144aaa..bc3c15ee 100644 --- a/tests/api_data/example_application_answers.py +++ b/tests/api_data/example_application_answers.py @@ -1,3 +1,5 @@ +# ruff: noqa + test_application_answers = { "feasibility": { "Tell us about the feasibility studies you have carried out for your project": "lots of surveys", diff --git a/tests/api_data/test_data.py b/tests/api_data/test_data.py index a2cbf7bc..66fd05c2 100644 --- a/tests/api_data/test_data.py +++ b/tests/api_data/test_data.py @@ -1,5 +1,6 @@ # There is config for any linked information shared across the mock api queries # General config +# ruff: noqa from dataclasses import dataclass from app.blueprints.services.models.flag import FlagType diff --git a/tests/test_deprecated_theme_mapper.py b/tests/test_deprecated_theme_mapper.py index cf044858..1f162ea3 100644 --- a/tests/test_deprecated_theme_mapper.py +++ b/tests/test_deprecated_theme_mapper.py @@ -213,4 +213,6 @@ def test_deprecated_sort_add_another_component_contents_log_when_no_answer( deprecated_sort_add_another_component_contents(themes_answers) - current_app.logger.debug.assert_called_with("Answer not provided for field_id: 123") + current_app.logger.debug.assert_called_with( + "Answer not provided for field_id: {field_id}", extra=dict(field_id="123") + ) diff --git a/tests/test_jinja_macros.py b/tests/test_jinja_macros.py index 4565fa46..074c32d9 100644 --- a/tests/test_jinja_macros.py +++ b/tests/test_jinja_macros.py @@ -1070,4 +1070,4 @@ def test_dashboard_summary_round_links( found_links = soup.find_all("a", class_="govuk-link") assert len(found_links) == len(exp_links_text) for text in exp_links_text: - assert soup.find("a", class_="govuk-link", string=lambda str: text in str) + assert soup.find("a", class_="govuk-link", string=lambda str: text in str) # noqa: B023 diff --git a/tests/test_validation.py b/tests/test_validation.py index 1c74f2c1..6a31b9cd 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -50,7 +50,7 @@ def test__normalise_country(country, expected): def test__get_all_country_roles(): - _get_all_country_roles("COF") == { + assert _get_all_country_roles("COF") == { "cof_england", "cof_scotland", "cof_wales",