diff --git a/pyorthanc/_resources/instance.py b/pyorthanc/_resources/instance.py index 646bbc8..c8ac044 100644 --- a/pyorthanc/_resources/instance.py +++ b/pyorthanc/_resources/instance.py @@ -42,13 +42,15 @@ def get_dicom_file_content(self) -> bytes: """ return self.client.get_instances_id_file(self.id_) - def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) -> None: + def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False, file_format: str = 'dcm') -> None: """Download the DICOM file to a target path or buffer This method is an alternative to the `.get_dicom_file_content()` method for large files. The `.get_dicom_file_content()` method will pull all the data in a single GET call, while `.download()` stream the data to a file or a buffer. Favor the `.download()` method to avoid timeout and memory issues. + file_format option accepts 'dcm', 'nii', 'nii.gz' + Examples -------- @@ -68,9 +70,19 @@ def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) - # Now do whatever you want to do buffer.seek(0) dicom_bytes = buffer.read() + + # download instance in nii.gz format + instance.download('instance.nii.gz', file_format='nii.gz') + ``` """ - self._download_file(f'{self.client.url}/instances/{self.id_}/file', filepath, with_progres) + if file_format.lower() not in ['dcm', 'nii', 'nii.gz']: + raise ValueError( + f"format should be one of ['dcm, 'nii', 'nii.gz'], " + f"got '{file_format}' instead." + ) + + self._download_file(f'{self.client.url}/instances/{self.id_}', filepath, with_progres, file_format) @property def uid(self) -> str: diff --git a/pyorthanc/_resources/patient.py b/pyorthanc/_resources/patient.py index 84fa5da..e42094e 100644 --- a/pyorthanc/_resources/patient.py +++ b/pyorthanc/_resources/patient.py @@ -139,7 +139,7 @@ def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) - zip_bytes = buffer.read() ``` """ - self._download_file(f'{self.client.url}/patients/{self.id_}/archive', filepath, with_progres) + self._download_file(f'{self.client.url}/patients/{self.id_}', filepath, with_progres, 'zip') def get_patient_module(self, simplify: bool = False, short: bool = False) -> Dict: """Get patient module in a simplified version diff --git a/pyorthanc/_resources/resource.py b/pyorthanc/_resources/resource.py index ec837ef..b14af92 100644 --- a/pyorthanc/_resources/resource.py +++ b/pyorthanc/_resources/resource.py @@ -1,10 +1,12 @@ import abc from typing import Any, BinaryIO, Dict, List, Optional, Union +from httpx import HTTPError from httpx._types import QueryParamTypes from .. import errors, util from ..client import Orthanc +from ..errors import PluginNotEnabledError class Resource: @@ -76,11 +78,38 @@ def _make_response_format_params(self, simplify: bool = False, short: bool = Fal return params + def _is_neuro_plugin_installed(self): + try: + _ = self.client.get_plugins_id(id_='neuro') + return True + except HTTPError: + return False + def _download_file( self, url: str, filepath: Union[str, BinaryIO], with_progress: bool = False, + file_format: str = 'zip', params: Optional[QueryParamTypes] = None): + + if file_format.lower() == 'zip': + url = f'{url}/archive' + elif file_format.lower() == 'dcm': + url = f'{url}/file' + elif file_format.lower() in ['nii', 'nii.gz']: + if not self._is_neuro_plugin_installed(): + raise PluginNotEnabledError( + 'Neuro plugin is not installed or enabled on Orthanc instance. More information on https://orthanc.uclouvain.be/book/plugins/neuro.html' + ) + url = f'{url}/nifti' + if file_format.lower() == 'nii.gz': + url += '?compress' + else: + raise ValueError( + f"format should be one of ['dcm, 'zip', 'nii', 'nii.gz'], " + f"got '{file_format}' instead." + ) + # Check if filepath is a path or a file object. if isinstance(filepath, str): is_file_object = False diff --git a/pyorthanc/_resources/series.py b/pyorthanc/_resources/series.py index 91bffa4..998220e 100644 --- a/pyorthanc/_resources/series.py +++ b/pyorthanc/_resources/series.py @@ -584,13 +584,14 @@ def get_zip(self) -> bytes: """ return self.client.get_series_id_archive(self.id_) - def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) -> None: + def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False, file_format: str = 'zip') -> None: """Download the zip file to a target path or buffer This method is an alternative to the `.get_zip()` method for large files. The `.get_zip()` method will pull all the data in a single GET call, while `.download()` stream the data to a file or a buffer. Favor the `.download()` method to avoid timeout and memory issues. + file_format option accepts 'zip', 'nii', 'nii.gz' Examples -------- @@ -610,9 +611,17 @@ def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) - # Now do whatever you want to do buffer.seek(0) zip_bytes = buffer.read() + + # download series in nii.gz format + a_series.download('series.nii.gz', file_format='nii.gz') ``` """ - self._download_file(f'{self.client.url}/series/{self.id_}/archive', filepath, with_progres) + if file_format.lower() not in ['zip', 'nii', 'nii.gz']: + raise ValueError( + f"format should be one of ['zip', 'nii', 'nii.gz'], " + f"got '{file_format}' instead." + ) + self._download_file(f'{self.client.url}/series/{self.id_}', filepath, with_progres, file_format) def get_shared_tags(self, simplify: bool = False, short: bool = False) -> Dict: """Retrieve the shared tags of the series""" diff --git a/pyorthanc/_resources/study.py b/pyorthanc/_resources/study.py index 1bd50a3..b88b9dc 100644 --- a/pyorthanc/_resources/study.py +++ b/pyorthanc/_resources/study.py @@ -563,7 +563,7 @@ def download(self, filepath: Union[str, BinaryIO], with_progres: bool = False) - zip_bytes = buffer.read() ``` """ - self._download_file(f'{self.client.url}/studies/{self.id_}/archive', filepath, with_progres) + self._download_file(f'{self.client.url}/studies/{self.id_}', filepath, with_progres, 'zip') def get_shared_tags(self, simplify: bool = False, short: bool = False) -> Dict: """Retrieve the shared tags of the study""" diff --git a/pyorthanc/errors.py b/pyorthanc/errors.py index 32c81b0..274b03e 100644 --- a/pyorthanc/errors.py +++ b/pyorthanc/errors.py @@ -8,3 +8,7 @@ class ModificationError(Exception): class NotInInternalEnvironmentError(Exception): pass + + +class PluginNotEnabledError(Exception): + pass