diff --git a/src/mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py b/src/mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py index c0a0d5d7d..0e31a24d0 100644 --- a/src/mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +++ b/src/mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py @@ -43,7 +43,10 @@ from mx_bluesky.common.parameters.gridscan import ( GridCommon, ) -from mx_bluesky.common.utils.exceptions import ISPyBDepositionNotMade +from mx_bluesky.common.utils.exceptions import ( + ISPyBDepositionNotMade, + SampleException, +) from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag if TYPE_CHECKING: @@ -281,5 +284,10 @@ def activity_gated_stop(self, doc: RunStop) -> RunStop: ) if self.ispyb_ids == IspybIds(): raise ISPyBDepositionNotMade("ispyb was not initialised at run start") + exception_type, message = SampleException.type_and_message_from_reason( + doc.get("reason", "") + ) + if exception_type: + doc["reason"] = message return super().activity_gated_stop(doc) return self._tag_doc(doc) diff --git a/src/mx_bluesky/common/utils/exceptions.py b/src/mx_bluesky/common/utils/exceptions.py index c242df11f..9cbd91e36 100644 --- a/src/mx_bluesky/common/utils/exceptions.py +++ b/src/mx_bluesky/common/utils/exceptions.py @@ -39,7 +39,8 @@ def type_and_message_from_reason(cls, reason: str) -> tuple[str, str]: class CrystalNotFoundException(SampleException): """Raised if grid detection completed normally but no crystal was found.""" - pass + def __init__(self, *args): + super().__init__("Diffraction not found, skipping sample.") def catch_exception_and_warn( diff --git a/tests/conftest.py b/tests/conftest.py index dce341575..a335fd8db 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -84,6 +84,9 @@ PlanNameConstants, TriggerConstants, ) +from mx_bluesky.common.utils.exceptions import ( + CrystalNotFoundException, +) from mx_bluesky.common.utils.log import ( ALL_LOGGERS, ISPYB_ZOCALO_CALLBACK_LOGGER, @@ -1215,6 +1218,13 @@ class TestData(OavGridSnapshotTestEvents): "subplan_name": PlanNameConstants.GRID_DETECT_AND_DO_GRIDSCAN, "mx_bluesky_parameters": dummy_params().model_dump_json(), } + test_gridscan3d_stop_document_with_crystal_exception: RunStop = { + "run_start": "d8bee3ee-f614-4e7a-a516-25d6b9e87ef3", + "time": 1666604299.6149616, + "uid": "65b2bde5-5740-42d7-9047-e860e06fbe15", + "exit_status": "fail", + "reason": f"{CrystalNotFoundException()}", + } test_gridscan2d_start_document = { "uid": "d8bee3ee-f614-4e7a-a516-25d6b9e87ef3", "time": 1666604299.6149616, diff --git a/tests/unit_tests/common/external_interaction/xray_centre/test_ispyb_callback.py b/tests/unit_tests/common/external_interaction/xray_centre/test_ispyb_callback.py index 28e96cb3a..0fbf38012 100644 --- a/tests/unit_tests/common/external_interaction/xray_centre/test_ispyb_callback.py +++ b/tests/unit_tests/common/external_interaction/xray_centre/test_ispyb_callback.py @@ -84,6 +84,23 @@ def test_activity_gated_start_3d(self, mock_ispyb_conn): mx_acq.upsert_data_collection.update_dc_position.assert_not_called() mx_acq.upsert_data_collection.upsert_dc_grid.assert_not_called() + def test_reason_provided_if_crystal_not_found_error(self, mock_ispyb_conn): + callback = GridscanISPyBCallback( + param_type=GridCommonWithHyperionDetectorParams + ) + callback.activity_gated_start(TestData.test_gridscan3d_start_document) # pyright: ignore + mx_acq = mx_acquisition_from_conn(mock_ispyb_conn) + callback.activity_gated_stop( + TestData.test_gridscan3d_stop_document_with_crystal_exception + ) + assert mx_acq.update_data_collection_append_comments.call_args_list[0] == ( + ( + TEST_DATA_COLLECTION_IDS[0], + "DataCollection Unsuccessful reason: Diffraction not found, skipping sample.", + " ", + ), + ) + def test_hardware_read_event_3d(self, mock_ispyb_conn): callback = GridscanISPyBCallback( param_type=GridCommonWithHyperionDetectorParams