diff --git a/tests/scorers/test_detailed_hla_scorer.py b/tests/scorers/test_detailed_hla_scorer.py index 5126ce9b3..221f1c5f0 100644 --- a/tests/scorers/test_detailed_hla_scorer.py +++ b/tests/scorers/test_detailed_hla_scorer.py @@ -59,68 +59,67 @@ def test_get_genotype(self): self.assertSetEqual(set(expected_result.donor_matches), set(actual_result.donor_matches)) self.assertSetEqual(set(expected_result.recipient_matches), set(actual_result.recipient_matches)) + def test_scorer_on_some_patients(self): + scorer = HLAAdditiveScorer() + donor = Donor( + db_id=1, + medical_id='donor', + parameters=PatientParameters( + blood_group=BloodGroup.A, + country_code=Country.CZE, + hla_typing=HLATyping( + [HLAType('A1'), + HLAType('A3'), + HLAType('B7'), + HLAType('B37'), + HLAType('DR11'), + HLAType('DR15'), + HLAType('DR52'), + HLAType('DR51'), + HLAType('DQ7'), + HLAType('DQ6')] + ) -def test_scorer_on_some_patients(self): - scorer = HLAAdditiveScorer() - donor = Donor( - db_id=1, - medical_id='donor', - parameters=PatientParameters( - blood_group=BloodGroup.A, - country_code=Country.CZE, - hla_typing=HLATyping( - [HLAType('A1'), - HLAType('A3'), - HLAType('B7'), - HLAType('B37'), - HLAType('DR11'), - HLAType('DR15'), - HLAType('DR52'), - HLAType('DR51'), - HLAType('DQ7'), - HLAType('DQ6')] ) - ) - ) - recipient = Recipient( - db_id=1, - acceptable_blood_groups=[], - related_donor_db_id=1, - medical_id='recipient', - parameters=PatientParameters( - blood_group=BloodGroup.A, - country_code=Country.CZE, - hla_typing=HLATyping( - [HLAType('A1'), - HLAType('A2'), - HLAType('B27'), - HLAType('B37'), - HLAType('DR1'), - HLAType('DR10')] + recipient = Recipient( + db_id=1, + acceptable_blood_groups=[], + related_donor_db_id=1, + medical_id='recipient', + parameters=PatientParameters( + blood_group=BloodGroup.A, + country_code=Country.CZE, + hla_typing=HLATyping( + [HLAType('A1'), + HLAType('A2'), + HLAType('B27'), + HLAType('B37'), + HLAType('DR1'), + HLAType('DR10')] + ) ) ) - ) - original_donor = Donor( - db_id=2, - medical_id='original_donor', - related_recipient_db_id=1, - parameters=PatientParameters( - blood_group=BloodGroup.A, - country_code=Country.CZE, - hla_typing=HLATyping( - [HLAType('A1'), - HLAType('A9'), - HLAType('B7'), - HLAType('B37'), - HLAType('DR11'), - HLAType('DR15'), - HLAType('DR52'), - HLAType('DR51'), - HLAType('DQ7'), - HLAType('DQ6')] + original_donor = Donor( + db_id=2, + medical_id='original_donor', + related_recipient_db_id=1, + parameters=PatientParameters( + blood_group=BloodGroup.A, + country_code=Country.CZE, + hla_typing=HLATyping( + [HLAType('A1'), + HLAType('A9'), + HLAType('B7'), + HLAType('B37'), + HLAType('DR11'), + HLAType('DR15'), + HLAType('DR52'), + HLAType('DR51'), + HLAType('DQ7'), + HLAType('DQ6')] + ) ) ) - ) - self.assertEqual(4, scorer.score_transplant(donor=donor, recipient=recipient, original_donor=original_donor)) + self.assertEqual(4, scorer.score_transplant(donor=donor, recipient=recipient, original_donor=original_donor)) diff --git a/tests/web/test_matching_api.py b/tests/web/test_matching_api.py index 6df6fb5fb..bc0031177 100644 --- a/tests/web/test_matching_api.py +++ b/tests/web/test_matching_api.py @@ -7,13 +7,11 @@ from txmatching.database.services import solver_service from txmatching.database.services.txm_event_service import \ get_txm_event_complete -from txmatching.database.sql_alchemy_schema import ConfigModel from txmatching.solve_service.solve_from_configuration import \ solve_from_configuration from txmatching.utils.enums import HLAGroup, MatchTypes from txmatching.utils.get_absolute_path import get_absolute_path -from txmatching.web import (API_VERSION, MATCHING_NAMESPACE, PATIENT_NAMESPACE, - TXM_EVENT_NAMESPACE) +from txmatching.web import API_VERSION, MATCHING_NAMESPACE, TXM_EVENT_NAMESPACE class TestSaveAndGetConfiguration(DbTests): @@ -213,74 +211,45 @@ def test_get_matchings(self): 'recipient_matches' ]) + def test_correct_config_applied(self): + txm_event_db_id = self.fill_db_with_patients(get_absolute_path(PATIENT_DATA_OBFUSCATED)) -def test_get_patients(self): - txm_event_db_id = self.fill_db_with_patients() - with self.app.test_client() as client: - res = client.get(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/{PATIENT_NAMESPACE}', - headers=self.auth_headers) - self.assertEqual(2, len(res.json['donors'])) - self.assertEqual(2, len(res.json['recipients'])) - - -def test_save_recipient(self): - txm_event_db_id = self.fill_db_with_patients_and_results() - recipient_update_dict = { - 'db_id': 1, - 'acceptable_blood_groups': ['A', 'AB'], - } - with self.app.test_client() as client: - self.assertIsNotNone(ConfigModel.query.get(1)) - res = client.put(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/{PATIENT_NAMESPACE}/recipient', - headers=self.auth_headers, - json=recipient_update_dict).json - self.assertEqual(['A', 'AB'], res['acceptable_blood_groups']) - recipients = client.get(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/{PATIENT_NAMESPACE}', - headers=self.auth_headers).json['recipients'] - self.assertEqual(recipient_update_dict['acceptable_blood_groups'], recipients[0]['acceptable_blood_groups']) - - self.assertIsNone(ConfigModel.query.get(1)) - - -def test_correct_config_applied(self): - txm_event_db_id = self.fill_db_with_patients(get_absolute_path(PATIENT_DATA_OBFUSCATED)) - - with self.app.test_client() as client: - conf_dto = dataclasses.asdict(Configuration(max_number_of_distinct_countries_in_round=1)) - - res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' - f'{MATCHING_NAMESPACE}/calculate-for-config', - json=conf_dto, - headers=self.auth_headers) - self.assertEqual(200, res.status_code) - self.assertEqual(9, len(res.json)) - - conf_dto2 = dataclasses.asdict(Configuration(max_number_of_distinct_countries_in_round=50)) - - res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' - f'{MATCHING_NAMESPACE}/calculate-for-config', - json=conf_dto2, - headers=self.auth_headers) - self.assertEqual(200, res.status_code) - self.assertEqual(503, len(res.json)) + with self.app.test_client() as client: + conf_dto = dataclasses.asdict(Configuration(max_number_of_distinct_countries_in_round=1)) + res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{MATCHING_NAMESPACE}/calculate-for-config', + json=conf_dto, + headers=self.auth_headers) + self.assertEqual(200, res.status_code) + self.assertEqual(9, res.json['found_matchings_count']) -def test_solver_multiple_txm_events(self): - txm_event_db_id = self.fill_db_with_patients(get_absolute_path(PATIENT_DATA_OBFUSCATED)) + conf_dto2 = dataclasses.asdict(Configuration(max_number_of_distinct_countries_in_round=50)) - with self.app.test_client() as client: - conf_dto = dataclasses.asdict(Configuration(max_number_of_distinct_countries_in_round=1)) + res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{MATCHING_NAMESPACE}/calculate-for-config', + json=conf_dto2, + headers=self.auth_headers) + self.assertEqual(200, res.status_code) + self.assertEqual(947, res.json['found_matchings_count']) - res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' - f'{MATCHING_NAMESPACE}/calculate-for-config', - json=conf_dto, - headers=self.auth_headers) - self.assertEqual(200, res.status_code) + def test_solver_multiple_txm_events(self): + txm_event_db_id = self.fill_db_with_patients(get_absolute_path(PATIENT_DATA_OBFUSCATED)) - create_or_overwrite_txm_event(name='test2') - res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' - f'{MATCHING_NAMESPACE}/calculate-for-config', - json=conf_dto, - headers=self.auth_headers) - self.assertEqual(200, res.status_code) - self.assertEqual(0, len(res.json)) + with self.app.test_client() as client: + conf_dto = dataclasses.asdict(Configuration(max_number_of_distinct_countries_in_round=1)) + + res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{MATCHING_NAMESPACE}/calculate-for-config', + json=conf_dto, + headers=self.auth_headers) + self.assertEqual(9, res.json['found_matchings_count']) + self.assertEqual(200, res.status_code) + + txm_event_db_id_2 = create_or_overwrite_txm_event(name='test2').db_id + res = client.post(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id_2}/' + f'{MATCHING_NAMESPACE}/calculate-for-config', + json=conf_dto, + headers=self.auth_headers) + self.assertEqual(200, res.status_code) + self.assertEqual(0, res.json['found_matchings_count']) diff --git a/tests/web/test_patient_api.py b/tests/web/test_patient_api.py index 19e3140f9..b1968aa83 100644 --- a/tests/web/test_patient_api.py +++ b/tests/web/test_patient_api.py @@ -6,8 +6,12 @@ from tests.test_utilities.prepare_app import DbTests from txmatching.database.services.txm_event_service import \ get_txm_event_complete -from txmatching.database.sql_alchemy_schema import UploadedFileModel -from txmatching.patients.patient import DonorType +from txmatching.database.sql_alchemy_schema import (ConfigModel, + UploadedFileModel) +from txmatching.patients.hla_model import HLAAntibodies, HLATyping +from txmatching.patients.patient import DonorType, RecipientRequirements +from txmatching.utils.blood_groups import BloodGroup +from txmatching.utils.enums import Sex from txmatching.utils.get_absolute_path import get_absolute_path from txmatching.web import API_VERSION, PATIENT_NAMESPACE, TXM_EVENT_NAMESPACE @@ -21,6 +25,8 @@ def test_get_patients(self): res = client.get(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/{PATIENT_NAMESPACE}', headers=self.auth_headers) self.assertEqual(200, res.status_code) + self.assertEqual(38, len(res.json['donors'])) + self.assertEqual(34, len(res.json['recipients'])) for donor in res.json['donors']: self.assertIn('detailed_score_with_related_recipient', donor) detailed_score_for_groups = donor['detailed_score_with_related_recipient'] @@ -226,3 +232,186 @@ def test_donor_recipient_pair_deletion(self): headers=self.auth_headers) self.assertEqual(400, res.status_code) self.assertEqual('application/json', res.content_type) + + def test_update_donor(self): + txm_event_db_id = self.fill_db_with_patients( + get_absolute_path(PATIENT_DATA_OBFUSCATED)) + donor_db_id = 10 + + # 1. update patient + with self.app.test_client() as client: + json_data = { + 'db_id': donor_db_id, + 'blood_group': 'A', + 'hla_typing': { + 'hla_types_list': [] + }, + 'sex': 'M', + 'height': 200, + 'weight': 100, + 'year_of_birth': 1990, + 'active': True + } + res = client.put(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{PATIENT_NAMESPACE}/donor', + headers=self.auth_headers, json=json_data) + self.assertEqual(200, res.status_code) + self.assertEqual('application/json', res.content_type) + + # Check that update was working + txm_event = get_txm_event_complete(txm_event_db_id) + donor = txm_event.active_donors_dict[donor_db_id] + self.assertEqual(donor.parameters.blood_group, BloodGroup.A) + self.assertEqual(donor.parameters.hla_typing, HLATyping([])) + self.assertEqual(donor.parameters.sex, Sex.M) + self.assertEqual(donor.parameters.height, 200) + self.assertEqual(donor.parameters.weight, 100) + self.assertEqual(donor.parameters.year_of_birth, 1990) + self.assertEqual(donor.active, True) + + # 2. update with unspecified values + with self.app.test_client() as client: + json_data = { + 'db_id': donor_db_id, + 'blood_group': 'B', + 'hla_typing': { + 'hla_types_list': [] + }, + 'sex': None, + 'height': None, + 'weight': None, + 'year_of_birth': None, + 'active': False + } + res = client.put(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{PATIENT_NAMESPACE}/donor', + headers=self.auth_headers, json=json_data) + self.assertEqual(200, res.status_code) + self.assertEqual('application/json', res.content_type) + + # Check that update was working + txm_event = get_txm_event_complete(txm_event_db_id) + donor = next(donor for donor in txm_event.all_donors if donor.db_id == donor_db_id) + + self.assertEqual(donor.parameters.blood_group, BloodGroup.B) + self.assertEqual(donor.parameters.hla_typing, HLATyping([])) + self.assertEqual(donor.parameters.sex, None) + self.assertEqual(donor.parameters.height, None) + self.assertEqual(donor.parameters.weight, None) + self.assertEqual(donor.parameters.year_of_birth, None) + self.assertEqual(donor.active, False) + + def test_update_recipient(self): # TODOO + txm_event_db_id = self.fill_db_with_patients( + get_absolute_path(PATIENT_DATA_OBFUSCATED)) + recipient_db_id = 10 + + # 1. update patient + with self.app.test_client() as client: + json_data = { + 'db_id': recipient_db_id, + 'blood_group': 'A', + 'hla_typing': { + 'hla_types_list': [] + }, + 'sex': 'M', + 'height': 200, + 'weight': 100, + 'year_of_birth': 1990, + 'acceptable_blood_groups': ['A', 'B', 'AB'], + 'hla_antibodies': { + 'hla_antibodies_list': [] + }, + 'recipient_requirements': { + 'require_better_match_in_compatibility_index': True, + 'require_better_match_in_compatibility_index_or_blood_group': True, + 'require_compatible_blood_group': True + }, + 'cutoff': 42 + } + res = client.put(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{PATIENT_NAMESPACE}/recipient', + headers=self.auth_headers, json=json_data) + self.assertEqual(200, res.status_code) + self.assertEqual('application/json', res.content_type) + + # Check that update was working + txm_event = get_txm_event_complete(txm_event_db_id) + recipient = txm_event.active_recipients_dict[recipient_db_id] + self.assertEqual(recipient.parameters.blood_group, BloodGroup.A) + self.assertEqual(recipient.parameters.hla_typing, HLATyping([])) + self.assertEqual(recipient.parameters.sex, Sex.M) + self.assertEqual(recipient.parameters.height, 200) + self.assertEqual(recipient.parameters.weight, 100) + self.assertEqual(recipient.parameters.year_of_birth, 1990) + # HACK: This should equal to [BloodGroup.A, BloodGroup.B, BloodGroup.AB] + # TODO: https://github.com/mild-blue/txmatching/issues/477 represent blood as enum + self.assertCountEqual(recipient.acceptable_blood_groups, ['A', 'B', 'AB']) + self.assertEqual(recipient.hla_antibodies, HLAAntibodies()) + self.assertEqual(recipient.recipient_requirements, RecipientRequirements(True, True, True)) + self.assertEqual(recipient.recipient_cutoff, 42) + + # 2. update with unspecified values + with self.app.test_client() as client: + json_data = { + 'db_id': recipient_db_id, + 'blood_group': 'B', + 'hla_typing': { + 'hla_types_list': [] + }, + 'sex': None, + 'height': None, + 'weight': None, + 'year_of_birth': None, + 'acceptable_blood_groups': ['0'], + 'hla_antibodies': { + 'hla_antibodies_list': [] + }, + 'recipient_requirements': { + 'require_better_match_in_compatibility_index': False, + 'require_better_match_in_compatibility_index_or_blood_group': False, + 'require_compatible_blood_group': False + }, + 'cutoff': None + } + res = client.put(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/' + f'{PATIENT_NAMESPACE}/recipient', + headers=self.auth_headers, json=json_data) + self.assertEqual(200, res.status_code) + self.assertEqual('application/json', res.content_type) + + # Check that update was working + txm_event = get_txm_event_complete(txm_event_db_id) + recipient = next(recipient for recipient in txm_event.all_recipients if recipient.db_id == recipient_db_id) + + self.assertEqual(recipient.parameters.blood_group, BloodGroup.B) + self.assertEqual(recipient.parameters.hla_typing, HLATyping([])) + self.assertEqual(recipient.parameters.sex, None) + self.assertEqual(recipient.parameters.height, None) + self.assertEqual(recipient.parameters.weight, None) + self.assertEqual(recipient.parameters.year_of_birth, None) + # HACK: This should equal to [BloodGroup.ZERO] + # TODO: https://github.com/mild-blue/txmatching/issues/477 represent blood as enum + self.assertCountEqual(recipient.acceptable_blood_groups, ['0']) + self.assertEqual(recipient.hla_antibodies, HLAAntibodies()) + self.assertEqual(recipient.recipient_requirements, RecipientRequirements(False, False, False)) + self.assertEqual(recipient.recipient_cutoff, 42) # Cutoff is unchanged + + def test_save_recipient(self): + txm_event_db_id = self.fill_db_with_patients_and_results() + recipient_update_dict = { + 'db_id': 1, + 'acceptable_blood_groups': ['A', 'AB'], + } + with self.app.test_client() as client: + self.assertIsNotNone(ConfigModel.query.get(1)) + res = client.put(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/{PATIENT_NAMESPACE}/recipient', + headers=self.auth_headers, + json=recipient_update_dict).json + self.assertEqual(['A', 'AB'], res['acceptable_blood_groups']) + recipients = client.get(f'{API_VERSION}/{TXM_EVENT_NAMESPACE}/{txm_event_db_id}/{PATIENT_NAMESPACE}', + headers=self.auth_headers).json['recipients'] + self.assertEqual(recipient_update_dict['acceptable_blood_groups'], recipients[0]['acceptable_blood_groups']) + + # Config is not deleted + self.assertIsNotNone(ConfigModel.query.get(1)) diff --git a/tests/web/test_swagger_endpoint.py b/tests/web/test_swagger_endpoint.py index b7120de82..491d95073 100644 --- a/tests/web/test_swagger_endpoint.py +++ b/tests/web/test_swagger_endpoint.py @@ -36,7 +36,9 @@ def test_server_logged_in(self): f'{API_VERSION[1:]}/{USER_NAMESPACE}/otp': [403], f'{API_VERSION[1:]}/{USER_NAMESPACE}/change-password': [401], f'{API_VERSION[1:]}/{TXM_EVENT_NAMESPACE}/patients': [403], - f'{API_VERSION[1:]}/{TXM_EVENT_NAMESPACE}/{{txm_event_id}}/{PATIENT_NAMESPACE}/add-patients-file': [400] + f'{API_VERSION[1:]}/{TXM_EVENT_NAMESPACE}/{{txm_event_id}}/{PATIENT_NAMESPACE}/add-patients-file': [400], + # TODO: Fix swagger test https://github.com/mild-blue/swagger-unittest/issues/5 + f'{API_VERSION[1:]}/{TXM_EVENT_NAMESPACE}/{{txm_event_id}}/{PATIENT_NAMESPACE}/recipient': [400], } } # we need this user as we need the report to be generated from filled txm event, but we create an empty diff --git a/txmatching/data_transfer_objects/configuration/configuration_swagger.py b/txmatching/data_transfer_objects/configuration/configuration_swagger.py index 0b77aecd6..81a4c7fcd 100644 --- a/txmatching/data_transfer_objects/configuration/configuration_swagger.py +++ b/txmatching/data_transfer_objects/configuration/configuration_swagger.py @@ -1,8 +1,7 @@ from flask_restx import fields from txmatching.configuration.configuration import Configuration -from txmatching.data_transfer_objects.matchings.matching_swagger import \ - CountryCodeJson +from txmatching.data_transfer_objects.enums_swagger import CountryCodeJson from txmatching.solvers.solver_from_config import SUPPORTED_SOLVERS from txmatching.web.api.namespaces import matching_api diff --git a/txmatching/data_transfer_objects/enums_swagger.py b/txmatching/data_transfer_objects/enums_swagger.py new file mode 100644 index 000000000..e9269e07c --- /dev/null +++ b/txmatching/data_transfer_objects/enums_swagger.py @@ -0,0 +1,27 @@ +from txmatching.patients.patient import DonorType +from txmatching.utils.blood_groups import BloodGroup +from txmatching.utils.country_enum import Country +from txmatching.utils.enums import Sex +from txmatching.web.api.namespaces import enums_api + +CountryCodeJson = enums_api.schema_model('CountryCode', { + 'enum': [country.value for country in Country], + 'type': 'string' +}) + +BloodGroupEnumJson = enums_api.schema_model('BloodGroupEnum', { + 'enum': [blood_group.value for blood_group in BloodGroup], + 'type': 'string' +}) + +SexEnumJson = enums_api.schema_model('SexEnum', { + 'enum': [sex.value for sex in Sex], + 'type': 'string', + 'description': 'Sex of the patient.' +}) + +DonorTypeEnumJson = enums_api.schema_model('DonorType', { + 'enum': [donor_type.value for donor_type in DonorType], + 'type': 'string', + 'description': 'Type of the donor.' +}) diff --git a/txmatching/data_transfer_objects/matchings/matching_swagger.py b/txmatching/data_transfer_objects/matchings/matching_swagger.py index fb4a63c98..d4040c84e 100644 --- a/txmatching/data_transfer_objects/matchings/matching_swagger.py +++ b/txmatching/data_transfer_objects/matchings/matching_swagger.py @@ -2,7 +2,6 @@ from txmatching.data_transfer_objects.hla.hla_swagger import (HLAAntibody, HLAType) -from txmatching.utils.country_enum import Country from txmatching.utils.enums import (HLA_GROUPS_NAMES_WITH_OTHER, AntibodyMatchTypes, HLAGroup, MatchTypes) from txmatching.web.api.namespaces import matching_api @@ -84,11 +83,6 @@ cls_or_instance=fields.Nested(DetailedScoreForGroupJson)) }) -CountryCodeJson = matching_api.schema_model('CountryCode', { - 'enum': [country.value for country in Country], - 'type': 'string' -}) - CountryInRoundJson = matching_api.model('Country', { 'country_code': fields.String(required=True), 'donor_count': fields.Integer(required=True), diff --git a/txmatching/data_transfer_objects/patients/patient_swagger.py b/txmatching/data_transfer_objects/patients/patient_swagger.py index e5f6b2561..ff8751aac 100644 --- a/txmatching/data_transfer_objects/patients/patient_swagger.py +++ b/txmatching/data_transfer_objects/patients/patient_swagger.py @@ -1,22 +1,23 @@ from flask_restx import fields +from txmatching.data_transfer_objects.enums_swagger import (BloodGroupEnumJson, + CountryCodeJson, + DonorTypeEnumJson, + SexEnumJson) from txmatching.data_transfer_objects.hla.hla_swagger import ( EXAMPLE_HLA_TYPING, HLAAntibodies, HLATyping) from txmatching.data_transfer_objects.matchings.matching_swagger import ( - DESCRIPTION_DETAILED_SCORE, EXAMPLE_DETAILED_SCORE, CountryCodeJson, + DESCRIPTION_DETAILED_SCORE, EXAMPLE_DETAILED_SCORE, DetailedScoreForGroupJson) from txmatching.data_transfer_objects.txm_event.txm_event_swagger import ( DonorJsonIn, RecipientJsonIn) -from txmatching.patients.patient import DonorType -from txmatching.utils.blood_groups import BloodGroup -from txmatching.utils.enums import Sex from txmatching.web.api.namespaces import patient_api PatientParametersJson = patient_api.model('PatientParameters', { - 'blood_group': fields.String(required=False, enum=[blood_group.value for blood_group in BloodGroup]), + 'blood_group': fields.Nested(BloodGroupEnumJson, required=True), 'hla_typing': fields.Nested(required=False, model=HLATyping), - 'country_code': fields.Nested(CountryCodeJson, required=False), - 'sex': fields.String(required=False, enum=[sex.value for sex in Sex]), + 'country_code': fields.Nested(CountryCodeJson, required=True), + 'sex': fields.Nested(SexEnumJson, required=False), 'height': fields.Integer(required=False), 'weight': fields.Float(required=False), 'year_of_birth': fields.Integer(required=False), @@ -33,7 +34,7 @@ 'db_id': fields.Integer(required=True, description='Database id of the patient'), 'medical_id': fields.String(required=True, description='Medical id of the patient'), 'parameters': fields.Nested(required=True, model=PatientParametersJson), - 'donor_type': fields.String(required=True, enum=[donor_type.value for donor_type in DonorType]), + 'donor_type': fields.Nested(DonorTypeEnumJson, required=True), 'related_recipient_db_id': fields.Integer(required=False, description='Database id of the related recipient'), 'score_with_related_recipient': fields.Float(required=False, description='Score calculated with related recipient'), 'compatible_blood_with_related_recipient': fields.Boolean( @@ -49,8 +50,7 @@ RecipientJson = patient_api.model('Recipient', { 'db_id': fields.Integer(required=True, description='Database id of the patient'), - 'acceptable_blood_groups': fields.List(required=False, cls_or_instance=fields.String( - enum=[blood_group.value for blood_group in BloodGroup])), + 'acceptable_blood_groups': fields.List(required=False, cls_or_instance=fields.Nested(BloodGroupEnumJson)), 'medical_id': fields.String(required=True, description='Medical id of the patient'), 'parameters': fields.Nested(required=True, model=PatientParametersJson), 'hla_antibodies': fields.Nested(required=True, model=HLAAntibodies), @@ -83,16 +83,28 @@ 'hla_antibodies_list': fields.List(required=True, cls_or_instance=fields.Nested(HLAAntibodyToUpdateJson)), }) -RecipientToUpdateJson = patient_api.model('RecipientModelToUpdate', { +PatientToUpdateJson = patient_api.model('PatientModelToUpdate', { 'db_id': fields.Integer(required=True, description='Database id of the patient', example=1), - 'acceptable_blood_groups': fields.List(required=False, cls_or_instance=fields.String( - enum=[blood_group.value for blood_group in BloodGroup]), - description='Provide full list of all the acceptable blood groups of the ' - 'patient, not just the change set'), + 'blood_group': fields.Nested(BloodGroupEnumJson, required=False), 'hla_typing': fields.Nested(HLATypingToUpdateJson, required=False, description='Provide full list of all the HLA types of the patient, not just ' 'the change set', example=EXAMPLE_HLA_TYPING), + 'sex': fields.Nested(SexEnumJson, required=False), + 'height': fields.Integer(required=False, example=180), + 'weight': fields.Float(required=False, example=90), + 'year_of_birth': fields.Integer(required=False, example=1990), +}) + +DonorToUpdateJson = patient_api.inherit('DonorModelToUpdate', PatientToUpdateJson, { + 'active': fields.Boolean(required=False, description='Information, whether or not given donor shall be considered' + ' in exchange.') +}) + +RecipientToUpdateJson = patient_api.inherit('RecipientModelToUpdate', PatientToUpdateJson, { + 'acceptable_blood_groups': fields.List(required=False, cls_or_instance=fields.Nested(BloodGroupEnumJson), + description='Provide full list of all the acceptable blood groups of the ' + 'patient, not just the change set'), 'hla_antibodies': fields.Nested(HLAAntibodiesToUpdateJson, required=False, description='Provide full list of all the HLA antibodies of the patient, not just ' 'the change set', @@ -106,26 +118,23 @@ description='Provide the whole recipients requirements object, it will be' ' overwritten', example={'require_better_match_in_compatibility_index': True}), + 'waiting_since': fields.Date(required=False, + example='2015-01-17', + description='Date since when the patient has been on waiting list. ' + 'Use format YYYY-MM-DD.'), + 'previous_transplants': fields.Integer(required=False, + example=0, + description='Number of previous kidney transplants.'), 'cutoff': fields.Integer(required=False) }) -DonorToUpdateJson = patient_api.model('DonorModelToUpdate', { - 'db_id': fields.Integer(required=True, description='Database id of the patient', example=1), - 'hla_typing': fields.Nested(HLATypingToUpdateJson, required=False, - description='Provide full list of all the HLA types of the patient, not just ' - 'the change set', - example=EXAMPLE_HLA_TYPING), - 'active': fields.Boolean(required=False, description='Information, whether or not given donor shall be considered' - ' in exchange.') -}) - HLAAntibodyPairInJson = patient_api.model('HLAAntibodyPairIn', { 'name': fields.String(required=True, example='A32', description='HLA antibody name.'), 'mfi': fields.Integer(required=True, example=2350, description='Mean fluorescence intensity. Use exact value.'), }) DonorModelPairInJson = patient_api.model('DonorModelPairIn', { - 'country_code': fields.Nested(CountryCodeJson, required=False), + 'country_code': fields.Nested(CountryCodeJson, required=True), 'donor': fields.Nested(required=True, model=DonorJsonIn), 'recipient': fields.Nested(required=False, model=RecipientJsonIn) }) diff --git a/txmatching/data_transfer_objects/patients/update_dtos/patient_update_dto.py b/txmatching/data_transfer_objects/patients/update_dtos/patient_update_dto.py index a7581a348..c871163e3 100644 --- a/txmatching/data_transfer_objects/patients/update_dtos/patient_update_dto.py +++ b/txmatching/data_transfer_objects/patients/update_dtos/patient_update_dto.py @@ -6,14 +6,24 @@ from txmatching.data_transfer_objects.patients.update_dtos.hla_code_update_dtos import \ HLATypingUpdateDTO from txmatching.patients.hla_model import HLAType +from txmatching.patients.patient_parameters import Centimeters, Kilograms +from txmatching.utils.blood_groups import BloodGroup +from txmatching.utils.enums import Sex from txmatching.utils.hla_system.hla_transformations import \ preprocess_hla_code_in @dataclass class PatientUpdateDTO: + # pylint: disable=too-many-instance-attributes + # It is reasonable to have many attributes here db_id: int + blood_group: Optional[BloodGroup] = None hla_typing: Optional[HLATypingUpdateDTO] = None + sex: Optional[Sex] = None + height: Optional[Centimeters] = None + weight: Optional[Kilograms] = None + year_of_birth: Optional[int] = None def __post_init__(self): """ diff --git a/txmatching/data_transfer_objects/patients/update_dtos/recipient_update_dto.py b/txmatching/data_transfer_objects/patients/update_dtos/recipient_update_dto.py index 008b05d8b..a0ca9f456 100644 --- a/txmatching/data_transfer_objects/patients/update_dtos/recipient_update_dto.py +++ b/txmatching/data_transfer_objects/patients/update_dtos/recipient_update_dto.py @@ -17,6 +17,8 @@ class RecipientUpdateDTO(PatientUpdateDTO): hla_antibodies: Optional[HLAAntibodiesDTO] = None recipient_requirements: Optional[RecipientRequirements] = None cutoff: Optional[int] = None + waiting_since: Optional[str] = None + previous_transplants: Optional[int] = None def __post_init__(self): super().__post_init__() diff --git a/txmatching/data_transfer_objects/txm_event/txm_event_swagger.py b/txmatching/data_transfer_objects/txm_event/txm_event_swagger.py index 7dca25de3..329ef49ff 100644 --- a/txmatching/data_transfer_objects/txm_event/txm_event_swagger.py +++ b/txmatching/data_transfer_objects/txm_event/txm_event_swagger.py @@ -1,10 +1,9 @@ from flask_restx import fields -from txmatching.data_transfer_objects.matchings.matching_swagger import \ - CountryCodeJson -from txmatching.patients.patient import DonorType -from txmatching.utils.blood_groups import BloodGroup -from txmatching.utils.enums import Sex +from txmatching.data_transfer_objects.enums_swagger import (BloodGroupEnumJson, + CountryCodeJson, + DonorTypeEnumJson, + SexEnumJson) from txmatching.web.api.namespaces import txm_event_api ANTIGENS_EXAMPLE = ['A1', 'A32', 'B7', 'B51', 'DR11', 'DR15', 'A*02:03', 'A*11:01:35', 'DPA1*01:07', 'DRB4*01:01', @@ -36,14 +35,14 @@ DonorJsonIn = txm_event_api.model('DonorInput', { 'medical_id': fields.String(required=True, example='D1037', description=MEDICAL_ID_DESCRIPTION), - 'blood_group': fields.String(required=True, enum=[blood_group.value for blood_group in BloodGroup]), + 'blood_group': fields.Nested(BloodGroupEnumJson, required=True), 'hla_typing': fields.List(required=True, cls_or_instance=fields.String, example=ANTIGENS_EXAMPLE, description=HLA_TYPING_DESCRIPTION), - 'donor_type': fields.String(required=True, enum=[donor_type.value for donor_type in DonorType]), + 'donor_type': fields.Nested(DonorTypeEnumJson, required=True), 'related_recipient_medical_id': fields.String(required=False, example='R1037', description='Medical ID of the related recipient, empty for bridging ' 'and non-directed donors.'), - 'sex': fields.String(required=False, description='Sex of the patient.', enum=[sex.value for sex in Sex]), + 'sex': fields.Nested(SexEnumJson, required=False), 'height': fields.Integer(required=False, example=178, description='Height of the patient in centimeters.'), 'weight': fields.Float(required=False, example=78.4, description='Weight of the patient in kilograms.'), 'year_of_birth': fields.Integer(required=False, example=1945, description='Year of birth of the patient.'), @@ -56,17 +55,16 @@ cls_or_instance=fields.Nested( HLAAntibodyJsonIn )), - 'acceptable_blood_groups': fields.List(required=False, cls_or_instance=fields.String( - enum=[blood_group.value for blood_group in BloodGroup]), + 'acceptable_blood_groups': fields.List(required=False, cls_or_instance=fields.Nested(BloodGroupEnumJson), description='Acceptable blood groups for the patient. Leave empty to use \ compatible blood groups.'), 'medical_id': fields.String(required=True, example='R1037', description=MEDICAL_ID_DESCRIPTION), - 'blood_group': fields.String(required=True, enum=[blood_group.value for blood_group in BloodGroup]), + 'blood_group': fields.Nested(BloodGroupEnumJson, required=True), 'hla_typing': fields.List(required=True, cls_or_instance=fields.String, example=ANTIGENS_EXAMPLE, description=HLA_TYPING_DESCRIPTION), - 'sex': fields.String(required=False, description='Sex of the patient.', enum=[sex.value for sex in Sex]), + 'sex': fields.Nested(SexEnumJson, required=False), 'height': fields.Integer(required=False, example=178, description='Height of the patient in centimeters.'), 'weight': fields.Float(required=False, example=78.4, description='Weight of the patient in kilograms.'), 'year_of_birth': fields.Integer(required=False, example=1945, description='Year of birth of the patient.'), diff --git a/txmatching/database/services/patient_service.py b/txmatching/database/services/patient_service.py index f93ffbcfc..d3f16e4ff 100644 --- a/txmatching/database/services/patient_service.py +++ b/txmatching/database/services/patient_service.py @@ -14,7 +14,8 @@ from txmatching.data_transfer_objects.patients.update_dtos.recipient_update_dto import \ RecipientUpdateDTO from txmatching.database.db import db -from txmatching.database.services.parsing_utils import get_hla_code +from txmatching.database.services.parsing_utils import (get_hla_code, + parse_date_to_datetime) from txmatching.database.sql_alchemy_schema import ( DonorModel, RecipientAcceptableBloodModel, RecipientHLAAntibodyModel, RecipientModel) @@ -66,6 +67,8 @@ def get_recipient_from_recipient_model(recipient_model: RecipientModel, for hla_antibody in recipient_model.hla_antibodies], logger_with_patient ), + # TODO: https://github.com/mild-blue/txmatching/issues/477 represent blood as enum, + # this conversion is not working properly now acceptable_blood_groups=[acceptable_blood_model.blood_type for acceptable_blood_model in recipient_model.acceptable_blood], recipient_cutoff=recipient_model.recipient_cutoff, @@ -76,14 +79,30 @@ def get_recipient_from_recipient_model(recipient_model: RecipientModel, return recipient +def _create_patient_update_dict_base(patient_update_dto: PatientUpdateDTO) -> dict: + patient_update_dict = {} + + if patient_update_dto.blood_group is not None: + patient_update_dict['blood'] = patient_update_dto.blood_group + if patient_update_dto.hla_typing is not None: + patient_update_dict['hla_typing'] = dataclasses.asdict(patient_update_dto.hla_typing_preprocessed) + + # For the following parameters we support setting null value + patient_update_dict['sex'] = patient_update_dto.sex + patient_update_dict['height'] = patient_update_dto.height + patient_update_dict['weight'] = patient_update_dto.weight + patient_update_dict['yob'] = patient_update_dto.year_of_birth + + return patient_update_dict + + def update_recipient(recipient_update_dto: RecipientUpdateDTO, txm_event_db_id: int) -> Recipient: - # TODO do not delete https://trello.com/c/zseK1Zcf recipient_update_dto = _update_patient_preprocessed_typing(recipient_update_dto) old_recipient_model = RecipientModel.query.get(recipient_update_dto.db_id) if txm_event_db_id != old_recipient_model.txm_event_id: raise InvalidArgumentException('Trying to update patient the user has no access to.') - recipient_update_dict = {} + recipient_update_dict = _create_patient_update_dict_base(recipient_update_dto) if recipient_update_dto.acceptable_blood_groups: acceptable_blood_models = [ RecipientAcceptableBloodModel(blood_type=blood, recipient_id=recipient_update_dto.db_id) for blood @@ -107,7 +126,6 @@ def update_recipient(recipient_update_dto: RecipientUpdateDTO, txm_event_db_id: else: new_antibody_list = old_recipient.hla_antibodies.hla_antibodies_list - recipient_update_dict['recipient_cutoff'] = new_cutoff hla_antibodies = [ RecipientHLAAntibodyModel( recipient_id=recipient_update_dto.db_id, @@ -120,11 +138,11 @@ def update_recipient(recipient_update_dto: RecipientUpdateDTO, txm_event_db_id: RecipientHLAAntibodyModel.query.filter( RecipientHLAAntibodyModel.recipient_id == recipient_update_dto.db_id).delete() db.session.add_all(hla_antibodies) - if recipient_update_dto.hla_typing: - recipient_update_dict['hla_typing'] = dataclasses.asdict(recipient_update_dto.hla_typing_preprocessed) if recipient_update_dto.recipient_requirements: recipient_update_dict['recipient_requirements'] = dataclasses.asdict( recipient_update_dto.recipient_requirements) + recipient_update_dict['waiting_since'] = parse_date_to_datetime(recipient_update_dto.waiting_since) + recipient_update_dict['previous_transplants'] = recipient_update_dto.previous_transplants RecipientModel.query.filter(RecipientModel.id == recipient_update_dto.db_id).update(recipient_update_dict) db.session.commit() @@ -132,15 +150,12 @@ def update_recipient(recipient_update_dto: RecipientUpdateDTO, txm_event_db_id: def update_donor(donor_update_dto: DonorUpdateDTO, txm_event_db_id: int) -> Donor: - # TODO do not delete https://trello.com/c/zseK1Zcf donor_update_dto = _update_patient_preprocessed_typing(donor_update_dto) old_donor_model = DonorModel.query.get(donor_update_dto.db_id) if txm_event_db_id != old_donor_model.txm_event_id: raise InvalidArgumentException('Trying to update patient the user has no access to') - donor_update_dict = {} - if donor_update_dto.hla_typing: - donor_update_dict['hla_typing'] = dataclasses.asdict(donor_update_dto.hla_typing_preprocessed) + donor_update_dict = _create_patient_update_dict_base(donor_update_dto) if donor_update_dto.active is not None: donor_update_dict['active'] = donor_update_dto.active DonorModel.query.filter(DonorModel.id == donor_update_dto.db_id).update(donor_update_dict) @@ -165,6 +180,8 @@ def _get_base_patient_from_patient_model(patient_model: Union[DonorModel, Recipi db_id=patient_model.id, medical_id=patient_model.medical_id, parameters=PatientParameters( + # TODO: https://github.com/mild-blue/txmatching/issues/477 represent blood by enum, + # this conversion is not working properly now blood_group=patient_model.blood, country_code=patient_model.country, hla_typing=dacite.from_dict(data_class=HLATyping, data=patient_model.hla_typing), diff --git a/txmatching/database/services/patient_upload_service.py b/txmatching/database/services/patient_upload_service.py index 80f0d9601..e5f9e5ec0 100644 --- a/txmatching/database/services/patient_upload_service.py +++ b/txmatching/database/services/patient_upload_service.py @@ -106,6 +106,7 @@ def _recipient_upload_dto_to_recipient_model( height=recipient.height, sex=recipient.sex, yob=recipient.year_of_birth, + previous_transplants=recipient.previous_transplants, ) return recipient_model diff --git a/txmatching/web/__init__.py b/txmatching/web/__init__.py index 830effedd..734c4c2f5 100644 --- a/txmatching/web/__init__.py +++ b/txmatching/web/__init__.py @@ -22,7 +22,8 @@ PATIENT_NAMESPACE, REPORTS_NAMESPACE, SERVICE_NAMESPACE, - TXM_EVENT_NAMESPACE, USER_NAMESPACE) + TXM_EVENT_NAMESPACE, USER_NAMESPACE, + enums_api) from txmatching.web.api.patient_api import patient_api from txmatching.web.api.report_api import report_api from txmatching.web.api.service_api import service_api @@ -222,3 +223,4 @@ def add_all_namespaces(api: Api): api.add_namespace(report_api, path=f'{API_VERSION}/{TXM_EVENT_NAMESPACE}//{REPORTS_NAMESPACE}') api.add_namespace(txm_event_api, path=f'{API_VERSION}/{TXM_EVENT_NAMESPACE}') + api.add_namespace(enums_api) diff --git a/txmatching/web/api/namespaces.py b/txmatching/web/api/namespaces.py index 70d6733b4..ced928f3a 100644 --- a/txmatching/web/api/namespaces.py +++ b/txmatching/web/api/namespaces.py @@ -21,4 +21,7 @@ REPORTS_NAMESPACE = 'reports' report_api = Namespace(REPORTS_NAMESPACE) +ENUMS_NAMESPACE = 'enums' +enums_api = Namespace(ENUMS_NAMESPACE) + # Note: namespace prefix urls are defined in txmatching.web.add_all_namespaces diff --git a/txmatching/web/api/patient_api.py b/txmatching/web/api/patient_api.py index 3da405223..a24a939a5 100644 --- a/txmatching/web/api/patient_api.py +++ b/txmatching/web/api/patient_api.py @@ -148,7 +148,7 @@ class AlterRecipient(Resource): @require_user_edit_access() @require_valid_txm_event_id() def put(self, txm_event_id: int): - recipient_update_dto = from_dict(data_class=RecipientUpdateDTO, data=request.json) + recipient_update_dto = from_dict(data_class=RecipientUpdateDTO, data=request.json, config=Config(cast=[Enum])) guard_user_country_access_to_recipient(user_id=get_current_user_id(), recipient_id=recipient_update_dto.db_id) return jsonify(update_recipient(recipient_update_dto, txm_event_id)) @@ -166,7 +166,7 @@ class AlterDonor(Resource): @require_user_edit_access() @require_valid_txm_event_id() def put(self, txm_event_id: int): - donor_update_dto = from_dict(data_class=DonorUpdateDTO, data=request.json) + donor_update_dto = from_dict(data_class=DonorUpdateDTO, data=request.json, config=Config(cast=[Enum])) guard_user_country_access_to_donor(user_id=get_current_user_id(), donor_id=donor_update_dto.db_id) txm_event = get_txm_event_complete(txm_event_id) all_recipients = txm_event.all_recipients diff --git a/txmatching/web/api/user_api.py b/txmatching/web/api/user_api.py index b865b6d2f..bdc8809df 100644 --- a/txmatching/web/api/user_api.py +++ b/txmatching/web/api/user_api.py @@ -13,8 +13,7 @@ refresh_token, resend_otp) from txmatching.auth.user.topt_auth_check import allow_otp_request from txmatching.auth.user.user_auth_check import require_user_login -from txmatching.data_transfer_objects.matchings.matching_swagger import \ - CountryCodeJson +from txmatching.data_transfer_objects.enums_swagger import CountryCodeJson from txmatching.data_transfer_objects.txm_event.txm_event_swagger import \ FailJson from txmatching.utils.country_enum import Country diff --git a/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.html b/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.html index 0aff13608..edb14747d 100644 --- a/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.html +++ b/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.html @@ -6,7 +6,9 @@

Donor

- +
@@ -14,7 +16,9 @@

Donor

Recipient

- +
diff --git a/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.ts b/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.ts index 7e1184ca9..8eddf9aa3 100644 --- a/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.ts +++ b/txmatching/web/frontend/src/app/components/add-new-patient/add-new-patient.component.ts @@ -5,8 +5,8 @@ import { RecipientEditable } from '@app/model/RecipientEditable'; import { PatientService } from '@app/services/patient/patient.service'; import { TxmEvent } from '@app/model/Event'; import { PatientEditable } from '@app/model/PatientEditable'; -import { Country } from '@app/model/enums/Country'; import { PatientPairToAdd } from '@app/services/patient/patient.service.interface'; +import { CountryCodeGenerated } from '@app/generated'; @Component({ selector: 'app-add-new-patient', @@ -62,7 +62,7 @@ export class AddNewPatientComponent { private _changeOtherPatientCountry(changes: KeyValueChanges, otherPatient: PatientEditable) { changes.forEachChangedItem((record) => { if (record.key === 'country') { - otherPatient.country = record.currentValue as Country; + otherPatient.country = record.currentValue as CountryCodeGenerated; } }); } diff --git a/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.html b/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.html index 3e6c9c54c..246c6c878 100644 --- a/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.html +++ b/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.html @@ -1,19 +1,28 @@ - - - - - - - - - - - {{ type | donorTypeLabel | titlecase }} - - - + + + + + + + + + + + + {{ type | donorTypeLabel | titlecase }} + + + + + + +
+ + Active + +
@@ -25,12 +34,12 @@ - + - + - +
diff --git a/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.scss b/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.scss index e69de29bb..3431e25f8 100644 --- a/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.scss +++ b/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.scss @@ -0,0 +1,6 @@ +@import "../../../styles/variables"; + +.active-checkbox { + margin: $item-gap 0 $padding-y; + border-bottom: 1px solid $gray-500; +} diff --git a/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.ts b/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.ts index a39ab5832..4623e5213 100644 --- a/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.ts +++ b/txmatching/web/frontend/src/app/components/donor-settings/donor-settings.component.ts @@ -10,6 +10,8 @@ import { DonorType } from '@app/model'; export class DonorSettingsComponent implements OnInit { @Input() donor?: DonorEditable; + @Input() showCountryMedicalIdAndDonorType: boolean = false; + @Input() showActiveCheckbox: boolean = false; public allDonorTypes: DonorType[] = Object.values(DonorType); constructor() { diff --git a/txmatching/web/frontend/src/app/components/patient-donor-detail/patient-donor-detail.component.html b/txmatching/web/frontend/src/app/components/patient-donor-detail/patient-donor-detail.component.html index d0ecfdc71..c553b12de 100644 --- a/txmatching/web/frontend/src/app/components/patient-donor-detail/patient-donor-detail.component.html +++ b/txmatching/web/frontend/src/app/components/patient-donor-detail/patient-donor-detail.component.html @@ -12,52 +12,11 @@
- -
- - Active - -
-

- Antigens - -

- - Donor's antigens - - - {{code.code}} - cancel - - - - - - {{code.code}} - - - - Add new antigens by typing codes followed by - Space space_bar - or - Enter keyboard_return - or select from available antigens. - - + +
; - public separatorKeysCodes: number[] = separatorKeysCodes; + public donorEditable?: DonorEditable; public loading: boolean = false; public success: boolean = false; @@ -36,79 +29,13 @@ export class PatientDonorDetailComponent extends ListItemDetailAbstractComponent public deleteIcon = faTrash; constructor(private _patientService: PatientService, - private _logger: LoggerService) { + private _logger: LoggerService, + private _alertService: AlertService) { super(_patientService); - - this.filtered = this.inputControl.valueChanges.pipe( - startWith(''), - map((value: string | Antigen) => { - return !value || typeof value === 'string' ? value : value.code; - }), - map(code => code ? hlaFullTextSearch(this.available, code) : this.available.slice()) - ); } ngOnInit(): void { - this._initAvailableCodes(); - } - - get selected(): Antigen[] { - return this.item ? this.item.parameters.hla_typing.hla_types_list : []; - } - - get available(): Antigen[] { - const selectedAntigensCodes = [...new Set(this.selected.map(antigen => antigen.code))]; - return this.allAntigens.filter(a => !selectedAntigensCodes.includes(a.code)); - } - - public setActiveValue(value: boolean): void { - if(this.item) this.item.active = value; - } - - public addNewAntigen(code: string, control: HTMLInputElement): void { - if (!this.item) { - this._logger.error('addNewAntigen failed because item not set'); - return; - } - if (!code.length) { - return; - } - - const formattedCode = code.trim().toUpperCase(); - this.item.parameters.hla_typing.hla_types_list.push({ code: formattedCode, raw_code: formattedCode }); - - // reset input - this.inputControl.reset(); - control.value = ''; - } - - public addAntigen(a: Antigen, control: HTMLInputElement): void { - if (!this.item) { - this._logger.error('addAntigen failed because item not set'); - return; - } - if (!a) { - return; - } - - this.item.parameters.hla_typing.hla_types_list.push(a); - - // reset input - this.inputControl.reset(); - control.value = ''; - } - - public remove(code: Antigen): void { - if (!this.item) { - this._logger.error('remove failed because item not set'); - return; - } - - const index = this.item.parameters.hla_typing.hla_types_list.indexOf(code); - - if (index >= 0) { - this.item.parameters.hla_typing.hla_types_list.splice(index, 1); - } + this._initDonorEditable(); } public handleSave(): void { @@ -116,6 +43,10 @@ export class PatientDonorDetailComponent extends ListItemDetailAbstractComponent this._logger.error('handleSave failed because item not set'); return; } + if (!this.donorEditable) { + this._logger.error('handleSave failed because donorEditable not set'); + return; + } if (!this.defaultTxmEvent) { this._logger.error('handleSave failed because defaultTxmEvent not set'); return; @@ -123,20 +54,37 @@ export class PatientDonorDetailComponent extends ListItemDetailAbstractComponent this.loading = true; this.success = false; - this._patientService.saveDonor(this.defaultTxmEvent.id, this.item) - .then(() => { - this.loading = false; + this._patientService.saveDonor(this.defaultTxmEvent.id, this.item.db_id, this.donorEditable) + .then((updatedDonor) => { + this._logger.log('Updated donor received from BE', [updatedDonor]); + Object.assign(this.item, updatedDonor); + this._initDonorEditable(); this.success = true; }) - .catch(() => this.loading = false); + .catch((e) => { + this._alertService.error(`Error saving donor: "${e.message || e}"`); + this._logger.error(`Error saving donor: "${e.message || e}"`); + }) + .finally(() => this.loading = false); + } + + private _initDonorEditable() { + if (!this.item) { + this._logger.error('_initDonorEditable failed because item not set'); + return; + } + + this.donorEditable = new DonorEditable(); + this.donorEditable.initializeFromPatient(this.item); + this._logger.log('DonorEditable initialized', [this.donorEditable]); } public handleDeleteDonor(): void { - if(!this.item) { + if (!this.item) { this._logger.error('handleDeleteDonor failed because item not set'); return; } - if(!this.defaultTxmEvent) { + if (!this.defaultTxmEvent) { this._logger.error('handleDeleteDonor failed because defaultTxmEvent not set'); return; } @@ -144,7 +92,7 @@ export class PatientDonorDetailComponent extends ListItemDetailAbstractComponent const message = this.item.related_recipient_db_id != undefined ? 'Are you sure you want to remove donor and its recipient?' : 'Are you sure you want to remove donor?'; - if(!confirm(message)) { + if (!confirm(message)) { return; } @@ -161,20 +109,4 @@ export class PatientDonorDetailComponent extends ListItemDetailAbstractComponent this.deleteLoading = false; }); } - - private _initAvailableCodes(): void { - if (!this.patients?.donors) { - this._logger.error('initAvailableCodes failed because donors not set'); - return; - } - - const allAntigens = []; - for (const d of this.patients.donors) { - allAntigens.push(...d.parameters.hla_typing.hla_types_list); - } - - this.allAntigens = [...new Set(allAntigens.map(a => a.code))].map(code => { - return { code, raw_code: code ?? '' }; - }); // only unique - } } diff --git a/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.html b/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.html index 0628b11fe..1c2cc98c4 100644 --- a/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.html +++ b/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.html @@ -11,76 +11,18 @@
- + - -

Matching conditions

-
- - Require compatible blood group - - - Require new donor having better CI match than original donor - - - Require new donor having better CI match than original donor or blood group match - -
+ + - -

- Antigens - -

- - Recipient's antigens - - - {{a.code}} - cancel - - - - - - {{a.code}} - - - - Add new antigens by typing codes followed by - Space space_bar - or - Enter keyboard_return - or select from available antigens. - - -
- - - - -
- Save changes -
+ diff --git a/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.scss b/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.scss index 916f7f127..afcc43c1e 100644 --- a/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.scss +++ b/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.scss @@ -23,34 +23,4 @@ font-size: 32px; } } - - .checkboxes { - margin: $item-gap 0 $padding-y; - } - - .placeholder, ::ng-deep .placeholder { - font-style: italic; - width: 100%; - text-align: center; - color: $gray-500; - } - - .save-button { - width: 100%; - display: flex; - justify-content: center; - align-items: center; - } - - .key { - background: $gray-200; - font-family: monospace; - border-radius: 3px; - padding: 2px 5px; - display: inline-block; - - mat-icon { - vertical-align: -2px; - } - } } diff --git a/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.ts b/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.ts index 3102173a7..9cb7c9830 100644 --- a/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.ts +++ b/txmatching/web/frontend/src/app/components/patient-recipient-detail/patient-recipient-detail.component.ts @@ -1,14 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { ListItemDetailAbstractComponent } from '@app/components/list-item/list-item.interface'; -import { FormControl, FormGroup } from '@angular/forms'; -import { Observable } from 'rxjs'; import { PatientService } from '@app/services/patient/patient.service'; -import { map, startWith } from 'rxjs/operators'; -import { hlaFullTextSearch, separatorKeysCodes } from '@app/directives/validators/form.directive'; import { Recipient } from '@app/model/Recipient'; -import { Antibody, Antigen } from '@app/model/Hla'; import { PatientList } from '@app/model/PatientList'; import { TxmEvent } from '@app/model/Event'; +import { RecipientEditable } from '@app/model/RecipientEditable'; +import { LoggerService } from '@app/services/logger/logger.service'; +import { AlertService } from '@app/services/alert/alert.service'; @Component({ selector: 'app-patient-detail-recipient', @@ -21,96 +19,26 @@ export class PatientRecipientDetailComponent extends ListItemDetailAbstractCompo @Input() item?: Recipient; @Input() defaultTxmEvent?: TxmEvent; - public success: boolean = false; - - public form: FormGroup = new FormGroup({ - antigens: new FormControl('') - }); - - public allAntigens: Antigen[] = []; - public filteredAntigensCodes: Observable; + public recipientEditable?: RecipientEditable; public loading: boolean = false; + public success: boolean = false; - public separatorKeysCodes: number[] = separatorKeysCodes; - - constructor(private _patientService: PatientService) { + constructor(private _patientService: PatientService, + private _logger: LoggerService, + private _alertService: AlertService) { super(_patientService); - - this.filteredAntigensCodes = this.form.controls.antigens.valueChanges.pipe( - startWith(''), - map((value: string | Antigen) => { - return !value || typeof value === 'string' ? value : value.code; - }), - map(code => code ? hlaFullTextSearch(this.availableAntigens, code) : this.availableAntigens.slice()) - ); } ngOnInit(): void { - this._initAntigensCodes(); - } - - get selectedAntigens(): Antigen[] { - return this.item ? this.item.parameters.hla_typing.hla_types_list : []; - } - - get availableAntigens(): Antigen[] { - const selectedAntigensCodes = [...new Set(this.selectedAntigens.map(antigen => antigen.code))]; - return this.allAntigens.filter(a => !selectedAntigensCodes.includes(a.code)); - } - - public addAntigen(a: Antigen, control: HTMLInputElement): void { - if (!this.item || !a) { - return; - } - - this.item.parameters.hla_typing.hla_types_list.push(a); - - // reset input - this.form.controls.antigens.reset(); - control.value = ''; - } - - public addNewAntigen(code: string, control: HTMLInputElement): void { - if (!this.item || !code) { - return; - } - - const formattedCode = code.trim().toUpperCase(); - this.item.parameters.hla_typing.hla_types_list.push({ code: formattedCode, raw_code: formattedCode }); - - // reset input - this.form.controls.antigens.reset(); - control.value = ''; - } - - public removeAntigen(antigen: Antigen): void { - if (!this.item) { - return; - } - - const index = this.item.parameters.hla_typing.hla_types_list.indexOf(antigen); - - if (index >= 0) { - this.item.parameters.hla_typing.hla_types_list.splice(index, 1); - } + this._initRecipientEditable(); } - public setCheckBoxValue(key: string, value: boolean): void { - if (this.item?.recipient_requirements && this.item.recipient_requirements[key] !== undefined) { - this.item.recipient_requirements[key] = value; - } - } - - public handleNewAntibody(antibody: Antibody): void { + public handleSave(): void { if (!this.item) { return; } - this.handleSave([antibody]); - } - - public handleSave(newAntibodies?: Antibody[]): void { - if (!this.item) { + if (!this.recipientEditable) { return; } if (!this.defaultTxmEvent) { @@ -119,28 +47,28 @@ export class PatientRecipientDetailComponent extends ListItemDetailAbstractCompo this.loading = true; this.success = false; - const oldAntibodies = this.item.hla_antibodies.hla_antibodies_list; - const antibodies = newAntibodies ? [...oldAntibodies, ...newAntibodies] : oldAntibodies; - this._patientService.saveRecipient(this.defaultTxmEvent.id, this.item, antibodies) - .then((recipient) => { + this._patientService.saveRecipient(this.defaultTxmEvent.id, this.item.db_id, this.recipientEditable) + .then((updatedRecipient) => { + this._logger.log('Updated recipient received from BE', [updatedRecipient]); + Object.assign(this.item, updatedRecipient); + this._initRecipientEditable(); this.success = true; - this.item = recipient; + }) + .catch((e) => { + this._alertService.error(`Error saving recipient: "${e.message || e}"`); + this._logger.error(`Error saving recipient: "${e.message || e}"`); }) .finally(() => this.loading = false); } - private _initAntigensCodes(): void { - if (!this.patients?.recipients) { + private _initRecipientEditable() { + if (!this.item) { + this._logger.error('_initDonorEditable failed because item not set'); return; } - const allAntigens = []; - for (const r of this.patients.recipients) { - allAntigens.push(...r.parameters.hla_typing.hla_types_list); - } - - this.allAntigens = [...new Set(allAntigens.map(a => a.code))].map(code => { - return { code, raw_code: code ?? '' }; - }); // only unique + this.recipientEditable = new RecipientEditable(); + this.recipientEditable.initializeFromPatient(this.item); + this._logger.log('RecipientEditable initialized', [this.recipientEditable]); } } diff --git a/txmatching/web/frontend/src/app/components/patient-settings/antigens/antigens.component.html b/txmatching/web/frontend/src/app/components/patient-settings/antigens/antigens.component.html index ce4ff277f..253172002 100644 --- a/txmatching/web/frontend/src/app/components/patient-settings/antigens/antigens.component.html +++ b/txmatching/web/frontend/src/app/components/patient-settings/antigens/antigens.component.html @@ -1,11 +1,10 @@ - - {{code}} + {{antigen.raw_code}} cancel ; public separatorKeysCodes: number[] = separatorKeysCodes; public countryControl: FormControl = new FormControl(''); @@ -59,7 +59,7 @@ export class CountrySettingComponent extends AbstractFormHandlerComponent implem } public handleCountrySelect(control: HTMLInputElement): void { - const country: Country = this.selectedCountryValue as Country; + const country: CountryCodeGenerated = this.selectedCountryValue as CountryCodeGenerated; // Set country to patient this.patient?.setCountry(country); diff --git a/txmatching/web/frontend/src/app/components/patient-settings/sex/sex.component.html b/txmatching/web/frontend/src/app/components/patient-settings/sex/sex.component.html index 32b938229..23f11622e 100644 --- a/txmatching/web/frontend/src/app/components/patient-settings/sex/sex.component.html +++ b/txmatching/web/frontend/src/app/components/patient-settings/sex/sex.component.html @@ -1,8 +1,14 @@ - + {{ sex }} + + Not specified + diff --git a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-antibodies/antibodies.component.ts b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-antibodies/antibodies.component.ts index 63a35d082..fce33ae14 100644 --- a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-antibodies/antibodies.component.ts +++ b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-antibodies/antibodies.component.ts @@ -35,7 +35,7 @@ export class RecipientAntibodiesComponent { const formattedCode = code.trim().toUpperCase(); this.recipient.antibodies.push({ - name: formattedCode, + raw_code: formattedCode, mfi }); diff --git a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.html b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.html index 39a8c6fc9..bddcfd73d 100644 --- a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.html +++ b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.html @@ -1,9 +1,11 @@ - - + + + - - + + + @@ -31,7 +33,7 @@
- {{a.name}} + {{a.raw_code}}
MFI: {{a.mfi}}
cancel @@ -53,13 +55,13 @@ - + - + - + @@ -71,6 +73,21 @@ - + + + +

Matching conditions

+
+ + Require compatible blood group + + + Require new donor having better CI match than original donor + + + Require new donor having better CI match than original donor or blood group match + +
+
diff --git a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.scss b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.scss index 650cf098f..0e45fe913 100644 --- a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.scss +++ b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.scss @@ -15,3 +15,7 @@ font-size: 12px; } } + +.checkboxes { + margin: $item-gap 0 $padding-y; +} diff --git a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.ts b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.ts index 7641899a9..6924fab8c 100644 --- a/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.ts +++ b/txmatching/web/frontend/src/app/components/recipient-settings/recipient-settings.component.ts @@ -10,6 +10,8 @@ import { BloodGroup } from '@app/model'; export class RecipientSettingsComponent implements OnInit { @Input() recipient?: RecipientEditable; + @Input() showCountryAndMedicalId: boolean = false; + @Input() showMatchingConditions: boolean = false; public allBloodGroups: BloodGroup[] = Object.values(BloodGroup); constructor() { diff --git a/txmatching/web/frontend/src/app/generated/model/bloodGroupEnumGenerated.ts b/txmatching/web/frontend/src/app/generated/model/bloodGroupEnumGenerated.ts new file mode 100644 index 000000000..d0a41260d --- /dev/null +++ b/txmatching/web/frontend/src/app/generated/model/bloodGroupEnumGenerated.ts @@ -0,0 +1,20 @@ +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export enum BloodGroupEnumGenerated { + A = 'A', + B = 'B', + Ab = 'AB', + _0 = '0' +}; + diff --git a/txmatching/web/frontend/src/app/generated/model/donorGenerated.ts b/txmatching/web/frontend/src/app/generated/model/donorGenerated.ts index 08cf1e0cf..3b2e2dcf9 100644 --- a/txmatching/web/frontend/src/app/generated/model/donorGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/donorGenerated.ts @@ -10,6 +10,7 @@ * Do not edit the class manually. */ import { PatientParametersGenerated } from './patientParametersGenerated'; +import { DonorTypeGenerated } from './donorTypeGenerated'; import { DetailedScoreForGroupGenerated } from './detailedScoreForGroupGenerated'; @@ -30,7 +31,7 @@ export interface DonorGenerated { * Contains details for compatibility index for each HLA Group compatibility index is calculated for. */ detailed_score_with_related_recipient?: Array; - donor_type: DonorGeneratedDonorTypeEnum; + donor_type: DonorTypeGenerated; /** * Medical id of the patient */ @@ -45,11 +46,4 @@ export interface DonorGenerated { */ score_with_related_recipient?: number; } -export enum DonorGeneratedDonorTypeEnum { - Donor = 'DONOR', - BridgingDonor = 'BRIDGING_DONOR', - NonDirected = 'NON_DIRECTED' -}; - - diff --git a/txmatching/web/frontend/src/app/generated/model/donorInputGenerated.ts b/txmatching/web/frontend/src/app/generated/model/donorInputGenerated.ts index c26486eb2..e2575c9aa 100644 --- a/txmatching/web/frontend/src/app/generated/model/donorInputGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/donorInputGenerated.ts @@ -9,11 +9,14 @@ * https://openapi-generator.tech * Do not edit the class manually. */ +import { SexEnumGenerated } from './sexEnumGenerated'; +import { DonorTypeGenerated } from './donorTypeGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; export interface DonorInputGenerated { - blood_group: DonorInputGeneratedBloodGroupEnum; - donor_type: DonorInputGeneratedDonorTypeEnum; + blood_group: BloodGroupEnumGenerated; + donor_type: DonorTypeGenerated; /** * Height of the patient in centimeters. */ @@ -30,10 +33,7 @@ export interface DonorInputGenerated { * Medical ID of the related recipient, empty for bridging and non-directed donors. */ related_recipient_medical_id?: string; - /** - * Sex of the patient. - */ - sex?: DonorInputGeneratedSexEnum; + sex?: SexEnumGenerated; /** * Weight of the patient in kilograms. */ @@ -43,21 +43,4 @@ export interface DonorInputGenerated { */ year_of_birth?: number; } -export enum DonorInputGeneratedBloodGroupEnum { - A = 'A', - B = 'B', - Ab = 'AB', - _0 = '0' -}; -export enum DonorInputGeneratedDonorTypeEnum { - Donor = 'DONOR', - BridgingDonor = 'BRIDGING_DONOR', - NonDirected = 'NON_DIRECTED' -}; -export enum DonorInputGeneratedSexEnum { - M = 'M', - F = 'F' -}; - - diff --git a/txmatching/web/frontend/src/app/generated/model/donorModelPairInGenerated.ts b/txmatching/web/frontend/src/app/generated/model/donorModelPairInGenerated.ts index e0bb86bcd..f60869703 100644 --- a/txmatching/web/frontend/src/app/generated/model/donorModelPairInGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/donorModelPairInGenerated.ts @@ -3,7 +3,7 @@ * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) * * The version of the OpenAPI document: 1.0 - * + * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). * https://openapi-generator.tech @@ -14,8 +14,8 @@ import { CountryCodeGenerated } from './countryCodeGenerated'; import { DonorInputGenerated } from './donorInputGenerated'; -export interface DonorModelPairInGenerated { - country_code?: CountryCodeGenerated; +export interface DonorModelPairInGenerated { + country_code: CountryCodeGenerated; donor: DonorInputGenerated; recipient?: RecipientInputGenerated; } diff --git a/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateAllOfGenerated.ts b/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateAllOfGenerated.ts new file mode 100644 index 000000000..85f0a1511 --- /dev/null +++ b/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateAllOfGenerated.ts @@ -0,0 +1,20 @@ +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface DonorModelToUpdateAllOfGenerated { + /** + * Information, whether or not given donor shall be considered in exchange. + */ + active?: boolean; +} + diff --git a/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateGenerated.ts b/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateGenerated.ts index 4bd38c09c..3c7de7fd3 100644 --- a/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/donorModelToUpdateGenerated.ts @@ -9,21 +9,30 @@ * https://openapi-generator.tech * Do not edit the class manually. */ +import { DonorModelToUpdateAllOfGenerated } from './donorModelToUpdateAllOfGenerated'; +import { SexEnumGenerated } from './sexEnumGenerated'; import { HlaTypingToUpdateGenerated } from './hlaTypingToUpdateGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; +import { PatientModelToUpdateGenerated } from './patientModelToUpdateGenerated'; export interface DonorModelToUpdateGenerated { - /** - * Information, whether or not given donor shall be considered in exchange. - */ - active?: boolean; + blood_group?: BloodGroupEnumGenerated; /** * Database id of the patient */ db_id: number; + height?: number; /** * Provide full list of all the HLA types of the patient, not just the change set */ hla_typing?: HlaTypingToUpdateGenerated; + sex?: SexEnumGenerated; + weight?: number; + year_of_birth?: number; + /** + * Information, whether or not given donor shall be considered in exchange. + */ + active?: boolean; } diff --git a/txmatching/web/frontend/src/app/generated/model/donorTypeGenerated.ts b/txmatching/web/frontend/src/app/generated/model/donorTypeGenerated.ts new file mode 100644 index 000000000..cfade89c6 --- /dev/null +++ b/txmatching/web/frontend/src/app/generated/model/donorTypeGenerated.ts @@ -0,0 +1,22 @@ +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Type of the donor. + */ +export enum DonorTypeGenerated { + Donor = 'DONOR', + BridgingDonor = 'BRIDGING_DONOR', + NonDirected = 'NON_DIRECTED' +}; + diff --git a/txmatching/web/frontend/src/app/generated/model/models.ts b/txmatching/web/frontend/src/app/generated/model/models.ts index 10f602a65..b97b59e96 100644 --- a/txmatching/web/frontend/src/app/generated/model/models.ts +++ b/txmatching/web/frontend/src/app/generated/model/models.ts @@ -1,6 +1,7 @@ export * from './antibodiesPerGroupGenerated'; export * from './antibodyMatchGenerated'; export * from './antigenMatchGenerated'; +export * from './bloodGroupEnumGenerated'; export * from './calculatedMatchingsGenerated'; export * from './configurationGenerated'; export * from './countryCodeGenerated'; @@ -10,7 +11,9 @@ export * from './detailedScoreForGroupGenerated'; export * from './donorGenerated'; export * from './donorInputGenerated'; export * from './donorModelPairInGenerated'; +export * from './donorModelToUpdateAllOfGenerated'; export * from './donorModelToUpdateGenerated'; +export * from './donorTypeGenerated'; export * from './failResponseGenerated'; export * from './forbiddenCountryCombinationGenerated'; export * from './hLAAntibodyInGenerated'; @@ -29,15 +32,18 @@ export * from './matchingGenerated'; export * from './newTxmEventGenerated'; export * from './otpLoginGenerated'; export * from './passwordChangeGenerated'; +export * from './patientModelToUpdateGenerated'; export * from './patientParametersGenerated'; export * from './patientUploadSuccessResponseGenerated'; export * from './patientsGenerated'; export * from './recipientGenerated'; export * from './recipientInputGenerated'; +export * from './recipientModelToUpdateAllOfGenerated'; export * from './recipientModelToUpdateGenerated'; export * from './recipientRequirementsGenerated'; export * from './roundGenerated'; export * from './serviceStatusGenerated'; +export * from './sexEnumGenerated'; export * from './statusResponseGenerated'; export * from './transplantGenerated'; export * from './txmEventGenerated'; diff --git a/txmatching/web/frontend/src/app/generated/model/patientModelToUpdateGenerated.ts b/txmatching/web/frontend/src/app/generated/model/patientModelToUpdateGenerated.ts new file mode 100644 index 000000000..21b47bf6b --- /dev/null +++ b/txmatching/web/frontend/src/app/generated/model/patientModelToUpdateGenerated.ts @@ -0,0 +1,32 @@ +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { SexEnumGenerated } from './sexEnumGenerated'; +import { HlaTypingToUpdateGenerated } from './hlaTypingToUpdateGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; + + +export interface PatientModelToUpdateGenerated { + blood_group?: BloodGroupEnumGenerated; + /** + * Database id of the patient + */ + db_id: number; + height?: number; + /** + * Provide full list of all the HLA types of the patient, not just the change set + */ + hla_typing?: HlaTypingToUpdateGenerated; + sex?: SexEnumGenerated; + weight?: number; + year_of_birth?: number; +} + diff --git a/txmatching/web/frontend/src/app/generated/model/patientParametersGenerated.ts b/txmatching/web/frontend/src/app/generated/model/patientParametersGenerated.ts index b07cac081..659182b4e 100644 --- a/txmatching/web/frontend/src/app/generated/model/patientParametersGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/patientParametersGenerated.ts @@ -9,29 +9,19 @@ * https://openapi-generator.tech * Do not edit the class manually. */ +import { SexEnumGenerated } from './sexEnumGenerated'; import { CountryCodeGenerated } from './countryCodeGenerated'; import { HlaTypingGenerated } from './hlaTypingGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; export interface PatientParametersGenerated { - blood_group?: PatientParametersGeneratedBloodGroupEnum; - country_code?: CountryCodeGenerated; + blood_group: BloodGroupEnumGenerated; + country_code: CountryCodeGenerated; height?: number; hla_typing?: HlaTypingGenerated; - sex?: PatientParametersGeneratedSexEnum; + sex?: SexEnumGenerated; weight?: number; year_of_birth?: number; } -export enum PatientParametersGeneratedBloodGroupEnum { - A = 'A', - B = 'B', - Ab = 'AB', - _0 = '0' -}; -export enum PatientParametersGeneratedSexEnum { - M = 'M', - F = 'F' -}; - - diff --git a/txmatching/web/frontend/src/app/generated/model/recipientGenerated.ts b/txmatching/web/frontend/src/app/generated/model/recipientGenerated.ts index d0b685a46..b0f46f76d 100644 --- a/txmatching/web/frontend/src/app/generated/model/recipientGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/recipientGenerated.ts @@ -11,11 +11,12 @@ */ import { RecipientRequirementsGenerated } from './recipientRequirementsGenerated'; import { PatientParametersGenerated } from './patientParametersGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; import { HlaAntibodiesGenerated } from './hlaAntibodiesGenerated'; export interface RecipientGenerated { - acceptable_blood_groups?: Array; + acceptable_blood_groups?: Array; /** * Database id of the patient */ @@ -35,12 +36,4 @@ export interface RecipientGenerated { related_donor_db_id: number; waiting_since?: string; } -export enum RecipientGeneratedAcceptableBloodGroupsEnum { - A = 'A', - B = 'B', - Ab = 'AB', - _0 = '0' -}; - - diff --git a/txmatching/web/frontend/src/app/generated/model/recipientInputGenerated.ts b/txmatching/web/frontend/src/app/generated/model/recipientInputGenerated.ts index 4ad8436f2..d4fc79321 100644 --- a/txmatching/web/frontend/src/app/generated/model/recipientInputGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/recipientInputGenerated.ts @@ -10,14 +10,16 @@ * Do not edit the class manually. */ import { HLAAntibodyInGenerated } from './hLAAntibodyInGenerated'; +import { SexEnumGenerated } from './sexEnumGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; export interface RecipientInputGenerated { /** * Acceptable blood groups for the patient. Leave empty to use compatible blood groups. */ - acceptable_blood_groups?: Array; - blood_group: RecipientInputGeneratedBloodGroupEnum; + acceptable_blood_groups?: Array; + blood_group: BloodGroupEnumGenerated; /** * Height of the patient in centimeters. */ @@ -38,10 +40,7 @@ export interface RecipientInputGenerated { * Number of previous kidney transplants. */ previous_transplants?: number; - /** - * Sex of the patient. - */ - sex?: RecipientInputGeneratedSexEnum; + sex?: SexEnumGenerated; /** * Date since when the patient has been on waiting list. Use format YYYY-MM-DD. */ @@ -55,22 +54,4 @@ export interface RecipientInputGenerated { */ year_of_birth?: number; } -export enum RecipientInputGeneratedAcceptableBloodGroupsEnum { - A = 'A', - B = 'B', - Ab = 'AB', - _0 = '0' -}; -export enum RecipientInputGeneratedBloodGroupEnum { - A = 'A', - B = 'B', - Ab = 'AB', - _0 = '0' -}; -export enum RecipientInputGeneratedSexEnum { - M = 'M', - F = 'F' -}; - - diff --git a/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateAllOfGenerated.ts b/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateAllOfGenerated.ts new file mode 100644 index 000000000..631dc0ba4 --- /dev/null +++ b/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateAllOfGenerated.ts @@ -0,0 +1,40 @@ +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +import { RecipientRequirementsGenerated } from './recipientRequirementsGenerated'; +import { HlaAntibodiesToUpdateGenerated } from './hlaAntibodiesToUpdateGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; + + +export interface RecipientModelToUpdateAllOfGenerated { + /** + * Provide full list of all the acceptable blood groups of the patient, not just the change set + */ + acceptable_blood_groups?: Array; + cutoff?: number; + /** + * Provide full list of all the HLA antibodies of the patient, not just the change set + */ + hla_antibodies?: HlaAntibodiesToUpdateGenerated; + /** + * Number of previous kidney transplants. + */ + previous_transplants?: number; + /** + * Provide the whole recipients requirements object, it will be overwritten + */ + recipient_requirements?: RecipientRequirementsGenerated; + /** + * Date since when the patient has been on waiting list. Use format YYYY-MM-DD. + */ + waiting_since?: string; +} + diff --git a/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateGenerated.ts b/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateGenerated.ts index 651392434..d81c3bfc0 100644 --- a/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateGenerated.ts +++ b/txmatching/web/frontend/src/app/generated/model/recipientModelToUpdateGenerated.ts @@ -9,40 +9,49 @@ * https://openapi-generator.tech * Do not edit the class manually. */ +import { RecipientModelToUpdateAllOfGenerated } from './recipientModelToUpdateAllOfGenerated'; import { RecipientRequirementsGenerated } from './recipientRequirementsGenerated'; +import { SexEnumGenerated } from './sexEnumGenerated'; import { HlaAntibodiesToUpdateGenerated } from './hlaAntibodiesToUpdateGenerated'; import { HlaTypingToUpdateGenerated } from './hlaTypingToUpdateGenerated'; +import { BloodGroupEnumGenerated } from './bloodGroupEnumGenerated'; +import { PatientModelToUpdateGenerated } from './patientModelToUpdateGenerated'; export interface RecipientModelToUpdateGenerated { - /** - * Provide full list of all the acceptable blood groups of the patient, not just the change set - */ - acceptable_blood_groups?: Array; - cutoff?: number; + blood_group?: BloodGroupEnumGenerated; /** * Database id of the patient */ db_id: number; + height?: number; + /** + * Provide full list of all the HLA types of the patient, not just the change set + */ + hla_typing?: HlaTypingToUpdateGenerated; + sex?: SexEnumGenerated; + weight?: number; + year_of_birth?: number; + /** + * Provide full list of all the acceptable blood groups of the patient, not just the change set + */ + acceptable_blood_groups?: Array; + cutoff?: number; /** * Provide full list of all the HLA antibodies of the patient, not just the change set */ hla_antibodies?: HlaAntibodiesToUpdateGenerated; /** - * Provide full list of all the HLA types of the patient, not just the change set + * Number of previous kidney transplants. */ - hla_typing?: HlaTypingToUpdateGenerated; + previous_transplants?: number; /** * Provide the whole recipients requirements object, it will be overwritten */ recipient_requirements?: RecipientRequirementsGenerated; + /** + * Date since when the patient has been on waiting list. Use format YYYY-MM-DD. + */ + waiting_since?: string; } -export enum RecipientModelToUpdateGeneratedAcceptableBloodGroupsEnum { - A = 'A', - B = 'B', - Ab = 'AB', - _0 = '0' -}; - - diff --git a/txmatching/web/frontend/src/app/generated/model/sexEnumGenerated.ts b/txmatching/web/frontend/src/app/generated/model/sexEnumGenerated.ts new file mode 100644 index 000000000..43c0254ac --- /dev/null +++ b/txmatching/web/frontend/src/app/generated/model/sexEnumGenerated.ts @@ -0,0 +1,21 @@ +/** + * API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +/** + * Sex of the patient. + */ +export enum SexEnumGenerated { + M = 'M', + F = 'F' +}; + diff --git a/txmatching/web/frontend/src/app/model/DonorEditable.ts b/txmatching/web/frontend/src/app/model/DonorEditable.ts index f6ab972f7..d9f3ba524 100644 --- a/txmatching/web/frontend/src/app/model/DonorEditable.ts +++ b/txmatching/web/frontend/src/app/model/DonorEditable.ts @@ -1,7 +1,14 @@ +import { Donor } from './Donor'; import { PatientEditable } from './PatientEditable'; import { DonorType } from '@app/model/enums/DonorType'; export class DonorEditable extends PatientEditable { type: DonorType = DonorType.DONOR; - relatedRecipientMedicalId?: string; + active: boolean = true; + + initializeFromPatient(donor: Donor) { + super.initializeFromPatient(donor); + this.type = donor.donor_type; + this.active = donor.active; + } } diff --git a/txmatching/web/frontend/src/app/model/Hla.ts b/txmatching/web/frontend/src/app/model/Hla.ts index 7681e20f7..ff9a471b2 100644 --- a/txmatching/web/frontend/src/app/model/Hla.ts +++ b/txmatching/web/frontend/src/app/model/Hla.ts @@ -14,12 +14,6 @@ export interface Antibody extends Hla { cutoff: number; } -export class AntibodyEditable { - name?: string; - mfi?: number; - cutoff?: number; -} - export interface DetailedScorePerGroup { hla_group: string; donor_matches: AntigenMatch[]; diff --git a/txmatching/web/frontend/src/app/model/HlaEditable.ts b/txmatching/web/frontend/src/app/model/HlaEditable.ts new file mode 100644 index 000000000..913fcf3e0 --- /dev/null +++ b/txmatching/web/frontend/src/app/model/HlaEditable.ts @@ -0,0 +1,8 @@ +export interface AntigenEditable { + raw_code: string; +} + +export interface AntibodyEditable { + raw_code: string; + mfi: number; +} diff --git a/txmatching/web/frontend/src/app/model/Patient.ts b/txmatching/web/frontend/src/app/model/Patient.ts index 22e9659f0..7fb0737f0 100644 --- a/txmatching/web/frontend/src/app/model/Patient.ts +++ b/txmatching/web/frontend/src/app/model/Patient.ts @@ -1,6 +1,8 @@ import { ListItem } from '@app/components/list-item/list-item.interface'; import { Antigen, HlaPerGroup } from '@app/model/Hla'; import { Sex } from '@app/model/enums/Sex'; +import { CountryCodeGenerated } from '@app/generated'; +import { BloodGroup } from '@app/model/enums/BloodGroup'; export interface Patient extends ListItem { db_id: number; @@ -9,12 +11,12 @@ export interface Patient extends ListItem { } export interface PatientParameters { - blood_group: string; + blood_group: BloodGroup; hla_typing: { hla_per_groups: HlaPerGroup[]; hla_types_list: Antigen[]; }; - country_code: string; + country_code: CountryCodeGenerated; sex?: Sex; height?: number; weight?: number; diff --git a/txmatching/web/frontend/src/app/model/PatientEditable.ts b/txmatching/web/frontend/src/app/model/PatientEditable.ts index d6683621d..f7022ad81 100644 --- a/txmatching/web/frontend/src/app/model/PatientEditable.ts +++ b/txmatching/web/frontend/src/app/model/PatientEditable.ts @@ -1,29 +1,42 @@ -import { Country } from '@app/model/enums/Country'; import { BloodGroup } from '@app/model/enums/BloodGroup'; import { Sex } from '@app/model/enums/Sex'; +import { Patient } from '@app/model/Patient'; +import { AntigenEditable } from '@app/model/HlaEditable'; +import { CountryCodeGenerated } from '@app/generated'; export class PatientEditable { - country?: Country = Country.Cze; + country?: CountryCodeGenerated = CountryCodeGenerated.Cze; medicalId: string = ''; - antigens: string[] = []; + antigens: AntigenEditable[] = []; bloodGroup: BloodGroup = BloodGroup.A; - sex: Sex = Sex.M; + sex?: Sex = Sex.M; height?: number; weight?: number; yearOfBirth?: number; - removeAntigen(code: string) { - const index = this.antigens.indexOf(code); + initializeFromPatient(patient: Patient) { + this.country = patient.parameters.country_code; + this.medicalId = patient.medical_id; + this.antigens = [...patient.parameters.hla_typing.hla_types_list]; + this.bloodGroup = patient.parameters.blood_group; + this.sex = patient.parameters.sex; + this.height = patient.parameters.height; + this.weight = patient.parameters.weight; + this.yearOfBirth = patient.parameters.year_of_birth; + } + + removeAntigen(a: AntigenEditable) { + const index = this.antigens.indexOf(a); if (index !== -1) { this.antigens.splice(index, 1); } } - addAntigen(code: string) { - this.antigens.push(code); + addAntigen(a: AntigenEditable) { + this.antigens.push(a); } - setCountry(country: Country | undefined) { + setCountry(country?: CountryCodeGenerated) { this.country = country; } } diff --git a/txmatching/web/frontend/src/app/model/Recipient.ts b/txmatching/web/frontend/src/app/model/Recipient.ts index ea460bb68..167a0ac60 100644 --- a/txmatching/web/frontend/src/app/model/Recipient.ts +++ b/txmatching/web/frontend/src/app/model/Recipient.ts @@ -1,8 +1,12 @@ import { Patient } from '@app/model/Patient'; import { AntibodiesPerGroup, Antibody } from '@app/model/Hla'; +import { BloodGroup } from '@app/model/enums/BloodGroup'; export interface Recipient extends Patient { - acceptable_blood_groups: string[]; + acceptable_blood_groups: BloodGroup[]; + cutoff?: number; + waitingSince?: Date; + previousTransplants?: number; hla_antibodies: { hla_antibodies_list: Antibody[]; hla_antibodies_per_groups: AntibodiesPerGroup[]; diff --git a/txmatching/web/frontend/src/app/model/RecipientEditable.ts b/txmatching/web/frontend/src/app/model/RecipientEditable.ts index 344788712..14b168d27 100644 --- a/txmatching/web/frontend/src/app/model/RecipientEditable.ts +++ b/txmatching/web/frontend/src/app/model/RecipientEditable.ts @@ -1,12 +1,31 @@ import { PatientEditable } from '@app/model/PatientEditable'; -import { AntibodyEditable } from '@app/model/Hla'; +import { Recipient, RecipientRequirements } from '@app/model/Recipient'; +import { BloodGroup } from '@app/model/enums/BloodGroup'; +import { AntibodyEditable } from '@app/model/HlaEditable'; export class RecipientEditable extends PatientEditable { - acceptableBloodGroups: string[] = []; + acceptableBloodGroups: BloodGroup[] = []; antibodies: AntibodyEditable[] = []; - antibodiesCutoff: number = 2000; - waitingSince: Date = new Date(); - previousTransplants: number = 0; + antibodiesCutoff?: number = 2000; + waitingSince?: Date = new Date(); + previousTransplants?: number = 0; + recipientRequirements: RecipientRequirements = { + require_better_match_in_compatibility_index: false, + require_better_match_in_compatibility_index_or_blood_group: false, + require_compatible_blood_group: false + }; + + initializeFromPatient(recipient: Recipient) { + super.initializeFromPatient(recipient); + this.acceptableBloodGroups = [...recipient.acceptable_blood_groups]; + this.antibodies = [...recipient.hla_antibodies.hla_antibodies_list]; + this.antibodiesCutoff = recipient.cutoff; + this.waitingSince = recipient.waitingSince; + this.previousTransplants = recipient.previousTransplants; + if (recipient.recipient_requirements) { + this.recipientRequirements = recipient.recipient_requirements; + } + } removeAntibody(a: AntibodyEditable) { const index = this.antibodies.indexOf(a); diff --git a/txmatching/web/frontend/src/app/model/enums/Country.ts b/txmatching/web/frontend/src/app/model/enums/Country.ts deleted file mode 100644 index 6abe8cdbc..000000000 --- a/txmatching/web/frontend/src/app/model/enums/Country.ts +++ /dev/null @@ -1,253 +0,0 @@ -// Todo: Handle same enums on BE and FE -export enum Country { - Tgo = 'TGO', - Mne = 'MNE', - Lao = 'LAO', - Mrt = 'MRT', - Nic = 'NIC', - Lva = 'LVA', - Omn = 'OMN', - Afg = 'AFG', - Cyp = 'CYP', - Ben = 'BEN', - Ata = 'ATA', - Chn = 'CHN', - Col = 'COL', - Cxr = 'CXR', - Atg = 'ATG', - Msr = 'MSR', - Mda = 'MDA', - Zmb = 'ZMB', - Vnm = 'VNM', - Atf = 'ATF', - Tcd = 'TCD', - Myt = 'MYT', - Lbn = 'LBN', - Maf = 'MAF', - Lux = 'LUX', - Mtq = 'MTQ', - Cze = 'CZE', - Are = 'ARE', - Cmr = 'CMR', - Bdi = 'BDI', - Arg = 'ARG', - Asm = 'ASM', - Bhr = 'BHR', - Chl = 'CHL', - And = 'AND', - Mnp = 'MNP', - Ltu = 'LTU', - Mdg = 'MDG', - Lca = 'LCA', - Tur = 'TUR', - Ukr = 'UKR', - Tuv = 'TUV', - Vir = 'VIR', - Mlt = 'MLT', - Nor = 'NOR', - Mco = 'MCO', - Che = 'CHE', - Blm = 'BLM', - Abw = 'ABW', - Blz = 'BLZ', - Bmu = 'BMU', - Civ = 'CIV', - Mus = 'MUS', - Usa = 'USA', - Twn = 'TWN', - Yem = 'YEM', - Mwi = 'MWI', - Nld = 'NLD', - Lso = 'LSO', - Bol = 'BOL', - Aut = 'AUT', - Cok = 'COK', - Blr = 'BLR', - Aus = 'AUS', - Brn = 'BRN', - Mar = 'MAR', - Nzl = 'NZL', - Lbr = 'LBR', - Mdv = 'MDV', - Tca = 'TCA', - Uga = 'UGA', - Tto = 'TTO', - Pol = 'POL', - Srb = 'SRB', - Ind = 'IND', - Geo = 'GEO', - Grc = 'GRC', - Sgs = 'SGS', - Grd = 'GRD', - Iot = 'IOT', - Hkg = 'HKG', - Prk = 'PRK', - Kgz = 'KGZ', - Spm = 'SPM', - Slv = 'SLV', - Reu = 'REU', - Sau = 'SAU', - Syc = 'SYC', - Stp = 'STP', - Ken = 'KEN', - Imn = 'IMN', - Kor = 'KOR', - Guf = 'GUF', - Dji = 'DJI', - Gnq = 'GNQ', - Glp = 'GLP', - Dnk = 'DNK', - Ggy = 'GGY', - Isr = 'ISR', - Pcn = 'PCN', - Slb = 'SLB', - Pry = 'PRY', - Rus = 'RUS', - Kwt = 'KWT', - Dom = 'DOM', - Gtm = 'GTM', - Gbr = 'GBR', - Gum = 'GUM', - Jey = 'JEY', - Hmd = 'HMD', - Sgp = 'SGP', - Pak = 'PAK', - Sur = 'SUR', - Swe = 'SWE', - Jpn = 'JPN', - Gnb = 'GNB', - Esh = 'ESH', - Dza = 'DZA', - Gab = 'GAB', - Fra = 'FRA', - Dma = 'DMA', - Hnd = 'HND', - Sdn = 'SDN', - Rwa = 'RWA', - Phl = 'PHL', - Ssd = 'SSD', - Qat = 'QAT', - Per = 'PER', - Pri = 'PRI', - Svn = 'SVN', - Hti = 'HTI', - Esp = 'ESP', - Grl = 'GRL', - Gmb = 'GMB', - Eri = 'ERI', - Fin = 'FIN', - Est = 'EST', - Kna = 'KNA', - Hun = 'HUN', - Irq = 'IRQ', - Cym = 'CYM', - Shn = 'SHN', - Pse = 'PSE', - Pyf = 'PYF', - Sjm = 'SJM', - Idn = 'IDN', - Isl = 'ISL', - Egy = 'EGY', - Flk = 'FLK', - Fji = 'FJI', - Gin = 'GIN', - Guy = 'GUY', - Irn = 'IRN', - Com = 'COM', - Irl = 'IRL', - Kaz = 'KAZ', - Rou = 'ROU', - Svk = 'SVK', - Png = 'PNG', - Prt = 'PRT', - Som = 'SOM', - Sxm = 'SXM', - Hrv = 'HRV', - Kir = 'KIR', - Jam = 'JAM', - Ecu = 'ECU', - Eth = 'ETH', - Fro = 'FRO', - Khm = 'KHM', - Syr = 'SYR', - Sen = 'SEN', - Plw = 'PLW', - Sle = 'SLE', - Fsm = 'FSM', - Gib = 'GIB', - Deu = 'DEU', - Gha = 'GHA', - Jor = 'JOR', - Ita = 'ITA', - Pan = 'PAN', - Swz = 'SWZ', - Smr = 'SMR', - Tun = 'TUN', - Mli = 'MLI', - Cog = 'COG', - Ala = 'ALA', - Ago = 'AGO', - Btn = 'BTN', - Brb = 'BRB', - Caf = 'CAF', - Mmr = 'MMR', - Lie = 'LIE', - Nam = 'NAM', - Moz = 'MOZ', - Ton = 'TON', - Vgb = 'VGB', - Ven = 'VEN', - Tza = 'TZA', - Tkm = 'TKM', - Mex = 'MEX', - Ncl = 'NCL', - Mac = 'MAC', - Lka = 'LKA', - Cod = 'COD', - Alb = 'ALB', - Bwa = 'BWA', - Cri = 'CRI', - Bvt = 'BVT', - Arm = 'ARM', - Aze = 'AZE', - Bih = 'BIH', - Mng = 'MNG', - Niu = 'NIU', - Mys = 'MYS', - Tls = 'TLS', - Wsm = 'WSM', - Tha = 'THA', - Xkx = 'XKX', - Nfk = 'NFK', - Lby = 'LBY', - Aia = 'AIA', - Bra = 'BRA', - Cpv = 'CPV', - Bel = 'BEL', - Can = 'CAN', - Bgd = 'BGD', - Cuw = 'CUW', - Bhs = 'BHS', - Nga = 'NGA', - Mkd = 'MKD', - Npl = 'NPL', - Vat = 'VAT', - Uzb = 'UZB', - Umi = 'UMI', - Tkl = 'TKL', - Vct = 'VCT', - Zwe = 'ZWE', - Nru = 'NRU', - Ner = 'NER', - Cub = 'CUB', - Bes = 'BES', - Bfa = 'BFA', - Bgr = 'BGR', - Cck = 'CCK', - Mhl = 'MHL', - Zaf = 'ZAF', - Ury = 'URY', - Wlf = 'WLF', - Vut = 'VUT', - Tjk = 'TJK' -}; diff --git a/txmatching/web/frontend/src/app/model/enums/Sex.ts b/txmatching/web/frontend/src/app/model/enums/Sex.ts index 383f0e0aa..185e0bc86 100644 --- a/txmatching/web/frontend/src/app/model/enums/Sex.ts +++ b/txmatching/web/frontend/src/app/model/enums/Sex.ts @@ -1,5 +1,4 @@ export enum Sex { M = 'M', - F = 'F', - NULL = 'Not specified' + F = 'F' } diff --git a/txmatching/web/frontend/src/app/model/index.ts b/txmatching/web/frontend/src/app/model/index.ts index 745874ddb..1f47883e3 100644 --- a/txmatching/web/frontend/src/app/model/index.ts +++ b/txmatching/web/frontend/src/app/model/index.ts @@ -21,3 +21,4 @@ export { Sex } from '@app/model/enums/Sex'; export { UserRole } from '@app/model/enums/UserRole'; export { UserTokenType } from '@app/model/enums/UserTokenType'; export { EnvironmentType } from '@app/model/enums/EnvironmentType'; +export { AntibodyEditable } from '@app/model/HlaEditable'; diff --git a/txmatching/web/frontend/src/app/parsers/donor.parsers.ts b/txmatching/web/frontend/src/app/parsers/donor.parsers.ts index 80ae0a1bf..5a48390c7 100644 --- a/txmatching/web/frontend/src/app/parsers/donor.parsers.ts +++ b/txmatching/web/frontend/src/app/parsers/donor.parsers.ts @@ -1,4 +1,4 @@ -import { DonorGenerated, DonorGeneratedDonorTypeEnum } from '../generated'; +import { DonorGenerated, DonorTypeGenerated } from '../generated'; import { Donor, DonorType } from '../model'; import { parsePatient } from './patient.parsers'; import { parseDetailedScorePerGroup } from './hla.parsers'; @@ -15,6 +15,6 @@ export const parseDonor = (data: DonorGenerated): Donor => { }; }; -export const parseDonorType = ( data: DonorGeneratedDonorTypeEnum ): DonorType => { +export const parseDonorType = ( data: DonorTypeGenerated ): DonorType => { return DonorType[data]; }; diff --git a/txmatching/web/frontend/src/app/parsers/patient.parsers.ts b/txmatching/web/frontend/src/app/parsers/patient.parsers.ts index 81c8a3718..7a7b7a496 100644 --- a/txmatching/web/frontend/src/app/parsers/patient.parsers.ts +++ b/txmatching/web/frontend/src/app/parsers/patient.parsers.ts @@ -1,5 +1,5 @@ -import { DonorGenerated, PatientParametersGenerated, PatientParametersGeneratedSexEnum, RecipientGenerated } from '../generated'; -import { Patient, PatientParameters, Sex } from '../model'; +import { BloodGroupEnumGenerated, DonorGenerated, PatientParametersGenerated, RecipientGenerated, SexEnumGenerated } from '../generated'; +import { BloodGroup, Patient, PatientParameters, Sex } from '../model'; import { DEFAULT_LIST_ITEM } from '../components/list-item/list-item.interface'; import { parseAntigen, parseHlaPerGroup } from '@app/parsers/hla.parsers'; @@ -16,23 +16,32 @@ export const parsePatient = ( data: DonorGenerated | RecipientGenerated ): Patie export const parsePatientParameters = ( data: PatientParametersGenerated ): PatientParameters => { return { - // TODO: https://github.com/mild-blue/txmatching/issues/401 create enum for blood group - blood_group: data.blood_group ?? '', + blood_group: parseBloodGroup(data.blood_group), // TODO: https://github.com/mild-blue/txmatching/issues/401 create hla typing model hla_typing: { hla_per_groups: data.hla_typing?.hla_per_groups.map(parseHlaPerGroup) ?? [], hla_types_list: data.hla_typing?.hla_types_list.map(parseAntigen) ?? [] }, - // TODO: https://github.com/mild-blue/txmatching/issues/401 create enum for country code - country_code: data.country_code ?? '', + country_code: data.country_code, sex: parsePatientSexType(data.sex), height: data.height, weight: data.weight, year_of_birth: data.year_of_birth - } + }; }; -export const parsePatientSexType = (data: PatientParametersGeneratedSexEnum | undefined): Sex | undefined => { +export const parsePatientSexType = (data: SexEnumGenerated | undefined): Sex | undefined => { return data !== undefined ? Sex[data] : undefined; }; + + +export const parseBloodGroup = (data: BloodGroupEnumGenerated): BloodGroup => { + switch(data) { + case BloodGroupEnumGenerated.A: return BloodGroup.A; + case BloodGroupEnumGenerated.B: return BloodGroup.B; + case BloodGroupEnumGenerated.Ab: return BloodGroup.AB; + case BloodGroupEnumGenerated._0: return BloodGroup.ZERO; + default: throw new Error(`Parsing blood group ${data} not implemented`); + } +}; diff --git a/txmatching/web/frontend/src/app/parsers/recipient.parsers.ts b/txmatching/web/frontend/src/app/parsers/recipient.parsers.ts index cf9e39e64..e52d6c2d7 100644 --- a/txmatching/web/frontend/src/app/parsers/recipient.parsers.ts +++ b/txmatching/web/frontend/src/app/parsers/recipient.parsers.ts @@ -1,17 +1,20 @@ import { RecipientGenerated, RecipientRequirementsGenerated } from '../generated'; import { Recipient, RecipientRequirements } from '../model'; -import { parsePatient } from './patient.parsers'; +import { parseBloodGroup, parsePatient } from './patient.parsers'; import { parseAntibodiesPerGroup, parseAntibody } from './hla.parsers'; export const parseRecipient = (data: RecipientGenerated): Recipient => { return { ...parsePatient(data), - acceptable_blood_groups: data.acceptable_blood_groups ?? [], + acceptable_blood_groups: data.acceptable_blood_groups?.map(parseBloodGroup) ?? [], // TODO: https://github.com/mild-blue/txmatching/issues/401 create hla_antibodies model hla_antibodies: { hla_antibodies_list: data.hla_antibodies?.hla_antibodies_list.map(parseAntibody) ?? [], hla_antibodies_per_groups: data.hla_antibodies?.hla_antibodies_per_groups.map(parseAntibodiesPerGroup) ?? [] }, + cutoff: data.recipient_cutoff, + waitingSince: data.waiting_since ? parseDate(data.waiting_since) : undefined, + previousTransplants: data.previous_transplants, recipient_requirements: data.recipient_requirements && parseRecipientRequirements(data.recipient_requirements), related_donor_db_id: data.related_donor_db_id, }; @@ -30,3 +33,7 @@ export const parseRecipientRequirements = (data: RecipientRequirementsGenerated) require_compatible_blood_group }; }; + +export const parseDate = (data: string): Date => { + return new Date(data); +}; diff --git a/txmatching/web/frontend/src/app/parsers/to-generated/donor.parsers.ts b/txmatching/web/frontend/src/app/parsers/to-generated/donor.parsers.ts new file mode 100644 index 000000000..273fd177d --- /dev/null +++ b/txmatching/web/frontend/src/app/parsers/to-generated/donor.parsers.ts @@ -0,0 +1,36 @@ +import { DonorEditable } from '../../model/DonorEditable'; +import { DonorInputGenerated, DonorModelToUpdateGenerated, DonorTypeGenerated } from '../../generated'; +import { fromBloodGroup, fromPatientEditableToUpdateGenerated, fromSex } from './patient.parsers'; +import { DonorType } from '@app/model'; + +/* Used for updating donor */ +export const fromDonorEditableToUpdateGenerated = ( donor: DonorEditable, donorId: number ): DonorModelToUpdateGenerated => { + return { + ...fromPatientEditableToUpdateGenerated(donor, donorId), + active: donor.active + }; +}; + +/* Used for creating donor */ +// TODO: share logic with recipient +export const fromDonorEditableToInputGenerated = ( donor: DonorEditable ): DonorInputGenerated => { + return { + medical_id: donor.medicalId, + blood_group: fromBloodGroup(donor.bloodGroup), + hla_typing: donor.antigens.map(antigen => antigen.raw_code), + donor_type: fromDonorType(donor.type), + sex: donor.sex ? fromSex(donor.sex) : undefined, + height: donor.height ? +donor.height : undefined, + weight: donor.weight ? +donor.weight : undefined, + year_of_birth: donor.yearOfBirth ? +donor.yearOfBirth : undefined + }; +}; + +export const fromDonorType = ( donorType: DonorType ): DonorTypeGenerated => { + switch (donorType) { + case DonorType.DONOR: return DonorTypeGenerated.Donor; + case DonorType.BRIDGING_DONOR: return DonorTypeGenerated.BridgingDonor; + case DonorType.NON_DIRECTED: return DonorTypeGenerated.NonDirected; + default: throw new Error(`Parsing from donor type ${donorType} not implemented`); + } +}; diff --git a/txmatching/web/frontend/src/app/parsers/to-generated/patient.parsers.ts b/txmatching/web/frontend/src/app/parsers/to-generated/patient.parsers.ts new file mode 100644 index 000000000..5caf669c6 --- /dev/null +++ b/txmatching/web/frontend/src/app/parsers/to-generated/patient.parsers.ts @@ -0,0 +1,46 @@ +import { BloodGroupEnumGenerated, PatientModelToUpdateGenerated, SexEnumGenerated } from '../../generated'; +import { BloodGroup, Sex } from '@app/model'; +import { PatientEditable } from '@app/model/PatientEditable'; + +/* Used for editing patient */ +export const fromPatientEditableToUpdateGenerated = (patient: PatientEditable, patientId: number ): PatientModelToUpdateGenerated => { + return { + db_id: patientId, + blood_group: fromBloodGroup(patient.bloodGroup), + hla_typing: { + hla_types_list: patient.antigens + }, + sex: patient.sex ? fromSex(patient.sex) : undefined, + // TODO: convert values to numbers in SimpleNumberComponent, not here + height: patient.height ? +patient.height : undefined, + weight: patient.weight ? +patient.weight : undefined, + year_of_birth: patient.yearOfBirth ? +patient.yearOfBirth : undefined + }; +}; + +export const fromBloodGroup = ( bloodGroup: BloodGroup ): BloodGroupEnumGenerated => { + switch (bloodGroup) { + case BloodGroup.A: return BloodGroupEnumGenerated.A; + case BloodGroup.B: return BloodGroupEnumGenerated.B; + case BloodGroup.AB: return BloodGroupEnumGenerated.Ab; + case BloodGroup.ZERO: return BloodGroupEnumGenerated._0; + default: throw new Error(`Parsing from blood group ${bloodGroup} not implemented`); + } +}; + +export const fromSex = ( sex: Sex ): SexEnumGenerated | undefined => { + switch (sex) { + case Sex.M: return SexEnumGenerated.M; + case Sex.F: return SexEnumGenerated.F; + default: throw new Error(`Parsing from sex ${sex} not implemented`); + } +}; + +export const fromDateToString = (date: Date): string => { + const y = date.getFullYear(); + const d = date.getDate(); + const m = date.getMonth() + 1; + const month = m < 10 ? `0${m}` : 'm'; + + return `${y}-${month}-${d}`; +}; diff --git a/txmatching/web/frontend/src/app/parsers/to-generated/patientPair.parsers.ts b/txmatching/web/frontend/src/app/parsers/to-generated/patientPair.parsers.ts new file mode 100644 index 000000000..8490b5e85 --- /dev/null +++ b/txmatching/web/frontend/src/app/parsers/to-generated/patientPair.parsers.ts @@ -0,0 +1,22 @@ +import { DonorEditable } from '../../model/DonorEditable'; +import { RecipientEditable } from '../../model/RecipientEditable'; +import { CountryCodeGenerated, DonorModelPairInGenerated } from '../../generated'; +import { fromDonorEditableToInputGenerated } from './donor.parsers'; +import { DonorType } from '../../model'; +import { fromRecipientEditableToInputGenerated } from './recipient.parsers'; + +/* Used for creating patients */ +export const fromPatientsEditableToInGenerated = (donor: DonorEditable, recipient: RecipientEditable ): DonorModelPairInGenerated => { + const generated: DonorModelPairInGenerated = { + // Assume same country for the donor and the recipient + country_code: donor.country ?? CountryCodeGenerated.Cze, + donor: fromDonorEditableToInputGenerated(donor) + }; + + if (donor.type.valueOf() === DonorType.DONOR.valueOf()) { + generated.donor.related_recipient_medical_id = recipient.medicalId; + generated.recipient = fromRecipientEditableToInputGenerated(recipient); + } + + return generated; +}; diff --git a/txmatching/web/frontend/src/app/parsers/to-generated/recipient.parsers.ts b/txmatching/web/frontend/src/app/parsers/to-generated/recipient.parsers.ts new file mode 100644 index 000000000..0a3e68f9d --- /dev/null +++ b/txmatching/web/frontend/src/app/parsers/to-generated/recipient.parsers.ts @@ -0,0 +1,43 @@ +import { RecipientInputGenerated, RecipientModelToUpdateGenerated } from '../../generated'; +import { fromBloodGroup, fromDateToString, fromPatientEditableToUpdateGenerated, fromSex } from './patient.parsers'; +import { RecipientEditable } from '../../model/RecipientEditable'; + +/* Used for updating recipient */ +export const fromRecipientEditableToUpdateGenerated = (recipient: RecipientEditable, recipientId: number ): RecipientModelToUpdateGenerated => { + return { + ...fromPatientEditableToUpdateGenerated(recipient, recipientId), + acceptable_blood_groups: recipient.acceptableBloodGroups.map(fromBloodGroup), + hla_antibodies: { + hla_antibodies_list: recipient.antibodies + }, + cutoff: recipient.antibodiesCutoff ? +recipient.antibodiesCutoff : undefined, + waiting_since: recipient.waitingSince ? fromDateToString(recipient.waitingSince) : undefined, + // TODO: convert value to number in SimpleNumberComponent, not here + previous_transplants: recipient.previousTransplants ? +recipient.previousTransplants : undefined, + recipient_requirements: recipient.recipientRequirements + }; +}; + +/* Used for creating recipient */ +// TODO: share logic with donor +export const fromRecipientEditableToInputGenerated = ( recipient: RecipientEditable ): RecipientInputGenerated => { + return { + acceptable_blood_groups: recipient.acceptableBloodGroups.map(fromBloodGroup), + blood_group: fromBloodGroup(recipient.bloodGroup), + height: recipient.height ? +recipient.height : undefined, + hla_antibodies: recipient.antibodies.map(a => { + return { + name: a.raw_code, + mfi: a.mfi, + cutoff: recipient.antibodiesCutoff ? +recipient.antibodiesCutoff : 0 + }; + }), + hla_typing: recipient.antigens.map(a => a.raw_code), + medical_id: recipient.medicalId, + previous_transplants: recipient.previousTransplants ? +recipient.previousTransplants : undefined, + sex: recipient.sex ? fromSex(recipient.sex) : undefined, + waiting_since: recipient.waitingSince ? fromDateToString(recipient.waitingSince) : undefined, + weight: recipient.weight ? +recipient.weight : undefined, + year_of_birth: recipient.yearOfBirth ? +recipient.yearOfBirth : undefined + }; +}; diff --git a/txmatching/web/frontend/src/app/services/patient/patient.service.interface.ts b/txmatching/web/frontend/src/app/services/patient/patient.service.interface.ts index ac3b87403..05102cf83 100644 --- a/txmatching/web/frontend/src/app/services/patient/patient.service.interface.ts +++ b/txmatching/web/frontend/src/app/services/patient/patient.service.interface.ts @@ -1,41 +1,7 @@ -import { AntibodyEditable } from '@app/model'; import { DonorEditable } from '@app/model/DonorEditable'; import { RecipientEditable } from '@app/model/RecipientEditable'; -// TODO: Use generated models instead export interface PatientPairToAdd { donor: DonorEditable; recipient: RecipientEditable; } - -export interface DonorModelPairIn { - country_code: string; - donor: DonorInput; - recipient?: RecipientInput; -} - -export interface DonorInput { - blood_group: string; - donor_type: string; - height?: number; - hla_typing: string[]; - medical_id: string; - related_recipient_medical_id?: string; - sex?: string; - weight?: number; - year_of_birth?: number; -} - -export interface RecipientInput { - acceptable_blood_groups?: string[]; - blood_group: string; - height?: number; - hla_antibodies: AntibodyEditable[]; - hla_typing: string[]; - medical_id: string; - previous_transplants?: number; - sex?: string; - waiting_since?: string; - weight?: number; - year_of_birth?: number; -} diff --git a/txmatching/web/frontend/src/app/services/patient/patient.service.ts b/txmatching/web/frontend/src/app/services/patient/patient.service.ts index 608222601..2c1fcd71f 100644 --- a/txmatching/web/frontend/src/app/services/patient/patient.service.ts +++ b/txmatching/web/frontend/src/app/services/patient/patient.service.ts @@ -6,16 +6,22 @@ import { LoggerService } from '@app/services/logger/logger.service'; import { PatientList } from '@app/model/PatientList'; import { Donor } from '@app/model/Donor'; import { Recipient } from '@app/model/Recipient'; -import { Antibody } from '@app/model/Hla'; -import { PatientsGenerated, PatientUploadSuccessResponseGenerated } from '@app/generated'; -import { parsePatientList } from '@app/parsers'; +import { + DonorGenerated, + DonorModelPairInGenerated, + DonorModelToUpdateGenerated, + PatientsGenerated, + PatientUploadSuccessResponseGenerated, + RecipientGenerated, + RecipientModelToUpdateGenerated +} from '@app/generated'; +import { parseDonor, parsePatientList, parseRecipient } from '@app/parsers'; import { BehaviorSubject, Observable } from 'rxjs'; import { DonorEditable } from '@app/model/DonorEditable'; import { RecipientEditable } from '@app/model/RecipientEditable'; -import { DonorModelPairIn } from '@app/services/patient/patient.service.interface'; -import { Sex } from '@app/model'; -import { Country } from '@app/model/enums/Country'; -import { DonorType } from '@app/model/enums/DonorType'; +import { fromDonorEditableToUpdateGenerated } from '@app/parsers/to-generated/donor.parsers'; +import { fromRecipientEditableToUpdateGenerated } from '@app/parsers/to-generated/recipient.parsers'; +import { fromPatientsEditableToInGenerated } from '@app/parsers/to-generated/patientPair.parsers'; @Injectable({ providedIn: 'root' @@ -41,92 +47,47 @@ export class PatientService { ).toPromise(); } - public async saveDonor(txmEventId: number, donor: Donor): Promise { - this._logger.log('Saving donor', [donor]); - return this._http.put( + public async saveDonor(txmEventId: number, donorId: number, donorEditable: DonorEditable): Promise { + this._logger.log(`Saving donor ${donorId}`, [donorEditable]); + const payload: DonorModelToUpdateGenerated = fromDonorEditableToUpdateGenerated(donorEditable, donorId); + this._logger.log('Sending payload', [payload]); + + return this._http.put( `${environment.apiUrl}/txm-event/${txmEventId}/patients/donor`, - { - db_id: donor.db_id, - hla_typing: { - hla_types_list: donor.parameters.hla_typing.hla_types_list - }, - active: donor.active - } - ).pipe(first()).toPromise(); + payload + ).pipe( + first(), + map(parseDonor) + ).toPromise(); } - public async saveRecipient(txmEventId: number, recipient: Recipient, antibodies: Antibody[]): Promise { - this._logger.log('Saving recipient', [recipient]); - return this._http.put( + public async saveRecipient(txmEventId: number, recipientId: number, recipientEditable: RecipientEditable): Promise { + this._logger.log(`Saving recipient ${recipientId}`, [recipientEditable]); + const payload: RecipientModelToUpdateGenerated = fromRecipientEditableToUpdateGenerated(recipientEditable, recipientId); + this._logger.log('Sending payload', [payload]); + + return this._http.put( `${environment.apiUrl}/txm-event/${txmEventId}/patients/recipient`, - { - db_id: recipient.db_id, - acceptable_blood_groups: recipient.acceptable_blood_groups, - hla_typing: { - hla_types_list: recipient.parameters.hla_typing.hla_types_list - }, - hla_antibodies: { - hla_antibodies_list: antibodies - }, - recipient_requirements: recipient.recipient_requirements - } - ).pipe(first()).toPromise(); + payload + ).pipe( + first(), + map(parseRecipient) + ).toPromise(); } public async addNewPair(txmEventId: number, donor: DonorEditable, recipient: RecipientEditable): Promise { this._logger.log('Adding new pair', [donor, recipient]); - const country = donor.country ?? Country.Cze; // Assume same country for the donor and the recipient - const body: DonorModelPairIn = { - country_code: country, - donor: { - medical_id: donor.medicalId, - blood_group: donor.bloodGroup, - hla_typing: donor.antigens, - donor_type: donor.type, - sex: donor.sex === Sex.NULL ? undefined : donor.sex, - height: donor.height ? +donor.height : undefined, - weight: donor.weight ? +donor.weight : undefined, - year_of_birth: donor.yearOfBirth ? +donor.yearOfBirth : undefined - } - }; - - if (donor.type.valueOf() === DonorType.DONOR.valueOf()) { - body.donor.related_recipient_medical_id = recipient.medicalId; - - // Put cutoff value to antibodies - recipient.antibodies.forEach(a => a.cutoff = recipient.antibodiesCutoff ? +recipient.antibodiesCutoff : 0); - - body.recipient = { - acceptable_blood_groups: recipient.acceptableBloodGroups, - blood_group: recipient.bloodGroup, - height: recipient.height ? +recipient.height : undefined, - hla_antibodies: recipient.antibodies, - hla_typing: recipient.antigens, - medical_id: recipient.medicalId, - previous_transplants: recipient.previousTransplants ? +recipient.previousTransplants : undefined, - sex: recipient.sex === Sex.NULL ? undefined : recipient.sex, - waiting_since: this._formatDate(recipient.waitingSince), - weight: recipient.weight ? +recipient.weight : undefined, - year_of_birth: recipient.yearOfBirth ? +recipient.yearOfBirth : undefined - }; - } + const payload: DonorModelPairInGenerated = fromPatientsEditableToInGenerated( + donor, recipient + ); return this._http.post( `${environment.apiUrl}/txm-event/${txmEventId}/patients/pairs`, - body + payload ).pipe(first()).toPromise(); } - private _formatDate(date: Date): string { - const y = date.getFullYear(); - const d = date.getDate(); - const m = date.getMonth(); - const month = m < 10 ? `0${m}` : 'm'; - - return `${y}-${month}-${d}`; - } - public async deleteDonor(txmEventId: number, donorDbId: number): Promise { this._logger.log(`Deleting donor ${donorDbId}`); await this._http.delete( diff --git a/txmatching/web/swagger.yaml b/txmatching/web/swagger.yaml index a49ee6873..6e2d72b50 100644 --- a/txmatching/web/swagger.yaml +++ b/txmatching/web/swagger.yaml @@ -50,6 +50,13 @@ definitions: - hla_type - match_type type: object + BloodGroupEnum: + enum: + - A + - B + - AB + - '0' + type: string CalculatedMatchings: properties: calculated_matchings: @@ -447,7 +454,7 @@ definitions: Group compatibility index is calculated for.' - example: &id003 + example: &id002 - antibody_matches: - hla_antibody: code: A9 @@ -500,12 +507,7 @@ definitions: $ref: '#/definitions/DetailedScoreForGroup' type: array donor_type: - enum: - - DONOR - - BRIDGING_DONOR - - NON_DIRECTED - example: DONOR - type: string + $ref: '#/definitions/DonorType' medical_id: description: Medical id of the patient type: string @@ -527,20 +529,9 @@ definitions: DonorInput: properties: blood_group: - enum: - - A - - B - - AB - - '0' - example: A - type: string + $ref: '#/definitions/BloodGroupEnum' donor_type: - enum: - - DONOR - - BRIDGING_DONOR - - NON_DIRECTED - example: DONOR - type: string + $ref: '#/definitions/DonorType' height: description: Height of the patient in centimeters. example: 178 @@ -575,12 +566,7 @@ definitions: example: R1037 type: string sex: - description: Sex of the patient. - enum: - - M - - F - example: M - type: string + $ref: '#/definitions/SexEnum' weight: description: Weight of the patient in kilograms. example: 78.4 @@ -604,31 +590,25 @@ definitions: recipient: $ref: '#/definitions/RecipientInput' required: + - country_code - donor type: object DonorModelToUpdate: - properties: - active: - description: Information, whether or not given donor shall be considered - in exchange. - type: boolean - db_id: - description: Database id of the patient - example: 1 - type: integer - hla_typing: - allOf: - - $ref: '#/definitions/HlaTypingToUpdate' - description: Provide full list of all the HLA types of the patient, - not just the change set - example: &id002 - hla_types_list: - - raw_code: A*01:02 - - raw_code: B7 - - raw_code: DR11 - required: - - db_id - type: object + allOf: + - $ref: '#/definitions/PatientModelToUpdate' + - properties: + active: + description: Information, whether or not given donor shall be + considered in exchange. + type: boolean + type: object + DonorType: + description: Type of the donor. + enum: + - DONOR + - BRIDGING_DONOR + - NON_DIRECTED + type: string FailResponse: properties: error: @@ -893,16 +873,42 @@ definitions: - current_password - new_password type: object + PatientModelToUpdate: + properties: + blood_group: + $ref: '#/definitions/BloodGroupEnum' + db_id: + description: Database id of the patient + example: 1 + type: integer + height: + example: 180 + type: integer + hla_typing: + allOf: + - $ref: '#/definitions/HlaTypingToUpdate' + description: Provide full list of all the HLA types of the patient, + not just the change set + example: + hla_types_list: + - raw_code: A*01:02 + - raw_code: B7 + - raw_code: DR11 + sex: + $ref: '#/definitions/SexEnum' + weight: + example: 90 + type: number + year_of_birth: + example: 1990 + type: integer + required: + - db_id + type: object PatientParameters: properties: blood_group: - enum: - - A - - B - - AB - - '0' - example: A - type: string + $ref: '#/definitions/BloodGroupEnum' country_code: $ref: '#/definitions/CountryCode' height: @@ -910,15 +916,14 @@ definitions: hla_typing: $ref: '#/definitions/HlaTyping' sex: - enum: - - M - - F - example: M - type: string + $ref: '#/definitions/SexEnum' weight: type: number year_of_birth: type: integer + required: + - blood_group + - country_code type: object PatientUploadSuccessResponse: properties: @@ -947,13 +952,7 @@ definitions: properties: acceptable_blood_groups: items: - enum: - - A - - B - - AB - - '0' - example: A - type: string + $ref: '#/definitions/BloodGroupEnum' type: array db_id: description: Database id of the patient @@ -991,22 +990,10 @@ definitions: to use compatible blood groups. items: - enum: - - A - - B - - AB - - '0' - example: A - type: string + $ref: '#/definitions/BloodGroupEnum' type: array blood_group: - enum: - - A - - B - - AB - - '0' - example: A - type: string + $ref: '#/definitions/BloodGroupEnum' height: description: Height of the patient in centimeters. example: 178 @@ -1034,12 +1021,7 @@ definitions: description: Number of previous kidney transplants. type: integer sex: - description: Sex of the patient. - enum: - - M - - F - example: M - type: string + $ref: '#/definitions/SexEnum' waiting_since: description: Date since when the patient has been on waiting list. Use format YYYY-MM-DD. @@ -1061,50 +1043,43 @@ definitions: - medical_id type: object RecipientModelToUpdate: - properties: - acceptable_blood_groups: - description: Provide full list of all the acceptable blood groups - of the patient, not just the change set - items: - enum: - - A - - B - - AB - - '0' - example: A + allOf: + - $ref: '#/definitions/PatientModelToUpdate' + - properties: + acceptable_blood_groups: + description: Provide full list of all the acceptable blood groups + of the patient, not just the change set + items: + $ref: '#/definitions/BloodGroupEnum' + type: array + cutoff: + type: integer + hla_antibodies: + allOf: + - $ref: '#/definitions/HlaAntibodiesToUpdate' + description: Provide full list of all the HLA antibodies of the + patient, not just the change set + example: + hla_antibodies_list: + - mfi: 10000 + raw_code: A*01:02 + previous_transplants: + description: Number of previous kidney transplants. + type: integer + recipient_requirements: + allOf: + - $ref: '#/definitions/RecipientRequirements' + description: Provide the whole recipients requirements object, + it will be overwritten + example: + require_better_match_in_compatibility_index: true + waiting_since: + description: Date since when the patient has been on waiting list. + Use format YYYY-MM-DD. + example: '2015-01-17' + format: date type: string - type: array - cutoff: - type: integer - db_id: - description: Database id of the patient - example: 1 - type: integer - hla_antibodies: - allOf: - - $ref: '#/definitions/HlaAntibodiesToUpdate' - description: Provide full list of all the HLA antibodies of the patient, - not just the change set - example: - hla_antibodies_list: - - mfi: 10000 - raw_code: A*01:02 - hla_typing: - allOf: - - $ref: '#/definitions/HlaTypingToUpdate' - description: Provide full list of all the HLA types of the patient, - not just the change set - example: *id002 - recipient_requirements: - allOf: - - $ref: '#/definitions/RecipientRequirements' - description: Provide the whole recipients requirements object, it - will be overwritten - example: - require_better_match_in_compatibility_index: true - required: - - db_id - type: object + type: object RecipientRequirements: properties: require_better_match_in_compatibility_index: @@ -1140,6 +1115,12 @@ definitions: required: - status type: object + SexEnum: + description: Sex of the patient. + enum: + - M + - F + type: string StatusResponse: properties: status: @@ -1156,7 +1137,7 @@ definitions: Group compatibility index is calculated for.' - example: *id003 + example: *id002 items: $ref: '#/definitions/DetailedScoreForGroup' type: array