diff --git a/lambdas/services/bulk_upload_service.py b/lambdas/services/bulk_upload_service.py index bc3c2a5e1..72c8753c3 100644 --- a/lambdas/services/bulk_upload_service.py +++ b/lambdas/services/bulk_upload_service.py @@ -8,6 +8,7 @@ from enums.patient_ods_inactive_status import PatientOdsInactiveStatus from enums.snomed_codes import SnomedCodes from enums.upload_status import UploadStatus +from enums.validation_score import ValidationScore from enums.virus_scan_result import VirusScanResult from models.fhir.R4.nrl_fhir_document_reference import Attachment from models.nhs_document_reference import NHSDocumentReference @@ -133,13 +134,43 @@ def handle_sqs_message(self, message: dict): patient_ods_code = ( pds_patient_details.get_ods_code_or_inactive_status_for_gp() ) - is_name_validation_based_on_historic_name = ( - validate_filename_with_patient_details(file_names, pds_patient_details) - ) + ( + name_validation_score, + is_dob_valid, + is_name_validation_based_on_historic_name, + ) = validate_filename_with_patient_details(file_names, pds_patient_details) if is_name_validation_based_on_historic_name: accepted_reason = self.concatenate_acceptance_reason( accepted_reason, "Patient matched on historical name" ) + if name_validation_score == ValidationScore.PARTIAL_MATCH and is_dob_valid: + accepted_reason = self.concatenate_acceptance_reason( + accepted_reason, "Patient matched on partial match 2/3" + ) + elif ( + name_validation_score == ValidationScore.MIXED_FULL_MATCH + and is_dob_valid + ): + accepted_reason = self.concatenate_acceptance_reason( + accepted_reason, "Patient matched on mixed match 3/3" + ) + elif name_validation_score == ValidationScore.FULL_MATCH and is_dob_valid: + accepted_reason = self.concatenate_acceptance_reason( + accepted_reason, "Patient matched on full match 3/3" + ) + elif ( + name_validation_score == ValidationScore.MIXED_FULL_MATCH + and not is_dob_valid + ): + accepted_reason = self.concatenate_acceptance_reason( + accepted_reason, "Patient matched on mixed match 2/3" + ) + elif ( + name_validation_score == ValidationScore.FULL_MATCH and not is_dob_valid + ): + accepted_reason = self.concatenate_acceptance_reason( + accepted_reason, "Patient matched on full match 2/3" + ) if not allowed_to_ingest_ods_code(patient_ods_code): raise LGInvalidFilesException("Patient not registered at your practice") patient_death_notification_status = ( diff --git a/lambdas/tests/unit/utils/test_lloyd_george_validator.py b/lambdas/tests/unit/utils/test_lloyd_george_validator.py index de95485f4..da7e03cd8 100644 --- a/lambdas/tests/unit/utils/test_lloyd_george_validator.py +++ b/lambdas/tests/unit/utils/test_lloyd_george_validator.py @@ -15,6 +15,10 @@ from tests.unit.helpers.data.pds.pds_patient_response import ( PDS_PATIENT_WITH_MIDDLE_NAME, ) +from tests.unit.helpers.data.pds.test_cases_for_date_logic import ( + build_test_name, + build_test_patient_with_names, +) from tests.unit.helpers.data.pds.test_cases_for_patient_name_matching import ( TEST_CASES_FOR_FAMILY_NAME_WITH_HYPHEN, TEST_CASES_FOR_TWO_WORDS_FAMILY_NAME, @@ -23,10 +27,6 @@ load_test_cases, ) from tests.unit.models.test_document_reference import MOCK_DOCUMENT_REFERENCE -from unit.helpers.data.pds.test_cases_for_date_logic import ( - build_test_name, - build_test_patient_with_names, -) from utils.common_query_filters import NotDeleted from utils.exceptions import ( PatientRecordAlreadyExistException, @@ -782,13 +782,13 @@ def test_validate_patient_name_return_false( @pytest.mark.parametrize( ["file_patient_name", "first_name_from_pds", "family_name_from_pds"], [ - ["Jane Smith", ["Jane"], "Smith"], - ["Jane Bob Smith Anderson", ["Jane"], "Smith Anderson"], - ["Jane Smith Anderson", ["Jane"], "Smith Anderson"], - ["Jane B Smith Anderson", ["Jane"], "Smith Anderson"], - ["Jane Smith-Anderson", ["Jane"], "Smith-Anderson"], - ["Jane Bob Smith Anderson", "Jane Bob", "Smith Anderson"], - ["Jane Bob Smith", "Jane Bob", "Smith"], + ("Jane Smith", ["Jane"], "Smith"), + ("Jane Bob Smith Anderson", ["Jane"], "Smith Anderson"), + ("Jane Smith Anderson", ["Jane"], "Smith Anderson"), + ("Jane B Smith Anderson", ["Jane"], "Smith Anderson"), + ("Jane Smith-Anderson", ["Jane"], "Smith-Anderson"), + ("Jane Bob Smith Anderson", "Jane Bob", "Smith Anderson"), + ("Jane Bob Smith", "Jane Bob", "Smith"), ], ) def test_validate_patient_name_return_true( @@ -803,18 +803,18 @@ def test_validate_patient_name_return_true( @pytest.mark.parametrize( ["file_patient_name", "score"], [ - ["Jane Smith", ValidationScore.FULL_MATCH], - ["Smith Jane", ValidationScore.FULL_MATCH], - ["Jane Bob Smith Moody", ValidationScore.FULL_MATCH], - ["Jane Smith Moody", ValidationScore.FULL_MATCH], - ["Jane B Smith Moody", ValidationScore.FULL_MATCH], - ["Jane Smith-Moody", ValidationScore.FULL_MATCH], - ["Jane Bob Smith Moody", ValidationScore.FULL_MATCH], - ["Jane Bob Smith", ValidationScore.FULL_MATCH], - ["Bob Smith", ValidationScore.PARTIAL_MATCH], - ["Bob Jane", ValidationScore.FULL_MATCH], - ["Alastor Moody", ValidationScore.NO_MATCH], - ["Jones Bob", ValidationScore.MIXED_FULL_MATCH], + ("Jane Smith", ValidationScore.FULL_MATCH), + ("Smith Jane", ValidationScore.FULL_MATCH), + ("Jane Bob Smith Moody", ValidationScore.FULL_MATCH), + ("Jane Smith Moody", ValidationScore.FULL_MATCH), + ("Jane B Smith Moody", ValidationScore.FULL_MATCH), + ("Jane Smith-Moody", ValidationScore.FULL_MATCH), + ("Jane Bob Smith Moody", ValidationScore.FULL_MATCH), + ("Jane Bob Smith", ValidationScore.FULL_MATCH), + ("Bob Smith", ValidationScore.PARTIAL_MATCH), + ("Bob Jane", ValidationScore.FULL_MATCH), + ("Alastor Moody", ValidationScore.NO_MATCH), + ("Jones Bob", ValidationScore.MIXED_FULL_MATCH), ], ) def test_calculate_validation_score(file_patient_name, score): diff --git a/lambdas/utils/lloyd_george_validator.py b/lambdas/utils/lloyd_george_validator.py index 63e02f455..5ae6ae920 100644 --- a/lambdas/utils/lloyd_george_validator.py +++ b/lambdas/utils/lloyd_george_validator.py @@ -140,7 +140,7 @@ def check_for_file_names_agrees_with_each_other(file_name_list: list[str]): def validate_filename_with_patient_details( file_name_list: list[str], patient_details: Patient -): +) -> (ValidationScore, bool, bool): try: file_name_info = extract_info_from_filename(file_name_list[0]) file_patient_name = file_name_info["patient_name"] @@ -154,7 +154,7 @@ def validate_filename_with_patient_details( file_date_of_birth, patient_details ) if not is_dob_valid and name_validation_score == ValidationScore.PARTIAL_MATCH: - raise LGInvalidFilesException("Patient name does not match our records") + raise LGInvalidFilesException("Patient name does not match our records 1/3") return name_validation_score, is_dob_valid, historical_match except ValueError as e: @@ -168,8 +168,8 @@ def calculate_validation_score( matched_on_given_name_set = set() matched_on_family_name_set = set() historical_match = True - - for index, name in enumerate(patient_details.name): + ordered_names = patient_details.get_names_by_start_date() + for index, name in enumerate(ordered_names): first_name_in_pds = name.given family_name_in_pds = name.family result = validate_patient_name(