Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speedup issue confirmation #945

Merged
merged 1 commit into from
Jul 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tests/utils/hla_system/test_code_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ def test_mfi_extraction(self):
).hla_antibodies_per_groups[3].hla_antibody_list))
# When MFI quite close to each other but one below and one above cutoff
self._compare_mfi_result(expected_mfi=1500, mfis=[1500, 2900])
# no warning in case the values are all quite low:
self._compare_mfi_result(expected_mfi=1201, mfis=[1339, 1058, 2058, 1206], has_issue=False)
self._compare_mfi_result(expected_mfi=1508, mfis=[3970, 1922, 1422, 1180], has_issue=True)

def test_no_ultra_high_res_with_multiple_splits(self):
"""
Expand Down
11 changes: 6 additions & 5 deletions txmatching/data_transfer_objects/patients/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@

from txmatching.data_transfer_objects.hla.parsing_issue_dto import (
ParsingIssue, ParsingIssueConfirmationDTO, ParsingIssuePublicDTO)
from txmatching.database.sql_alchemy_schema import ParsingIssueModel
from txmatching.data_transfer_objects.patients.upload_dtos.donor_upload_dto import \
DonorUploadDTO
from txmatching.data_transfer_objects.patients.upload_dtos.hla_antibodies_upload_dto import \
HLAAntibodiesUploadDTO
from txmatching.data_transfer_objects.patients.upload_dtos.recipient_upload_dto import \
RecipientUploadDTO
from txmatching.patients.patient import Donor, Recipient, TxmEvent
from txmatching.database.sql_alchemy_schema import ParsingIssueModel
from txmatching.patients.patient import Donor, Recipient, TxmEventBase

logger = logging.getLogger(__name__)

def parsing_issue_to_dto(parsing_issue: ParsingIssue, txm_event: TxmEvent) -> ParsingIssuePublicDTO:

def parsing_issue_to_dto(parsing_issue: ParsingIssue, txm_event: TxmEventBase) -> ParsingIssuePublicDTO:
return ParsingIssuePublicDTO(
hla_code_or_group=parsing_issue.hla_code_or_group,
parsing_issue_detail=parsing_issue.parsing_issue_detail,
Expand All @@ -27,13 +28,13 @@ def parsing_issue_to_dto(parsing_issue: ParsingIssue, txm_event: TxmEvent) -> Pa


def parsing_issue_model_to_confirmation_dto(parsing_issue: ParsingIssueModel,
txm_event: TxmEvent) -> ParsingIssueConfirmationDTO:
txm_event_id: int) -> ParsingIssueConfirmationDTO:
return ParsingIssueConfirmationDTO(
db_id=parsing_issue.id,
hla_code_or_group=parsing_issue.hla_code_or_group,
parsing_issue_detail=parsing_issue.parsing_issue_detail,
message=parsing_issue.message,
txm_event_id=txm_event.db_id,
txm_event_id=txm_event_id,
confirmed_at=parsing_issue.confirmed_at,
confirmed_by=parsing_issue.confirmed_by,
donor_id=parsing_issue.donor_id,
Expand Down
30 changes: 17 additions & 13 deletions txmatching/database/services/parsing_issue_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@

from sqlalchemy import and_

from txmatching.auth.exceptions import InvalidArgumentException, OverridingException
from txmatching.data_transfer_objects.hla.parsing_issue_dto import ParsingIssue, ParsingIssueConfirmationDTO
from txmatching.data_transfer_objects.patients.utils import parsing_issue_model_to_confirmation_dto
from txmatching.auth.exceptions import (InvalidArgumentException,
OverridingException)
from txmatching.data_transfer_objects.hla.parsing_issue_dto import (
ParsingIssue, ParsingIssueConfirmationDTO)
from txmatching.data_transfer_objects.patients.utils import \
parsing_issue_model_to_confirmation_dto
from txmatching.database.db import db
from txmatching.database.sql_alchemy_schema import ParsingIssueModel
from txmatching.patients.patient import TxmEvent
from txmatching.utils.hla_system.hla_transformations.parsing_issue_detail import WARNING_PROCESSING_RESULTS
from txmatching.utils.hla_system.hla_transformations.parsing_issue_detail import \
WARNING_PROCESSING_RESULTS


def confirm_a_parsing_issue(user_id: int, parsing_issue_id: int, txm_event: TxmEvent) -> ParsingIssueConfirmationDTO:
def confirm_a_parsing_issue(user_id: int, parsing_issue_id: int,
txm_event_id: int) -> ParsingIssueConfirmationDTO:
parsing_issue = ParsingIssueModel.query.get(parsing_issue_id)

if parsing_issue is None or parsing_issue.txm_event_id != txm_event.db_id:
raise InvalidArgumentException(f'Parsing issue {parsing_issue_id} not found in txm event {txm_event.db_id}')
if parsing_issue is None or parsing_issue.txm_event_id != txm_event_id:
raise InvalidArgumentException(f'Parsing issue {parsing_issue_id} not found in txm event {txm_event_id}')

if parsing_issue.parsing_issue_detail not in WARNING_PROCESSING_RESULTS:
raise InvalidArgumentException(f'Parsing issue {parsing_issue_id} is not a warning')
Expand All @@ -28,14 +32,14 @@ def confirm_a_parsing_issue(user_id: int, parsing_issue_id: int, txm_event: TxmE
parsing_issue.confirmed_at = datetime.now()

db.session.commit()
return parsing_issue_model_to_confirmation_dto(parsing_issue, txm_event)
return parsing_issue_model_to_confirmation_dto(parsing_issue, txm_event_id)


def unconfirm_a_parsing_issue(parsing_issue_id: int, txm_event: TxmEvent) -> ParsingIssueConfirmationDTO:
def unconfirm_a_parsing_issue(parsing_issue_id: int, txm_event_id: int) -> ParsingIssueConfirmationDTO:
parsing_issue = ParsingIssueModel.query.get(parsing_issue_id)

if parsing_issue is None or parsing_issue.txm_event_id != txm_event.db_id:
raise InvalidArgumentException(f'Parsing issue {parsing_issue_id} not found in txm event {txm_event.db_id}')
if parsing_issue is None or parsing_issue.txm_event_id != txm_event_id:
raise InvalidArgumentException(f'Parsing issue {parsing_issue_id} not found in txm event {txm_event_id}')

if parsing_issue.parsing_issue_detail not in WARNING_PROCESSING_RESULTS:
raise InvalidArgumentException(f'Parsing issue {parsing_issue_id} is not a warning')
Expand All @@ -47,7 +51,7 @@ def unconfirm_a_parsing_issue(parsing_issue_id: int, txm_event: TxmEvent) -> Par
parsing_issue.confirmed_at = None

db.session.commit()
return parsing_issue_model_to_confirmation_dto(parsing_issue, txm_event)
return parsing_issue_model_to_confirmation_dto(parsing_issue, txm_event_id)


def parsing_issues_to_models(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

RELATIVE_DIFFERENCE_THRESHOLD_FOR_SUSPICIOUS_MFI = 3 / 4

RELATIVE_CLOSENESS_TO_CUTOFF_FROM_BELOW = 0.5
RELATIVE_CLOSENESS_TO_CUTOFF_FROM_BELOW = 3 / 4
RELATIVE_CLOSENESS_TO_CUTOFF_FROM_ABOVE = 1.25
RELATIVE_CLOSENESS_TO_MINIMUM = 1 / 2
DIFFERENCE_THRESHOLD_RATIO = 1 / 4

MAX_MFI_RATIO_TO_BE_JUST_BELOW_CUTOFF = 7 / 8


def get_mfi_from_multiple_hla_codes(mfis: List[int],
cutoff: int,
Expand Down Expand Up @@ -73,15 +75,16 @@ def get_mfi_from_multiple_hla_codes(mfis: List[int],
else:
relevant_mean = int(np.mean(mfis))

if RELATIVE_CLOSENESS_TO_CUTOFF_FROM_BELOW * cutoff < relevant_mean < cutoff:
if RELATIVE_CLOSENESS_TO_CUTOFF_FROM_BELOW * cutoff < relevant_mean < cutoff and \
max_mfi > cutoff * MAX_MFI_RATIO_TO_BE_JUST_BELOW_CUTOFF:
parsing_issues.append(
ParsingIssue(
hla_code_or_group=raw_code,
parsing_issue_detail=ParsingIssueDetail.MFI_PROBLEM,
message=f'Dropping {raw_code} antibody: '
f'Calculated MFI: {int(relevant_mean)}, Cutoff: {cutoff}, MFI values: {mfis}. '
f'To be consulted with immunologist, because the antibody is dropped even though it has some '
f'high MFI values.'
f'To be consulted with immunologist, the estimated overall MFI is below cutoff, but the '
f'value is close to cutoff.'
)
)

Expand All @@ -92,8 +95,8 @@ def get_mfi_from_multiple_hla_codes(mfis: List[int],
parsing_issue_detail=ParsingIssueDetail.MFI_PROBLEM,
message=f'Not dropping {raw_code} antibody: '
f'Calculated MFI: {int(relevant_mean)}, Cutoff: {cutoff}, MFI values: {mfis}. '
f'To be consulted with immunologist, because the antibody is NOT dropped even though there are'
f' some MFI values below cutoff.'
f'To be consulted with immunologist, because the antibody is NOT dropped even though '
f'there are some MFI values below cutoff.'
)
)

Expand All @@ -104,9 +107,9 @@ def get_mfi_from_multiple_hla_codes(mfis: List[int],
parsing_issue_detail=ParsingIssueDetail.MFI_PROBLEM,
message=f'Dropping {raw_code} antibody: '
f'Calculated MFI: {int(relevant_mean)}, Cutoff: {cutoff}, MFI values: {mfis}. '
f'To be consulted with immunologist, because the antibody is dropped even though only one MFI '
f'value was identified as relevant for the MFI calculation and therefore the final calculated '
f'mean is less relevant.'
f'To be consulted with immunologist, because the antibody is dropped even though only'
f' one MFI value was identified as relevant for the MFI calculation and therefore '
f'the final calculated mean is less relevant.'
)
)

Expand Down
12 changes: 6 additions & 6 deletions txmatching/web/api/patient_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
ConfigIdPathParamDefinition
from txmatching.data_transfer_objects.external_patient_upload.swagger import \
PatientUploadSuccessJson
from txmatching.data_transfer_objects.hla.parsing_issue_swagger import ParsingIssueConfirmationJson
from txmatching.data_transfer_objects.hla.parsing_issue_swagger import \
ParsingIssueConfirmationJson
from txmatching.data_transfer_objects.patients.out_dtos.conversions import (
donor_to_donor_dto_out, recipient_to_recipient_dto_out, to_lists_for_fe)
from txmatching.data_transfer_objects.patients.out_dtos.donor_dto_out import \
Expand All @@ -45,7 +46,8 @@
from txmatching.database.services.config_service import \
get_configuration_parameters_from_db_id_or_default
from txmatching.database.services.parsing_issue_service import (
confirm_a_parsing_issue, get_parsing_issues_confirmation_dto_for_patients, unconfirm_a_parsing_issue)
confirm_a_parsing_issue, get_parsing_issues_confirmation_dto_for_patients,
unconfirm_a_parsing_issue)
from txmatching.database.services.patient_service import (
delete_donor_recipient_pair, get_donor_recipient_pair,
recompute_hla_and_antibodies_parsing_for_all_patients_in_txm_event,
Expand Down Expand Up @@ -300,8 +302,7 @@ class ConfirmWarning(Resource):
@require_role(UserRole.ADMIN)
def put(self, txm_event_id: int, parsing_issue_id: int):
user_id = get_current_user_id()
txm_event = get_txm_event_complete(txm_event_id)
result = confirm_a_parsing_issue(user_id, parsing_issue_id, txm_event)
result = confirm_a_parsing_issue(user_id, parsing_issue_id, txm_event_id)
return response_ok(result)


Expand All @@ -326,6 +327,5 @@ class UnconfirmWarning(Resource):
@require_valid_txm_event_id()
@require_role(UserRole.ADMIN)
def put(self, txm_event_id: int, parsing_issue_id: int):
txm_event = get_txm_event_complete(txm_event_id)
result = unconfirm_a_parsing_issue(parsing_issue_id, txm_event)
result = unconfirm_a_parsing_issue(parsing_issue_id, txm_event_id)
return response_ok(result)