Skip to content

Commit 5001ca1

Browse files
authored
Merge pull request #174 from openradx/more-dicomweb-routes
More dicomweb routes
2 parents 05ba97e + a814fa7 commit 5001ca1

File tree

23 files changed

+539
-313
lines changed

23 files changed

+539
-313
lines changed

TODO.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@
122122
- <https://playwright.dev/python/docs/auth>
123123
- Unfortunately, we can't use live_server fixture inside session fixtures
124124
- example <https://github.com/automationneemo/PlaywrightDemoYt>
125-
- Get rid of dicom_connector.download_study/move_study. Do everything at the series level. That way filtering series (e.g. exlcude modalities) is much easier.
126125
- Evaluate if services should be better restarted with pywatchman instead of watchdog and watchmedo
127126
- pywatchman is used by Django autoreload
128127
- See <https://github.com/django/django/blob/main/django/utils/autoreload.py>

adit/batch_query/processors.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from django.conf import settings
21
from django.template.defaultfilters import pluralize
32

43
from adit.core.errors import DicomError
@@ -97,15 +96,7 @@ def _find_studies(self, patient_id: str) -> list[ResultDataset]:
9796
end_date = self.query_task.study_date_end
9897
study_date = (start_date, end_date)
9998

100-
modalities_query: list[str] = []
101-
if self.query_task.modalities:
102-
modalities_query = [
103-
modality
104-
for modality in self.query_task.modalities
105-
if modality not in settings.EXCLUDED_MODALITIES
106-
]
107-
108-
if not modalities_query:
99+
if not self.query_task.modalities:
109100
study_results = list(
110101
self.operator.find_studies(
111102
QueryDataset.create(
@@ -121,7 +112,9 @@ def _find_studies(self, patient_id: str) -> list[ResultDataset]:
121112
else:
122113
seen: set[str] = set()
123114
study_results: list[ResultDataset] = []
124-
for modality in modalities_query:
115+
for modality in self.query_task.modalities:
116+
# ModalitiesInStudy does not support to query multiple modalities at once,
117+
# so we have to query them one by one.
125118
studies = list(
126119
self.operator.find_studies(
127120
QueryDataset.create(

adit/core/processors.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ def _download_to_folder(
151151
download_folder: Path,
152152
) -> Path:
153153
pseudonym = self.transfer_task.pseudonym
154+
154155
if pseudonym:
155156
patient_folder = download_folder / sanitize_filename(pseudonym)
156157
else:
@@ -160,9 +161,11 @@ def _download_to_folder(
160161
study = self._find_study()
161162
modalities = study.ModalitiesInStudy
162163

163-
modalities = [
164-
modality for modality in modalities if modality not in settings.EXCLUDED_MODALITIES
165-
]
164+
exclude_modalities = settings.EXCLUDE_MODALITIES
165+
if pseudonym and exclude_modalities:
166+
# When we download the study we will exclude the specified modalities, but only
167+
# if we are pseudonymizing the study, see also _download_study().
168+
modalities = [modality for modality in modalities if modality not in exclude_modalities]
166169

167170
# If some series are explicitly chosen then check if their Series Instance UIDs
168171
# are correct and only use those modalities for the name of the study folder.
@@ -310,16 +313,64 @@ def callback(ds: Dataset | None) -> None:
310313
file_path = final_folder / file_name
311314
write_dataset(ds, file_path)
312315

316+
pseudonymize = bool(self.transfer_task.pseudonym)
317+
exclude_modalities = settings.EXCLUDE_MODALITIES
318+
313319
if series_uids:
320+
# If specific series are selected we transfer only those series. When pseudonymizing
321+
# we have to check if a modality should be excluded.
322+
if pseudonymize and exclude_modalities:
323+
filtered_series = []
324+
for series_uid in series_uids:
325+
series_list = list(
326+
self.source_operator.find_series(
327+
QueryDataset.create(
328+
PatientID=patient_id,
329+
StudyInstanceUID=study_uid,
330+
SeriesInstanceUID=series_uid,
331+
)
332+
)
333+
)
334+
if not series_list:
335+
logger.warning(f"Series with UID {series_uid} not found.")
336+
continue
337+
338+
assert len(series_list) == 1
339+
series = series_list[0]
340+
341+
if series.Modality in exclude_modalities:
342+
continue
343+
344+
filtered_series.append(series)
345+
346+
series_uids = [series.SeriesInstanceUID for series in filtered_series]
347+
314348
for series_uid in series_uids:
315349
self.source_operator.fetch_series(
316350
patient_id=patient_id,
317351
study_uid=study_uid,
318352
series_uid=series_uid,
319353
callback=callback,
320354
)
355+
356+
elif pseudonymize:
357+
# If the whole study should be transferred and pseudonymized, we transfer on the
358+
# series level to exclude the specified modalities.
359+
series_list = list(
360+
self.source_operator.find_series(
361+
QueryDataset.create(PatientID=patient_id, StudyInstanceUID=study_uid)
362+
)
363+
)
364+
for series in series_list:
365+
series_uid = series.SeriesInstanceUID
366+
modality = series.Modality
367+
if modality in settings.EXCLUDE_MODALITIES:
368+
continue
369+
370+
self.source_operator.fetch_series(patient_id, study_uid, series_uid, callback)
371+
321372
else:
322-
# If no series are explicitly chosen then download all series of the study
373+
# Without pseudonymization we transfer the whole study as it is.
323374
self.source_operator.fetch_study(
324375
patient_id=patient_id,
325376
study_uid=study_uid,

adit/core/templatetags/core_extras.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import logging
22

3-
from django.conf import settings
43
from django.template import Library
54

65
from ..models import DicomJob, DicomTask
@@ -19,12 +18,6 @@ def person_name_from_dicom(value: str) -> str:
1918
return value.replace("^", ", ")
2019

2120

22-
@register.simple_tag
23-
def filter_modalities(modalities: list[str]) -> list[str]:
24-
exclude_modalities = settings.EXCLUDED_MODALITIES
25-
return [modality for modality in modalities if modality not in exclude_modalities]
26-
27-
2821
@register.filter
2922
def dicom_job_status_css_class(status: DicomJob.Status) -> str:
3023
css_classes = {

adit/core/utils/dicom_dataset.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ def create(
180180
SeriesDescription: str | type[_NoValue] = _NoValue,
181181
SeriesNumber: int | str | type[_NoValue] = _NoValue,
182182
Modality: str | type[_NoValue] = _NoValue,
183+
SOPInstanceUID: str | type[_NoValue] = _NoValue,
183184
) -> "QueryDataset":
184185
"""A helper factory method for type hinting query parameters."""
185186
ds = Dataset()

0 commit comments

Comments
 (0)