diff --git a/src/aind_metadata_service/__init__.py b/src/aind_metadata_service/__init__.py index eaaa3b51..991e4187 100644 --- a/src/aind_metadata_service/__init__.py +++ b/src/aind_metadata_service/__init__.py @@ -1,3 +1,3 @@ """REST service to retrieve metadata from databases.""" -__version__ = "0.18.0" +__version__ = "0.18.1" diff --git a/src/aind_metadata_service/response_handler.py b/src/aind_metadata_service/response_handler.py index b36c5dec..fd37442c 100644 --- a/src/aind_metadata_service/response_handler.py +++ b/src/aind_metadata_service/response_handler.py @@ -1,6 +1,7 @@ """Module to handle responses""" import json +import logging from typing import Generic, List, Optional, TypeVar, Union from aind_data_schema.core.instrument import Instrument @@ -157,7 +158,7 @@ def _map_data_response( # noqa: C901 validation_errors = [] for model in self.aind_models: error = self._validate_model(model) - print(error) + logging.error(error) if error: validation_errors.append(error) diff --git a/src/aind_metadata_service/smartsheet/protocols/mapping.py b/src/aind_metadata_service/smartsheet/protocols/mapping.py index 5c6c34ce..b32814d1 100644 --- a/src/aind_metadata_service/smartsheet/protocols/mapping.py +++ b/src/aind_metadata_service/smartsheet/protocols/mapping.py @@ -148,6 +148,8 @@ def get_protocols_list(self, response: ModelResponse) -> List: for subject_procedure in procedures.subject_procedures: if isinstance(subject_procedure, Surgery): protocol_list.append(ProtocolNames.SURGERY.value) + if not hasattr(subject_procedure, "procedures"): + continue for procedure in subject_procedure.procedures: protocol_name = self._get_protocol_name( procedure=procedure @@ -199,6 +201,9 @@ def integrate_protocols( pass else: status_code = StatusCodes.MULTI_STATUS + if not hasattr(subject_procedure, "procedures"): + output_aind_models = [pre_procedures] + continue for procedure in subject_procedure.procedures: protocol_name = self._get_protocol_name(procedure) smartsheet_response = protocols_mapping.get(protocol_name) diff --git a/src/aind_metadata_service/tars/mapping.py b/src/aind_metadata_service/tars/mapping.py index b5f29ea1..17e4024d 100644 --- a/src/aind_metadata_service/tars/mapping.py +++ b/src/aind_metadata_service/tars/mapping.py @@ -215,6 +215,8 @@ def get_virus_strains(response: ModelResponse) -> List: if len(response.aind_models) > 0: procedures = response.aind_models[0] for subject_procedure in procedures.subject_procedures: + if not hasattr(subject_procedure, "procedures"): + continue for procedure in subject_procedure.procedures: if ( isinstance(procedure, Injection) @@ -244,6 +246,9 @@ def integrate_injection_materials( if len(response.aind_models) > 0: pre_procedures = response.aind_models[0] for subject_procedure in pre_procedures.subject_procedures: + if not hasattr(subject_procedure, "procedures"): + output_aind_models = [pre_procedures] + continue for procedure in subject_procedure.procedures: if isinstance(procedure, Injection) and hasattr( procedure, "injection_materials" diff --git a/tests/smartsheet/test_protocols.py b/tests/smartsheet/test_protocols.py index 5f9d2f68..5d99bb6d 100644 --- a/tests/smartsheet/test_protocols.py +++ b/tests/smartsheet/test_protocols.py @@ -254,6 +254,20 @@ def test_get_protocols_list(self): ] self.assertEqual(expected_list, protocols_list) + def test_get_protocols_list_missing_procedures(self): + """Tests that protocols mapping is crated as expected when objects + are missing procedures.""" + surgery = Surgery.model_construct() + procedures = Procedures.model_construct(subject_procedures=[surgery]) + response = ModelResponse( + aind_models=[procedures], status_code=StatusCodes.DB_RESPONDED + ) + protocols_list = self.protocols_integrator.get_protocols_list(response) + expected_list = [ + ProtocolNames.SURGERY.value, + ] + self.assertEqual(expected_list, protocols_list) + def test_integrate_protocols(self): """Tests that protocols are integrated into procedures response as expected""" @@ -321,6 +335,65 @@ def test_integrate_protocols(self): expected_response.status_code, merged_response.status_code ) + def test_integrate_protocols_missing_procedures(self): + """Tests that protocols are integrated into procedures + response as expected when objects are missing procedures attribute""" + nano_protocol = ProtocolInformation.model_construct( + protocol_type="Surgical Procedures", + procedure_name="Injection Nanoject", + protocol_name=self.nano_name, + doi="dx.doi.org/some/doi/1", + version="1.0", + protocol_collection=None, + ) + surgery_protocol = ProtocolInformation.model_construct( + protocol_type="Surgical Procedures", + procedure_name="Surgery", + protocol_name=self.surgery_name, + doi="dx.doi.org/some/doi/2", + version="1.0", + protocol_collection=None, + ) + protocols_response1 = ModelResponse( + aind_models=[nano_protocol], status_code=StatusCodes.DB_RESPONDED + ) + protocols_response2 = ModelResponse( + aind_models=[surgery_protocol], + status_code=StatusCodes.DB_RESPONDED, + ) + protocols_mapping = { + self.nano_name: protocols_response1.map_to_json_response(), + self.surgery_name: protocols_response2.map_to_json_response(), + } + surgery = Surgery.model_construct(experimenter_full_name="NSB-123") + procedures_response = ModelResponse( + aind_models=[ + Procedures(subject_id="12345", subject_procedures=[surgery]) + ], + status_code=StatusCodes.DB_RESPONDED, + ) + merged_response = self.protocols_integrator.integrate_protocols( + response=procedures_response, protocols_mapping=protocols_mapping + ) + expected_surgery = Surgery.model_construct( + protocol_id="dx.doi.org/some/doi/2", + experimenter_full_name="NSB-123", + ) + expected_response = ModelResponse( + aind_models=[ + Procedures( + subject_id="12345", subject_procedures=[expected_surgery] + ) + ], + status_code=StatusCodes.DB_RESPONDED, + ) + self.assertEqual( + expected_response.aind_models, merged_response.aind_models + ) + self.assertEqual( + expected_response.status_code, merged_response.status_code + ) + def test_integrate_protocols_error(self): """Tests that injection materials are integrated into procedures response as expected""" diff --git a/tests/tars/test_mapping.py b/tests/tars/test_mapping.py index 1fae9931..f24339dc 100644 --- a/tests/tars/test_mapping.py +++ b/tests/tars/test_mapping.py @@ -314,6 +314,20 @@ def test_get_virus_strains(self): ) self.assertEqual(empty_virus_strains, []) + def test_get_virus_strains_no_procedures(self): + """Tests that virus strains are retrieved as expected when objects + are missing procedures attribute.""" + surgery = Surgery.model_construct() + procedures_response = ModelResponse( + aind_models=[ + Procedures(subject_id="12345", subject_procedures=[surgery]) + ], + status_code=StatusCodes.DB_RESPONDED, + ) + virus_strains = self.handler.get_virus_strains(procedures_response) + expected_virus_strains = [] + self.assertEqual(virus_strains, expected_virus_strains) + def test_integrate_injection_materials(self): """Tests that injection materials are integrated into procedures response as expected""" @@ -378,6 +392,47 @@ def test_integrate_injection_materials(self): merged_response.aind_models, expected_merged_response.aind_models ) + def test_integrate_injection_materials_no_procedures(self): + """Tests that injection materials are integrated into + procedures response as expected when objects are missing procedures.""" + expected_injection_material = ViralMaterial.model_construct( + name="rAAV-MGT_789", + tars_identifiers=TarsVirusIdentifiers.model_construct( + virus_tars_id="AiV456", + plasmid_tars_alias="AiP123", + prep_lot_number="12345", + prep_date=date(2023, 12, 15), + prep_type=VirusPrepType.CRUDE, + prep_protocol="SOP#VC002", + ), + ) + tars_response1 = ModelResponse( + aind_models=[expected_injection_material], + status_code=StatusCodes.DB_RESPONDED, + ) + tars_mapping = { + "12345": tars_response1.map_to_json_response(), + } + surgery = Surgery.model_construct() + procedures_response = ModelResponse( + aind_models=[ + Procedures(subject_id="12345", subject_procedures=[surgery]) + ], + status_code=StatusCodes.DB_RESPONDED, + ) + merged_response = self.handler.integrate_injection_materials( + response=procedures_response, tars_mapping=tars_mapping + ) + expected_merged_response = ModelResponse( + aind_models=[ + Procedures(subject_id="12345", subject_procedures=[surgery]) + ], + status_code=StatusCodes.DB_RESPONDED, + ) + self.assertEqual( + expected_merged_response.aind_models, merged_response.aind_models + ) + def test_integrate_injection_materials_error(self): """Tests that injection materials are integrated into procedures response as expected"""