From 3263af6370c43d71889f0297079bc2797e7d1ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Tovar?= Date: Sun, 12 Nov 2023 23:24:26 +0100 Subject: [PATCH 1/2] Corrects ping --- dispatch/target_types/xnat.py | 113 +++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/dispatch/target_types/xnat.py b/dispatch/target_types/xnat.py index 8a9a99ab..40500be8 100644 --- a/dispatch/target_types/xnat.py +++ b/dispatch/target_types/xnat.py @@ -17,9 +17,19 @@ import datetime import glob from pydicom import dcmread +from urllib.parse import urlparse logger = config.get_logger() + +def get_domain(url): + parsed_url = urlparse(url) + if parsed_url.scheme and parsed_url.netloc: + return parsed_url.netloc + else: + return None + + @handler_for(XnatTarget) class XnatTargetHandler(TargetHandler[XnatTarget]): view_template = "targets/xnat.html" @@ -29,21 +39,25 @@ class XnatTargetHandler(TargetHandler[XnatTarget]): display_name = "XNAT" def send_to_target( - self, task_id: str, target: XnatTarget, dispatch_info: TaskDispatch, source_folder: Path + self, + task_id: str, + target: XnatTarget, + dispatch_info: TaskDispatch, + source_folder: Path, ) -> str: try: - _send_dicom_to_xnat(target=target, folder=source_folder, dispatch_info=dispatch_info) + _send_dicom_to_xnat( + target=target, folder=source_folder, dispatch_info=dispatch_info + ) except ConnectionError as e: - self.handle_error(e, '') + self.handle_error(e, "") raise return "" - def handle_error(self, e, command) -> None: logger.error(e) - async def test_connection(self, target: XnatTarget, target_name: str): url = f"{target.host}/data/auth" @@ -51,12 +65,16 @@ async def test_connection(self, target: XnatTarget, target_name: str): ping_ok = False if target.host: - ping_result, *_ = await async_run(f"ping -w 1 -c 1 {target.host}") + ping_result, *_ = await async_run( + f"ping -w 1 -c 1 {get_domain(target.host)}" + ) if ping_result == 0: ping_ok = True - try: - async with session.get(url, auth=aiohttp.BasicAuth(target.user, target.password)) as resp: + try: + async with session.get( + url, auth=aiohttp.BasicAuth(target.user, target.password) + ) as resp: response_ok = resp.status == 200 text = await resp.text() if not response_ok else "" return dict(ping=ping_ok, loggedin=response_ok, err=text) @@ -66,31 +84,35 @@ async def test_connection(self, target: XnatTarget, target_name: str): def _send_dicom_to_xnat(target: XnatTarget, dispatch_info: TaskDispatch, folder: Path): - logger.info(f"Connecting to {dispatch_info.target_name}({target.host}) XNAT server...") - with InterfaceManager(server=target.host, user=target.user, password=target.password).open() as session: # type: ignore + logger.info( + f"Connecting to {dispatch_info.target_name}({target.host}) XNAT server..." + ) + with InterfaceManager(server=target.host, user=target.user, password=target.password).open() as session: # type: ignore project_id = target.project_id - dicom_file_path = glob.glob(os.path.join(folder, '*.dcm'))[0] + dicom_file_path = glob.glob(os.path.join(folder, "*.dcm"))[0] dcmFile = dcmread(dicom_file_path, stop_before_pixels=True) - subject_id = f'{dcmFile.PatientID}' - experiment_id = f"{subject_id}_{datetime.datetime.strptime(dcmFile.StudyDate, '%Y%m%d').strftime('%Y-%m-%d')}" # TODO make it more generic. + subject_id = f"{dcmFile.PatientID}" + experiment_id = f"{subject_id}_{datetime.datetime.strptime(dcmFile.StudyDate, '%Y%m%d').strftime('%Y-%m-%d')}" # TODO make it more generic. - logger.info(f'Uploading {folder} to {dispatch_info.target_name} ...') + logger.info(f"Uploading {folder} to {dispatch_info.target_name} ...") _upload_dicom_session_to_xnat( session=session, project_id=project_id, subject_id=subject_id, experiment_label=experiment_id, dicom_path=folder, - overwrite_dicom=True) + overwrite_dicom=True, + ) def _upload_dicom_session_to_xnat( - session : pyxnat.Interface, - project_id, - subject_id, - experiment_label, - dicom_path, - overwrite_dicom=True): + session: pyxnat.Interface, + project_id, + subject_id, + experiment_label, + dicom_path, + overwrite_dicom=True, +): """ Uploads the dicoms from the given path to an XNAT server using the Image Session Import Service API. If the dicom_path contains more than one scan, all will be uploaded to the session. @@ -105,35 +127,38 @@ def _upload_dicom_session_to_xnat( """ # Create a zip file with the dicom_path in a temporary directory with tempfile.TemporaryDirectory() as tmp_path: - zip_filepath = os.path.join(tmp_path, 'dicom.zip') + zip_filepath = os.path.join(tmp_path, "dicom.zip") # rename *.dcm files of dicom_path incrementaly and zip them - with zipfile.ZipFile(zip_filepath, 'w') as zip_file: + with zipfile.ZipFile(zip_filepath, "w") as zip_file: i = 0 for root, dirs, files in os.walk(dicom_path): for file in files: - if file.endswith('.dcm'): - zip_file.write(os.path.join(root, file), f'{i}.dcm') + if file.endswith(".dcm"): + zip_file.write(os.path.join(root, file), f"{i}.dcm") i += 1 # Upload zip file to XNAT server using the Image Session Import API - with open(zip_filepath, 'rb') as data: + with open(zip_filepath, "rb") as data: resp = session.post( - uri='/data/services/import', + uri="/data/services/import", params={ - 'PROJECT_ID': project_id, - 'SUBJECT_ID': subject_id, - 'EXPT_LABEL': experiment_label, - 'rename': 'true', - 'overwrite': 'delete' if overwrite_dicom else 'append', - 'inbody': 'true'}, - headers={ - 'Content-Type': 'application/zip'}, - data=data) + "PROJECT_ID": project_id, + "SUBJECT_ID": subject_id, + "EXPT_LABEL": experiment_label, + "rename": "true", + "overwrite": "delete" if overwrite_dicom else "append", + "inbody": "true", + }, + headers={"Content-Type": "application/zip"}, + data=data, + ) if resp.status_code != 200: - raise ConnectionError(f'Response not 200 OK while uploading DICOM with Image Session Import Service API. ' - f'Response code: {resp.status_code} ' - f'Response: {resp}') + raise ConnectionError( + f"Response not 200 OK while uploading DICOM with Image Session Import Service API. " + f"Response code: {resp.status_code} " + f"Response: {resp}" + ) class InterfaceManager(object): @@ -154,21 +179,21 @@ class InterfaceManager(object): session.disconnect() ``` """ + def __init__(self, server, user, password): self.host = server self.user = user self.psswd = password - @contextmanager def open(self): try: - sess = pyxnat.Interface(server=self.host, user=self.user, password=self.psswd) + sess = pyxnat.Interface( + server=self.host, user=self.user, password=self.psswd + ) yield sess finally: sess.disconnect() - def open_persistent(self): - return pyxnat.Interface(server=self.host, user=self.user, password=self.psswd) - \ No newline at end of file + return pyxnat.Interface(server=self.host, user=self.user, password=self.psswd) From 8db6f39d90597b40af1d27ebb74b3706520c396a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Tovar?= Date: Mon, 13 Nov 2023 00:10:25 +0100 Subject: [PATCH 2/2] Mypy pass for the get_domain function --- dispatch/target_types/xnat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dispatch/target_types/xnat.py b/dispatch/target_types/xnat.py index 40500be8..e9f507a9 100644 --- a/dispatch/target_types/xnat.py +++ b/dispatch/target_types/xnat.py @@ -22,12 +22,12 @@ logger = config.get_logger() -def get_domain(url): +def get_domain(url: str) -> str: parsed_url = urlparse(url) if parsed_url.scheme and parsed_url.netloc: return parsed_url.netloc else: - return None + return "" @handler_for(XnatTarget)