From 705cfbb21de9f98075a2d768e656a06b249f6595 Mon Sep 17 00:00:00 2001 From: Steve Laing Date: Tue, 17 Mar 2026 15:11:26 +0000 Subject: [PATCH 1/3] Support AccessionNumber exact matches in find worklist item query --- src/services/storage.py | 6 ++++ tests/services/test_storage.py | 57 ++++++++++++++-------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/services/storage.py b/src/services/storage.py index 3245a361..67f8e249 100644 --- a/src/services/storage.py +++ b/src/services/storage.py @@ -364,6 +364,7 @@ def store_worklist_item( def find_worklist_items( self, + accession_number: Optional[str] = None, modality: Optional[str] = None, scheduled_date: Optional[str] = None, patient_id: Optional[str] = None, @@ -372,6 +373,7 @@ def find_worklist_items( Query worklist items with optional filters. Args: + accession_number: Filter by accession number modality: Filter by modality (e.g., "MG") scheduled_date: Filter by scheduled date (YYYYMMDD) patient_id: Filter by patient ID @@ -388,6 +390,10 @@ def find_worklist_items( where_clauses = [] params = [] + if accession_number: + where_clauses.append("accession_number = ?") + params.append(accession_number) + if modality: where_clauses.append("modality = ?") params.append(modality) diff --git a/tests/services/test_storage.py b/tests/services/test_storage.py index 2435a503..90655a21 100644 --- a/tests/services/test_storage.py +++ b/tests/services/test_storage.py @@ -219,7 +219,16 @@ def test_find_worklist_items(self, mock_db, tmp_dir, result): assert len(results) == 1 assert results[0] == WorklistItem(**result) - def test_find_worklist_items_with_filters(self, mock_db, tmp_dir): + @pytest.mark.parametrize( + "query_param_name, query_param_value", + [ + ("accession_number", "ACC123456"), + ("patient_id", "999123456"), + ("modality", "CT"), + ("scheduled_date", "20240101"), + ], + ) + def test_find_worklist_items_with_filters(self, mock_db, tmp_dir, query_param_name, query_param_value): mock_cursor = MagicMock() mock_cursor.fetchall.return_value = [] mock_connection = MagicMock() @@ -229,47 +238,29 @@ def test_find_worklist_items_with_filters(self, mock_db, tmp_dir): subject = MWLStorage(tmp_dir) mock_connection.reset_mock() - subject.find_worklist_items(patient_id="999123456") + find_args = {query_param_name: query_param_value} + subject.find_worklist_items(**find_args) mock_connection.execute.assert_called_once_with( ( "SELECT accession_number, modality, patient_birth_date, patient_id, " "patient_name, patient_sex, procedure_code, scheduled_date, scheduled_time, " "source_message_id, study_description, study_instance_uid, status, mpps_instance_uid " - "FROM worklist_items WHERE patient_id = ? ORDER BY scheduled_date, scheduled_time" + f"FROM worklist_items WHERE {query_param_name} = ? ORDER BY scheduled_date, scheduled_time" ), - ["999123456"], + [query_param_value], ) - mock_connection.reset_mock() - subject.find_worklist_items(modality="CT") - - mock_connection.execute.assert_called_once_with( - ( - "SELECT accession_number, modality, patient_birth_date, patient_id, " - "patient_name, patient_sex, procedure_code, scheduled_date, scheduled_time, " - "source_message_id, study_description, study_instance_uid, status, mpps_instance_uid " - "FROM worklist_items WHERE modality = ? ORDER BY scheduled_date, scheduled_time" - ), - ["CT"], - ) - - mock_connection.reset_mock() - subject.find_worklist_items(scheduled_date="20240101") - - mock_connection.execute.assert_called_once_with( - ( - "SELECT accession_number, modality, patient_birth_date, patient_id, " - "patient_name, patient_sex, procedure_code, scheduled_date, scheduled_time, " - "source_message_id, study_description, study_instance_uid, status, mpps_instance_uid " - "FROM worklist_items WHERE scheduled_date = ? " - "ORDER BY scheduled_date, scheduled_time" - ), - ["20240101"], - ) + def test_find_worklist_items_with_multiple_filters(self, mock_db, tmp_dir): + mock_cursor = MagicMock() + mock_cursor.fetchall.return_value = [] + mock_connection = MagicMock() + mock_connection.execute.return_value = mock_cursor + mock_db.connect.return_value = mock_connection + subject = MWLStorage(tmp_dir) mock_connection.reset_mock() - subject.find_worklist_items(modality="MG", scheduled_date="20240101", patient_id="999123456") + subject.find_worklist_items(accession_number="ACC123456", modality="MG", scheduled_date="20240101", patient_id="999123456") mock_connection.execute.assert_called_once_with( ( @@ -277,10 +268,10 @@ def test_find_worklist_items_with_filters(self, mock_db, tmp_dir): "patient_name, patient_sex, procedure_code, scheduled_date, scheduled_time, " "source_message_id, study_description, study_instance_uid, status, mpps_instance_uid " "FROM worklist_items " - "WHERE modality = ? AND scheduled_date = ? AND patient_id = ? " + "WHERE accession_number = ? AND modality = ? AND scheduled_date = ? AND patient_id = ? " "ORDER BY scheduled_date, scheduled_time" ), - ["MG", "20240101", "999123456"], + ["ACC123456", "MG", "20240101", "999123456"], ) def test_get_worklist_item(self, mock_db, tmp_dir, result): From 4201eb2127f8d1029cba73d9f002b8f0c0dc8c11 Mon Sep 17 00:00:00 2001 From: Steve Laing Date: Tue, 17 Mar 2026 15:17:21 +0000 Subject: [PATCH 2/3] Support exact match on AccessionNumber in C-FIND query --- src/services/mwl/c_find.py | 8 +++++++- .../test_c_find_returns_worklist_items.py | 17 +++++++++++++++++ tests/services/mwl/test_c_find.py | 18 +++++++++++++++--- tests/services/test_storage.py | 4 +++- 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/services/mwl/c_find.py b/src/services/mwl/c_find.py index 04857e1e..c09a0286 100644 --- a/src/services/mwl/c_find.py +++ b/src/services/mwl/c_find.py @@ -44,13 +44,19 @@ def call(self, event: evt.Event) -> Iterator[Tuple[int, Dataset | None]]: procedure_sequence = identifier.get("ScheduledProcedureStepSequence", [{}]) query_modality = procedure_sequence[0].get("Modality") query_date = procedure_sequence[0].get("ScheduledProcedureStepStartDate") + query_accession_number = identifier.get("AccessionNumber") logger.debug( - "Query parameters: modality=%s, date=%s, patient_id=%s", query_modality, query_date, anonymised_patient_id + "Query parameters: accession_number=%s, modality=%s, date=%s, patient_id=%s", + query_accession_number, + query_modality, + query_date, + anonymised_patient_id, ) try: items = self.storage.find_worklist_items( + accession_number=query_accession_number if query_accession_number else None, modality=query_modality if query_modality else None, scheduled_date=query_date if query_date else None, patient_id=query_patient_id if query_patient_id else None, diff --git a/tests/integration/test_c_find_returns_worklist_items.py b/tests/integration/test_c_find_returns_worklist_items.py index ce60182e..921a07be 100644 --- a/tests/integration/test_c_find_returns_worklist_items.py +++ b/tests/integration/test_c_find_returns_worklist_items.py @@ -116,6 +116,23 @@ def test_cfind_filters_by_scheduled_date(self, event, storage): assert status == SUCCESS assert ds is None + def test_cfind_filters_by_accession_number(self, event, storage): + event.identifier.AccessionNumber = "ACC234567" + + results = list(CFind(storage).call(event)) + + assert len(results) == 2 + + status, ds = results[0] + assert status == PENDING + assert ds.PatientID == "999234567" + assert ds.PatientName == "JONES^MARY" + assert ds.AccessionNumber == "ACC234567" + + status, ds = results[1] + assert status == SUCCESS + assert ds is None + def test_cfind_filters_by_modality(self, event, storage): storage.store_worklist_item( WorklistItem( diff --git a/tests/services/mwl/test_c_find.py b/tests/services/mwl/test_c_find.py index f3a7804b..36d8c358 100644 --- a/tests/services/mwl/test_c_find.py +++ b/tests/services/mwl/test_c_find.py @@ -107,6 +107,16 @@ def test_call_with_multiple_results(self, handler, mock_storage, mock_event): status, ds = results[3] assert status == SUCCESS + def test_call_with_accession_number_filter(self, handler, mock_storage, mock_event): + mock_event.identifier.AccessionNumber = "ACC12345" + mock_storage.find_worklist_items.return_value = [] + + list(handler.call(mock_event)) + + mock_storage.find_worklist_items.assert_called_once_with( + accession_number="ACC12345", modality=None, scheduled_date=None, patient_id=None + ) + def test_call_with_modality_filter(self, handler, mock_storage, mock_event): # Add modality to query sps_item = Dataset() @@ -116,7 +126,9 @@ def test_call_with_modality_filter(self, handler, mock_storage, mock_event): list(handler.call(mock_event)) - mock_storage.find_worklist_items.assert_called_once_with(modality="MG", scheduled_date=None, patient_id=None) + mock_storage.find_worklist_items.assert_called_once_with( + accession_number=None, modality="MG", scheduled_date=None, patient_id=None + ) def test_call_with_date_filter(self, handler, mock_storage, mock_event): sps_item = Dataset() @@ -127,7 +139,7 @@ def test_call_with_date_filter(self, handler, mock_storage, mock_event): list(handler.call(mock_event)) mock_storage.find_worklist_items.assert_called_once_with( - modality=None, scheduled_date="20260107", patient_id=None + accession_number=None, modality=None, scheduled_date="20260107", patient_id=None ) def test_call_with_patient_id_filter(self, handler, mock_storage, mock_event): @@ -137,7 +149,7 @@ def test_call_with_patient_id_filter(self, handler, mock_storage, mock_event): list(handler.call(mock_event)) mock_storage.find_worklist_items.assert_called_once_with( - modality=None, scheduled_date=None, patient_id="9876543210" + accession_number=None, modality=None, scheduled_date=None, patient_id="9876543210" ) def test_call_handles_storage_exception(self, handler, mock_storage, mock_event): diff --git a/tests/services/test_storage.py b/tests/services/test_storage.py index 90655a21..f2e28cef 100644 --- a/tests/services/test_storage.py +++ b/tests/services/test_storage.py @@ -260,7 +260,9 @@ def test_find_worklist_items_with_multiple_filters(self, mock_db, tmp_dir): subject = MWLStorage(tmp_dir) mock_connection.reset_mock() - subject.find_worklist_items(accession_number="ACC123456", modality="MG", scheduled_date="20240101", patient_id="999123456") + subject.find_worklist_items( + accession_number="ACC123456", modality="MG", scheduled_date="20240101", patient_id="999123456" + ) mock_connection.execute.assert_called_once_with( ( From deb97daec11152c53313aa75055b6f67a5a47632 Mon Sep 17 00:00:00 2001 From: Steve Laing Date: Wed, 18 Mar 2026 10:06:24 +0000 Subject: [PATCH 3/3] Remove logging debug statement We can revisit this, CodeQL and SonarQube keep whining about it cos of sensitive information. --- src/services/mwl/c_find.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/services/mwl/c_find.py b/src/services/mwl/c_find.py index c09a0286..ac6a62c6 100644 --- a/src/services/mwl/c_find.py +++ b/src/services/mwl/c_find.py @@ -39,21 +39,12 @@ def call(self, event: evt.Event) -> Iterator[Tuple[int, Dataset | None]]: logger.info(f"C-FIND request from {requestor_aet}") query_patient_id = identifier.get("PatientID") - anonymised_patient_id = f"*******{query_patient_id[7:]}" if query_patient_id else "None" procedure_sequence = identifier.get("ScheduledProcedureStepSequence", [{}]) query_modality = procedure_sequence[0].get("Modality") query_date = procedure_sequence[0].get("ScheduledProcedureStepStartDate") query_accession_number = identifier.get("AccessionNumber") - logger.debug( - "Query parameters: accession_number=%s, modality=%s, date=%s, patient_id=%s", - query_accession_number, - query_modality, - query_date, - anonymised_patient_id, - ) - try: items = self.storage.find_worklist_items( accession_number=query_accession_number if query_accession_number else None,