Skip to content

Commit

Permalink
Merge pull request #1187 from mild-blue/1168_add_support_for_theoreti…
Browse files Browse the repository at this point in the history
…cal_and_double_antibodies_

Add support for theoretical and double antibodies in crossmatch api
  • Loading branch information
kubantjan authored May 3, 2023
2 parents a4eaa80 + 74adb2d commit 22613b4
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 40 deletions.
82 changes: 57 additions & 25 deletions tests/web/test_do_crossmatch_api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from tests.test_utilities.prepare_app_for_tests import DbTests
from txmatching.utils.hla_system.hla_transformations.parsing_issue_detail import \
ParsingIssueDetail
from txmatching.web import API_VERSION, CROSSMATCH_NAMESPACE


Expand Down Expand Up @@ -30,14 +32,12 @@ def test_do_crossmatch_api(self):
self.assertEqual([], res.json['hla_to_antibody'][1]['antibody_matches'])

self.assertEqual(
'All antibodies are in high resolution, some of them below cutoff and less then 20 were provided. '
'This is fine and antibodies will be processed properly, but we are assuming that not all antibodies '
'the patient was tested for were sent. It is better to send all to improve crossmatch estimation.',
res.json['parsing_issues'][0]['message'])
self.assertEqual('This HLA group should contain at least one antigen.',
res.json['parsing_issues'][1]['message'])
self.assertEqual('This HLA group should contain at least one antigen.',
res.json['parsing_issues'][2]['message'])
ParsingIssueDetail.INSUFFICIENT_NUMBER_OF_ANTIBODIES_IN_HIGH_RES,
res.json['parsing_issues'][0]['parsing_issue_detail'])
self.assertEqual(ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
res.json['parsing_issues'][1]['parsing_issue_detail'])
self.assertEqual(ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
res.json['parsing_issues'][2]['parsing_issue_detail'])

def test_do_crossmatch_api_with_different_code_formats(self):
# case: donor - HIGH_RES, recipient - SPLIT
Expand Down Expand Up @@ -68,10 +68,10 @@ def test_do_crossmatch_api_with_different_code_formats(self):
self.assertEqual([], res.json['hla_to_antibody'][1]['antibody_matches'])

self.assertEqual(
'This HLA group should contain at least one antigen.',
res.json['parsing_issues'][0]['message'])
self.assertEqual('This HLA group should contain at least one antigen.',
res.json['parsing_issues'][1]['message'])
ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
res.json['parsing_issues'][0]['parsing_issue_detail'])
self.assertEqual(ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
res.json['parsing_issues'][1]['parsing_issue_detail'])

# case: donor - SPLIT, recipient - HIGH_RES
json = {
Expand Down Expand Up @@ -101,16 +101,14 @@ def test_do_crossmatch_api_with_different_code_formats(self):
self.assertEqual([], res.json['hla_to_antibody'][1]['antibody_matches'])

self.assertEqual(
'All antibodies are in high resolution, some of them below cutoff and less then 20 were provided. '
'This is fine and antibodies will be processed properly, but we are assuming that not all antibodies '
'the patient was tested for were sent. It is better to send all to improve crossmatch estimation.',
res.json['parsing_issues'][0]['message'])
self.assertEqual('This HLA group should contain at least one antigen.',
res.json['parsing_issues'][1]['message'])

def test_theoretical_and_double_antibodies_not_implemented(self):
ParsingIssueDetail.INSUFFICIENT_NUMBER_OF_ANTIBODIES_IN_HIGH_RES,
res.json['parsing_issues'][0]['parsing_issue_detail'])
self.assertEqual(ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
res.json['parsing_issues'][1]['parsing_issue_detail'])

def test_theoretical_and_double_antibodies(self):
json = {
"donor_hla_typing": ['DPA1*01:03', 'DPA1*02:01', 'DPA1*01:04'],
"donor_hla_typing": ['DPA1*01:03', 'DPB1*03:01', 'DPA1*01:04', 'DPA1*02:01'],
"recipient_antibodies": [{'mfi': 2100,
'name': 'DP[01:04,03:01]',
'cutoff': 2000
Expand All @@ -126,13 +124,47 @@ def test_theoretical_and_double_antibodies_not_implemented(self):
{'mfi': 1000,
'name': 'DP[01:03,03:01]',
'cutoff': 2000
},
{'mfi': 3000,
'name': 'DP[02:01,01:01]',
'cutoff': 2000
},
{'mfi': 1000,
'name': 'DP[02:01,01: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(501, res.status_code)
self.assertEqual('This functionality is not currently available for dual antibodies. '
'We apologize and will try to change this in future versions.',
res.json['message'])
self.assertCountEqual([ParsingIssueDetail.CREATED_THEORETICAL_ANTIBODY,
ParsingIssueDetail.CREATED_THEORETICAL_ANTIBODY,
ParsingIssueDetail.INSUFFICIENT_NUMBER_OF_ANTIBODIES_IN_HIGH_RES,
ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY,
ParsingIssueDetail.BASIC_HLA_GROUP_IS_EMPTY],
[parsing_issue['parsing_issue_detail'] for parsing_issue in res.json['parsing_issues']])
double_antibody_match = {'hla_antibody': {'code': {'broad': 'DPA1', 'high_res': 'DPA1*01:04', 'split': 'DPA1'},
'cutoff': 2000,
'mfi': 2100,
'raw_code': 'DPA1*01:04',
'second_code': {'broad': 'DP3', 'high_res': 'DPB1*03:01', 'split': 'DP3'},
'second_raw_code': 'DPB1*03:01', 'type': 'NORMAL'},
'match_type': 'HIGH_RES'}
theoretical_antibody_match = {'hla_antibody': {'code': {'broad': 'DPA2', 'high_res': 'DPA1*02:01', 'split': 'DPA2'},
'cutoff': 2000,
'mfi': 3000,
'raw_code':
'DPA1*02:01',
'second_code': None,
'second_raw_code': None,
'type': 'THEORETICAL'},
'match_type': 'THEORETICAL'}

self.assertTrue(
double_antibody_match in res.json['hla_to_antibody'][1]['antibody_matches'])
self.assertTrue(
double_antibody_match in res.json['hla_to_antibody'][2]['antibody_matches'])
self.assertTrue(
theoretical_antibody_match in res.json['hla_to_antibody'][3]['antibody_matches'])
2 changes: 1 addition & 1 deletion txmatching/utils/hla_system/hla_crossmatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class AntibodyMatchForHLAType:

def get_crossmatched_antibodies_per_group(donor_hla_typing: HLATyping,
recipient_antibodies: HLAAntibodies,
use_high_resolution: bool):
use_high_resolution: bool) -> List[AntibodyMatchForHLAGroup]:
if is_recipient_type_a(recipient_antibodies):
antibody_matches_for_groups = do_crossmatch_in_type_a(donor_hla_typing,
recipient_antibodies,
Expand Down
29 changes: 15 additions & 14 deletions txmatching/web/api/crossmatch_api.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
from flask_restx import Resource

from txmatching.auth.exceptions import TXMNotImplementedFeatureException
from txmatching.utils.hla_system.hla_preparation_utils import create_hla_typing, create_hla_type, \
create_antibody
from txmatching.data_transfer_objects.crossmatch.crossmatch_dto import CrossmatchDTOIn, AntibodyMatchForHLAType, \
CrossmatchDTOOut
from txmatching.data_transfer_objects.crossmatch.crossmatch_in_swagger import CrossmatchJsonIn, CrossmatchJsonOut
from txmatching.data_transfer_objects.patients.patient_parameters_dto import HLATypingRawDTO
from txmatching.patients.hla_model import HLATypeRaw, HLAAntibodies
from txmatching.utils.enums import HLAAntibodyType
from txmatching.patients.hla_model import HLAAntibodies, HLATypeRaw
from txmatching.utils.hla_system.hla_crossmatch import get_crossmatched_antibodies_per_group
from txmatching.utils.hla_system.hla_preparation_utils import create_hla_typing, create_hla_type, \
create_antibody
from txmatching.utils.hla_system.hla_transformations.hla_transformations_store import \
parse_hla_antibodies_raw_and_return_parsing_issue_list, parse_hla_typing_raw_and_return_parsing_issue_list
from txmatching.web.web_utils.namespaces import crossmatch_api
Expand Down Expand Up @@ -49,21 +48,23 @@ def post(self):

antibody_matches_for_hla_type = [AntibodyMatchForHLAType(hla_type=create_hla_type(raw_code=hla),
antibody_matches=[])
for hla in crossmatch_dto.donor_hla_typing]
for hla in crossmatch_dto.donor_hla_typing]
for match_per_group in crossmatched_antibodies_per_group:
for antibody_group_match in match_per_group.antibody_matches:
if antibody_group_match.hla_antibody.type == HLAAntibodyType.THEORETICAL or \
antibody_group_match.hla_antibody.second_raw_code:
raise TXMNotImplementedFeatureException(
'This functionality is not currently available for dual antibodies. '
'We apologize and will try to change this in future versions.')
# get AntibodyMatchForHLAType object with the same hla_type
# as the antibody_group_match and append the antibody_group_match
common_matches = [antibody_hla_match for antibody_hla_match in
antibody_matches_for_hla_type
if antibody_hla_match.hla_type.code == antibody_group_match.hla_antibody.code]
if antibody_group_match.hla_antibody.second_raw_code:
common_matches = [antibody_hla_match for antibody_hla_match in
antibody_matches_for_hla_type
if antibody_hla_match.hla_type.code in (antibody_group_match.hla_antibody.code,
antibody_group_match.hla_antibody.second_code)]
else:
common_matches = [antibody_hla_match for antibody_hla_match in
antibody_matches_for_hla_type
if antibody_hla_match.hla_type.code == antibody_group_match.hla_antibody.code]
if common_matches:
common_matches[0].antibody_matches.append(antibody_group_match)
for common_match in common_matches:
common_match.antibody_matches.append(antibody_group_match)

return response_ok(CrossmatchDTOOut(
hla_to_antibody=antibody_matches_for_hla_type,
Expand Down

0 comments on commit 22613b4

Please sign in to comment.