diff --git a/.env.development b/.env.development index 2972ec8c..2940eef6 100644 --- a/.env.development +++ b/.env.development @@ -4,9 +4,11 @@ FLASK_RUN_PORT=5000 FLASK_RUN_HOST=localhost # Below credentials belongs to local development server. Do not commit any external services credentials +# pragma: allowlist nextline secret DATABASE_URL=postgresql://postgres:password@localhost:5432/assessment_store APPLICATION_STORE_API_HOST=http://localhost:3002 AWS_ACCESS_KEY_ID=FSDIOSFODNN7EXAMPLE +# pragma: allowlist nextline secret AWS_SECRET_ACCESS_KEY=fsdlrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY AWS_ENDPOINT_OVERRIDE=http://localhost:4566 AWS_REGION=eu-west-2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7549736e..9b85ba91 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,39 +1,35 @@ repos: -- repo: https://github.com/psf/black - rev: 23.10.1 - hooks: - - id: black - language_version: python3 - args: - - --target-version=py310 -- repo: https://github.com/PyCQA/flake8 - rev: 7.0.0 + - repo: https://github.com/PyCQA/docformatter + # We are pinning a specific hash here because the current latest released version (v1.7.5) is + # incompatible with pre-commit v4+ + # issue: https://github.com/PyCQA/docformatter/issues/289 + rev: eb1df347edd128b30cd3368dddc3aa65edcfac38 hooks: - - id: flake8 - additional_dependencies: [Flake8-pyproject] -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-ast -- repo: https://github.com/asottile/reorder-python-imports - rev: v3.12.0 - hooks: - - id: reorder-python-imports - name: Reorder Python imports (src, tests) - args: ["--application-directories", "src"] -- repo: https://github.com/PyCQA/docformatter - # We are pinning a specific hash here because the current latest released version (v1.7.5) is - # incompatible with pre-commit v4+ - # issue: https://github.com/PyCQA/docformatter/issues/289 - rev: eb1df347edd128b30cd3368dddc3aa65edcfac38 - hooks: - - id: docformatter - additional_dependencies: [tomli] -- repo: https://github.com/Yelp/detect-secrets - rev: v1.4.0 - hooks: - - id: detect-secrets - args: ['--disable-plugin', 'HexHighEntropyString'] - exclude: .env.development + - id: docformatter + additional_dependencies: [tomli] + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-ast + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. If bumping this, please also bump requirements-dev.in + rev: v0.7.4 + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format + - repo: https://github.com/Yelp/detect-secrets + rev: v1.4.0 + hooks: + - id: detect-secrets + args: + [ + "--disable-plugin", + "HexHighEntropyString", + "--disable-plugin", + "Base64HighEntropyString", + ] diff --git a/_helpers/task_executer_service.py b/_helpers/task_executer_service.py index fa49bf8c..92ea4be2 100644 --- a/_helpers/task_executer_service.py +++ b/_helpers/task_executer_service.py @@ -1,9 +1,10 @@ import json import threading +from fsd_utils.sqs_scheduler.task_executer_service import TaskExecutorService + from config.mappings.assessment_mapping_fund_round import fund_round_data_key_mappings from db.queries import bulk_insert_application_record -from fsd_utils.sqs_scheduler.task_executer_service import TaskExecutorService class AssessmentTaskExecutorService(TaskExecutorService): @@ -12,7 +13,7 @@ def message_executor(self, message): the GOV notify service to send emails :param message Json message.""" current_thread = threading.current_thread() thread_id = f"[{current_thread.name}:{current_thread.ident}]" - self.logger.info(f"[{thread_id}] Notification Triggered") + self.logger.info("[{thread_id}] Notification Triggered", extra=dict(thread_id=thread_id)) message_id = message["sqs"]["MessageId"] try: application_json = json.loads(message["s3"]) @@ -20,13 +21,21 @@ def message_executor(self, message): fund_round_shortname = "".join(application_json["reference"].split("-")[:2]) # Check if the import config exists for the application if fund_round_shortname not in fund_round_data_key_mappings.keys(): - self.logger.warning(f"Missing import config for the application: {application_json['reference']}.") + self.logger.warning( + "Missing import config for the application: {reference}.", + extra=dict(reference=application_json["reference"]), + ) return message application_json_list.append(application_json) bulk_insert_application_record(application_json_list, is_json=True) - self.logger.info(f"{thread_id} Processed the message: {message_id}") + self.logger.info( + "{thread_id} Processed the message: {message_id}", + extra=dict(thread_id=thread_id, message_id=message_id), + ) return message - except Exception as e: - self.logger.error(f"An error occurred while processing the message {message_id}", e) + except Exception: + self.logger.exception( + "An error occurred while processing the message {message_id}", extra=dict(message_id=message_id) + ) diff --git a/api/__init__.py b/api/__init__.py index 6a756a83..a57e9901 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -1,4 +1,3 @@ """ .. include:: ./README.md """ -# flake8: noqa diff --git a/api/models/notification.py b/api/models/notification.py index 2576b86f..fcd72499 100644 --- a/api/models/notification.py +++ b/api/models/notification.py @@ -1,9 +1,10 @@ import json from uuid import uuid4 -from config import Config from flask import current_app +from config import Config + NOTIFICATION_CONST = "notification" NOTIFICATION_S3_KEY_CONST = "application/notification" @@ -30,7 +31,10 @@ def send(template_type: str, to_email: str, full_name: str, content: dict): "full_name": full_name, "content": content, } - current_app.logger.info(f" json payload '{template_type}' to '{to_email}'.") + current_app.logger.info( + " json payload '{template_type}' to '{to_email}'.", + extra=dict(template_type=template_type, to_email=to_email), + ) try: sqs_extended_client = Notification._get_sqs_client() message_id = sqs_extended_client.submit_single_message( @@ -45,11 +49,12 @@ def send(template_type: str, to_email: str, full_name: str, content: dict): }, }, ) - current_app.logger.info(f"Message sent to SQS queue and message id is [{message_id}]") + current_app.logger.info( + "Message sent to SQS queue and message id is [{message_id}]", extra=dict(message_id=message_id) + ) return message_id - except Exception as e: - current_app.logger.error("An error occurred while sending message") - current_app.logger.error(e) + except Exception: + current_app.logger.exception("An error occurred while sending message") @staticmethod def _get_sqs_client(): diff --git a/api/models/sub_criteria.py b/api/models/sub_criteria.py index f837019c..6557a42b 100644 --- a/api/models/sub_criteria.py +++ b/api/models/sub_criteria.py @@ -2,9 +2,10 @@ import inspect from dataclasses import dataclass -from api.models.theme import Theme from dataclass_dict_convert import dataclass_dict_convert +from api.models.theme import Theme + @dataclass_dict_convert() @dataclass() diff --git a/api/models/theme.py b/api/models/theme.py index d65797f2..b749ab21 100644 --- a/api/models/theme.py +++ b/api/models/theme.py @@ -2,9 +2,10 @@ from dataclasses import dataclass from typing import List -from api.models.answer import Answer from dataclass_dict_convert import dataclass_dict_convert +from api.models.answer import Answer + @dataclass_dict_convert() @dataclass() diff --git a/api/routes/__init__.py b/api/routes/__init__.py index d836a360..382bd6f5 100644 --- a/api/routes/__init__.py +++ b/api/routes/__init__.py @@ -1,39 +1,44 @@ -from .assessment_routes import all_assessments_for_fund_round_id -from .assessment_routes import assessment_metadata_for_application_id -from .assessment_routes import assessment_stats_for_fund_round_id -from .assessment_routes import assessment_stats_for_multiple_round_ids -from .assessment_routes import get_application_data_for_export -from .assessment_routes import get_application_json -from .assessment_routes import get_application_json_and_sub_criterias -from .assessment_routes import get_assessor_task_list_state -from .assessment_routes import get_banner_state -from .assessment_routes import sub_criteria -from .assessment_routes import update_ar_status_to_completed -from .comment_routes import get_comments -from .comment_routes import post_comments -from .comment_routes import put_comments -from .flag_routes import create_flag_for_application -from .flag_routes import get_all_flags_for_application -from .flag_routes import get_flag -from .flag_routes import get_team_flag_stats -from .flag_routes import update_flag_for_application -from .progress_routes import get_progress_for_applications -from .progress_routes import post_progress_for_applications -from .qa_complete_routes import post_qa_complete_for_application -from .qa_complete_routes import qa_complete_record_for_application -from .score_routes import get_score_for_application_sub_criteria -from .score_routes import get_scoring_system_name_for_round_id -from .score_routes import post_score_for_application_sub_criteria -from .tag_routes import add_tag_for_fund_round -from .tag_routes import associate_tags_with_assessment -from .tag_routes import get_active_tags_associated_with_assessment -from .tag_routes import get_tag -from .tag_routes import get_tags_for_fund_round -from .user_routes import add_user_application_association -from .user_routes import get_all_users_associated_with_application -from .user_routes import get_user_application_association -from .user_routes import update_user_application_association - +from .assessment_routes import ( + all_assessments_for_fund_round_id, + assessment_metadata_for_application_id, + assessment_stats_for_fund_round_id, + assessment_stats_for_multiple_round_ids, + get_application_data_for_export, + get_application_json, + get_application_json_and_sub_criterias, + get_assessor_task_list_state, + get_banner_state, + sub_criteria, + update_ar_status_to_completed, +) +from .comment_routes import get_comments, post_comments, put_comments +from .flag_routes import ( + create_flag_for_application, + get_all_flags_for_application, + get_flag, + get_team_flag_stats, + update_flag_for_application, +) +from .progress_routes import get_progress_for_applications, post_progress_for_applications +from .qa_complete_routes import post_qa_complete_for_application, qa_complete_record_for_application +from .score_routes import ( + get_score_for_application_sub_criteria, + get_scoring_system_name_for_round_id, + post_score_for_application_sub_criteria, +) +from .tag_routes import ( + add_tag_for_fund_round, + associate_tags_with_assessment, + get_active_tags_associated_with_assessment, + get_tag, + get_tags_for_fund_round, +) +from .user_routes import ( + add_user_application_association, + get_all_users_associated_with_application, + get_user_application_association, + update_user_application_association, +) __all__ = [ "all_assessments_for_fund_round_id", diff --git a/api/routes/_helpers.py b/api/routes/_helpers.py index 552b5cba..f9fd0837 100644 --- a/api/routes/_helpers.py +++ b/api/routes/_helpers.py @@ -2,10 +2,10 @@ import json from uuid import UUID +from flask import current_app, make_response + from config import Config from db.models.assessment_record.enums import Status -from flask import current_app -from flask import make_response def compress_response(data): diff --git a/api/routes/assessment_routes.py b/api/routes/assessment_routes.py index 5700fa29..b9fd5047 100644 --- a/api/routes/assessment_routes.py +++ b/api/routes/assessment_routes.py @@ -1,15 +1,12 @@ -# flake8: noqa import copy -from typing import Dict -from typing import List +from typing import Dict, List + +from flask import current_app, request from api.models.sub_criteria import SubCriteria -from api.routes._helpers import compress_response -from api.routes._helpers import transform_to_assessor_task_list_metadata +from api.routes._helpers import compress_response, transform_to_assessor_task_list_metadata from api.routes.subcriterias.get_sub_criteria import ( get_all_subcriteria, -) -from api.routes.subcriterias.get_sub_criteria import ( return_subcriteria_from_mapping, ) from config import Config @@ -18,22 +15,19 @@ ) from db.models.flags.flag_update import FlagStatus from db.queries import get_metadata_for_fund_round_id -from db.queries.assessment_records.queries import find_assessor_task_list_state -from db.queries.assessment_records.queries import get_application_jsonb_blob -from db.queries.assessment_records.queries import get_assessment_export_data from db.queries.assessment_records.queries import ( + find_assessor_task_list_state, + get_application_jsonb_blob, + get_assessment_export_data, get_assessment_sub_critera_state, + get_metadata_for_application, + update_status_to_completed, ) -from db.queries.assessment_records.queries import get_metadata_for_application -from db.queries.assessment_records.queries import update_status_to_completed from db.queries.comments.queries import get_sub_criteria_to_has_comment_map from db.queries.qa_complete.queries import ( get_qa_complete_record_for_application, ) -from db.queries.scores.queries import get_scoring_system_for_round_id -from db.queries.scores.queries import get_sub_criteria_to_latest_score_map -from flask import current_app -from flask import request +from db.queries.scores.queries import get_scoring_system_for_round_id, get_sub_criteria_to_latest_score_map def calculate_overall_score_percentage_for_application(application): @@ -45,7 +39,10 @@ def calculate_overall_score_percentage_for_application(application): highest_possible_weighted_score_for_round = 0 if mapping["scored_criteria"] == []: # We have no scoring config for this round (possibly an EOI) - current_app.logger.info(f"No scoring config found for {application['fund_id']}:{application['round_id']}") + current_app.logger.info( + "No scoring config found for {fund_id}:{round_id}", + extra=dict(fund_id=application["fund_id"], round_id=application["round_id"]), + ) return None # Combine mapping and highest possible score calculation diff --git a/api/routes/comment_routes.py b/api/routes/comment_routes.py index 51a220d5..8be5fc1c 100644 --- a/api/routes/comment_routes.py +++ b/api/routes/comment_routes.py @@ -1,10 +1,9 @@ from typing import Dict -from db.queries.comments import create_comment -from db.queries.comments import get_comments_from_db -from db.queries.comments import update_comment from flask import request +from db.queries.comments import create_comment, get_comments_from_db, update_comment + def get_comments( application_id: str = None, diff --git a/api/routes/flag_routes.py b/api/routes/flag_routes.py index 6cdd4b34..68e3e269 100644 --- a/api/routes/flag_routes.py +++ b/api/routes/flag_routes.py @@ -1,13 +1,14 @@ -# flake8: noqa +from flask import current_app, request + from db.models.flags.flag_update import FlagStatus from db.queries import get_metadata_for_fund_round_id -from db.queries.flags.queries import add_flag_for_application -from db.queries.flags.queries import add_update_to_assessment_flag -from db.queries.flags.queries import get_flag_by_id -from db.queries.flags.queries import get_flags_for_application +from db.queries.flags.queries import ( + add_flag_for_application, + add_update_to_assessment_flag, + get_flag_by_id, + get_flags_for_application, +) from db.schemas.schemas import AssessmentFlagSchema -from flask import current_app -from flask import request def _fix_country(country): @@ -69,14 +70,14 @@ def create_team_dict(team_name): def get_flag(flag_id: str): - current_app.logger.info(f"Get flags for id {flag_id}") + current_app.logger.info("Get flags for id {flag_id}", extra=dict(flag_id=flag_id)) flags = get_flag_by_id(flag_id) flag_schema = AssessmentFlagSchema() return flag_schema.dump(flags, many=True)[0] def get_all_flags_for_application(application_id): - current_app.logger.info(f"Get all flags for application {application_id}") + current_app.logger.info("Get all flags for application {application_id}", extra=dict(application_id=application_id)) flags = get_flags_for_application(application_id) flag_schema = AssessmentFlagSchema() return flag_schema.dump(flags, many=True) @@ -84,13 +85,15 @@ def get_all_flags_for_application(application_id): def create_flag_for_application(): create_flag_json = request.json - current_app.logger.info(f"Create flag for application {create_flag_json['application_id']}") + current_app.logger.info( + "Create flag for application {application_id}", extra=dict(application_id=create_flag_json["application_id"]) + ) created_flag = add_flag_for_application(**create_flag_json) return AssessmentFlagSchema().dump(created_flag) def update_flag_for_application(): - current_app.logger.info(f"Update flag") + current_app.logger.info("Update flag") update_flag_json = request.json updated_flag = add_update_to_assessment_flag(**update_flag_json) return AssessmentFlagSchema().dump(updated_flag) diff --git a/api/routes/progress_routes.py b/api/routes/progress_routes.py index 2c526ab9..902b2bd9 100644 --- a/api/routes/progress_routes.py +++ b/api/routes/progress_routes.py @@ -1,9 +1,10 @@ import copy from typing import List +from flask import request + from config import Config from db.queries.progress.queries import get_progress_for_app -from flask import request def post_progress_for_applications(fund_id, round_id) -> List[dict]: diff --git a/api/routes/qa_complete_routes.py b/api/routes/qa_complete_routes.py index 8d03f233..8aa77fd3 100644 --- a/api/routes/qa_complete_routes.py +++ b/api/routes/qa_complete_routes.py @@ -1,7 +1,6 @@ -# flake8: noqa from db.queries.assessment_records.queries import get_metadata_for_application -from db.queries.qa_complete.queries import create_qa_complete_record from db.queries.qa_complete.queries import ( + create_qa_complete_record, get_qa_complete_record_for_application, ) diff --git a/api/routes/score_routes.py b/api/routes/score_routes.py index 709e9e0d..9e53fc93 100644 --- a/api/routes/score_routes.py +++ b/api/routes/score_routes.py @@ -1,12 +1,9 @@ -# flake8: noqa -from typing import Dict -from typing import List +from typing import Dict, List -from db.queries import create_score_for_app_sub_crit -from db.queries import get_scores_for_app_sub_crit -from db.queries import get_scoring_system_for_round_id from flask import request +from db.queries import create_score_for_app_sub_crit, get_scores_for_app_sub_crit, get_scoring_system_for_round_id + def get_scoring_system_name_for_round_id(round_id: str) -> dict: """get_scoring_system_for_round_id Function used by the get endpoint @@ -18,7 +15,10 @@ def get_scoring_system_name_for_round_id(round_id: str) -> dict: """ scoring_system = get_scoring_system_for_round_id(round_id) - return {"round_id": round_id, "scoring_system": scoring_system["scoring_system_name"].name} + return { + "round_id": round_id, + "scoring_system": scoring_system["scoring_system_name"].name, + } def get_score_for_application_sub_criteria( diff --git a/api/routes/subcriterias/get_sub_criteria.py b/api/routes/subcriterias/get_sub_criteria.py index 4d82fa8c..8c4fd9d1 100644 --- a/api/routes/subcriterias/get_sub_criteria.py +++ b/api/routes/subcriterias/get_sub_criteria.py @@ -1,8 +1,8 @@ import copy +from flask import abort, current_app + from config import Config -from flask import abort -from flask import current_app def get_all_subcriteria(fund_id, round_id, language): @@ -24,7 +24,9 @@ def get_all_subcriteria(fund_id, round_id, language): def return_subcriteria_from_mapping(sub_criteria_id, fund_id, round_id, language): - current_app.logger.info(f"Finding sub criteria data in config for: {sub_criteria_id}") + current_app.logger.info( + "Finding sub criteria data in config for: {sub_criteria_id}", extra=dict(sub_criteria_id=sub_criteria_id) + ) display_config = copy.deepcopy(Config.ASSESSMENT_MAPPING_CONFIG[f"{fund_id}:{round_id}"]) sub_criterias = get_all_subcriteria(fund_id, round_id, language) matching_sub_criteria = list( @@ -45,10 +47,12 @@ def return_subcriteria_from_mapping(sub_criteria_id, fund_id, round_id, language return sub_crit elif len(matching_sub_criteria) > 1: - msg = "sub_criteria: '{sub_criteria_id}' duplicated." - current_app.logger.error(msg) - raise ValueError(msg) + current_app.logger.error( + "sub_criteria: '{sub_criteria_id}' duplicated.", extra=dict(sub_criteria_id=sub_criteria_id) + ) + raise ValueError(f"sub_criteria: '{sub_criteria_id}' duplicated.") 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.") diff --git a/api/routes/tag_routes.py b/api/routes/tag_routes.py index f3e4bb52..55c03601 100644 --- a/api/routes/tag_routes.py +++ b/api/routes/tag_routes.py @@ -1,25 +1,24 @@ -# flake8: noqa -from db.queries.assessment_records.queries import associate_assessment_tags +from flask import Response, abort, current_app, request + from db.queries.assessment_records.queries import ( + associate_assessment_tags, select_active_tags_associated_with_assessment, -) -from db.queries.assessment_records.queries import ( select_all_tags_associated_with_application, ) -from db.queries.tags.queries import get_tag_by_id -from db.queries.tags.queries import insert_tags -from db.queries.tags.queries import select_tags_for_fund_round -from db.queries.tags.queries import select_tags_types -from db.queries.tags.queries import update_tags -from db.schemas.schemas import JoinedTagAssociationSchema -from db.schemas.schemas import JoinedTagSchema -from db.schemas.schemas import TagAssociationSchema -from db.schemas.schemas import TagSchema -from db.schemas.schemas import TagTypeSchema -from flask import abort -from flask import current_app -from flask import request -from flask import Response +from db.queries.tags.queries import ( + get_tag_by_id, + insert_tags, + select_tags_for_fund_round, + select_tags_types, + update_tags, +) +from db.schemas.schemas import ( + JoinedTagAssociationSchema, + JoinedTagSchema, + TagAssociationSchema, + TagSchema, + TagTypeSchema, +) def get_tags_for_fund_round( @@ -75,7 +74,7 @@ def add_tag_for_fund_round(fund_id, round_id): serialiser = TagSchema() serialised_tags = [serialiser.dump(r) for r in inserted_tags] return serialised_tags - current_app.logger.error(f"Add tags attempt failed for tags: {tags}.") + current_app.logger.error("Add tags attempt failed for tags: {tags}.", extra=dict(tags=tags)) abort(404) @@ -99,7 +98,7 @@ def update_tags_for_fund_round(fund_id, round_id): serialiser = TagSchema() serialised_tags = [serialiser.dump(r) for r in updated_tags] return serialised_tags - current_app.logger.error(f"Update tags attempt failed for tags: {tags}.") + current_app.logger.error("Update tags attempt failed for tags: {tags}.", extra=dict(tags=tags)) abort(404) @@ -113,7 +112,7 @@ def get_tag(fund_id, round_id, tag_id): def associate_tags_with_assessment(application_id): args = request.get_json() tags = args - current_app.logger.info(f"Associating tag with assessment") + current_app.logger.info("Associating tag with assessment") associated_tags = associate_assessment_tags(application_id, tags) if associated_tags: @@ -123,7 +122,10 @@ def associate_tags_with_assessment(application_id): def get_active_tags_associated_with_assessment(application_id): - current_app.logger.info(f"Getting tags associated with assessment with application_id: {application_id}.") + current_app.logger.info( + "Getting tags associated with assessment with application_id: {application_id}.", + extra=dict(application_id=application_id), + ) associated_tags = select_active_tags_associated_with_assessment(application_id) if associated_tags: serialiser = JoinedTagAssociationSchema() @@ -133,7 +135,9 @@ def get_active_tags_associated_with_assessment(application_id): def get_all_tags_associated_with_application(application_id): - current_app.logger.info(f"Getting tags associated with with application_id: {application_id}.") + current_app.logger.info( + "Getting tags associated with with application_id: {application_id}.", extra=dict(application_id=application_id) + ) associated_tags = select_all_tags_associated_with_application(application_id) if associated_tags: serialiser = JoinedTagAssociationSchema() diff --git a/api/routes/user_routes.py b/api/routes/user_routes.py index 648c4456..c804cdbd 100644 --- a/api/routes/user_routes.py +++ b/api/routes/user_routes.py @@ -1,14 +1,15 @@ -from db.queries.assessment_records.queries import create_user_application_association -from db.queries.assessment_records.queries import get_metadata_for_application -from db.queries.assessment_records.queries import get_user_application_associations +from flask import abort, current_app, request +from fsd_utils.config.notify_constants import NotifyConstants + +from db.queries.assessment_records.queries import ( + create_user_application_association, + get_metadata_for_application, + get_user_application_associations, +) from db.queries.assessment_records.queries import ( update_user_application_association as update_user_application_association_db, ) from db.schemas.schemas import AllocationAssociationSchema -from flask import abort -from flask import current_app -from flask import request -from fsd_utils.config.notify_constants import NotifyConstants from services.data_services import send_notification_email @@ -31,7 +32,10 @@ def get_all_users_associated_with_application(application_id, active=None): serialiser = AllocationAssociationSchema() return serialiser.dump(associations, many=True) - current_app.logger.error(f"Could not find any users associated with application {application_id}") + current_app.logger.error( + "Could not find any users associated with application {application_id}", + extra=dict(application_id=application_id), + ) abort(404) @@ -55,7 +59,10 @@ def get_user_application_association(application_id, user_id): serialiser = AllocationAssociationSchema() return serialiser.dump(association[0]) - current_app.logger.error(f"Could not find association between {user_id} and application {application_id}") + current_app.logger.error( + "Could not find association between {user_id} and application {application_id}", + extra=dict(user_id=user_id, application_id=application_id), + ) abort(404) @@ -96,7 +103,10 @@ def add_user_application_association(application_id, user_id): serialiser = AllocationAssociationSchema() return serialiser.dump(association), 201 - current_app.logger.error(f"Could not create association between {user_id} and application {application_id}") + current_app.logger.error( + "Could not create association between {user_id} and application {application_id}", + extra=dict(user_id=user_id, application_id=application_id), + ) abort(404) @@ -126,7 +136,10 @@ def update_user_application_association(application_id, user_id): send_email = args.get("send_email") active = args.get("active") association = update_user_application_association_db( - application_id=application_id, user_id=user_id, active=active, assigner_id=args["assigner_id"] + application_id=application_id, + user_id=user_id, + active=active, + assigner_id=args["assigner_id"], ) if association: @@ -145,7 +158,10 @@ def update_user_application_association(application_id, user_id): serialiser = AllocationAssociationSchema() return serialiser.dump(association) - current_app.logger.error(f"Could not update association between {user_id} and application {application_id}") + current_app.logger.error( + "Could not update association between {user_id} and application {application_id}", + extra=dict(user_id=user_id, application_id=application_id), + ) abort(404) @@ -168,7 +184,9 @@ def get_all_applications_associated_with_user(user_id, active=None): serialiser = AllocationAssociationSchema() return serialiser.dump(associations, many=True) - current_app.logger.error(f"Could not find any applications associated with user {user_id}") + current_app.logger.error( + "Could not find any applications associated with user {user_id}", extra=dict(user_id=user_id) + ) abort(404) @@ -191,5 +209,7 @@ def get_all_associations_assigned_by_user(assigner_id, active=None): serialiser = AllocationAssociationSchema() return serialiser.dump(associations, many=True) - current_app.logger.error(f"Could not find any applications assigned by user {assigner_id}") + current_app.logger.error( + "Could not find any applications assigned by user {assigner_id}", extra=dict(assigner_id=assigner_id) + ) abort(404) diff --git a/app.py b/app.py index dc8427d5..986c9451 100644 --- a/app.py +++ b/app.py @@ -1,19 +1,19 @@ from os import getenv import connexion -from _helpers.task_executer_service import AssessmentTaskExecutorService from apscheduler.schedulers.background import BackgroundScheduler -from config import Config from connexion import FlaskApp from connexion.resolver import MethodViewResolver from fsd_utils import init_sentry -from fsd_utils.healthchecks.checkers import DbChecker -from fsd_utils.healthchecks.checkers import FlaskRunningChecker +from fsd_utils.healthchecks.checkers import DbChecker, FlaskRunningChecker from fsd_utils.healthchecks.healthcheck import Healthcheck from fsd_utils.logging import logging from fsd_utils.services.aws_extended_client import SQSExtendedClient from fsd_utils.sqs_scheduler.context_aware_executor import ContextAwareExecutor from fsd_utils.sqs_scheduler.scheduler_service import scheduler_executor + +from _helpers.task_executer_service import AssessmentTaskExecutorService +from config import Config from openapi.utils import get_bundled_specs @@ -55,7 +55,9 @@ def create_app() -> FlaskApp: create_sqs_extended_client(flask_app) executor = ContextAwareExecutor( - max_workers=Config.TASK_EXECUTOR_MAX_THREAD, thread_name_prefix="NotifTask", flask_app=flask_app + max_workers=Config.TASK_EXECUTOR_MAX_THREAD, + thread_name_prefix="NotifTask", + flask_app=flask_app, ) # Configure Task Executor service task_executor_service = AssessmentTaskExecutorService( diff --git a/config/__init__.py b/config/__init__.py index 494df434..8a7d39c1 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -1,4 +1,3 @@ -# flake8: noqa __pdoc__ = {".": False} from os import environ diff --git a/config/envs/default.py b/config/envs/default.py index a49348ba..37573ba3 100644 --- a/config/envs/default.py +++ b/config/envs/default.py @@ -1,12 +1,13 @@ """Flask configuration.""" + from os import environ from pathlib import Path +from fsd_utils import CommonConfig, configclass + from config.mappings.assessment_mapping_fund_round import ( fund_round_to_assessment_mapping, ) -from fsd_utils import CommonConfig -from fsd_utils import configclass @configclass diff --git a/config/envs/dev.py b/config/envs/dev.py index 3fa60097..170ac58f 100644 --- a/config/envs/dev.py +++ b/config/envs/dev.py @@ -1,7 +1,9 @@ """Flask Dev Pipeline Environment Configuration.""" -from config.envs.default import DefaultConfig + from fsd_utils import configclass +from config.envs.default import DefaultConfig + @configclass class DevConfig(DefaultConfig): diff --git a/config/envs/development.py b/config/envs/development.py index aab73029..52ae94b8 100644 --- a/config/envs/development.py +++ b/config/envs/development.py @@ -1,9 +1,10 @@ """Flask Local Development Environment Configuration.""" + import logging +from fsd_utils import CommonConfig, configclass + from config.envs.default import DefaultConfig -from fsd_utils import CommonConfig -from fsd_utils import configclass @configclass diff --git a/config/envs/production.py b/config/envs/production.py index 524d5e2d..701d7567 100644 --- a/config/envs/production.py +++ b/config/envs/production.py @@ -1,7 +1,9 @@ """Flask Production Environment Configuration.""" -from config.envs.default import DefaultConfig + from fsd_utils import configclass +from config.envs.default import DefaultConfig + @configclass class ProductionConfig(DefaultConfig): diff --git a/config/envs/test.py b/config/envs/test.py index 2fbc5671..2c907d51 100644 --- a/config/envs/test.py +++ b/config/envs/test.py @@ -1,9 +1,11 @@ """Flask Test Environment Configuration.""" + from os import environ -from config.envs.default import DefaultConfig from fsd_utils import configclass +from config.envs.default import DefaultConfig + @configclass class TestConfig(DefaultConfig): diff --git a/config/envs/unit_testing.py b/config/envs/unit_testing.py index 473c13e0..0f58ccf9 100644 --- a/config/envs/unit_testing.py +++ b/config/envs/unit_testing.py @@ -1,7 +1,8 @@ """Flask Unit Testing Environment Configuration.""" + +from fsd_utils import CommonConfig, configclass + from config.envs.default import DefaultConfig -from fsd_utils import CommonConfig -from fsd_utils import configclass @configclass diff --git a/config/mappings/assessment_mapping_fund_round.py b/config/mappings/assessment_mapping_fund_round.py index b46a0c9c..b77da48b 100644 --- a/config/mappings/assessment_mapping_fund_round.py +++ b/config/mappings/assessment_mapping_fund_round.py @@ -1,4 +1,5 @@ -# flake8: noqa +# ruff: noqa: E501 + from uuid import uuid4 from config.mappings.cof_mapping_parts.cof25_r1_scored_criteria import ( @@ -373,7 +374,10 @@ "en": {"title": "Your expression of interest (EOI) application reference"}, "cy": {"title": "Cyfeirnod eich ffurflen mynegi diddordeb (EOI)."}, }, - "YdtlQZ": {"en": {"title": "Organisation name"}, "cy": {"title": "Enw'r sefydliad"}}, + "YdtlQZ": { + "en": {"title": "Organisation name"}, + "cy": {"title": "Enw'r sefydliad"}, + }, "lajFtB": { "en": { "title": "Type of organisation", @@ -384,8 +388,14 @@ "field_type": "radiosField", }, }, - "aHIGbK": {"en": {"title": "Charity number"}, "cy": {"title": "Rhif elusen"}}, - "GlPmCX": {"en": {"title": "Company registration number"}, "cy": {"title": "Rhif cofrestru'r cwmni"}}, + "aHIGbK": { + "en": {"title": "Charity number"}, + "cy": {"title": "Rhif elusen"}, + }, + "GlPmCX": { + "en": {"title": "Company registration number"}, + "cy": {"title": "Rhif cofrestru'r cwmni"}, + }, "oXGwlA": { "en": { "title": "Asset type", @@ -396,17 +406,30 @@ "field_type": "radiosField", }, }, - "aJGyCR": {"en": {"title": "Type of asset (other)"}, "cy": {"title": "Math o eiddo (arall)"}}, + "aJGyCR": { + "en": {"title": "Type of asset (other)"}, + "cy": {"title": "Math o eiddo (arall)"}, + }, "EfdliG": { "en": {"title": "Postcode of asset", "field_type": "uk_postcode"}, "cy": {"title": "Cod post o ased", "field_type": "uk_postcode"}, }, - "ABROnB": {"en": {"title": "Capital funding request"}, "cy": {"title": "Cais cyllido cyfalaf"}}, + "ABROnB": { + "en": {"title": "Capital funding request"}, + "cy": {"title": "Cais cyllido cyfalaf"}, + }, "tSKhQQ": { - "en": {"title": "Revenue costs (optional)", "field_type": "sum_list", "field_to_sum": "UyaAHw"}, + "en": { + "title": "Revenue costs (optional)", + "field_type": "sum_list", + "field_to_sum": "UyaAHw", + }, "cy": {"title": "Costau refeniw (dewisol)"}, }, - "apGjFS": {"en": {"title": "Project name"}, "cy": {"title": "Enw'r prosiect"}}, + "apGjFS": { + "en": {"title": "Project name"}, + "cy": {"title": "Enw'r prosiect"}, + }, } }, "OUTPUT_TRACKER": {}, @@ -450,7 +473,10 @@ "en": {"title": "Your expression of interest (EOI) application reference"}, "cy": {"title": "Cyfeirnod eich ffurflen mynegi diddordeb (EOI)."}, }, - "YdtlQZ": {"en": {"title": "Organisation name"}, "cy": {"title": "Enw'r sefydliad"}}, + "YdtlQZ": { + "en": {"title": "Organisation name"}, + "cy": {"title": "Enw'r sefydliad"}, + }, "lajFtB": { "en": { "title": "Type of organisation", @@ -461,8 +487,14 @@ "field_type": "radiosField", }, }, - "aHIGbK": {"en": {"title": "Charity number"}, "cy": {"title": "Rhif elusen"}}, - "GlPmCX": {"en": {"title": "Company registration number"}, "cy": {"title": "Rhif cofrestru'r cwmni"}}, + "aHIGbK": { + "en": {"title": "Charity number"}, + "cy": {"title": "Rhif elusen"}, + }, + "GlPmCX": { + "en": {"title": "Company registration number"}, + "cy": {"title": "Rhif cofrestru'r cwmni"}, + }, "oXGwlA": { "en": { "title": "Asset type", @@ -473,17 +505,30 @@ "field_type": "radiosField", }, }, - "aJGyCR": {"en": {"title": "Type of asset (other)"}, "cy": {"title": "Math o eiddo (arall)"}}, + "aJGyCR": { + "en": {"title": "Type of asset (other)"}, + "cy": {"title": "Math o eiddo (arall)"}, + }, "EfdliG": { "en": {"title": "Postcode of asset", "field_type": "uk_postcode"}, "cy": {"title": "Cod post o ased", "field_type": "uk_postcode"}, }, - "ABROnB": {"en": {"title": "Capital funding request"}, "cy": {"title": "Cais cyllido cyfalaf"}}, + "ABROnB": { + "en": {"title": "Capital funding request"}, + "cy": {"title": "Cais cyllido cyfalaf"}, + }, "tSKhQQ": { - "en": {"title": "Revenue costs (optional)", "field_type": "sum_list", "field_to_sum": "UyaAHw"}, + "en": { + "title": "Revenue costs (optional)", + "field_type": "sum_list", + "field_to_sum": "UyaAHw", + }, "cy": {"title": "Costau refeniw (dewisol)"}, }, - "apGjFS": {"en": {"title": "Project name"}, "cy": {"title": "Enw'r prosiect"}}, + "apGjFS": { + "en": {"title": "Project name"}, + "cy": {"title": "Enw'r prosiect"}, + }, } }, "OUTPUT_TRACKER": {}, @@ -509,28 +554,40 @@ }, }, "zurxox": { - "en": {"title": "Is the asset based in the UK?", "field_type": "yesNoField"}, + "en": { + "title": "Is the asset based in the UK?", + "field_type": "yesNoField", + }, "cy": { "title": "A yw'r ased yn y DU?", "field_type": "yesNoField", }, }, "dnqIdW": { - "en": {"title": "Address of the asset", "field_type": "ukAddressField"}, + "en": { + "title": "Address of the asset", + "field_type": "ukAddressField", + }, "cy": { "title": "Cyfeiriad yr ased", "field_type": "ukAddressField", }, }, "lLQmNb": { - "en": {"title": "Is the asset at risk?", "field_type": "yesNoField"}, + "en": { + "title": "Is the asset at risk?", + "field_type": "yesNoField", + }, "cy": { "title": "A yw'r ased mewn perygl?", "field_type": "yesNoField", }, }, "ilMbMH": { - "en": {"title": "What is the risk to the asset?", "field_type": "checkboxesField"}, + "en": { + "title": "What is the risk to the asset?", + "field_type": "checkboxesField", + }, "cy": { "title": "Beth yw'r perygl i'r ased?", "field_type": "checkboxesField", @@ -547,21 +604,30 @@ }, }, "cPcZos": { - "en": {"title": "Do you already own the asset?", "field_type": "yesNoField"}, + "en": { + "title": "Do you already own the asset?", + "field_type": "yesNoField", + }, "cy": { "title": "A ydych eisoes yn berchen ar yr ased?", "field_type": "yesNoField", }, }, "jOpXfi": { - "en": {"title": "Help with public authority", "field_type": "details"}, + "en": { + "title": "Help with public authority", + "field_type": "details", + }, "cy": { "title": "Help gydag awdurdod cyhoeddus", "field_type": "details", }, }, "XuAyrs": { - "en": {"title": "Does the asset belong to a public authority?", "field_type": "radiosField"}, + "en": { + "title": "Does the asset belong to a public authority?", + "field_type": "radiosField", + }, "cy": { "title": "A yw'r ased yn perthyn i awdurdod cyhoeddus?", "field_type": "radiosField", @@ -595,28 +661,40 @@ }, }, "SxkwhF": { - "en": {"title": "Does your organisation have any alternative names?", "field_type": "yesNoField"}, + "en": { + "title": "Does your organisation have any alternative names?", + "field_type": "yesNoField", + }, "cy": { "title": "A oes gan eich sefydliad unrhyw enwau amgen?", "field_type": "yesNoField", }, }, "OpeSdM": { - "en": {"title": "Organisation address", "field_type": "ukAddressField"}, + "en": { + "title": "Organisation address", + "field_type": "ukAddressField", + }, "cy": { "title": "Cyfeiriad y sefydliad", "field_type": "ukAddressField", }, }, "Fepkam": { - "en": {"title": "Help with organisation type", "field_type": "details"}, + "en": { + "title": "Help with organisation type", + "field_type": "details", + }, "cy": { "title": "Help gyda'r math o sefydliad", "field_type": "details", }, }, "uYiLsv": { - "en": {"title": "Organisation classification", "field_type": "radiosField"}, + "en": { + "title": "Organisation classification", + "field_type": "radiosField", + }, "cy": { "title": "Dosbarthiad y sefydliad", "field_type": "radiosField", @@ -661,7 +739,10 @@ }, }, "aocRmv": { - "en": {"title": "What do you plan to use COF's funding for?", "field_type": "checkboxesField"}, + "en": { + "title": "What do you plan to use COF's funding for?", + "field_type": "checkboxesField", + }, "cy": { "title": "At ba ddiben ydych chi'n bwriadu defnyddio cyllid o'r Gronfa Perchnogaeth Gymunedol?", "field_type": "checkboxesField", @@ -688,35 +769,50 @@ }, }, "oblxxv": { - "en": {"title": "Do you plan to request any revenue funding?", "field_type": "yesNoField"}, + "en": { + "title": "Do you plan to request any revenue funding?", + "field_type": "yesNoField", + }, "cy": { "title": "A ydych yn bwriadu gwneud cais am unrhyw gyllid refeniw?", "field_type": "yesNoField", }, }, "eOWKoO": { - "en": {"title": "Do you plan to secure match funding?", "field_type": "yesNoField"}, + "en": { + "title": "Do you plan to secure match funding?", + "field_type": "yesNoField", + }, "cy": { "title": "A ydych yn bwriadu sicrhau arian cyfatebol?", "field_type": "yesNoField", }, }, "BykoQQ": { - "en": {"title": "Where do you plan to source match funding?", "field_type": "checkboxesField"}, + "en": { + "title": "Where do you plan to source match funding?", + "field_type": "checkboxesField", + }, "cy": { "title": "O ble rydych yn bwriadu cael arian cyfatebol?", "field_type": "checkboxesField", }, }, "yZxdeJ": { - "en": {"title": "Does your project include an element of housing?", "field_type": "yesNoField"}, + "en": { + "title": "Does your project include an element of housing?", + "field_type": "yesNoField", + }, "cy": { "title": "A yw eich prosiect yn cynnwys elfen dai?", "field_type": "yesNoField", }, }, "UORyaF": { - "en": {"title": "Will you need planning permission for your project?", "field_type": "radiosField"}, + "en": { + "title": "Will you need planning permission for your project?", + "field_type": "radiosField", + }, "cy": { "title": "A fydd angen caniatâd cynllunio ar gyfer eich prosiect?", "field_type": "radiosField", @@ -733,7 +829,10 @@ }, }, "kWRuac": { - "en": {"title": "What progress have you made to secure this funding?", "field_type": "radiosField"}, + "en": { + "title": "What progress have you made to secure this funding?", + "field_type": "radiosField", + }, "cy": { "title": "Pa gynnydd ydych chi wedi'i wneud i sicrhau'r arian hwn?", "field_type": "radiosField", @@ -760,7 +859,10 @@ }, }, "MxzEYq": { - "en": {"title": "Describe your project and its aims", "field_type": "freeTextField"}, + "en": { + "title": "Describe your project and its aims", + "field_type": "freeTextField", + }, "cy": { "title": "Disgrifiwch eich prosiect a'i nodau", "field_type": "freeTextField", @@ -774,14 +876,20 @@ }, }, "NQoGIm": { - "en": {"title": "Lead contact email address", "field_type": "emailAddressField"}, + "en": { + "title": "Lead contact email address", + "field_type": "emailAddressField", + }, "cy": { "title": "Cyfeiriad e-bost y prif unigolyn cyswllt", "field_type": "emailAddressField", }, }, "srxZmv": { - "en": {"title": "Lead contact telephone number", "field_type": "telephoneNumberField"}, + "en": { + "title": "Lead contact telephone number", + "field_type": "telephoneNumberField", + }, "cy": { "title": "Rhif ffôn y prif unigolyn cyswllt", "field_type": "telephoneNumberField", @@ -812,28 +920,40 @@ }, }, "zurxox": { - "en": {"title": "Is the asset based in the UK?", "field_type": "yesNoField"}, + "en": { + "title": "Is the asset based in the UK?", + "field_type": "yesNoField", + }, "cy": { "title": "A yw'r ased yn y DU?", "field_type": "yesNoField", }, }, "dnqIdW": { - "en": {"title": "Address of the asset", "field_type": "ukAddressField"}, + "en": { + "title": "Address of the asset", + "field_type": "ukAddressField", + }, "cy": { "title": "Cyfeiriad yr ased", "field_type": "ukAddressField", }, }, "lLQmNb": { - "en": {"title": "Is the asset at risk?", "field_type": "yesNoField"}, + "en": { + "title": "Is the asset at risk?", + "field_type": "yesNoField", + }, "cy": { "title": "A yw'r ased mewn perygl?", "field_type": "yesNoField", }, }, "ilMbMH": { - "en": {"title": "What is the risk to the asset?", "field_type": "checkboxesField"}, + "en": { + "title": "What is the risk to the asset?", + "field_type": "checkboxesField", + }, "cy": { "title": "Beth yw'r perygl i'r ased?", "field_type": "checkboxesField", @@ -850,21 +970,30 @@ }, }, "cPcZos": { - "en": {"title": "Do you already own the asset?", "field_type": "yesNoField"}, + "en": { + "title": "Do you already own the asset?", + "field_type": "yesNoField", + }, "cy": { "title": "A ydych eisoes yn berchen ar yr ased?", "field_type": "yesNoField", }, }, "jOpXfi": { - "en": {"title": "Help with public authority", "field_type": "details"}, + "en": { + "title": "Help with public authority", + "field_type": "details", + }, "cy": { "title": "Help gydag awdurdod cyhoeddus", "field_type": "details", }, }, "XuAyrs": { - "en": {"title": "Does the asset belong to a public authority?", "field_type": "radiosField"}, + "en": { + "title": "Does the asset belong to a public authority?", + "field_type": "radiosField", + }, "cy": { "title": "A yw'r ased yn perthyn i awdurdod cyhoeddus?", "field_type": "radiosField", @@ -898,28 +1027,40 @@ }, }, "SxkwhF": { - "en": {"title": "Does your organisation have any alternative names?", "field_type": "yesNoField"}, + "en": { + "title": "Does your organisation have any alternative names?", + "field_type": "yesNoField", + }, "cy": { "title": "A oes gan eich sefydliad unrhyw enwau amgen?", "field_type": "yesNoField", }, }, "OpeSdM": { - "en": {"title": "Organisation address", "field_type": "ukAddressField"}, + "en": { + "title": "Organisation address", + "field_type": "ukAddressField", + }, "cy": { "title": "Cyfeiriad y sefydliad", "field_type": "ukAddressField", }, }, "Fepkam": { - "en": {"title": "Help with organisation type", "field_type": "details"}, + "en": { + "title": "Help with organisation type", + "field_type": "details", + }, "cy": { "title": "Help gyda'r math o sefydliad", "field_type": "details", }, }, "uYiLsv": { - "en": {"title": "Organisation classification", "field_type": "radiosField"}, + "en": { + "title": "Organisation classification", + "field_type": "radiosField", + }, "cy": { "title": "Dosbarthiad y sefydliad", "field_type": "radiosField", @@ -964,7 +1105,10 @@ }, }, "aocRmv": { - "en": {"title": "What do you plan to use COF's funding for?", "field_type": "checkboxesField"}, + "en": { + "title": "What do you plan to use COF's funding for?", + "field_type": "checkboxesField", + }, "cy": { "title": "At ba ddiben ydych chi'n bwriadu defnyddio cyllid o'r Gronfa Perchnogaeth Gymunedol?", "field_type": "checkboxesField", @@ -991,35 +1135,50 @@ }, }, "oblxxv": { - "en": {"title": "Do you plan to request any revenue funding?", "field_type": "yesNoField"}, + "en": { + "title": "Do you plan to request any revenue funding?", + "field_type": "yesNoField", + }, "cy": { "title": "A ydych yn bwriadu gwneud cais am unrhyw gyllid refeniw?", "field_type": "yesNoField", }, }, "eOWKoO": { - "en": {"title": "Do you plan to secure match funding?", "field_type": "yesNoField"}, + "en": { + "title": "Do you plan to secure match funding?", + "field_type": "yesNoField", + }, "cy": { "title": "A ydych yn bwriadu sicrhau arian cyfatebol?", "field_type": "yesNoField", }, }, "BykoQQ": { - "en": {"title": "Where do you plan to source match funding?", "field_type": "checkboxesField"}, + "en": { + "title": "Where do you plan to source match funding?", + "field_type": "checkboxesField", + }, "cy": { "title": "O ble rydych yn bwriadu cael arian cyfatebol?", "field_type": "checkboxesField", }, }, "yZxdeJ": { - "en": {"title": "Does your project include an element of housing?", "field_type": "yesNoField"}, + "en": { + "title": "Does your project include an element of housing?", + "field_type": "yesNoField", + }, "cy": { "title": "A yw eich prosiect yn cynnwys elfen dai?", "field_type": "yesNoField", }, }, "UORyaF": { - "en": {"title": "Will you need planning permission for your project?", "field_type": "radiosField"}, + "en": { + "title": "Will you need planning permission for your project?", + "field_type": "radiosField", + }, "cy": { "title": "A fydd angen caniatâd cynllunio ar gyfer eich prosiect?", "field_type": "radiosField", @@ -1036,7 +1195,10 @@ }, }, "kWRuac": { - "en": {"title": "What progress have you made to secure this funding?", "field_type": "radiosField"}, + "en": { + "title": "What progress have you made to secure this funding?", + "field_type": "radiosField", + }, "cy": { "title": "Pa gynnydd ydych chi wedi'i wneud i sicrhau'r arian hwn?", "field_type": "radiosField", @@ -1063,7 +1225,10 @@ }, }, "MxzEYq": { - "en": {"title": "Describe your project and its aims", "field_type": "freeTextField"}, + "en": { + "title": "Describe your project and its aims", + "field_type": "freeTextField", + }, "cy": { "title": "Disgrifiwch eich prosiect a'i nodau", "field_type": "freeTextField", @@ -1077,14 +1242,20 @@ }, }, "NQoGIm": { - "en": {"title": "Lead contact email address", "field_type": "emailAddressField"}, + "en": { + "title": "Lead contact email address", + "field_type": "emailAddressField", + }, "cy": { "title": "Cyfeiriad e-bost y prif unigolyn cyswllt", "field_type": "emailAddressField", }, }, "srxZmv": { - "en": {"title": "Lead contact telephone number", "field_type": "telephoneNumberField"}, + "en": { + "title": "Lead contact telephone number", + "field_type": "telephoneNumberField", + }, "cy": { "title": "Rhif ffôn y prif unigolyn cyswllt", "field_type": "telephoneNumberField", @@ -1248,6 +1419,9 @@ } fund_round_mapping_config_with_round_id = { - v["round_id"]: {"fund_id": v["fund_id"], "type_of_application": v["type_of_application"]} + v["round_id"]: { + "fund_id": v["fund_id"], + "type_of_application": v["type_of_application"], + } for k, v in fund_round_mapping_config.items() } diff --git a/config/mappings/cof_mapping_parts/cof25_r1_scored_criteria.py b/config/mappings/cof_mapping_parts/cof25_r1_scored_criteria.py index fe4a7cb7..e584f0d4 100644 --- a/config/mappings/cof_mapping_parts/cof25_r1_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/cof25_r1_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/cof25_r1_unscored_sections.py b/config/mappings/cof_mapping_parts/cof25_r1_unscored_sections.py index 99b8df4f..58bfa399 100644 --- a/config/mappings/cof_mapping_parts/cof25_r1_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/cof25_r1_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/eoi25_unscored_sections.py b/config/mappings/cof_mapping_parts/eoi25_unscored_sections.py index 2cbfea6a..922aacac 100644 --- a/config/mappings/cof_mapping_parts/eoi25_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/eoi25_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/eoi_unscored_sections.py b/config/mappings/cof_mapping_parts/eoi_unscored_sections.py index 67e0cbe0..c93df532 100644 --- a/config/mappings/cof_mapping_parts/eoi_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/eoi_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/r2_scored_criteria.py b/config/mappings/cof_mapping_parts/r2_scored_criteria.py index 3b76fac6..f72e47c2 100644 --- a/config/mappings/cof_mapping_parts/r2_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/r2_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/r2_unscored_sections.py b/config/mappings/cof_mapping_parts/r2_unscored_sections.py index 279f7540..ba70502a 100644 --- a/config/mappings/cof_mapping_parts/r2_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/r2_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/r3_scored_criteria.py b/config/mappings/cof_mapping_parts/r3_scored_criteria.py index 7dd4acf4..89debf1c 100644 --- a/config/mappings/cof_mapping_parts/r3_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/r3_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/r3_unscored_sections.py b/config/mappings/cof_mapping_parts/r3_unscored_sections.py index 17f37b00..55c2c9b9 100644 --- a/config/mappings/cof_mapping_parts/r3_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/r3_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/r3w2_scored_criteria.py b/config/mappings/cof_mapping_parts/r3w2_scored_criteria.py index fcd7c0a7..b3830a08 100644 --- a/config/mappings/cof_mapping_parts/r3w2_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/r3w2_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/r3w2_unscored_sections.py b/config/mappings/cof_mapping_parts/r3w2_unscored_sections.py index 34e4c028..2b9e6142 100644 --- a/config/mappings/cof_mapping_parts/r3w2_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/r3w2_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/r3w3_scored_criteria.py b/config/mappings/cof_mapping_parts/r3w3_scored_criteria.py index 80bd9fa7..b2e26ff0 100644 --- a/config/mappings/cof_mapping_parts/r3w3_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/r3w3_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/r3w3_unscored_sections.py b/config/mappings/cof_mapping_parts/r3w3_unscored_sections.py index 5c0b64df..d74ab90d 100644 --- a/config/mappings/cof_mapping_parts/r3w3_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/r3w3_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/r4w1_scored_criteria.py b/config/mappings/cof_mapping_parts/r4w1_scored_criteria.py index eba39429..32a5fbd7 100644 --- a/config/mappings/cof_mapping_parts/r4w1_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/r4w1_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/r4w1_unscored_sections.py b/config/mappings/cof_mapping_parts/r4w1_unscored_sections.py index 5c0b64df..d74ab90d 100644 --- a/config/mappings/cof_mapping_parts/r4w1_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/r4w1_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cof_mapping_parts/r4w2_scored_criteria.py b/config/mappings/cof_mapping_parts/r4w2_scored_criteria.py index eba39429..32a5fbd7 100644 --- a/config/mappings/cof_mapping_parts/r4w2_scored_criteria.py +++ b/config/mappings/cof_mapping_parts/r4w2_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cof_mapping_parts/r4w2_unscored_sections.py b/config/mappings/cof_mapping_parts/r4w2_unscored_sections.py index 5c0b64df..d74ab90d 100644 --- a/config/mappings/cof_mapping_parts/r4w2_unscored_sections.py +++ b/config/mappings/cof_mapping_parts/r4w2_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/ctdf_mapping_parts/r1_unscored_sections.py b/config/mappings/ctdf_mapping_parts/r1_unscored_sections.py index 05ba8c69..7c25b916 100644 --- a/config/mappings/ctdf_mapping_parts/r1_unscored_sections.py +++ b/config/mappings/ctdf_mapping_parts/r1_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/cyp_mapping_parts/r1_scored_criteria.py b/config/mappings/cyp_mapping_parts/r1_scored_criteria.py index a35cc3ac..992c571e 100644 --- a/config/mappings/cyp_mapping_parts/r1_scored_criteria.py +++ b/config/mappings/cyp_mapping_parts/r1_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/cyp_mapping_parts/r1_unscored_criteria.py b/config/mappings/cyp_mapping_parts/r1_unscored_criteria.py index 5a792a7f..e8d31eb8 100644 --- a/config/mappings/cyp_mapping_parts/r1_unscored_criteria.py +++ b/config/mappings/cyp_mapping_parts/r1_unscored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/dpif_mappping_parts/r2_scored_criteria.py b/config/mappings/dpif_mappping_parts/r2_scored_criteria.py index eb977685..2b73008e 100644 --- a/config/mappings/dpif_mappping_parts/r2_scored_criteria.py +++ b/config/mappings/dpif_mappping_parts/r2_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/dpif_mappping_parts/r2_unscored_criteria.py b/config/mappings/dpif_mappping_parts/r2_unscored_criteria.py index b9e476d9..4d700623 100644 --- a/config/mappings/dpif_mappping_parts/r2_unscored_criteria.py +++ b/config/mappings/dpif_mappping_parts/r2_unscored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/hsra_mapping_parts/r1_scored_criteria.py b/config/mappings/hsra_mapping_parts/r1_scored_criteria.py index 64efb852..70ec7a18 100644 --- a/config/mappings/hsra_mapping_parts/r1_scored_criteria.py +++ b/config/mappings/hsra_mapping_parts/r1_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/hsra_mapping_parts/r1_unscored_sections.py b/config/mappings/hsra_mapping_parts/r1_unscored_sections.py index 21651fe0..15566351 100644 --- a/config/mappings/hsra_mapping_parts/r1_unscored_sections.py +++ b/config/mappings/hsra_mapping_parts/r1_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/nstf_mapping_parts/r2_scored_criteria.py b/config/mappings/nstf_mapping_parts/r2_scored_criteria.py index 86543d27..14a70e9c 100644 --- a/config/mappings/nstf_mapping_parts/r2_scored_criteria.py +++ b/config/mappings/nstf_mapping_parts/r2_scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/nstf_mapping_parts/r2_unscored_sections.py b/config/mappings/nstf_mapping_parts/r2_unscored_sections.py index 14a71638..52a893d4 100644 --- a/config/mappings/nstf_mapping_parts/r2_unscored_sections.py +++ b/config/mappings/nstf_mapping_parts/r2_unscored_sections.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/config/mappings/template_mapping_parts/scored_criteria.py b/config/mappings/template_mapping_parts/scored_criteria.py index dd22b99f..ba04d90f 100644 --- a/config/mappings/template_mapping_parts/scored_criteria.py +++ b/config/mappings/template_mapping_parts/scored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 scored_criteria = [ { diff --git a/config/mappings/template_mapping_parts/unscored_criteria.py b/config/mappings/template_mapping_parts/unscored_criteria.py index 48ed6829..80cf1be0 100644 --- a/config/mappings/template_mapping_parts/unscored_criteria.py +++ b/config/mappings/template_mapping_parts/unscored_criteria.py @@ -1,5 +1,4 @@ -# flake8: noqa -# Ignore line length +# ruff: noqa: E501 unscored_sections = [ { diff --git a/db/__init__.py b/db/__init__.py index 93c3565e..453705dd 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -1,6 +1,7 @@ """ .. include:: ./README.md """ + from flask_migrate import Migrate from flask_sqlalchemy import SQLAlchemy from sqlalchemy import MetaData diff --git a/db/migrations/env.py b/db/migrations/env.py index 22cbfb13..dd9fb8b4 100644 --- a/db/migrations/env.py +++ b/db/migrations/env.py @@ -5,10 +5,10 @@ from alembic import context from alembic_utils.replaceable_entity import register_entities -from db.models.assessment_record import block_json_func -from db.models.assessment_record import block_json_updates_trig from flask import current_app +from db.models.assessment_record import block_json_func, block_json_updates_trig + # Comment this out if the functions/trigs refer to tables # which havent been migrated yet. These must be in a seperate # migration to table creation. diff --git a/db/migrations/versions/14bff16bccc8_.py b/db/migrations/versions/14bff16bccc8_.py index a0c906f2..1fb0b5f9 100644 --- a/db/migrations/versions/14bff16bccc8_.py +++ b/db/migrations/versions/14bff16bccc8_.py @@ -5,6 +5,7 @@ Create Date: 2023-06-09 10:57:20.927960 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/1508d2c4dfa8_.py b/db/migrations/versions/1508d2c4dfa8_.py index b76b008d..36ce36a6 100644 --- a/db/migrations/versions/1508d2c4dfa8_.py +++ b/db/migrations/versions/1508d2c4dfa8_.py @@ -5,6 +5,7 @@ Create Date: 2022-12-02 15:02:26.905696 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/190bf5378715_.py b/db/migrations/versions/190bf5378715_.py index cba6e67f..f945705c 100644 --- a/db/migrations/versions/190bf5378715_.py +++ b/db/migrations/versions/190bf5378715_.py @@ -5,6 +5,7 @@ Create Date: 2022-11-21 13:54:55.349527 """ + from alembic import op from alembic_utils.pg_function import PGFunction from alembic_utils.pg_trigger import PGTrigger @@ -36,7 +37,7 @@ def upgrade(): on_entity="public.assessment_records", is_constraint=False, definition=( - "BEFORE UPDATE\n ON assessment_records\n FOR EACH ROW\n " " EXECUTE PROCEDURE block_blob_mutate()" + "BEFORE UPDATE\n ON assessment_records\n FOR EACH ROW\n EXECUTE PROCEDURE block_blob_mutate()" ), ) op.create_entity(public_assessment_records_block_updates_on_app_blob) @@ -52,7 +53,7 @@ def downgrade(): on_entity="public.assessment_records", is_constraint=False, definition=( - "BEFORE UPDATE\n ON assessment_records\n FOR EACH ROW\n " " EXECUTE PROCEDURE block_blob_mutate()" + "BEFORE UPDATE\n ON assessment_records\n FOR EACH ROW\n EXECUTE PROCEDURE block_blob_mutate()" ), ) op.drop_entity(public_assessment_records_block_updates_on_app_blob) diff --git a/db/migrations/versions/222a1c3b6321_.py b/db/migrations/versions/222a1c3b6321_.py index 22cd5b60..2f0b5224 100644 --- a/db/migrations/versions/222a1c3b6321_.py +++ b/db/migrations/versions/222a1c3b6321_.py @@ -5,6 +5,7 @@ Create Date: 2023-08-08 11:13:18.177809 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/30d27919f1d8_.py b/db/migrations/versions/30d27919f1d8_.py index 3c3162cf..f2ff7220 100644 --- a/db/migrations/versions/30d27919f1d8_.py +++ b/db/migrations/versions/30d27919f1d8_.py @@ -5,6 +5,7 @@ Create Date: 2023-01-19 10:55:23.151831 """ + import sqlalchemy as sa from alembic import op diff --git a/db/migrations/versions/3a22407701c8_.py b/db/migrations/versions/3a22407701c8_.py index 2f2b9663..0f996d17 100644 --- a/db/migrations/versions/3a22407701c8_.py +++ b/db/migrations/versions/3a22407701c8_.py @@ -5,6 +5,7 @@ Create Date: 2023-01-09 16:06:23.980091 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql @@ -29,7 +30,7 @@ def downgrade(): # ### commands auto generated by Alembic - please adjust! ### op.execute("ALTER TYPE flagtype RENAME TO flagtype_old") op.execute("CREATE TYPE flagtype AS ENUM('FLAGGED', 'STOPPED', 'QA_COMPLETED')") - op.execute(("ALTER TABLE flags ALTER COLUMN flag_type TYPE flagtype USING" " flag_type::text::flagtype")) + op.execute(("ALTER TABLE flags ALTER COLUMN flag_type TYPE flagtype USING flag_type::text::flagtype")) op.execute("DROP TYPE flagtype_old;") resolution_type_enum = postgresql.ENUM("QUERY_RESOLVED", "STOP_ASSESSMENT", name="resolutiontype") diff --git a/db/migrations/versions/409915dc382e_.py b/db/migrations/versions/409915dc382e_.py index c9af998c..750f386c 100644 --- a/db/migrations/versions/409915dc382e_.py +++ b/db/migrations/versions/409915dc382e_.py @@ -5,6 +5,7 @@ Create Date: 2022-11-28 17:09:29.370884 """ + from alembic import op from alembic_utils.pg_extension import PGExtension diff --git a/db/migrations/versions/4208ac886129_.py b/db/migrations/versions/4208ac886129_.py index ed5f6465..58e3bf81 100644 --- a/db/migrations/versions/4208ac886129_.py +++ b/db/migrations/versions/4208ac886129_.py @@ -5,6 +5,7 @@ Create Date: 2022-12-21 14:20:41.094674 """ + import sqlalchemy as sa from alembic import op diff --git a/db/migrations/versions/4bdc171458b2_.py b/db/migrations/versions/4bdc171458b2_.py index e0f61a1b..7bc5daa6 100644 --- a/db/migrations/versions/4bdc171458b2_.py +++ b/db/migrations/versions/4bdc171458b2_.py @@ -5,6 +5,7 @@ Create Date: 2023-06-30 15:53:26.608073 """ + from alembic import op # revision identifiers, used by Alembic. diff --git a/db/migrations/versions/55dcd4abf66a_.py b/db/migrations/versions/55dcd4abf66a_.py index b1ef532f..c49f4b74 100644 --- a/db/migrations/versions/55dcd4abf66a_.py +++ b/db/migrations/versions/55dcd4abf66a_.py @@ -5,6 +5,7 @@ Create Date: 2023-07-10 16:49:35.360072 """ + import uuid import sqlalchemy as sa diff --git a/db/migrations/versions/5ce0b8e0e1b6_.py b/db/migrations/versions/5ce0b8e0e1b6_.py index 8abd14eb..c819b74a 100644 --- a/db/migrations/versions/5ce0b8e0e1b6_.py +++ b/db/migrations/versions/5ce0b8e0e1b6_.py @@ -5,10 +5,10 @@ Create Date: 2022-11-25 14:24:40.464115 """ + import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "5ce0b8e0e1b6" down_revision = "190bf5378715" diff --git a/db/migrations/versions/6277d910baa8_.py b/db/migrations/versions/6277d910baa8_.py index 7f219a03..35c830da 100644 --- a/db/migrations/versions/6277d910baa8_.py +++ b/db/migrations/versions/6277d910baa8_.py @@ -5,6 +5,7 @@ Create Date: 2023-07-12 11:09:23.553438 """ + from alembic import op # revision identifiers, used by Alembic. diff --git a/db/migrations/versions/6ad072a15e50_.py b/db/migrations/versions/6ad072a15e50_.py index 9683b16b..35896d60 100644 --- a/db/migrations/versions/6ad072a15e50_.py +++ b/db/migrations/versions/6ad072a15e50_.py @@ -5,6 +5,7 @@ Create Date: 2023-06-27 16:39:06.293214 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/78d40ab26730_.py b/db/migrations/versions/78d40ab26730_.py index 6bcf0e74..76a34b43 100644 --- a/db/migrations/versions/78d40ab26730_.py +++ b/db/migrations/versions/78d40ab26730_.py @@ -5,6 +5,7 @@ Create Date: 2022-12-23 10:04:12.923579 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/817e90e9bab4_.py b/db/migrations/versions/817e90e9bab4_.py index 2cc514e3..0b09e84b 100644 --- a/db/migrations/versions/817e90e9bab4_.py +++ b/db/migrations/versions/817e90e9bab4_.py @@ -5,6 +5,7 @@ Create Date: 2022-12-16 15:10:13.337229 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/910aa9530de5_.py b/db/migrations/versions/910aa9530de5_.py index cdff635b..f2b8c7f0 100644 --- a/db/migrations/versions/910aa9530de5_.py +++ b/db/migrations/versions/910aa9530de5_.py @@ -5,6 +5,7 @@ Create Date: 2022-11-21 13:53:40.212689 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/99ad87a4f888_.py b/db/migrations/versions/99ad87a4f888_.py index 08a9f580..1afd45f0 100644 --- a/db/migrations/versions/99ad87a4f888_.py +++ b/db/migrations/versions/99ad87a4f888_.py @@ -5,6 +5,7 @@ Create Date: 2023-01-06 13:22:23.467188 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/ea9b2ccebd34_.py b/db/migrations/versions/ea9b2ccebd34_.py index 8d46d380..297adec1 100644 --- a/db/migrations/versions/ea9b2ccebd34_.py +++ b/db/migrations/versions/ea9b2ccebd34_.py @@ -6,6 +6,7 @@ Create Date: 2023-07-14 10:17:05.239734 """ + from uuid import uuid4 import sqlalchemy as sa diff --git a/db/migrations/versions/~2023_10_04_1057-342b7a05b923_.py b/db/migrations/versions/~2023_10_04_1057-342b7a05b923_.py index 6ae5f51f..abda1c96 100644 --- a/db/migrations/versions/~2023_10_04_1057-342b7a05b923_.py +++ b/db/migrations/versions/~2023_10_04_1057-342b7a05b923_.py @@ -5,6 +5,7 @@ Create Date: 2023-10-04 10:57:01.282981 """ + import sqlalchemy as sa from alembic import op diff --git a/db/migrations/versions/~2023_10_27_1246-482f385dd1ee_.py b/db/migrations/versions/~2023_10_27_1246-482f385dd1ee_.py index 77da1afb..76a57443 100644 --- a/db/migrations/versions/~2023_10_27_1246-482f385dd1ee_.py +++ b/db/migrations/versions/~2023_10_27_1246-482f385dd1ee_.py @@ -5,6 +5,7 @@ Create Date: 2023-10-27 12:46:04.648090 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/~2023_11_10_1109-f0625240974a_.py b/db/migrations/versions/~2023_11_10_1109-f0625240974a_.py index 2b642959..6732539c 100644 --- a/db/migrations/versions/~2023_11_10_1109-f0625240974a_.py +++ b/db/migrations/versions/~2023_11_10_1109-f0625240974a_.py @@ -5,6 +5,7 @@ Create Date: 2023-11-10 11:09:45.918590 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql diff --git a/db/migrations/versions/~2023_11_10_1122-af78512b644f_.py b/db/migrations/versions/~2023_11_10_1122-af78512b644f_.py index 151a77c0..b88af59d 100644 --- a/db/migrations/versions/~2023_11_10_1122-af78512b644f_.py +++ b/db/migrations/versions/~2023_11_10_1122-af78512b644f_.py @@ -5,6 +5,7 @@ Create Date: 2023-11-10 11:22:18.680876 """ + import uuid import sqlalchemy as sa @@ -71,11 +72,11 @@ def downgrade(): else: comments_update_dict[comment_id] = [{"id": id, "comment": comment, "date_created": date_created}] - for comment_id, date_created in comments: + for comment_id, _date_created in comments: for i_comment_id, val in comments_update_dict.items(): val = sorted(val, key=lambda x: x["date_created"]) if comment_id == i_comment_id: - update_query = sa.text("UPDATE comments SET comment = :comment" " WHERE comment_id = :comment_id") + update_query = sa.text("UPDATE comments SET comment = :comment WHERE comment_id = :comment_id") params = { "comment_id": comment_id, "comment": val[-1]["comment"], diff --git a/db/migrations/versions/~2024_01_05_1434-5c03105a204c_.py b/db/migrations/versions/~2024_01_05_1434-5c03105a204c_.py index ad950679..0b34777e 100644 --- a/db/migrations/versions/~2024_01_05_1434-5c03105a204c_.py +++ b/db/migrations/versions/~2024_01_05_1434-5c03105a204c_.py @@ -5,6 +5,7 @@ Create Date: 2024-01-05 14:34:23.729866 """ + import sqlalchemy as sa from alembic import op @@ -19,7 +20,7 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### connection = op.get_bind() insert_query = sa.text( - "INSERT INTO assessment_round(round_id, scoring_system)" "VALUES (:uuid, :scoring_system) RETURNING round_id;" + "INSERT INTO assessment_round(round_id, scoring_system)VALUES (:uuid, :scoring_system) RETURNING round_id;" ) params = { diff --git a/db/migrations/versions/~2024_02_12_1512-3d9f0c0345ae_.py b/db/migrations/versions/~2024_02_12_1512-3d9f0c0345ae_.py index 22120d38..0c950cef 100644 --- a/db/migrations/versions/~2024_02_12_1512-3d9f0c0345ae_.py +++ b/db/migrations/versions/~2024_02_12_1512-3d9f0c0345ae_.py @@ -5,6 +5,7 @@ Create Date: 2024-02-12 15:12:50.627103 """ + import sqlalchemy as sa from alembic import op @@ -19,7 +20,7 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### connection = op.get_bind() insert_query = sa.text( - "INSERT INTO assessment_round(round_id, scoring_system)" "VALUES (:uuid, :scoring_system) RETURNING round_id;" + "INSERT INTO assessment_round(round_id, scoring_system)VALUES (:uuid, :scoring_system) RETURNING round_id;" ) params = { diff --git a/db/migrations/versions/~2024_02_16_1050-6c8205510de6_.py b/db/migrations/versions/~2024_02_16_1050-6c8205510de6_.py index 34a43e23..104a9824 100644 --- a/db/migrations/versions/~2024_02_16_1050-6c8205510de6_.py +++ b/db/migrations/versions/~2024_02_16_1050-6c8205510de6_.py @@ -5,6 +5,7 @@ Create Date: 2024-02-16 10:50:21.677064 """ + import sqlalchemy as sa from alembic import op @@ -19,7 +20,7 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### connection = op.get_bind() insert_query = sa.text( - "INSERT INTO assessment_round(round_id, scoring_system)" "VALUES (:uuid, :scoring_system) RETURNING round_id;" + "INSERT INTO assessment_round(round_id, scoring_system)VALUES (:uuid, :scoring_system) RETURNING round_id;" ) params = { diff --git a/db/migrations/versions/~2024_04_15_1844-5d3468e62492_add_whole_application_to_commenttype.py b/db/migrations/versions/~2024_04_15_1844-5d3468e62492_add_whole_application_to_commenttype.py index b64a472c..df989530 100644 --- a/db/migrations/versions/~2024_04_15_1844-5d3468e62492_add_whole_application_to_commenttype.py +++ b/db/migrations/versions/~2024_04_15_1844-5d3468e62492_add_whole_application_to_commenttype.py @@ -5,10 +5,10 @@ Create Date: 2024-04-15 18:44:34.874758 """ + import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "5d3468e62492" down_revision = "dac03038236f" @@ -38,9 +38,7 @@ def upgrade(): old_type.drop(op.get_bind(), checkfirst=False) # Create and convert to the "new" status type new_type.create(op.get_bind(), checkfirst=False) - op.execute( - f"ALTER TABLE comments ALTER COLUMN comment_type TYPE {enum_name}" f" USING comment_type::text::{enum_name}" - ) + op.execute(f"ALTER TABLE comments ALTER COLUMN comment_type TYPE {enum_name} USING comment_type::text::{enum_name}") tmp_type.drop(op.get_bind(), checkfirst=False) @@ -56,7 +54,5 @@ def downgrade(): new_type.drop(op.get_bind(), checkfirst=False) # Create and convert to the "new" status type old_type.create(op.get_bind(), checkfirst=False) - op.execute( - f"ALTER TABLE comments ALTER COLUMN comment_type TYPE {enum_name}" f" USING comment_type::text::{enum_name}" - ) + op.execute(f"ALTER TABLE comments ALTER COLUMN comment_type TYPE {enum_name} USING comment_type::text::{enum_name}") tmp_type.drop(op.get_bind(), checkfirst=False) diff --git a/db/migrations/versions/~2024_04_16_1018-dac03038236f_.py b/db/migrations/versions/~2024_04_16_1018-dac03038236f_.py index 08bb8b33..e2def5bf 100644 --- a/db/migrations/versions/~2024_04_16_1018-dac03038236f_.py +++ b/db/migrations/versions/~2024_04_16_1018-dac03038236f_.py @@ -5,6 +5,7 @@ Create Date: 2024-04-16 10:18:08.511694 """ + import sqlalchemy as sa from alembic import op diff --git a/db/migrations/versions/~2024_05_03_1645-4bd13b6df99b_.py b/db/migrations/versions/~2024_05_03_1645-4bd13b6df99b_.py index 8c4c4eca..9ec16852 100644 --- a/db/migrations/versions/~2024_05_03_1645-4bd13b6df99b_.py +++ b/db/migrations/versions/~2024_05_03_1645-4bd13b6df99b_.py @@ -5,6 +5,7 @@ Create Date: 2024-05-03 16:45:58.017724 """ + import sqlalchemy as sa from alembic import op @@ -19,7 +20,7 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### connection = op.get_bind() insert_query = sa.text( - "INSERT INTO assessment_round(round_id, scoring_system)" "VALUES (:uuid, :scoring_system) RETURNING round_id;" + "INSERT INTO assessment_round(round_id, scoring_system)VALUES (:uuid, :scoring_system) RETURNING round_id;" ) params = { diff --git a/db/migrations/versions/~2024_05_09_1538-94b31619f50a_.py b/db/migrations/versions/~2024_05_09_1538-94b31619f50a_.py index a4e6ffe9..8aa81552 100644 --- a/db/migrations/versions/~2024_05_09_1538-94b31619f50a_.py +++ b/db/migrations/versions/~2024_05_09_1538-94b31619f50a_.py @@ -5,10 +5,10 @@ Create Date: 2024-05-09 15:38:33.124627 """ + import sqlalchemy as sa from alembic import op - # revision identifiers, used by Alembic. revision = "94b31619f50a" down_revision = "4bd13b6df99b" diff --git a/db/migrations/versions/~2024_05_09_1807-c0fd614c99ea_.py b/db/migrations/versions/~2024_05_09_1807-c0fd614c99ea_.py index f808f68e..58165bd5 100644 --- a/db/migrations/versions/~2024_05_09_1807-c0fd614c99ea_.py +++ b/db/migrations/versions/~2024_05_09_1807-c0fd614c99ea_.py @@ -5,6 +5,7 @@ Create Date: 2024-05-09 18:07:40.734667 """ + import sqlalchemy as sa from alembic import op @@ -19,7 +20,7 @@ def upgrade(): # ### commands auto generated by Alembic - please adjust! ### connection = op.get_bind() insert_query = sa.text( - "INSERT INTO assessment_round(round_id, scoring_system)" "VALUES (:uuid, :scoring_system) RETURNING round_id;" + "INSERT INTO assessment_round(round_id, scoring_system)VALUES (:uuid, :scoring_system) RETURNING round_id;" ) params = { diff --git a/db/migrations/versions/~2024_06_20_0919-cf1169998c27_.py b/db/migrations/versions/~2024_06_20_0919-cf1169998c27_.py index d975aea3..8465b8b0 100644 --- a/db/migrations/versions/~2024_06_20_0919-cf1169998c27_.py +++ b/db/migrations/versions/~2024_06_20_0919-cf1169998c27_.py @@ -5,6 +5,7 @@ Create Date: 2024-06-20 09:19:33.420477 """ + import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql @@ -22,7 +23,12 @@ def upgrade(): "allocation_association", sa.Column("application_id", sa.UUID(), nullable=False), sa.Column("user_id", sa.UUID(), nullable=False), - sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=True), + sa.Column( + "created_at", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=True, + ), sa.Column("active", sa.Boolean(), nullable=False), sa.Column("log", postgresql.JSONB(astext_type=sa.Text()), nullable=False), sa.ForeignKeyConstraint( diff --git a/db/migrations/versions/~2024_07_02_1335-2737a83d7605_.py b/db/migrations/versions/~2024_07_02_1335-2737a83d7605_.py index 19ff4aa5..88710cbf 100644 --- a/db/migrations/versions/~2024_07_02_1335-2737a83d7605_.py +++ b/db/migrations/versions/~2024_07_02_1335-2737a83d7605_.py @@ -5,6 +5,7 @@ Create Date: 2024-07-02 13:35:20.268046 """ + import sqlalchemy as sa from alembic import op diff --git a/db/migrations/versions/~2024_07_17_1058-eecdd097df78_.py b/db/migrations/versions/~2024_07_17_1058-eecdd097df78_.py index ae39773f..c253838a 100644 --- a/db/migrations/versions/~2024_07_17_1058-eecdd097df78_.py +++ b/db/migrations/versions/~2024_07_17_1058-eecdd097df78_.py @@ -5,6 +5,7 @@ Create Date: 2024-07-17 10:58:42.292111 """ + import uuid import sqlalchemy as sa diff --git a/db/models/__init__.py b/db/models/__init__.py index 9280a28f..96e83c03 100644 --- a/db/models/__init__.py +++ b/db/models/__init__.py @@ -1,16 +1,9 @@ -# flake8: noqa -from db.models.assessment_record import AllocationAssociation -from db.models.assessment_record import AssessmentRecord -from db.models.assessment_record import TagAssociation +from db.models.assessment_record import AllocationAssociation, AssessmentRecord, TagAssociation from db.models.comment import Comment -from db.models.flags import AssessmentFlag -from db.models.flags import FlagUpdate +from db.models.flags import AssessmentFlag, FlagUpdate from db.models.qa_complete.qa_complete import QaComplete -from db.models.score import AssessmentRound -from db.models.score import Score -from db.models.score import ScoringSystem -from db.models.tag import Tag -from db.models.tag import TagType +from db.models.score import AssessmentRound, Score, ScoringSystem +from db.models.tag import Tag, TagType __all__ = [ "AssessmentRecord", diff --git a/db/models/assessment_record/__init__.py b/db/models/assessment_record/__init__.py index e07b7189..e62bf9a8 100644 --- a/db/models/assessment_record/__init__.py +++ b/db/models/assessment_record/__init__.py @@ -1,5 +1,12 @@ -# flake8: noqa from .allocation_association import AllocationAssociation from .assessment_records import AssessmentRecord -from .db_utils import * +from .db_utils import block_json_func, block_json_updates_trig from .tag_association import TagAssociation + +__all__ = [ + "AllocationAssociation", + "AssessmentRecord", + "TagAssociation", + "block_json_func", + "block_json_updates_trig", +] diff --git a/db/models/assessment_record/allocation_association.py b/db/models/assessment_record/allocation_association.py index 9ed783a9..d4dcca0e 100644 --- a/db/models/assessment_record/allocation_association.py +++ b/db/models/assessment_record/allocation_association.py @@ -1,11 +1,8 @@ -from db import db from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import func -from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import Column, ForeignKey, func +from sqlalchemy.dialects.postgresql import JSONB, UUID +from db import db BaseModel: DefaultMeta = db.Model diff --git a/db/models/assessment_record/assessment_records.py b/db/models/assessment_record/assessment_records.py index a1009988..d8f6701a 100644 --- a/db/models/assessment_record/assessment_records.py +++ b/db/models/assessment_record/assessment_records.py @@ -5,23 +5,16 @@ files. """ -from db import db -from db.models.assessment_record.enums import Language -from db.models.assessment_record.enums import Status + from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import cast -from sqlalchemy import Column -from sqlalchemy import Computed -from sqlalchemy import func -from sqlalchemy import Index -from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.dialects.postgresql import TEXT -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import column_property -from sqlalchemy.orm import relationship +from sqlalchemy import Column, Computed, Index, cast, func +from sqlalchemy.dialects.postgresql import ENUM, JSONB, TEXT, UUID +from sqlalchemy.orm import column_property, relationship from sqlalchemy.types import Boolean +from db import db +from db.models.assessment_record.enums import Language, Status + BaseModel: DefaultMeta = db.Model diff --git a/db/models/assessment_record/db_utils.py b/db/models/assessment_record/db_utils.py index 41444c44..86960551 100644 --- a/db/models/assessment_record/db_utils.py +++ b/db/models/assessment_record/db_utils.py @@ -1,10 +1,10 @@ from alembic_utils.pg_extension import PGExtension from alembic_utils.pg_function import PGFunction from alembic_utils.pg_trigger import PGTrigger +from sqlalchemy import event, text + from db.models.comment import Comment from db.models.score import Score -from sqlalchemy import event -from sqlalchemy import text # A method of imposing a database level block to mutating application json. diff --git a/db/models/assessment_record/tag_association.py b/db/models/assessment_record/tag_association.py index 246d17c8..39f68a64 100644 --- a/db/models/assessment_record/tag_association.py +++ b/db/models/assessment_record/tag_association.py @@ -1,13 +1,12 @@ import uuid -from db import db from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import func +from sqlalchemy import Column, ForeignKey, func from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship +from db import db + BaseModel: DefaultMeta = db.Model diff --git a/db/models/comment/__init__.py b/db/models/comment/__init__.py index 930ae5cb..cfa7bf7e 100644 --- a/db/models/comment/__init__.py +++ b/db/models/comment/__init__.py @@ -1,4 +1,3 @@ -# flake8: noqa from .comments import Comment from .comments_update import CommentsUpdate diff --git a/db/models/comment/comments.py b/db/models/comment/comments.py index 01ce8a48..6a6b36e7 100644 --- a/db/models/comment/comments.py +++ b/db/models/comment/comments.py @@ -1,14 +1,14 @@ """The module containing all code related to the `comments` table within the Postgres db.""" + import uuid +from sqlalchemy import ForeignKey, func +from sqlalchemy.dialects.postgresql import ENUM, UUID +from sqlalchemy.orm import relationship + from db import db from db.models.comment.enums import CommentType -from sqlalchemy import ForeignKey -from sqlalchemy import func -from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.dialects.postgresql import UUID -from sqlalchemy.orm import relationship class Comment(db.Model): diff --git a/db/models/comment/comments_update.py b/db/models/comment/comments_update.py index ac8bce0f..f4b65004 100644 --- a/db/models/comment/comments_update.py +++ b/db/models/comment/comments_update.py @@ -1,13 +1,13 @@ """The module containing all code related to the `comments` table within the Postgres db.""" + import uuid -from db import db -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import func +from sqlalchemy import Column, ForeignKey, func from sqlalchemy.dialects.postgresql import UUID +from db import db + class CommentsUpdate(db.Model): """CommentsUpdate The sqlalchemy-flask model class used to define the diff --git a/db/models/flags/__init__.py b/db/models/flags/__init__.py index 1b77d5f9..ba071fa6 100644 --- a/db/models/flags/__init__.py +++ b/db/models/flags/__init__.py @@ -1,6 +1,4 @@ -# flake8: noqa from .assessment_flag import AssessmentFlag -from .flag_update import FlagStatus -from .flag_update import FlagUpdate +from .flag_update import FlagStatus, FlagUpdate __all__ = ["AssessmentFlag", "FlagUpdate", "FlagStatus"] diff --git a/db/models/flags/assessment_flag.py b/db/models/flags/assessment_flag.py index 30862a50..a4df2936 100644 --- a/db/models/flags/assessment_flag.py +++ b/db/models/flags/assessment_flag.py @@ -1,16 +1,13 @@ from uuid import uuid4 -from db import db -from db.models.flags.flag_update import FlagStatus from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import String -from sqlalchemy.dialects.postgresql import ARRAY -from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import Column, ForeignKey, String +from sqlalchemy.dialects.postgresql import ARRAY, ENUM, UUID from sqlalchemy.orm import relationship +from db import db +from db.models.flags.flag_update import FlagStatus + BaseModel: DefaultMeta = db.Model diff --git a/db/models/flags/flag_update.py b/db/models/flags/flag_update.py index 0fcd2693..c878a33d 100644 --- a/db/models/flags/flag_update.py +++ b/db/models/flags/flag_update.py @@ -1,15 +1,13 @@ from enum import IntEnum from uuid import uuid4 -from db import db from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import String -from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy import Column, ForeignKey, String +from sqlalchemy.dialects.postgresql import ENUM, UUID from sqlalchemy.sql import func +from db import db + BaseModel: DefaultMeta = db.Model diff --git a/db/models/qa_complete/__init__.py b/db/models/qa_complete/__init__.py index fa1245da..8fdf01b4 100644 --- a/db/models/qa_complete/__init__.py +++ b/db/models/qa_complete/__init__.py @@ -1,4 +1,3 @@ -# flake8: noqa from .qa_complete import QaComplete __all__ = ["QaComplete"] diff --git a/db/models/qa_complete/qa_complete.py b/db/models/qa_complete/qa_complete.py index bf8a2b59..dafa1acf 100644 --- a/db/models/qa_complete/qa_complete.py +++ b/db/models/qa_complete/qa_complete.py @@ -1,13 +1,12 @@ from uuid import uuid4 -from db import db from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import String +from sqlalchemy import Column, ForeignKey, String from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.sql import func +from db import db + BaseModel: DefaultMeta = db.Model diff --git a/db/models/score/__init__.py b/db/models/score/__init__.py index c66328cc..447d38d8 100644 --- a/db/models/score/__init__.py +++ b/db/models/score/__init__.py @@ -1,6 +1,3 @@ -# flake8: noqa -from .scores import AssessmentRound -from .scores import Score -from .scores import ScoringSystem +from .scores import AssessmentRound, Score, ScoringSystem __all__ = ["Score", "AssessmentRound", "ScoringSystem"] diff --git a/db/models/score/scores.py b/db/models/score/scores.py index 8c8d2fb6..c121b0c2 100644 --- a/db/models/score/scores.py +++ b/db/models/score/scores.py @@ -1,14 +1,15 @@ """The module containing all code related to the `scores` table within the Postgres db.""" + import uuid -from db import db -from db.models.score.enums import ScoringSystem from sqlalchemy import ForeignKey -from sqlalchemy.dialects.postgresql import ENUM -from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.dialects.postgresql import ENUM, UUID from sqlalchemy.sql import func +from db import db +from db.models.score.enums import ScoringSystem + class Score(db.Model): """Score The sqlalchemy-flask model class used to define the `scores` table in @@ -37,11 +38,20 @@ class AssessmentRound(db.Model): __tablename__ = "assessment_round" - round_id = db.Column("round_id", UUID(as_uuid=True), default=uuid.uuid4, primary_key=True, nullable=False) + round_id = db.Column( + "round_id", + UUID(as_uuid=True), + default=uuid.uuid4, + primary_key=True, + nullable=False, + ) # link to the scoring system table scoring_system_id = db.Column( - "scoring_system_id", UUID(as_uuid=True), ForeignKey("scoring_system.id"), nullable=False + "scoring_system_id", + UUID(as_uuid=True), + ForeignKey("scoring_system.id"), + nullable=False, ) diff --git a/db/models/tag/__init__.py b/db/models/tag/__init__.py index 9610bb8f..17c2673c 100644 --- a/db/models/tag/__init__.py +++ b/db/models/tag/__init__.py @@ -1,3 +1,7 @@ -# flake8: noqa from .tag_types import TagType from .tags import Tag + +__all__ = [ + "Tag", + "TagType", +] diff --git a/db/models/tag/tag_types.py b/db/models/tag/tag_types.py index 37fda79f..af8a183b 100644 --- a/db/models/tag/tag_types.py +++ b/db/models/tag/tag_types.py @@ -1,10 +1,11 @@ import uuid -from db import db from flask_sqlalchemy.model import DefaultMeta from sqlalchemy import Column from sqlalchemy.dialects.postgresql import UUID +from db import db + BaseModel: DefaultMeta = db.Model diff --git a/db/models/tag/tags.py b/db/models/tag/tags.py index 89245105..bda0f165 100644 --- a/db/models/tag/tags.py +++ b/db/models/tag/tags.py @@ -1,17 +1,13 @@ import re import uuid -from db import db from flask_sqlalchemy.model import DefaultMeta -from sqlalchemy import Column -from sqlalchemy import ForeignKey -from sqlalchemy import func -from sqlalchemy import Index -from sqlalchemy import text +from sqlalchemy import Column, ForeignKey, Index, func, text from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.event import listens_for -from sqlalchemy.orm import relationship -from sqlalchemy.orm import validates +from sqlalchemy.orm import relationship, validates + +from db import db BaseModel: DefaultMeta = db.Model diff --git a/db/queries/__init__.py b/db/queries/__init__.py index d74af382..8a340d4f 100644 --- a/db/queries/__init__.py +++ b/db/queries/__init__.py @@ -1,17 +1,19 @@ -from .assessment_records.queries import bulk_insert_application_record -from .assessment_records.queries import delete_assessment_record -from .assessment_records.queries import find_answer_by_key_runner -from .assessment_records.queries import get_metadata_for_fund_round_id -from .assessment_records.queries import insert_application_record -from .comments.queries import create_comment -from .comments.queries import get_comments_from_db +from .assessment_records.queries import ( + bulk_insert_application_record, + delete_assessment_record, + find_answer_by_key_runner, + get_metadata_for_fund_round_id, + insert_application_record, +) +from .comments.queries import create_comment, get_comments_from_db from .progress.queries import get_progress_for_app -from .scores.queries import _insert_scoring_system -from .scores.queries import create_score_for_app_sub_crit -from .scores.queries import get_scores_for_app_sub_crit -from .scores.queries import get_scoring_system_for_round_id -from .tags.queries import insert_tags -from .tags.queries import select_tags_for_fund_round +from .scores.queries import ( + _insert_scoring_system, + create_score_for_app_sub_crit, + get_scores_for_app_sub_crit, + get_scoring_system_for_round_id, +) +from .tags.queries import insert_tags, select_tags_for_fund_round __all__ = [ "select_tags_for_fund_round", diff --git a/db/queries/assessment_records/_helpers.py b/db/queries/assessment_records/_helpers.py index 31cbe66b..94ba3147 100644 --- a/db/queries/assessment_records/_helpers.py +++ b/db/queries/assessment_records/_helpers.py @@ -3,11 +3,12 @@ import jsonpath_rw_ext import requests +from flask import current_app + from config.mappings.assessment_mapping_fund_round import ( fund_round_data_key_mappings, ) from db.models.assessment_record import TagAssociation -from flask import current_app def get_answer_value(application_json, answer_key): @@ -60,7 +61,7 @@ def get_location_json_from_postcode(raw_postcode): return location_data -def derive_application_values(application_json): +def derive_application_values(application_json): # noqa: C901 - historical sadness # TODO: implement mapping function to match # fund+round fields to derived values derived_values = {} @@ -123,9 +124,9 @@ def derive_application_values(application_json): derived_values["application_id"] = application_id if application_json["project_name"] is None and fund_round_shortname == "COFEOI": - derived_values[ - "project_name" - ] = "" # EOI does not have a project name form compoent. Maybe this has to become nullable? + derived_values["project_name"] = ( + "" # EOI does not have a project name form compoent. Maybe this has to become nullable? + ) else: derived_values["project_name"] = application_json["project_name"] @@ -222,7 +223,9 @@ def filter_tags(incoming_tags, existing_tags): None, ) if _incoming_tag: - current_app.logger.info(f"Tag id is already associated: {existing_tag_id}") + current_app.logger.info( + "Tag id is already associated: {existing_tag_id}", extra=dict(existing_tag_id=existing_tag_id) + ) else: filtered_tags.append(existing_tag_list) return filtered_tags diff --git a/db/queries/assessment_records/queries.py b/db/queries/assessment_records/queries.py index 028d6509..f64cd3e0 100644 --- a/db/queries/assessment_records/queries.py +++ b/db/queries/assessment_records/queries.py @@ -3,49 +3,36 @@ Joins allowed. """ + import json -from datetime import datetime -from datetime import timezone -from typing import Dict -from typing import List +from datetime import datetime, timezone +from typing import Dict, List from bs4 import BeautifulSoup +from flask import current_app +from sqlalchemy import String, and_, bindparam, cast, desc, exc, func, or_, select, update +from sqlalchemy.dialects.postgresql import insert as postgres_insert +from sqlalchemy.orm import aliased, defer, load_only, selectinload + from config.mappings.assessment_mapping_fund_round import ( fund_round_mapping_config_with_round_id, ) from db import db -from db.models.assessment_record import AssessmentRecord -from db.models.assessment_record import TagAssociation +from db.models.assessment_record import AssessmentRecord, TagAssociation from db.models.assessment_record.allocation_association import AllocationAssociation from db.models.assessment_record.enums import Status from db.models.flags.flag_update import FlagStatus from db.models.score import Score from db.models.tag.tag_types import TagType from db.models.tag.tags import Tag -from db.queries.assessment_records._helpers import derive_application_values -from db.queries.assessment_records._helpers import filter_tags -from db.queries.assessment_records._helpers import get_existing_tags -from db.queries.assessment_records._helpers import update_tag_associations -from db.schemas import AssessmentRecordMetadata -from db.schemas import AssessmentSubCriteriaMetadata -from db.schemas import AssessorTaskListMetadata -from flask import current_app +from db.queries.assessment_records._helpers import ( + derive_application_values, + filter_tags, + get_existing_tags, + update_tag_associations, +) +from db.schemas import AssessmentRecordMetadata, AssessmentSubCriteriaMetadata, AssessorTaskListMetadata from services.data_services import get_account_name -from sqlalchemy import and_ -from sqlalchemy import bindparam -from sqlalchemy import cast -from sqlalchemy import desc -from sqlalchemy import exc -from sqlalchemy import func -from sqlalchemy import or_ -from sqlalchemy import select -from sqlalchemy import String -from sqlalchemy import update -from sqlalchemy.dialects.postgresql import insert as postgres_insert -from sqlalchemy.orm import aliased -from sqlalchemy.orm import defer -from sqlalchemy.orm import load_only -from sqlalchemy.orm import selectinload def get_metadata_for_application( @@ -65,7 +52,7 @@ def get_metadata_for_application( return metadata_serializer.dump(result) -def get_metadata_for_fund_round_id( +def get_metadata_for_fund_round_id( # noqa: C901 - historical sadness fund_id: str, round_id: str, search_term: str = "", @@ -73,7 +60,7 @@ def get_metadata_for_fund_round_id( status: str = "", search_in: str = "", funding_type: str = "", - countries: List[str] = ["all"], + countries: List[str] | None = None, filter_by_tag: str = "", country: str = "", region: str = "", @@ -94,6 +81,8 @@ def get_metadata_for_fund_round_id( :return: A list of dictionaries. """ + if countries is None: + countries = ["all"] statement = ( select(AssessmentRecord) @@ -104,14 +93,18 @@ def get_metadata_for_fund_round_id( selectinload(AssessmentRecord.flags), selectinload(AssessmentRecord.user_associations), selectinload(AssessmentRecord.tag_associations).selectinload(TagAssociation.tag).selectinload(Tag.tag_type), - ).where( + ) + .where( AssessmentRecord.fund_id == fund_id, AssessmentRecord.round_id == round_id, AssessmentRecord.is_withdrawn == False, # noqa: E712 ) ) if search_term != "": - current_app.logger.info(f"Performing assessment search on search term: {search_term} in fields {search_in}") + current_app.logger.info( + "Performing assessment search on search term: {search_term} in fields {search_in}", + extra=dict(search_term=search_term, search_in=search_in), + ) search_term = search_term.replace(" ", "%") filters = [] @@ -142,28 +135,35 @@ def get_metadata_for_fund_round_id( statement = statement.where(AssessmentRecord.application_id.in_(record_ids_with_tag_id)) if "all" not in countries: - current_app.logger.info(f"Performing assessment search on countries: {countries}.") + current_app.logger.info( + "Performing assessment search on countries: {countries}.", extra=dict(countries=countries) + ) statement = statement.where(AssessmentRecord.location_json_blob["country"].astext.ilike(func.any_(countries))) if asset_type != "ALL" and asset_type != "": - current_app.logger.info(f"Performing assessment search on asset type: {asset_type}.") + current_app.logger.info( + "Performing assessment search on asset type: {asset_type}.", extra=dict(asset_type=asset_type) + ) statement = statement.where(AssessmentRecord.asset_type == asset_type) if country != "" and country != "ALL": - current_app.logger.info(f"Performing assessment search on country: {country}.") + current_app.logger.info("Performing assessment search on country: {country}.", extra=dict(country=country)) statement = statement.where(AssessmentRecord.location_json_blob["country"].astext == country) if region != "" and region != "ALL": - current_app.logger.info(f"Performing assessment search on region: {region}.") + current_app.logger.info("Performing assessment search on region: {region}.", extra=dict(region=region)) statement = statement.where(AssessmentRecord.location_json_blob["region"].astext == region) if datasets != "" and datasets != "ALL": datasets = True if str(datasets).lower() == "yes" or datasets is True else False - current_app.logger.info(f"Performing assessment search on datasets: {datasets}.") + current_app.logger.info("Performing assessment search on datasets: {datasets}.", extra=dict(datasets=datasets)) statement = statement.where(cast(AssessmentRecord.datasets, String) == str(datasets).lower()) if publish_datasets != "" and publish_datasets != "ALL": - current_app.logger.info(f"Performing assessment search on publish_datasets: {publish_datasets}.") + current_app.logger.info( + "Performing assessment search on publish_datasets: {publish_datasets}.", + extra=dict(publish_datasets=publish_datasets), + ) if publish_datasets == "None": statement = statement.where(AssessmentRecord.publish_datasets.is_(None)) else: @@ -171,11 +171,16 @@ def get_metadata_for_fund_round_id( if team_in_place != "" and team_in_place != "ALL": team_in_place = True if str(team_in_place).lower() == "yes" or team_in_place is True else False - current_app.logger.info(f"Performing assessment search on team_in_place: {team_in_place}.") + current_app.logger.info( + "Performing assessment search on team_in_place: {team_in_place}.", extra=dict(team_in_place=team_in_place) + ) statement = statement.where(cast(AssessmentRecord.team_in_place, String) == str(team_in_place).lower()) if local_authority != "" and local_authority != "ALL": - current_app.logger.info(f"Performing assessment search on local_authority: {local_authority}.") + current_app.logger.info( + "Performing assessment search on local_authority: {local_authority}.", + extra=dict(local_authority=local_authority), + ) subquery = ( select(AssessmentRecord.application_id).where( @@ -190,7 +195,10 @@ def get_metadata_for_fund_round_id( statement = statement.where(AssessmentRecord.application_id.in_(subquery)) if joint_application != "" and joint_application != "ALL" and joint_application in ["true", "false"]: - current_app.logger.info(f"Performing assessment search on joint_application: {joint_application}.") + current_app.logger.info( + "Performing assessment search on joint_application: {joint_application}.", + extra=dict(joint_application=joint_application), + ) subquery = ( select(AssessmentRecord.application_id).where( @@ -204,7 +212,9 @@ def get_metadata_for_fund_round_id( statement = statement.where(AssessmentRecord.application_id.in_(subquery)) if funding_type != "ALL" and funding_type != "": - current_app.logger.info(f"Performing assessment search on funding type: {funding_type}.") + current_app.logger.info( + "Performing assessment search on funding type: {funding_type}.", extra=dict(funding_type=funding_type) + ) # TODO SS figure out how to stop double quoting this - it works but is ugly # it's because when we retrieve the json element as funding_type, we get it as a json element, not pure text, # so it has the double quotes from the json so we have to include them in the comparison @@ -278,8 +288,8 @@ def bulk_insert_application_record( if derived_values["location_json_blob"]["error"]: current_app.logger.error( - "Location key not found or invalid postcode provided for the " - f"application: {derived_values['short_id']}." + "Location key not found or invalid postcode provided for the application: {short_id}.", + extra=dict(short_id=derived_values["short_id"]), ) row = { @@ -309,7 +319,7 @@ def bulk_insert_application_record( print(f"Error occurred while inserting application {row['application_id']}, error: {e}") raise e - print("Inserted application_ids (i.e. application rows) :" f" {[row['application_id'] for row in rows]}") + print("Inserted application_ids (i.e. application rows) : {[row['application_id'] for row in rows]}") return rows @@ -552,7 +562,10 @@ def bulk_update_location_jsonb_blob(application_ids_to_location_data): def update_status_to_completed(application_id): - current_app.logger.info("Updating application status to COMPLETED" f" for application: {application_id}.") + current_app.logger.info( + "Updating application status to COMPLETED for application: {application_id}.", + extra=dict(application_id=application_id), + ) db.session.query(AssessmentRecord).filter(AssessmentRecord.application_id == application_id).update( {AssessmentRecord.workflow_status: Status.COMPLETED}, synchronize_session=False, @@ -660,7 +673,9 @@ def associate_assessment_tags(application_id, tags: List): if incoming_tag_id and not _existing_tags: # If no existing tags are found, create a new tag(s) with incoming tags info. - current_app.logger.info(f"Creating new tag(s) for {incoming_tag_id}") + current_app.logger.info( + "Creating new tag(s) for {incoming_tag_id}", extra=dict(incoming_tag_id=incoming_tag_id) + ) create_tag(application_id, incoming_tag_id, True, incoming_user_id) if incoming_tag_id and _existing_tags: @@ -674,9 +689,13 @@ def associate_assessment_tags(application_id, tags: List): )[1] # If it's already associated, skip, otherwise, create a new associated tag. if most_recent_tag.associated: - current_app.logger.info(f"Tag is alreday associated: {most_recent_tag.tag_id}") + current_app.logger.info( + "Tag is alreday associated: {tag_id}", extra=dict(tag_id=most_recent_tag.tag_id) + ) else: - current_app.logger.info(f"Creating new tag: {incoming_tag_id}") + current_app.logger.info( + "Creating new tag: {incoming_tag_id}", extra=dict(incoming_tag_id=incoming_tag_id) + ) create_tag( application_id, incoming_tag_id, @@ -684,7 +703,9 @@ def associate_assessment_tags(application_id, tags: List): incoming_user_id, ) else: - current_app.logger.info(f"Creating new tag: {incoming_tag_id}") + current_app.logger.info( + "Creating new tag: {incoming_tag_id}", extra=dict(incoming_tag_id=incoming_tag_id) + ) create_tag(application_id, incoming_tag_id, True, incoming_user_id) if not incoming_tag_id: @@ -692,7 +713,10 @@ def associate_assessment_tags(application_id, tags: List): for filterted_tag in filterted_tags: most_recent_tag = max(filterted_tag, key=lambda x: x[0])[1] if most_recent_tag.associated: - current_app.logger.info(f"Dis-associating existing associated tag_id: {most_recent_tag.tag_id}") + current_app.logger.info( + "Dis-associating existing associated tag_id: {tag_id}", + extra=dict(tag_id=most_recent_tag.tag_id), + ) create_tag( application_id, most_recent_tag.tag_id, @@ -790,7 +814,7 @@ def select_active_tags_associated_with_assessment(application_id): return associated_tags except Exception as e: - current_app.logger.error(f"Error: {e}") + current_app.logger.exception("Error") raise e @@ -860,7 +884,7 @@ def get_assessment_export_data(fund_id: str, round_id: str, report_type: str, li return obj -def get_export_data( +def get_export_data( # noqa: C901 - historical sadness round_id: str, report_type: str, list_of_fields: dict, @@ -943,7 +967,7 @@ def get_export_data( def add_missing_elements_with_empty_values(applicant_info, form_fields, language): result_data = applicant_info.copy() - for key, value in form_fields.items(): + for _key, value in form_fields.items(): title = value[language]["title"] if title not in result_data: result_data[title] = "" @@ -1003,7 +1027,12 @@ def create_user_application_association(application_id, user_id, assigner_id): application_id=application_id, assigner_id=assigner_id, active=True, - log={datetime.now(tz=timezone.utc).isoformat(): {"status": "activated", "assigner": str(assigner_id)}}, + log={ + datetime.now(tz=timezone.utc).isoformat(): { + "status": "activated", + "assigner": str(assigner_id), + } + }, ) try: db.session.add(allocation_association) @@ -1019,7 +1048,10 @@ def create_user_application_association(application_id, user_id, assigner_id): def update_user_application_association(application_id, user_id, active, assigner_id): allocation_association = ( db.session.query(AllocationAssociation) - .filter(AllocationAssociation.application_id == application_id, AllocationAssociation.user_id == user_id) + .filter( + AllocationAssociation.application_id == application_id, + AllocationAssociation.user_id == user_id, + ) .one_or_none() ) allocation_association.assigner_id = assigner_id diff --git a/db/queries/comments/__init__.py b/db/queries/comments/__init__.py index 804fb48c..2027d84a 100644 --- a/db/queries/comments/__init__.py +++ b/db/queries/comments/__init__.py @@ -1,6 +1,4 @@ -from .queries import create_comment -from .queries import get_comments_from_db -from .queries import update_comment +from .queries import create_comment, get_comments_from_db, update_comment __all__ = [ "get_comments_from_db", diff --git a/db/queries/comments/queries.py b/db/queries/comments/queries.py index f0ef3de2..7f8ed957 100644 --- a/db/queries/comments/queries.py +++ b/db/queries/comments/queries.py @@ -3,16 +3,17 @@ Joins allowed. """ + from collections import defaultdict from typing import Dict +from sqlalchemy import and_, select + from db import db from db.models.comment.comments import Comment from db.models.comment.comments_update import CommentsUpdate from db.models.comment.enums import CommentType from db.schemas import CommentMetadata -from sqlalchemy import and_ -from sqlalchemy import select def get_comments_from_db( diff --git a/db/queries/flags/queries.py b/db/queries/flags/queries.py index 79f84571..4d3a4c20 100644 --- a/db/queries/flags/queries.py +++ b/db/queries/flags/queries.py @@ -1,10 +1,10 @@ from typing import Dict +from sqlalchemy import select + from db import db from db.models.flags.assessment_flag import AssessmentFlag -from db.models.flags.flag_update import FlagStatus -from db.models.flags.flag_update import FlagUpdate -from sqlalchemy import select +from db.models.flags.flag_update import FlagStatus, FlagUpdate def get_flags_for_application(application_id): diff --git a/db/queries/progress/queries.py b/db/queries/progress/queries.py index 89e57bce..35713bce 100644 --- a/db/queries/progress/queries.py +++ b/db/queries/progress/queries.py @@ -3,10 +3,12 @@ Joins allowed. """ + +from sqlalchemy import func + from db import db from db.models.score import Score from db.schemas.schemas import ProgressSchema -from sqlalchemy import func def get_progress_for_app(application_ids=None): diff --git a/db/queries/qa_complete/queries.py b/db/queries/qa_complete/queries.py index b69d9a84..fbcd29a2 100644 --- a/db/queries/qa_complete/queries.py +++ b/db/queries/qa_complete/queries.py @@ -3,6 +3,7 @@ Joins allowed. """ + from typing import Dict from db import db diff --git a/db/queries/scores/queries.py b/db/queries/scores/queries.py index 7dfa3079..abb289da 100644 --- a/db/queries/scores/queries.py +++ b/db/queries/scores/queries.py @@ -3,21 +3,19 @@ Joins allowed. """ + import uuid from typing import Dict -from db import db -from db.models import AssessmentRecord -from db.models.score import AssessmentRound -from db.models.score import Score -from db.models.score import ScoringSystem -from db.schemas import AssessmentRoundMetadata -from db.schemas import ScoreMetadata -from db.schemas import ScoringSystemMetadata from flask import current_app as app from sqlalchemy import select from sqlalchemy.orm.exc import NoResultFound +from db import db +from db.models import AssessmentRecord +from db.models.score import AssessmentRound, Score, ScoringSystem +from db.schemas import AssessmentRoundMetadata, ScoreMetadata, ScoringSystemMetadata + def get_scores_for_app_sub_crit( application_id: str, @@ -145,7 +143,9 @@ def get_scoring_system_for_round_id(round_id: str) -> dict: metadata_serialiser = ScoringSystemMetadata() processed_scoring_system = metadata_serialiser.dump(scoring_system_instance) - app.logger.warning(f"No scoring system found for round_id: {round_id}. Defaulting to OneToFive") + app.logger.warning( + "No scoring system found for round_id: {round_id}. Defaulting to OneToFive", extra=dict(round_id=round_id) + ) return processed_scoring_system diff --git a/db/queries/tags/queries.py b/db/queries/tags/queries.py index 6657fd00..486ddde3 100644 --- a/db/queries/tags/queries.py +++ b/db/queries/tags/queries.py @@ -1,14 +1,13 @@ from typing import List +from flask import current_app +from sqlalchemy import distinct, func, or_ +from sqlalchemy.exc import NoResultFound + from db import db from db.models.assessment_record.tag_association import TagAssociation from db.models.tag.tag_types import TagType from db.models.tag.tags import Tag -from flask import current_app -from sqlalchemy import distinct -from sqlalchemy import func -from sqlalchemy import or_ -from sqlalchemy.exc import NoResultFound def insert_tags(tags, fund_id, round_id): @@ -47,8 +46,7 @@ def insert_tags(tags, fund_id, round_id): db.session.flush() # Flush changes to trigger validation except Exception as e: db.session.rollback() - current_app.logger.error(f"Error inserting tag '{value}': {str(e)}") - raise ValueError(f"Error inserting tag '{value}': {str(e)}") + raise ValueError(f"Error inserting tag '{value}': {str(e)}") from e inserted_tags.append(tag) @@ -56,8 +54,7 @@ def insert_tags(tags, fund_id, round_id): db.session.commit() except Exception as e: db.session.rollback() - current_app.logger.error(f"Error inserting tags: {str(e)}") - raise ValueError(f"Error inserting tags: {str(e)}") + raise ValueError(f"Error inserting tags: {str(e)}") from e return inserted_tags @@ -104,11 +101,11 @@ def update_tags(tags, fund_id, round_id): tag.type_id = tag_type_id if tag_type_id is not None else tag.type_id tag.active = active_status if active_status is not None else tag.active - except NoResultFound: + except NoResultFound as e: # If the tag doesn't exist, raise an error raise ValueError( f"Tag with id '{tag_id}' does not exist for fund_id '{fund_id}' and round_id '{round_id}'." - ) + ) from e updated_tags.append(tag) @@ -116,8 +113,7 @@ def update_tags(tags, fund_id, round_id): db.session.commit() except Exception as e: db.session.rollback() - current_app.logger.error(f"Error updating tags: {str(e)}") - raise ValueError(f"Error updating tags: {str(e)}") + raise ValueError(f"Error updating tags: {str(e)}") from e return updated_tags @@ -147,7 +143,10 @@ def select_tags_for_fund_round( .where(Tag.round_id == round_id) ) if search_term != "": - current_app.logger.info(f"Performing tag search on search term: {search_term} in fields {search_in}") + current_app.logger.info( + "Performing tag search on search term: {search_term} in fields {search_in}", + extra=dict(search_term=search_term, search_in=search_in), + ) # using % for sql LIKE search search_term = search_term.replace(" ", "%") diff --git a/db/schemas/__init__.py b/db/schemas/__init__.py index 905322bd..017e10dc 100644 --- a/db/schemas/__init__.py +++ b/db/schemas/__init__.py @@ -1,10 +1,12 @@ -from .schemas import AssessmentRecordMetadata -from .schemas import AssessmentRoundMetadata -from .schemas import AssessmentSubCriteriaMetadata -from .schemas import AssessorTaskListMetadata -from .schemas import CommentMetadata -from .schemas import ScoreMetadata -from .schemas import ScoringSystemMetadata +from .schemas import ( + AssessmentRecordMetadata, + AssessmentRoundMetadata, + AssessmentSubCriteriaMetadata, + AssessorTaskListMetadata, + CommentMetadata, + ScoreMetadata, + ScoringSystemMetadata, +) __all__ = [ "AssessmentRecordMetadata", diff --git a/db/schemas/schemas.py b/db/schemas/schemas.py index 8c386507..a228b17e 100644 --- a/db/schemas/schemas.py +++ b/db/schemas/schemas.py @@ -1,31 +1,19 @@ +from marshmallow import Schema, fields +from marshmallow.fields import UUID, Boolean, Enum, Field, Integer, Method, Nested, String +from marshmallow_sqlalchemy import SQLAlchemyAutoSchema, auto_field + from db.models.assessment_record import AssessmentRecord from db.models.assessment_record.allocation_association import AllocationAssociation -from db.models.assessment_record.enums import Language -from db.models.assessment_record.enums import Status +from db.models.assessment_record.enums import Language, Status from db.models.assessment_record.tag_association import TagAssociation -from db.models.comment import Comment -from db.models.comment import CommentsUpdate +from db.models.comment import Comment, CommentsUpdate from db.models.comment.enums import CommentType from db.models.flags.assessment_flag import AssessmentFlag from db.models.flags.flag_update import FlagUpdate from db.models.qa_complete import QaComplete -from db.models.score import AssessmentRound -from db.models.score import Score -from db.models.score import ScoringSystem +from db.models.score import AssessmentRound, Score, ScoringSystem from db.models.tag.tag_types import TagType from db.models.tag.tags import Tag -from marshmallow import fields -from marshmallow import Schema -from marshmallow.fields import Boolean -from marshmallow.fields import Enum -from marshmallow.fields import Field -from marshmallow.fields import Integer -from marshmallow.fields import Method -from marshmallow.fields import Nested -from marshmallow.fields import String -from marshmallow.fields import UUID -from marshmallow_sqlalchemy import auto_field -from marshmallow_sqlalchemy import SQLAlchemyAutoSchema class AssessmentRecordMetadata(SQLAlchemyAutoSchema): diff --git a/openapi/utils.py b/openapi/utils.py index 0eb9f237..b01a5008 100644 --- a/openapi/utils.py +++ b/openapi/utils.py @@ -1,7 +1,7 @@ -from typing import Any -from typing import Dict +from typing import Any, Dict import prance + from config import Config diff --git a/pyproject.toml b/pyproject.toml index ea606f08..97b4cd9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,27 +27,43 @@ dependencies = [ "uvicorn==0.30.1", ] -[tool.black] -line-length = 120 - [tool.docformatter] recursive = true wrap-summaries = 82 blank = true -[tool.flake8] -max-line-length = 120 -count = true +[tool.ruff] +line-length = 120 + +target-version = "py310" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle + "W", # pycodestyle + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C90", # mccabe cyclomatic complexity + "G", # flake8-logging-format +] +ignore = [] +exclude = [ + "db/migrations/versions/", + "venv*", + ".venv*", + "__pycache__", + "fund_store/config/fund_loader_config/FAB/" +] +mccabe.max-complexity = 12 [tool.uv] [dependency-groups] dev = [ - "black==24.4.2", "colored==2.2.4", "debugpy==1.8.2", "deepdiff==7.0.1", - "flake8-pyproject==1.2.3", "invoke==2.2.0", "json2html==1.3.0", "moto[s3,sqs]==5.0.10", @@ -56,4 +72,5 @@ dev = [ "pytest-env==1.1.3", "pytest-flask==1.3.0", "pytest-mock==3.14.0", + "ruff==0.7.4", ] diff --git a/scripts/data_updates/FS-3661-total-funding-requested-cyp.py b/scripts/data_updates/FS-3661-total-funding-requested-cyp.py index 6e74fd4a..ec357575 100644 --- a/scripts/data_updates/FS-3661-total-funding-requested-cyp.py +++ b/scripts/data_updates/FS-3661-total-funding-requested-cyp.py @@ -1,11 +1,11 @@ +from flask import current_app +from sqlalchemy import select, update + from config.mappings.assessment_mapping_fund_round import ( fund_round_data_key_mappings, ) from db import db from db.models import AssessmentRecord -from flask import current_app -from sqlalchemy import select -from sqlalchemy import update def update_funding_amount_requested_for_cyp(): @@ -20,7 +20,9 @@ def update_funding_amount_requested_for_cyp(): cyp_records = db.session.execute(select_assessment_records_for_round_stmt) for application_id, jsonb_blob in cyp_records: - current_app.logger.info(f"Processing application id {application_id}.") + current_app.logger.info( + "Processing application id {application_id}.", extra=dict(application_id=application_id) + ) total_funding = 0 for key in fund_round_data_key_mappings["CYPR1"]["funding_two"]: total_funding = total_funding + int( @@ -34,7 +36,10 @@ def update_funding_amount_requested_for_cyp(): ) new_funding_amount_requested = total_funding - current_app.logger.info(f"New funding amount requested: {new_funding_amount_requested}") + current_app.logger.info( + "New funding amount requested: {new_funding_amount_requested}", + extra=dict(new_funding_amount_requested=new_funding_amount_requested), + ) update_statement = ( update(AssessmentRecord) .values(funding_amount_requested=new_funding_amount_requested) diff --git a/scripts/delete_data.py b/scripts/delete_data.py index b5b1a62a..8982c772 100755 --- a/scripts/delete_data.py +++ b/scripts/delete_data.py @@ -2,13 +2,11 @@ from datetime import datetime import click + from db import db -from db.models.assessment_record import AssessmentRecord -from db.models.assessment_record import TagAssociation -from db.models.comment import Comment -from db.models.comment import CommentsUpdate -from db.models.flags import AssessmentFlag -from db.models.flags import FlagUpdate +from db.models.assessment_record import AssessmentRecord, TagAssociation +from db.models.comment import Comment, CommentsUpdate +from db.models.flags import AssessmentFlag, FlagUpdate from db.models.qa_complete import QaComplete from db.models.score import Score @@ -65,7 +63,13 @@ def cli(ctx, q): @cli.command() @click.option("-id", prompt=True) -@click.option("-c", "--do-commit", flag_value=True, default=False, help="Whether to commit changes to DB") +@click.option( + "-c", + "--do-commit", + flag_value=True, + default=False, + help="Whether to commit changes to DB", +) @click.pass_context def delete_assessment_record(ctx, id, do_commit): """Deletes a single assessment record, along with child records (tags, @@ -78,7 +82,13 @@ def delete_assessment_record(ctx, id, do_commit): @cli.command() @click.option("-r", "--round-id", prompt=True) -@click.option("-c", "--do-commit", flag_value=True, default=False, help="Whether to commit changes to DB") +@click.option( + "-c", + "--do-commit", + flag_value=True, + default=False, + help="Whether to commit changes to DB", +) @click.pass_context def delete_all_assessments_in_round(ctx, round_id, do_commit): """Deletes all assessment records in a round, along with their child records diff --git a/scripts/import_from_application.py b/scripts/import_from_application.py index b07140a7..40e5b91b 100755 --- a/scripts/import_from_application.py +++ b/scripts/import_from_application.py @@ -1,15 +1,15 @@ #!/usr/bin/env python3 -# flake8: noqa import argparse import requests +from fsd_utils import CommonConfig # noqa: E402 + from app import app # noqa: E402 from config import Config # noqa: E402 from config.mappings.assessment_mapping_fund_round import ( fund_round_mapping_config, ) from db.queries import bulk_insert_application_record # noqa: E402 -from fsd_utils import CommonConfig # noqa: E402 def init_argparse() -> argparse.ArgumentParser: diff --git a/scripts/location_utils.py b/scripts/location_utils.py index 6a2a023f..b307a901 100644 --- a/scripts/location_utils.py +++ b/scripts/location_utils.py @@ -1,13 +1,12 @@ import csv import requests + from config.mappings.assessment_mapping_fund_round import ( fund_round_data_key_mappings, ) from db.queries.assessment_records.queries import ( bulk_update_location_jsonb_blob, -) -from db.queries.assessment_records.queries import ( get_metadata_for_fund_round_id, ) @@ -150,7 +149,7 @@ def get_all_location_data(just_postcodes) -> dict: postcode = postcode_data_item["query"] location_data = extract_location_data(postcode_data_item) if location_data[postcode]["error"]: - print("There was a problem extracting address" f" information for postcode: {postcode}.") + print("There was a problem extracting address information for postcode: {postcode}.") fail_count += 1 postcodes_to_location_data[postcode] = location_data[postcode] print(f"Failed to retrieve address information for {fail_count} postcodes.") @@ -167,7 +166,7 @@ def update_db_with_location_data(application_ids_to_postcodes, postcodes_to_loca } for application_id, postcode in application_ids_to_postcodes.items() ] - print("Updating assessment records with postcode matched address" " information.") + print("Updating assessment records with postcode matched address information.") bulk_update_location_jsonb_blob(application_ids_to_location_data) diff --git a/scripts/populate_location_data.py b/scripts/populate_location_data.py index 01f359ec..292a9a7d 100755 --- a/scripts/populate_location_data.py +++ b/scripts/populate_location_data.py @@ -7,22 +7,22 @@ # from app import app # noqa: E402 -from db.queries.assessment_records.queries import ( # noqa: E402 - get_application_jsonb_blob, # noqa: E402 -) # noqa: E402 +from distutils.util import strtobool # noqa: E402 + from config.mappings.assessment_mapping_fund_round import ( # noqa: E402 fund_round_mapping_config, # noqa: E402 ) # noqa: E402 -from distutils.util import strtobool # noqa: E402 +from db.queries.assessment_records.queries import ( # noqa: E402 + get_application_jsonb_blob, # noqa: E402 +) # noqa: E402 from scripts.location_utils import ( # noqa: E402 get_all_application_ids_for_fund_round, # noqa: E402 + get_all_location_data, # noqa: E402 + get_application_form, # noqa: E402 + get_postcode_from_questions, # noqa: E402 + update_db_with_location_data, # noqa: E402 + write_locations_to_csv, # noqa: E402 ) # noqa: E402 -from scripts.location_utils import get_all_location_data # noqa: E402 -from scripts.location_utils import get_application_form # noqa: E402 -from scripts.location_utils import get_postcode_from_questions # noqa: E402 -from scripts.location_utils import update_db_with_location_data # noqa: E402 -from scripts.location_utils import write_locations_to_csv # noqa: E402 - local_workspace = os.path.abspath(os.path.join(os.getcwd(), os.pardir)) file_locations_csv = local_workspace + "/locations.csv" diff --git a/services/data_services.py b/services/data_services.py index 79d2b246..a1eb18d0 100644 --- a/services/data_services.py +++ b/services/data_services.py @@ -1,19 +1,22 @@ -import traceback from typing import Dict import requests +from flask import current_app + from api.models.notification import Notification from config import Config # noqa: E402 -from flask import current_app 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"]: @@ -21,16 +24,20 @@ def get_data(endpoint: str, payload: Dict = None): else: return response.content elif response.status_code == 204: - current_app.logger.warning("Request successful but no resources returned for endpoint" f" '{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}' ") - except requests.exceptions.RequestException as e: - stack_trace = traceback.format_exc() - current_app.logger.error(f"{e}\n{stack_trace}") + current_app.logger.error("Could not get data for endpoint '{endpoint}' ", extra=dict(endpoint=endpoint)) + except requests.exceptions.RequestException: + current_app.logger.exception("Unable to get_data") def get_account_data(account_id: str): - return get_data(Config.ACCOUNT_STORE_API_HOST + Config.ACCOUNTS_ENDPOINT, {"account_id": account_id}) + return get_data( + Config.ACCOUNT_STORE_API_HOST + Config.ACCOUNTS_ENDPOINT, + {"account_id": account_id}, + ) def get_fund_data(fund_id: str): @@ -77,11 +84,11 @@ def send_notification_email(application, user_id, assigner_id, template, message user_response["full_name"] if user_response["full_name"] else None, content, ) - current_app.logger.info(f"Message added to the queue msg_id: [{message_id}]") + current_app.logger.info("Message added to the queue msg_id: [{message_id}]", extra=dict(message_id=message_id)) except Exception: current_app.logger.info( - f"Could not send email for template: {template}, user: {user_id}, " - f"application {application['application_id']}" + "Could not send email for template: {template}, user: {user_id}, application {application_id}", + extra=dict(template=template, user_id=user_id, application_id=application["application_id"]), ) diff --git a/tasks/__init__.py b/tasks/__init__.py index 98fe5767..597b77ad 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -2,13 +2,11 @@ include:: ./README.md """ + from invoke import task -from tasks.db_tasks import bootstrap_dev_db -from tasks.db_tasks import create_seeded_db -from tasks.db_tasks import generate_test_data -from tasks.db_tasks import seed_dev_db -from tasks.helper_tasks import profile_pytest -from tasks.helper_tasks import reqs + +from tasks.db_tasks import bootstrap_dev_db, create_seeded_db, generate_test_data, seed_dev_db +from tasks.helper_tasks import profile_pytest, reqs task.auto_dash_names = False diff --git a/tasks/db_tasks.py b/tasks/db_tasks.py index 94ea966e..cd0b3e0f 100644 --- a/tasks/db_tasks.py +++ b/tasks/db_tasks.py @@ -4,10 +4,13 @@ sys.path.insert(1, ".") from invoke import task # noqa:E402 -from tasks.helper_tasks import _echo_input # noqa:E402 -from tasks.helper_tasks import _echo_print # noqa:E402 -from tasks.helper_tasks import _env_var # noqa:E402 + from app import app as connexionapp # noqa:E402 +from tasks.helper_tasks import ( + _echo_input, # noqa:E402 + _echo_print, # noqa:E402 + _env_var, # noqa:E402 +) # Needed for invoke to work on python3.11 # Remove once invoke has been updated. @@ -23,8 +26,7 @@ def bootstrap_dev_db(c): """ - from sqlalchemy_utils.functions import create_database - from sqlalchemy_utils.functions import database_exists + from sqlalchemy_utils.functions import create_database, database_exists with _env_var("FLASK_ENV", "development"): with connexionapp.app.app_context(): @@ -45,9 +47,10 @@ def bootstrap_dev_db(c): @task def generate_test_data(c): - from tests._db_seed_data import get_dynamic_rows import json + from tests._db_seed_data import get_dynamic_rows + _echo_print("Generating data.") rows = [json.loads(row) for row in get_dynamic_rows(3, 3, 10)] @@ -69,17 +72,17 @@ def seed_dev_db(c, fundround=None, appcount=None): with _env_var("FLASK_ENV", "development"): with connexionapp.app.app_context(): - from tests._helpers import seed_database_for_fund_round from config import Config from config.mappings.assessment_mapping_fund_round import ( fund_round_mapping_config, ) + from tests._helpers import seed_database_for_fund_round choosing = not bool(fundround and appcount) if not choosing: fund_round = fund_round_mapping_config[fundround] apps = int(appcount) - print(f"Seeding {apps} applications for " f"fund_round: '{fundround}'") + print(f"Seeding {apps} applications for fund_round: '{fundround}'") while choosing: new_line = "\n" @@ -92,9 +95,9 @@ def seed_dev_db(c, fundround=None, appcount=None): ), ) fund_round = fund_round_mapping_config[fundround] - apps = int(_echo_input("How many applications?" f"{new_line} > ")) + apps = int(_echo_input("How many applications?{new_line} > ")) choosing = ( - not _echo_input(f"Would you like to insert {apps} applications" f" for {fundround}? y/n \n").lower() + not _echo_input(f"Would you like to insert {apps} applications for {fundround}? y/n \n").lower() == "y" ) @@ -104,7 +107,7 @@ def seed_dev_db(c, fundround=None, appcount=None): upgrade() - _echo_print(f"Seeding db {Config.SQLALCHEMY_DATABASE_URI} with" f" {apps} test rows.") + _echo_print(f"Seeding db {Config.SQLALCHEMY_DATABASE_URI} with {apps} test rows.") seed_database_for_fund_round(apps, {fundround: fund_round}) _echo_print(f"Seeding db {Config.SQLALCHEMY_DATABASE_URI} complete.") diff --git a/tasks/helper_tasks.py b/tasks/helper_tasks.py index ae579606..26f09000 100644 --- a/tasks/helper_tasks.py +++ b/tasks/helper_tasks.py @@ -19,9 +19,7 @@ def _env_var(key, value): def _echo_print(to_print): - from colored import attr - from colored import fg - from colored import stylize + from colored import attr, fg, stylize ECHO_STYLE = fg("blue") + attr("bold") @@ -29,9 +27,7 @@ def _echo_print(to_print): def _error_print(to_print): - from colored import attr - from colored import fg - from colored import stylize + from colored import attr, fg, stylize ECHO_STYLE = fg("red") + attr("bold") @@ -39,9 +35,7 @@ def _error_print(to_print): def _echo_input(to_print): - from colored import attr - from colored import fg - from colored import stylize + from colored import attr, fg, stylize ECHO_STYLE = fg("blue") + attr("bold") diff --git a/tests/_application_store_json.py b/tests/_application_store_json.py index 8892b69d..c96fce86 100644 --- a/tests/_application_store_json.py +++ b/tests/_application_store_json.py @@ -1,4 +1,5 @@ -# flake8: noqa +# ruff: noqa: E501 + from string import Template application_store_json_template = Template( diff --git a/tests/_db_seed_data.py b/tests/_db_seed_data.py index c4b9fc9b..ab7ad903 100644 --- a/tests/_db_seed_data.py +++ b/tests/_db_seed_data.py @@ -1,42 +1,19 @@ -from tests._application_store_json import application_store_json_template from tests._application_store_json import ( + application_store_json_template, cof25_eoi_application_store_json_template, -) -from tests._application_store_json import ( cof25r1_application_store_json_template, -) -from tests._application_store_json import ( cof_eoi_application_store_json_template, -) -from tests._application_store_json import ( cofr3w1_application_store_json_template, -) -from tests._application_store_json import ( cofr3w2_application_store_json_template, -) -from tests._application_store_json import ( cofr3w3_application_store_json_template, -) -from tests._application_store_json import ( cofr4w1_application_store_json_template, -) -from tests._application_store_json import ( cofr4w2_application_store_json_template, -) -from tests._application_store_json import ( cypr1_application_store_json_template, -) -from tests._application_store_json import ( dpifr2_application_store_json_template, -) -from tests._application_store_json import ( hsra1_application_store_json_template, -) -from tests._application_store_json import ( nstfr2_application_store_json_template, ) - # Put a sample of output data (from application store) for a fund-round here mappings_application_store_json = { "COFR2W2": application_store_json_template, @@ -143,7 +120,7 @@ def get_dynamic_rows( ("3050", "850"), ] - for count, fund_id in enumerate(funds): + for _count, fund_id in enumerate(funds): print("fund id:", fund_id) rounds = [fund_round_config["round_id"]] if fund_round_config else [uuid4() for _ in range(number_of_rounds)] @@ -162,7 +139,7 @@ def get_dynamic_rows( funding_type = choice(funding_types) org_name = choice(org_names) - project_name = f"{choice(verbs)} the" f" {choice(adjects)} {picked_place[0]} in {picked_city[0]}" + project_name = f"{choice(verbs)} the {choice(adjects)} {picked_place[0]} in {picked_city[0]}" short_ref = application_short_ref_prefix + "-" + "".join(sample(ascii_uppercase, 6)) diff --git a/tests/_helpers.py b/tests/_helpers.py index 5fdc3c7e..0ec39932 100644 --- a/tests/_helpers.py +++ b/tests/_helpers.py @@ -1,14 +1,13 @@ from contextlib import contextmanager -from db import db -from db.models.assessment_record import AssessmentRecord -from db.queries import bulk_insert_application_record -from sqlalchemy import event -from sqlalchemy import func -from sqlalchemy import select +from sqlalchemy import event, func, select from sqlalchemy.engine import Engine from sqlalchemy.inspection import inspect from sqlalchemy.orm import defer + +from db import db +from db.models.assessment_record import AssessmentRecord +from db.queries import bulk_insert_application_record from tests._db_seed_data import get_dynamic_rows @@ -56,7 +55,8 @@ def get_rows_by_filters(fund_id, round_id, filters): stmt = ( select(AssessmentRecord) # Dont load json into memory - .options(defer(AssessmentRecord.jsonb_blob)).where( + .options(defer(AssessmentRecord.jsonb_blob)) + .where( AssessmentRecord.fund_id == fund_id, AssessmentRecord.round_id == round_id, *filters, @@ -77,7 +77,8 @@ def get_assessment_record(application_id): stmt = ( select(AssessmentRecord) # Dont load json into memory - .options(defer(AssessmentRecord.jsonb_blob)).where( + .options(defer(AssessmentRecord.jsonb_blob)) + .where( AssessmentRecord.application_id == application_id, ) ) diff --git a/tests/_sql_infos.py b/tests/_sql_infos.py index 9a9d01d6..c0f63e43 100644 --- a/tests/_sql_infos.py +++ b/tests/_sql_infos.py @@ -3,14 +3,15 @@ from collections import defaultdict from statistics import mean -from config import Config -from db import db from rich import print as fancy_print from rich.syntax import Syntax from rich.text import Text from sqlalchemy import event from sqlalchemy.engine import Engine +from config import Config +from db import db + query_info = { "queries_types": defaultdict(int), "slow_queries": [], diff --git a/tests/conftest.py b/tests/conftest.py index 34e6df58..29b83826 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,24 +3,22 @@ from uuid import uuid4 import pytest + from app import create_app from db.models import AssessmentRound from db.models.assessment_record import AssessmentRecord from db.models.assessment_record.allocation_association import AllocationAssociation from db.models.assessment_record.tag_association import TagAssociation -from db.models.comment import Comment -from db.models.comment import CommentsUpdate +from db.models.comment import Comment, CommentsUpdate from db.models.flags.assessment_flag import AssessmentFlag from db.models.flags.flag_update import FlagUpdate from db.models.qa_complete import QaComplete from db.models.score import Score from db.models.tag.tag_types import TagType from db.queries import bulk_insert_application_record -from db.queries.scores.queries import _insert_scoring_system -from db.queries.scores.queries import insert_scoring_system_for_round_id +from db.queries.scores.queries import _insert_scoring_system, insert_scoring_system_for_round_id from db.queries.tags.queries import insert_tags -from db.schemas.schemas import TagSchema -from db.schemas.schemas import TagTypeSchema +from db.schemas.schemas import TagSchema, TagTypeSchema from tests._sql_infos import attach_listeners # Loads the fixtures in this module in utils to create and diff --git a/tests/test_assessment_mapping.py b/tests/test_assessment_mapping.py index f4fd1f9b..4086d9d2 100644 --- a/tests/test_assessment_mapping.py +++ b/tests/test_assessment_mapping.py @@ -1,10 +1,11 @@ +from jsonschema import validate + from config import Config from config.mappings.assessment_mapping_schema import ( top_level_assessment_mapping_schema as schema, ) -from jsonschema import validate def test_assessment_mapping_conforms_to_schema(): - for key, value in Config.ASSESSMENT_MAPPING_CONFIG.items(): + for _key, value in Config.ASSESSMENT_MAPPING_CONFIG.items(): assert validate(instance=value, schema=schema) is None diff --git a/tests/test_assessment_mapping_fund_round.py b/tests/test_assessment_mapping_fund_round.py index 81e7e454..d6a75621 100644 --- a/tests/test_assessment_mapping_fund_round.py +++ b/tests/test_assessment_mapping_fund_round.py @@ -1,6 +1,7 @@ from collections import defaultdict import pytest + from api.routes._helpers import transform_to_assessor_task_list_metadata COF_FUND_ID = "47aef2f5-3fcb-4d45-acb5-f0152b5f03c4" diff --git a/tests/test_assessment_mapping_fund_round_cof.py b/tests/test_assessment_mapping_fund_round_cof.py index f2759364..2acec5a6 100644 --- a/tests/test_assessment_mapping_fund_round_cof.py +++ b/tests/test_assessment_mapping_fund_round_cof.py @@ -1,17 +1,20 @@ from uuid import UUID import pytest -from config.mappings.assessment_mapping_fund_round import applicant_info_mapping -from config.mappings.assessment_mapping_fund_round import COF25_EOI_FUND_ID -from config.mappings.assessment_mapping_fund_round import COF25_EOI_ROUND_ID -from config.mappings.assessment_mapping_fund_round import COF25_FUND_ID -from config.mappings.assessment_mapping_fund_round import COF25_ROUND_ID -from config.mappings.assessment_mapping_fund_round import COF_EOI_FUND_ID -from config.mappings.assessment_mapping_fund_round import COF_EOI_ROUND_ID -from config.mappings.assessment_mapping_fund_round import fund_round_data_key_mappings -from config.mappings.assessment_mapping_fund_round import fund_round_mapping_config -from config.mappings.assessment_mapping_fund_round import fund_round_mapping_config_with_round_id -from config.mappings.assessment_mapping_fund_round import fund_round_to_assessment_mapping + +from config.mappings.assessment_mapping_fund_round import ( + COF25_EOI_FUND_ID, + COF25_EOI_ROUND_ID, + COF25_FUND_ID, + COF25_ROUND_ID, + COF_EOI_FUND_ID, + COF_EOI_ROUND_ID, + applicant_info_mapping, + fund_round_data_key_mappings, + fund_round_mapping_config, + fund_round_mapping_config_with_round_id, + fund_round_to_assessment_mapping, +) def test_fund_round_ids_are_valid_uuids(): @@ -56,7 +59,13 @@ def test_fund_round_to_assessment_mapping_structure(): def test_fund_round_data_key_mappings_structure(): """Test the structure of fund_round_data_key_mappings.""" expected_keys_eoi = ["location", "asset_type", "funding_one", "funding_two"] - expected_keys_cof = ["location", "asset_type", "funding_one", "funding_two", "funding_field_type"] + expected_keys_cof = [ + "location", + "asset_type", + "funding_one", + "funding_two", + "funding_field_type", + ] assert "COFEOI" in fund_round_data_key_mappings assert "COF25EOI" in fund_round_data_key_mappings @@ -101,9 +110,9 @@ def check_field_structure(field_data): "COF25_FUND_ID": COF25_FUND_ID, } - for fund_name, fund_id in fund_ids.items(): + for _fund_name, fund_id in fund_ids.items(): form_fields = applicant_info_mapping[fund_id]["ASSESSOR_EXPORT"]["form_fields"] - for field_id, field_data in form_fields.items(): + for _field_id, field_data in form_fields.items(): check_field_structure(field_data) @@ -124,7 +133,7 @@ def test_fund_round_mapping_config_structure(): def test_fund_round_mapping_config_with_round_id_generation(): """Test the generation of fund_round_mapping_config_with_round_id.""" # Check that all round IDs from the original config are present as keys - for config_key, config in fund_round_mapping_config.items(): + for _config_key, config in fund_round_mapping_config.items(): round_id = config["round_id"] assert round_id in fund_round_mapping_config_with_round_id diff --git a/tests/test_cof_import_mapper.py b/tests/test_cof_import_mapper.py index 67a1d539..7637f1bb 100644 --- a/tests/test_cof_import_mapper.py +++ b/tests/test_cof_import_mapper.py @@ -1,6 +1,7 @@ import json import pytest + from db.queries.assessment_records._helpers import derive_application_values @@ -51,9 +52,9 @@ def test_derive_cof_values_location_present_no_error(postcode, expected_country, with open(single_application_json, "r") as f: loaded_test_json = json.load(f) # set mock location in json - loaded_test_json["forms"][12]["questions"][2]["fields"][0][ - "answer" - ] = f"Test Address,null, Test Town Or City,null, {postcode}" + loaded_test_json["forms"][12]["questions"][2]["fields"][0]["answer"] = ( + f"Test Address,null, Test Town Or City,null, {postcode}" + ) derived_fields = derive_application_values(loaded_test_json) assert "TEST-REF" == derived_fields["short_id"], "Wrong Short ID" diff --git a/tests/test_db_allocation_associations.py b/tests/test_db_allocation_associations.py index be7cfd28..0a87b7f4 100644 --- a/tests/test_db_allocation_associations.py +++ b/tests/test_db_allocation_associations.py @@ -1,12 +1,14 @@ import uuid -from datetime import datetime -from datetime import timezone +from datetime import datetime, timezone import pytest + from db.models.assessment_record.allocation_association import AllocationAssociation -from db.queries.assessment_records.queries import create_user_application_association -from db.queries.assessment_records.queries import get_user_application_associations -from db.queries.assessment_records.queries import update_user_application_association +from db.queries.assessment_records.queries import ( + create_user_application_association, + get_user_application_associations, + update_user_application_association, +) from tests.conftest import test_input_data @@ -21,14 +23,24 @@ def test_get_users_for_application(_db, seed_application_records): assigner_id=assigner_id, application_id=app_id, active=True, - log={datetime.now(tz=timezone.utc).isoformat(): {"status": "activated", "assigner": str(assigner_id)}}, + log={ + datetime.now(tz=timezone.utc).isoformat(): { + "status": "activated", + "assigner": str(assigner_id), + } + }, ) allocation_association_2 = AllocationAssociation( user_id=user_id_2, assigner_id=assigner_id, application_id=app_id, active=False, - log={datetime.now(tz=timezone.utc).isoformat(): {"status": "deactivated", "assigner": str(assigner_id)}}, + log={ + datetime.now(tz=timezone.utc).isoformat(): { + "status": "deactivated", + "assigner": str(assigner_id), + } + }, ) _db.session.add(allocation_association_1) _db.session.commit() @@ -58,14 +70,24 @@ def test_get_applications_for_user(_db, seed_application_records): assigner_id=assigner_id_1, application_id=app_id_1, active=True, - log={datetime.now(tz=timezone.utc).isoformat(): {"status": "activated", "assigner": str(assigner_id_1)}}, + log={ + datetime.now(tz=timezone.utc).isoformat(): { + "status": "activated", + "assigner": str(assigner_id_1), + } + }, ) allocation_association_2 = AllocationAssociation( user_id=user_id, assigner_id=assigner_id_2, application_id=app_id_2, active=False, - log={datetime.now(tz=timezone.utc).isoformat(): {"status": "deactivated", "assigner": str(assigner_id_2)}}, + log={ + datetime.now(tz=timezone.utc).isoformat(): { + "status": "deactivated", + "assigner": str(assigner_id_2), + } + }, ) _db.session.add(allocation_association_1) _db.session.commit() @@ -100,8 +122,8 @@ def test_create_user_application_association(_db, seed_application_records): assert {"assigner": assigner_id, "status": "activated"} == list(new_association.log.values())[0] try: datetime.fromisoformat(list(new_association.log.keys())[0]) - except ValueError: - assert False, "log keys should be in isoformat" + except ValueError as e: + raise AssertionError("log keys should be in isoformat") from e @pytest.mark.apps_to_insert([{**test_input_data[0]}]) diff --git a/tests/test_db_flags.py b/tests/test_db_flags.py index 48c02b87..4b06f558 100644 --- a/tests/test_db_flags.py +++ b/tests/test_db_flags.py @@ -1,19 +1,19 @@ from uuid import uuid4 import pytest +from sqlalchemy import select + from db.models.assessment_record import AssessmentRecord from db.models.assessment_record.enums import Status from db.models.flags.flag_update import FlagStatus from db.queries.flags.queries import ( add_flag_for_application, + add_update_to_assessment_flag, + get_flags_for_application, ) -from db.queries.flags.queries import add_update_to_assessment_flag -from db.queries.flags.queries import get_flags_for_application -from sqlalchemy import select from tests._helpers import get_assessment_record from tests.conftest import test_input_data -from tests.test_data.flags import add_flag_update_request_json -from tests.test_data.flags import flag_config +from tests.test_data.flags import add_flag_update_request_json, flag_config @pytest.mark.apps_to_insert([{**test_input_data[0]}]) diff --git a/tests/test_db_function.py b/tests/test_db_function.py index b166e1bc..c743f886 100644 --- a/tests/test_db_function.py +++ b/tests/test_db_function.py @@ -2,10 +2,9 @@ import pytest import sqlalchemy -from config.mappings.assessment_mapping_fund_round import applicant_info_mapping -from config.mappings.assessment_mapping_fund_round import COF_FUND_ID -from db.models import Comment -from db.models import Score + +from config.mappings.assessment_mapping_fund_round import COF_FUND_ID, applicant_info_mapping +from db.models import Comment, Score from db.models.assessment_record.assessment_records import AssessmentRecord from db.models.assessment_record.enums import Status from db.models.comment import CommentsUpdate @@ -13,14 +12,16 @@ from db.queries import find_answer_by_key_runner from db.queries.assessment_records.queries import ( bulk_update_location_jsonb_blob, + find_assessor_task_list_state, + get_assessment_export_data, + get_export_data, +) +from db.queries.comments.queries import ( + create_comment, + get_comments_from_db, + get_sub_criteria_to_has_comment_map, + update_comment, ) -from db.queries.assessment_records.queries import find_assessor_task_list_state -from db.queries.assessment_records.queries import get_assessment_export_data -from db.queries.assessment_records.queries import get_export_data -from db.queries.comments.queries import create_comment -from db.queries.comments.queries import get_comments_from_db -from db.queries.comments.queries import get_sub_criteria_to_has_comment_map -from db.queries.comments.queries import update_comment from db.queries.scores.queries import create_score_for_app_sub_crit from tests._expected_responses import BULK_UPDATE_LOCATION_JSONB_BLOB from tests._helpers import get_assessment_record @@ -76,8 +77,8 @@ def test_non_blob_columns_mutable(_db, seed_application_records): picked_row = get_assessment_record(seed_application_records[0]["application_id"]) picked_row.workflow_status = "IN_PROGRESS" _db.session.commit() - except sqlalchemy.exc.InternalError: - raise AssertionError + except sqlalchemy.exc.InternalError as e: + raise AssertionError from e finally: _db.session.rollback() @@ -563,7 +564,13 @@ def test_output_tracker_columns_remain_same_for_scored_and_unscored_reports(seed def test_get_cof_r4w1_export_data_en(seed_application_records): app_id = test_input_data[4]["id"] test_record = get_assessment_record(app_id) - result = get_export_data("round_id", "ASSESSOR_EXPORT", applicant_info_mapping[COF_FUND_ID], [test_record], "en") + result = get_export_data( + "round_id", + "ASSESSOR_EXPORT", + applicant_info_mapping[COF_FUND_ID], + [test_record], + "en", + ) assert len(result) == 1 assert str(result[0]["Application ID"]) == app_id assert result[0]["Name of lead contact"] == "test lead person" @@ -581,7 +588,13 @@ def test_get_cof_r4w1_export_data_en(seed_application_records): def test_get_cof_r4w1_export_data_cy(seed_application_records): app_id = test_input_data[5]["id"] test_record = get_assessment_record(app_id) - result = get_export_data("round_id", "ASSESSOR_EXPORT", applicant_info_mapping[COF_FUND_ID], [test_record], "cy") + result = get_export_data( + "round_id", + "ASSESSOR_EXPORT", + applicant_info_mapping[COF_FUND_ID], + [test_record], + "cy", + ) assert len(result) == 1 assert str(result[0]["Application ID"]) == app_id assert result[0]["Enw'r cyswllt arweiniol"] == "asdf" diff --git a/tests/test_db_scores.py b/tests/test_db_scores.py index 6230e183..330087e2 100644 --- a/tests/test_db_scores.py +++ b/tests/test_db_scores.py @@ -3,12 +3,15 @@ from unittest.mock import MagicMock import pytest + from api.routes import get_scoring_system_name_for_round_id from api.routes.progress_routes import get_progress_for_applications from db.models import Score -from db.queries.scores.queries import create_score_for_app_sub_crit -from db.queries.scores.queries import get_scores_for_app_sub_crit -from db.queries.scores.queries import get_sub_criteria_to_latest_score_map +from db.queries.scores.queries import ( + create_score_for_app_sub_crit, + get_scores_for_app_sub_crit, + get_sub_criteria_to_latest_score_map, +) from tests._helpers import get_assessment_record from tests.conftest import test_input_data diff --git a/tests/test_db_tag_associations.py b/tests/test_db_tag_associations.py index 1e270737..73e27543 100644 --- a/tests/test_db_tag_associations.py +++ b/tests/test_db_tag_associations.py @@ -1,12 +1,13 @@ import pytest +from sqlalchemy import select + from api.routes.tag_routes import ( get_active_tags_associated_with_assessment, + update_tags_for_fund_round, ) -from api.routes.tag_routes import update_tags_for_fund_round from app import app from db.models.assessment_record import AssessmentRecord from db.queries.assessment_records.queries import associate_assessment_tags -from sqlalchemy import select from tests.conftest import test_input_data diff --git a/tests/test_db_tags.py b/tests/test_db_tags.py index 7a7a2b6a..c45d1213 100644 --- a/tests/test_db_tags.py +++ b/tests/test_db_tags.py @@ -2,12 +2,11 @@ from uuid import uuid4 import pytest -from api.routes.tag_routes import get_tags_for_fund_round -from api.routes.tag_routes import update_tags_for_fund_round + +from api.routes.tag_routes import get_tags_for_fund_round, update_tags_for_fund_round from app import app from db.queries.assessment_records.queries import associate_assessment_tags -from db.queries.tags.queries import get_tag_by_id -from db.queries.tags.queries import insert_tags +from db.queries.tags.queries import get_tag_by_id, insert_tags from tests.conftest import test_input_data # _db.session.remove() diff --git a/tests/test_import_applications.py b/tests/test_import_applications.py index 16a737db..754d82ce 100644 --- a/tests/test_import_applications.py +++ b/tests/test_import_applications.py @@ -3,10 +3,11 @@ from unittest import mock import pytest +from requests import Response + from config.mappings.assessment_mapping_fund_round import ( fund_round_mapping_config, ) -from requests import Response from scripts.import_from_application import main from tests._helpers import row_data diff --git a/tests/test_location_utils.py b/tests/test_location_utils.py index 9da3dee1..3899f7fa 100644 --- a/tests/test_location_utils.py +++ b/tests/test_location_utils.py @@ -2,11 +2,13 @@ import json import os -from scripts.location_utils import extract_location_data -from scripts.location_utils import get_all_location_data -from scripts.location_utils import get_application_form -from scripts.location_utils import get_postcode_from_questions -from scripts.location_utils import write_locations_to_csv +from scripts.location_utils import ( + extract_location_data, + get_all_location_data, + get_application_form, + get_postcode_from_questions, + write_locations_to_csv, +) def test_get_application_form(): diff --git a/tests/test_qa_complete.py b/tests/test_qa_complete.py index 80e5ee04..9f2be6ef 100644 --- a/tests/test_qa_complete.py +++ b/tests/test_qa_complete.py @@ -1,6 +1,7 @@ import pytest -from db.queries.qa_complete.queries import create_qa_complete_record + from db.queries.qa_complete.queries import ( + create_qa_complete_record, get_qa_complete_record_for_application, ) from tests._helpers import get_assessment_record diff --git a/tests/test_routes.py b/tests/test_routes.py index 2ca65764..520f85d2 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -5,20 +5,21 @@ from uuid import uuid4 import pytest -from api.routes.assessment_routes import calculate_overall_score_percentage_for_application + +from api.routes.assessment_routes import ( + calculate_overall_score_percentage_for_application, +) from config.mappings.assessment_mapping_fund_round import ( applicant_info_mapping, ) from db.models.flags.assessment_flag import AssessmentFlag from db.models.flags.flag_update import FlagStatus from db.models.tag.tags import Tag -from db.queries.flags.queries import add_flag_for_application -from db.queries.flags.queries import add_update_to_assessment_flag +from db.queries.flags.queries import add_flag_for_application, add_update_to_assessment_flag from db.queries.qa_complete.queries import create_qa_complete_record from tests._expected_responses import APPLICATION_METADATA_RESPONSE from tests.conftest import test_input_data -from tests.test_data.flags import add_flag_update_request_json -from tests.test_data.flags import create_flag_request_json +from tests.test_data.flags import add_flag_update_request_json, create_flag_request_json COF_FUND_ID = "47aef2f5-3fcb-4d45-acb5-f0152b5f03c4" COF_ROUND_2_ID = "c603d114-5364-4474-a0c4-c41cbf4d3bbd" @@ -468,7 +469,8 @@ def test_get_all_users_associated_with_application(flask_test_client): expected_response[1]["created_at"] = expected_response[1]["created_at"].isoformat() with mock.patch( - "api.routes.user_routes.get_user_application_associations", return_value=mock_users + "api.routes.user_routes.get_user_application_associations", + return_value=mock_users, ) as mock_get_users: response = flask_test_client.get("/application/app1/users") @@ -491,7 +493,8 @@ def test_get_user_application_association(flask_test_client): expected_response["created_at"] = expected_response["created_at"].isoformat() with mock.patch( - "api.routes.user_routes.get_user_application_associations", return_value=[mock_association] + "api.routes.user_routes.get_user_application_associations", + return_value=[mock_association], ) as mock_get_association: response = flask_test_client.get("/application/app1/user/user1") @@ -514,13 +517,17 @@ def test_add_user_application_association(flask_test_client, send_email_value): expected_response = deepcopy(mock_association) expected_response["created_at"] = expected_response["created_at"].isoformat() - with mock.patch( - "api.routes.user_routes.create_user_application_association", return_value=mock_association - ) as mock_create_association, mock.patch("api.routes.user_routes.get_metadata_for_application"), mock.patch( - "api.routes.user_routes.send_notification_email" - ) as mock_notify_email: + with ( + mock.patch( + "api.routes.user_routes.create_user_application_association", + return_value=mock_association, + ) as mock_create_association, + mock.patch("api.routes.user_routes.get_metadata_for_application"), + mock.patch("api.routes.user_routes.send_notification_email") as mock_notify_email, + ): response = flask_test_client.post( - "/application/app1/user/user1", json={"assigner_id": "assigner1", "send_email": send_email_value} + "/application/app1/user/user1", + json={"assigner_id": "assigner1", "send_email": send_email_value}, ) assert response.status_code == 201 @@ -546,20 +553,30 @@ def test_update_user_application_association(flask_test_client, send_email_value expected_response = deepcopy(mock_association) expected_response["created_at"] = expected_response["created_at"].isoformat() - with mock.patch( - "api.routes.user_routes.update_user_application_association_db", return_value=mock_association - ) as mock_update_association, mock.patch("api.routes.user_routes.get_metadata_for_application"), mock.patch( - "api.routes.user_routes.send_notification_email" - ) as mock_notify_email: + with ( + mock.patch( + "api.routes.user_routes.update_user_application_association_db", + return_value=mock_association, + ) as mock_update_association, + mock.patch("api.routes.user_routes.get_metadata_for_application"), + mock.patch("api.routes.user_routes.send_notification_email") as mock_notify_email, + ): response = flask_test_client.put( "/application/app1/user/user1", - json={"active": "false", "assigner_id": "assigner1", "send_email": send_email_value}, + json={ + "active": "false", + "assigner_id": "assigner1", + "send_email": send_email_value, + }, ) assert response.status_code == 200 assert response.json() == expected_response mock_update_association.assert_called_once_with( - application_id="app1", user_id="user1", active="false", assigner_id="assigner1" + application_id="app1", + user_id="user1", + active="false", + assigner_id="assigner1", ) if send_email_value: mock_notify_email.assert_called_once() @@ -592,7 +609,8 @@ def test_get_all_applications_associated_with_user(flask_test_client): expected_response[1]["created_at"] = expected_response[1]["created_at"].isoformat() with mock.patch( - "api.routes.user_routes.get_user_application_associations", return_value=mock_applications + "api.routes.user_routes.get_user_application_associations", + return_value=mock_applications, ) as mock_get_applications: response = flask_test_client.get("/user/user1/applications") @@ -626,7 +644,8 @@ def test_get_all_applications_assigned_by_user(flask_test_client): expected_response[1]["created_at"] = expected_response[1]["created_at"].isoformat() with mock.patch( - "api.routes.user_routes.get_user_application_associations", return_value=mock_applications + "api.routes.user_routes.get_user_application_associations", + return_value=mock_applications, ) as mock_get_applications: response = flask_test_client.get("/user/assigner1/assignees") @@ -644,7 +663,11 @@ def test_get_all_applications_assigned_by_user(flask_test_client): mapping_config = { f"{COF_FUND_ID}:{COF_ROUND_2_ID}": { "scored_criteria": [ - {"id": "criteria1", "weighting": 2, "sub_criteria": [{"id": "sub1"}, {"id": "sub2"}]}, + { + "id": "criteria1", + "weighting": 2, + "sub_criteria": [{"id": "sub1"}, {"id": "sub2"}], + }, {"id": "criteria2", "weighting": 1, "sub_criteria": [{"id": "sub3"}]}, ] } @@ -653,13 +676,17 @@ def test_get_all_applications_assigned_by_user(flask_test_client): @pytest.fixture def mock_get_scoring_system(mocker): - return mocker.patch("api.routes.assessment_routes.get_scoring_system_for_round_id", return_value=scoring_system) + return mocker.patch( + "api.routes.assessment_routes.get_scoring_system_for_round_id", + return_value=scoring_system, + ) @pytest.fixture def mock_get_scores(mocker): return mocker.patch( - "api.routes.assessment_routes.get_sub_criteria_to_latest_score_map", return_value=sub_criteria_scores + "api.routes.assessment_routes.get_sub_criteria_to_latest_score_map", + return_value=sub_criteria_scores, ) diff --git a/tests/test_services.py b/tests/test_services.py index 94a90a04..5ff648f1 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -1,6 +1,7 @@ from unittest import mock import pytest + from services.data_services import send_notification_email @@ -26,7 +27,12 @@ def test_send_notification_email( expected_message_in_content, app, ): - test_application = {"application_id": "app1", "fund_id": "fund1", "short_id": "APP123", "project_name": "Project X"} + test_application = { + "application_id": "app1", + "fund_id": "fund1", + "short_id": "APP123", + "project_name": "Project X", + } mock_get_account_data.side_effect = [ {"email_address": "user@example.com", "full_name": "User One"}, # user data @@ -56,12 +62,25 @@ def test_send_notification_email( @mock.patch("services.data_services.get_account_data") @mock.patch("services.data_services.get_fund_data") @mock.patch("services.data_services.create_assessment_url_for_application") -@mock.patch("services.data_services.Notification.send", side_effect=Exception("Error sending notification")) +@mock.patch( + "services.data_services.Notification.send", + side_effect=Exception("Error sending notification"), +) @mock.patch("services.data_services.current_app.logger") def test_send_notification_email_failure( - mock_logger, mock_notification_send, mock_assessment_url, mock_get_fund_data, mock_get_account_data, app + mock_logger, + mock_notification_send, + mock_assessment_url, + mock_get_fund_data, + mock_get_account_data, + app, ): - test_application = {"application_id": "app1", "fund_id": "fund1", "short_id": "APP123", "project_name": "Project X"} + test_application = { + "application_id": "app1", + "fund_id": "fund1", + "short_id": "APP123", + "project_name": "Project X", + } mock_get_account_data.side_effect = [ {"email_address": "user@example.com", "full_name": "User One"}, # user data @@ -73,5 +92,6 @@ def test_send_notification_email_failure( send_notification_email(test_application, "user1", "assigner1", "assignment_template") mock_logger.info.assert_called_with( - "Could not send email for template: assignment_template, user: user1, application app1" + "Could not send email for template: {template}, user: {user_id}, application {application_id}", + extra={"template": "assignment_template", "user_id": "user1", "application_id": "app1"}, ) diff --git a/tests/test_task_executor_service.py b/tests/test_task_executor_service.py index 157ae4cc..2db32f70 100644 --- a/tests/test_task_executor_service.py +++ b/tests/test_task_executor_service.py @@ -1,16 +1,16 @@ import time import unittest -from unittest.mock import MagicMock -from unittest.mock import patch +from unittest.mock import MagicMock, patch from uuid import uuid4 import boto3 import pytest -from _helpers.task_executer_service import TaskExecutorService -from config import Config from fsd_utils.sqs_scheduler.context_aware_executor import ContextAwareExecutor from moto import mock_aws from sqlalchemy.exc import SQLAlchemyError + +from _helpers.task_executer_service import TaskExecutorService +from config import Config from tests.test_data.test_data_util import send_message_to_queue @@ -41,10 +41,16 @@ def _mock_aws_client(self): self.flask_app = MagicMock() self.executor = ContextAwareExecutor(max_workers=10, thread_name_prefix="NotifTask", flask_app=self.flask_app) s3_connection = boto3.client( - "s3", region_name="us-east-1", aws_access_key_id="test_accesstoken", aws_secret_access_key="secret_key" + "s3", + region_name="us-east-1", + aws_access_key_id="test_accesstoken", + aws_secret_access_key="secret_key", # pragma: allowlist secret ) sqs_connection = boto3.client( - "sqs", region_name="us-east-1", aws_access_key_id="test_accesstoken", aws_secret_access_key="secret_key" + "sqs", + region_name="us-east-1", + aws_access_key_id="test_accesstoken", + aws_secret_access_key="secret_key", # pragma: allowlist secret ) s3_connection.create_bucket(Bucket=Config.AWS_MSG_BUCKET_NAME) self.queue_response = sqs_connection.create_queue( @@ -69,9 +75,12 @@ def _mock_aws_client(self): self.task_executor.sqs_extended_client.s3_client = s3_connection def _add_data_to_queue(self): - for x in range(1): + for _x in range(1): application_attributes = { - "application_id": {"StringValue": "8be9756e-8404-4d79-9b70-abf15066845f", "DataType": "String"}, + "application_id": { + "StringValue": "8be9756e-8404-4d79-9b70-abf15066845f", + "DataType": "String", + }, "S3Key": { "StringValue": "assessment", "DataType": "String", diff --git a/uv.lock b/uv.lock index 32df9300..e135abdb 100644 --- a/uv.lock +++ b/uv.lock @@ -127,28 +127,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b1/fe/e8c672695b37eecc5cbf43e1d0638d88d66ba3a44c4d321c796f4e59167f/beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed", size = 147925 }, ] -[[package]] -name = "black" -version = "24.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, - { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a2/47/c9997eb470a7f48f7aaddd3d9a828244a2e4199569e38128715c48059ac1/black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d", size = 642299 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/40/f6/3adc48c210527a7b651aaed43824a9b8bd04b3fb361a5227bad046e1c876/black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce", size = 1631487 }, - { url = "https://files.pythonhosted.org/packages/a2/25/70aa1bec12c841a03e333e312daa0cf2fee50ea6336ac4851c93c0e2b411/black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021", size = 1456317 }, - { url = "https://files.pythonhosted.org/packages/e0/7d/7f8df0fdbbbefc4362d3eca6b69b7a8a4249a8a88dabc00a207d31fddcd7/black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063", size = 1822765 }, - { url = "https://files.pythonhosted.org/packages/5c/21/1ee97841c469c1551133cbe47448cdba9628c7d9431f74f114f02e3b233c/black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96", size = 1409336 }, - { url = "https://files.pythonhosted.org/packages/0f/89/294c9a6b6c75a08da55e9d05321d0707e9418735e3062b12ef0f54c33474/black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c", size = 205925 }, -] - [[package]] name = "blinker" version = "1.8.2" @@ -444,32 +422,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7", size = 16159 }, ] -[[package]] -name = "flake8" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4e/34/64f8a43736d9862ced7dd0ea5c3ed99815b8ff4b826a4f3bfd3a1b0639b1/flake8-7.1.0.tar.gz", hash = "sha256:48a07b626b55236e0fb4784ee69a465fbf59d79eec1f5b4785c3d3bc57d17aa5", size = 48240 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/43/d5147aadaa52558e94e024811f2f9543b4bd7203b3a9659eeb5dff9c61b3/flake8-7.1.0-py2.py3-none-any.whl", hash = "sha256:2e416edcc62471a64cea09353f4e7bdba32aeb079b6e360554c659a122b1bc6a", size = 57569 }, -] - -[[package]] -name = "flake8-pyproject" -version = "1.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/1d/635e86f9f3a96b7ea9e9f19b5efe17a987e765c39ca496e4a893bb999112/flake8_pyproject-1.2.3-py3-none-any.whl", hash = "sha256:6249fe53545205af5e76837644dc80b4c10037e73a0e5db87ff562d75fb5bd4a", size = 4756 }, -] - [[package]] name = "flask" version = "3.0.3" @@ -593,11 +545,9 @@ dependencies = [ [package.dev-dependencies] dev = [ - { name = "black" }, { name = "colored" }, { name = "debugpy" }, { name = "deepdiff" }, - { name = "flake8-pyproject" }, { name = "invoke" }, { name = "json2html" }, { name = "moto", extra = ["s3"] }, @@ -606,6 +556,7 @@ dev = [ { name = "pytest-env" }, { name = "pytest-flask" }, { name = "pytest-mock" }, + { name = "ruff" }, ] [package.metadata] @@ -632,11 +583,9 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "black", specifier = "==24.4.2" }, { name = "colored", specifier = "==2.2.4" }, { name = "debugpy", specifier = "==1.8.2" }, { name = "deepdiff", specifier = "==7.0.1" }, - { name = "flake8-pyproject", specifier = "==1.2.3" }, { name = "invoke", specifier = "==2.2.0" }, { name = "json2html", specifier = "==1.3.0" }, { name = "moto", extras = ["s3", "sqs"], specifier = "==5.0.10" }, @@ -645,6 +594,7 @@ dev = [ { name = "pytest-env", specifier = "==1.1.3" }, { name = "pytest-flask", specifier = "==1.3.0" }, { name = "pytest-mock", specifier = "==3.14.0" }, + { name = "ruff", specifier = "==0.7.4" }, ] [[package]] @@ -975,15 +925,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/32/95b3e03d41480e5e8963034ed569e94cd5febe64bc23240936b108592bbb/marshmallow_sqlalchemy-1.0.0-py3-none-any.whl", hash = "sha256:f415d57809e3555b6323356589aba91e36e4470f35953d3a10c755ac5c3307df", size = 14427 }, ] -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, -] - [[package]] name = "moto" version = "5.0.10" @@ -1010,15 +951,6 @@ s3 = [ { name = "pyyaml" }, ] -[[package]] -name = "mypy-extensions" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, -] - [[package]] name = "nodeenv" version = "1.9.1" @@ -1093,15 +1025,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/0a/acfb251ba01009d3053f04f4661e96abf9d485266b04a0a4deebc702d9cb/pathable-0.4.3-py3-none-any.whl", hash = "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14", size = 9587 }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - [[package]] name = "pbr" version = "6.0.0" @@ -1199,15 +1122,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/57/6188aa69b7b46e819a40bfd8b1013b3922fe8ee50aaf9efad8d928b913c2/py_partiql_parser-0.5.5-py2.py3-none-any.whl", hash = "sha256:90d278818385bd60c602410c953ee78f04ece599d8cd21c656fc5e47399577a1", size = 23243 }, ] -[[package]] -name = "pycodestyle" -version = "2.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/56/52d8283e1a1c85695291040192776931782831e21117c84311cbdd63f70c/pycodestyle-2.12.0.tar.gz", hash = "sha256:442f950141b4f43df752dd303511ffded3a04c2b6fb7f65980574f0c31e6e79c", size = 39055 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/c4/bf8ede2d1641e0a2e027c6d0c7060e00332851ea772cc5cee42a4a207707/pycodestyle-2.12.0-py2.py3-none-any.whl", hash = "sha256:949a39f6b86c3e1515ba1787c2022131d165a8ad271b11370a8819aa070269e4", size = 31221 }, -] - [[package]] name = "pycparser" version = "2.22" @@ -1217,15 +1131,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, ] -[[package]] -name = "pyflakes" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 }, -] - [[package]] name = "pygments" version = "2.18.0" @@ -1508,6 +1413,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/5b/744df20285a75ac4c606452ce9a0fcc42087d122f42294518ded1017697c/ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31", size = 117825 }, ] +[[package]] +name = "ruff" +version = "0.7.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/8b/bc4e0dfa1245b07cf14300e10319b98e958a53ff074c1dd86b35253a8c2a/ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2", size = 3275547 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/4b/f5094719e254829766b807dadb766841124daba75a37da83e292ae5ad12f/ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478", size = 10447512 }, + { url = "https://files.pythonhosted.org/packages/9e/1d/3d2d2c9f601cf6044799c5349ff5267467224cefed9b35edf5f1f36486e9/ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63", size = 10235436 }, + { url = "https://files.pythonhosted.org/packages/62/83/42a6ec6216ded30b354b13e0e9327ef75a3c147751aaf10443756cb690e9/ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20", size = 9888936 }, + { url = "https://files.pythonhosted.org/packages/4d/26/e1e54893b13046a6ad05ee9b89ee6f71542ba250f72b4c7a7d17c3dbf73d/ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109", size = 10697353 }, + { url = "https://files.pythonhosted.org/packages/21/24/98d2e109c4efc02bfef144ec6ea2c3e1217e7ce0cfddda8361d268dfd499/ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452", size = 10228078 }, + { url = "https://files.pythonhosted.org/packages/ad/b7/964c75be9bc2945fc3172241b371197bb6d948cc69e28bc4518448c368f3/ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea", size = 11264823 }, + { url = "https://files.pythonhosted.org/packages/12/8d/20abdbf705969914ce40988fe71a554a918deaab62c38ec07483e77866f6/ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7", size = 11951855 }, + { url = "https://files.pythonhosted.org/packages/b8/fc/6519ce58c57b4edafcdf40920b7273dfbba64fc6ebcaae7b88e4dc1bf0a8/ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05", size = 11516580 }, + { url = "https://files.pythonhosted.org/packages/37/1a/5ec1844e993e376a86eb2456496831ed91b4398c434d8244f89094758940/ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06", size = 12692057 }, + { url = "https://files.pythonhosted.org/packages/50/90/76867152b0d3c05df29a74bb028413e90f704f0f6701c4801174ba47f959/ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc", size = 11085137 }, + { url = "https://files.pythonhosted.org/packages/c8/eb/0a7cb6059ac3555243bd026bb21785bbc812f7bbfa95a36c101bd72b47ae/ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172", size = 10681243 }, + { url = "https://files.pythonhosted.org/packages/5e/76/2270719dbee0fd35780b05c08a07b7a726c3da9f67d9ae89ef21fc18e2e5/ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a", size = 10319187 }, + { url = "https://files.pythonhosted.org/packages/9f/e5/39100f72f8ba70bec1bd329efc880dea8b6c1765ea1cb9d0c1c5f18b8d7f/ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd", size = 10803715 }, + { url = "https://files.pythonhosted.org/packages/a5/89/40e904784f305fb56850063f70a998a64ebba68796d823dde67e89a24691/ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a", size = 11162912 }, + { url = "https://files.pythonhosted.org/packages/8d/1b/dd77503b3875c51e3dbc053fd8367b845ab8b01c9ca6d0c237082732856c/ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac", size = 8702767 }, + { url = "https://files.pythonhosted.org/packages/63/76/253ddc3e89e70165bba952ecca424b980b8d3c2598ceb4fc47904f424953/ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6", size = 9497534 }, + { url = "https://files.pythonhosted.org/packages/aa/70/f8724f31abc0b329ca98b33d73c14020168babcf71b0cba3cded5d9d0e66/ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f", size = 8851590 }, +] + [[package]] name = "s3transfer" version = "0.10.2"