From ec974a4fbfe441180f3e0a1895fb973515624c32 Mon Sep 17 00:00:00 2001 From: abragtim Date: Wed, 19 Apr 2023 12:37:48 +0200 Subject: [PATCH] Add validation for assumed HLA Type. --- tests/web/test_do_crossmatch_api.py | 44 +++++++++++++++++++ txmatching/utils/hla_system/hla_crossmatch.py | 7 ++- txmatching/web/api/crossmatch_api.py | 28 ++++++------ 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/tests/web/test_do_crossmatch_api.py b/tests/web/test_do_crossmatch_api.py index e29c34cd0..2fc0710fc 100644 --- a/tests/web/test_do_crossmatch_api.py +++ b/tests/web/test_do_crossmatch_api.py @@ -141,6 +141,7 @@ def test_theoretical_and_double_antibodies_not_implemented(self): res.json['message']) def test_do_crossmatch_for_assumed_hla_type(self): + # general case json = { "assumed_donor_hla_typing": [['DPA1*01:03', 'DPA1*01:04', 'DPA1*01:06'], ['DPA1*02:01'], @@ -168,3 +169,46 @@ def test_do_crossmatch_for_assumed_hla_type(self): [asdict(create_hla_type('DPA1*02:01'))]] self.assertCountEqual(expected_assumed_hla_typing, res_assumed_hla_typing) + + # hla type is assumed in split/broad + json = { + "assumed_donor_hla_typing": [['DPA1*01:03', 'DPA1*01:04', 'DPA1*02:06'], + ['DPA1*02:01'], + ['DPA1*01:04']], + "recipient_antibodies": [{'mfi': 2100, + 'name': 'DPA1*01:04', + 'cutoff': 2000 + }, + {'mfi': 2100, + 'name': 'DPA1*02:01', + 'cutoff': 2000 + }], + } + + with self.app.test_client() as client: + res = client.post(f'{API_VERSION}/{CROSSMATCH_NAMESPACE}/do-crossmatch', json=json, + headers=self.auth_headers) + self.assertEqual(400, res.status_code) # ValueError + self.assertEqual('HLA Type can be assumed just in high resolution.', + res.json['message']) + + # low res codes in assumed hla type + json = { + "assumed_donor_hla_typing": [['DPA1*01:03', 'DPA1*01:04', 'DPA1'], + ['DPA1*02:01'], + ['DPA1*01:04']], + "recipient_antibodies": [{'mfi': 2100, + 'name': 'DPA1*01:04', + 'cutoff': 2000 + }, + {'mfi': 2100, + 'name': 'DPA1*02:01', + 'cutoff': 2000 + }], + } + with self.app.test_client() as client: + res = client.post(f'{API_VERSION}/{CROSSMATCH_NAMESPACE}/do-crossmatch', json=json, + headers=self.auth_headers) + self.assertEqual(400, res.status_code) # ValueError + self.assertEqual('Assumed HLA type is available only for HLA types in high resolution.', + res.json['message']) diff --git a/txmatching/utils/hla_system/hla_crossmatch.py b/txmatching/utils/hla_system/hla_crossmatch.py index 0e6312b03..5a578052f 100644 --- a/txmatching/utils/hla_system/hla_crossmatch.py +++ b/txmatching/utils/hla_system/hla_crossmatch.py @@ -40,7 +40,9 @@ class AntibodyMatchForHLAType: def __post_init__(self): if self.is_hla_type_assumed() and not self.__is_hla_type_in_high_res(): raise ValueError("Assumed HLA type is available only" - " for HLA type in high resolution.") + " for HLA types in high resolution.") + if self.__is_hla_type_assumed_in_low_res(): + raise ValueError("HLA Type can be assumed just in high resolution.") def is_hla_type_assumed(self): return len(self.hla_type) > 1 @@ -49,6 +51,9 @@ def __is_hla_type_in_high_res(self): return len([hla_type for hla_type in self.hla_type if not hla_type.code.is_in_high_res()]) == 0 + def __is_hla_type_assumed_in_low_res(self): + return len({hla_type.code.get_low_res_code() for hla_type in self.hla_type}) > 1 + def __hash__(self): return hash(tuple(self.hla_type)) diff --git a/txmatching/web/api/crossmatch_api.py b/txmatching/web/api/crossmatch_api.py index 9da2916d9..d878ec81d 100644 --- a/txmatching/web/api/crossmatch_api.py +++ b/txmatching/web/api/crossmatch_api.py @@ -57,13 +57,17 @@ def get_hla_types_correspond_antibody(assumed_hla_type: List[HLAType], return [hla_type for hla_type in assumed_hla_type if hla_type.code == hla_antibody.code] - def fulfill_with_common_matches(antibody_hla_match, antibody_group_match): - common_matched_hla_types: List[HLAType] = get_hla_types_correspond_antibody( - antibody_hla_match.hla_type, antibody_group_match.hla_antibody - ) - if len(common_matched_hla_types) > 0: - antibody_hla_match.hla_type = common_matched_hla_types - antibody_hla_match.antibody_matches.append(antibody_group_match) + def fulfill_with_common_matches(antibody_matches, crossmatched_antibodies): + for match_per_group in crossmatched_antibodies: + for antibody_group_match in match_per_group.antibody_matches: + raise_not_implemented_if_theoretical_antibody(antibody_group_match.hla_antibody) + for antibody_hla_match in antibody_matches: + common_matched_hla_types: List[HLAType] = get_hla_types_correspond_antibody( + antibody_hla_match.hla_type, antibody_group_match.hla_antibody + ) + if len(common_matched_hla_types) > 0: + antibody_hla_match.hla_type = common_matched_hla_types + antibody_hla_match.antibody_matches.append(antibody_group_match) def solve_uncrossmatched_hla_types(antibody_hla_matches: List[AntibodyMatchForHLAType]): def convert_assumed_hla_type_to_split(antibody_hla_match): @@ -90,15 +94,9 @@ def convert_assumed_hla_type_to_split(antibody_hla_match): antibody_matches_for_hla_type = [AntibodyMatchForHLAType( hla_type=[create_hla_type(raw_code=hla) for hla in hla_typing], antibody_matches=[]) for hla_typing in crossmatch_dto.assumed_donor_hla_typing] - # TODO: validate (1. - # - ALL IN HIGH RES - # - ALL HAVE THE SAME LOW RES) - for match_per_group in crossmatched_antibodies_per_group: - for antibody_group_match in match_per_group.antibody_matches: - raise_not_implemented_if_theoretical_antibody(antibody_group_match.hla_antibody) - for antibody_hla_match in antibody_matches_for_hla_type: - fulfill_with_common_matches(antibody_hla_match, antibody_group_match) + fulfill_with_common_matches(antibody_matches_for_hla_type, + crossmatched_antibodies_per_group) solve_uncrossmatched_hla_types(antibody_matches_for_hla_type) # TODO: rename? typing_parsing_issues, _ = parse_hla_typing_raw_and_return_parsing_issue_list(