From c6c6df5c4b857af3a10b1f03ef734a11ee25f903 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Thu, 18 Jul 2024 16:42:59 -0400 Subject: [PATCH 01/20] added telemetry call --- pypet2bids/pypet2bids/dcm2niix4pet.py | 32 +++++++++ pypet2bids/pypet2bids/telemetry.py | 94 +++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 pypet2bids/pypet2bids/telemetry.py diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index a05380d..e1a1f77 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -42,6 +42,7 @@ metadata_dictionaries, get_metadata_from_spreadsheet, ) + from telemetry import send_telemetry, count_input_files, enable_disable_telemetry, count_output_files except ModuleNotFoundError: import pypet2bids.helper_functions as helper_functions import pypet2bids.is_pet as is_pet @@ -54,6 +55,7 @@ metadata_dictionaries, get_metadata_from_spreadsheet, ) + from pypet2bids.telemetry import send_telemetry, count_input_files, enable_disable_telemetry, count_output_files logger = helper_functions.logger("pypet2bids") @@ -319,6 +321,7 @@ def __init__( # next we use the loaded python script to extract the information we need self.load_spread_sheet_data() elif metadata_path and not metadata_translation_script or metadata_path == "": + self.metadata_path = Path(metadata_path) if not self.spreadsheet_metadata.get("nifti_json", None): self.spreadsheet_metadata["nifti_json"] = {} @@ -833,9 +836,38 @@ def post_dcm2niix(self): json.dump(blood_json_data, outfile, indent=4) def convert(self): + # check the size of out the output folder + before_output_files = {} + if self.destination_path.exists(): + before_output_files = count_output_files(self.destination_path) self.run_dcm2niix() self.post_dcm2niix() + # if telemetry isn't disabled we send a telemetry event to the pypet2bids server + if enable_disable_telemetry: + # count the number of files + data_out = count_input_files(self.image_folder) + # record if a blood tsv and json file were created + if self.spreadsheet_metadata.get("blood_tsv", {}): + data_out["blood_tsv"] = True + if self.spreadsheet_metadata.get("blood_json", {}): + data_out["blood_json"] = True + # record if a metadata spreadsheet was used + if self.metadata_path == "" or self.metadata_path is not None: + data_out["metadata_spreadsheet_used"] = True + + # count the output files + after_output_files = count_output_files(self.destination_path) + + if before_output_files != {}: + for key, value in after_output_files.items(): + data_out[key] = abs(after_output_files[key] - before_output_files[key]) + + send_telemetry(data_out) + + + + def match_dicom_header_to_file(self, destination_path=None): """ Matches a dicom header to a nifti or json file produced by dcm2niix, this is run after dcm2niix converts the diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py new file mode 100644 index 0000000..8d132a3 --- /dev/null +++ b/pypet2bids/pypet2bids/telemetry.py @@ -0,0 +1,94 @@ +import requests +import os +import pathlib +from dotenv import load_dotenv +from typing import Union + + +# collect pet2bids version +from pypet2bids.helper_functions import get_version + + +def enable_disable_telemetry(config_path=None): + # load dcm2niix file + if not config_path: + config_file = pathlib.Path.home() / ".pet2bidsconfig" + else: + config_file = pathlib.Path(config_path) + + if config_file.exists(): + load_dotenv(dotenv_path=config_file) + + # check to see if telemetry is disabled + if os.getenv("PET2BIDS_TELEMETRY_ENABLED") == "False".upper(): + return False + else: + return True + + +def send_telemetry(json_data: dict, url: str = "http://52.87.154.236/telemetry/"): + if enable_disable_telemetry(): + # update data with version of pet2bids + json_data['pypet2bids_version'] = get_version() + # Send a POST request to the telemetry server + requests.post(url, json=json_data) + else: + pass + + +def count_input_files(input_file_path: Union[str, pathlib.Path]): + # Count the number of input files in the input file path + if isinstance(input_file_path, str): + input_file_path = pathlib.Path(input_file_path) + + if input_file_path.is_dir(): + # collect all files in the input dir + all_files = [f for f in input_file_path.iterdir()] + # count the number of dicom files + num_dicom_files = 0 + dicom_files_size = 0 + total_files = 0 + total_files_size = 0 + for file in all_files: + if file.is_file(): + total_files += 1 + total_files_size += file.stat().st_size + if file.suffix == ".dcm" or file.suffix == ".ima" or file.suffix == ".img" or file.suffix == "": + num_dicom_files += 1 + dicom_files_size += file.stat().st_size + return { + "TotalInputFiles": total_files, + "TotalInputFilesSize": total_files_size, + "DicomFiles": num_dicom_files, + "DicomFilesSize": dicom_files_size + } + + +def count_output_files(output_file_path: Union[str, pathlib.Path]): + if isinstance(output_file_path, str): + output_file_path = pathlib.Path(output_file_path) + + if output_file_path.is_file(): + output_file_path = output_file_path.parent + + if output_file_path.is_dir(): + # collect all files in the output dir + all_files = [f for f in output_file_path.iterdir()] + # count the number nifti files + num_nifti_files = 0 + nifti_files_size = 0 + total_files = 0 + total_files_size = 0 + for file in all_files: + if file.is_file(): + total_files += 1 + total_files_size += file.stat().st_size + if file.suffix == ".nii" or file.suffix == ".nii.gz": + num_nifti_files += 1 + nifti_files_size += file.stat().st_size + return { + "TotalOutputFiles": total_files, + "TotalOutputFilesSize": total_files_size, + "NiftiFiles": num_nifti_files, + "NiftiFilesSize": nifti_files_size + } \ No newline at end of file From 5a84ebbe5611ebaf07dd9e763f51703f76fa821e Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:59:07 -0400 Subject: [PATCH 02/20] added file count to telemetery tracking for dcm2niix --- pypet2bids/pypet2bids/dcm2niix4pet.py | 25 +++++++++++-------------- pypet2bids/pypet2bids/telemetry.py | 15 +++++---------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index e1a1f77..7c43bc6 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -204,7 +204,7 @@ def __init__( # check to see if dcm2niix is installed self.blood_json = None self.blood_tsv = None - + self.telemetry_data = {} self.dcm2niix_path = self.check_for_dcm2niix() if not self.dcm2niix_path: logger.error( @@ -455,10 +455,15 @@ def run_dcm2niix(self): file_format_args = "" with TemporaryDirectory(dir=self.tempdir_location) as tempdir: tempdir_pathlike = Path(tempdir) + self.tempdir_location = tempdir_pathlike # people use screwy paths, we do this before running dcm2niix to account for that image_folder = helper_functions.sanitize_bad_path(self.image_folder) cmd = f"{self.dcm2niix_path} -b y -w 1 -z y {file_format_args} -o {tempdir_pathlike} {image_folder}" convert = subprocess.run(cmd, shell=True, capture_output=True) + self.telemetry_data['dcm2niix'] = { + "returncode": convert.returncode, + } + self.telemetry_data.update(count_output_files(self.tempdir_location)) if convert.returncode != 0: print( @@ -838,32 +843,24 @@ def post_dcm2niix(self): def convert(self): # check the size of out the output folder before_output_files = {} - if self.destination_path.exists(): - before_output_files = count_output_files(self.destination_path) self.run_dcm2niix() self.post_dcm2niix() # if telemetry isn't disabled we send a telemetry event to the pypet2bids server if enable_disable_telemetry: # count the number of files - data_out = count_input_files(self.image_folder) + self.telemetry_data.update(count_input_files(self.image_folder)) # record if a blood tsv and json file were created if self.spreadsheet_metadata.get("blood_tsv", {}): - data_out["blood_tsv"] = True + self.telemetry_data["blood_tsv"] = True if self.spreadsheet_metadata.get("blood_json", {}): - data_out["blood_json"] = True + self.telemetry_data["blood_json"] = True # record if a metadata spreadsheet was used if self.metadata_path == "" or self.metadata_path is not None: - data_out["metadata_spreadsheet_used"] = True - - # count the output files - after_output_files = count_output_files(self.destination_path) + self.telemetry_data["metadata_spreadsheet_used"] = True - if before_output_files != {}: - for key, value in after_output_files.items(): - data_out[key] = abs(after_output_files[key] - before_output_files[key]) - send_telemetry(data_out) + send_telemetry(self.telemetry_data) diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index 8d132a3..746a9b3 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -53,14 +53,9 @@ def count_input_files(input_file_path: Union[str, pathlib.Path]): if file.is_file(): total_files += 1 total_files_size += file.stat().st_size - if file.suffix == ".dcm" or file.suffix == ".ima" or file.suffix == ".img" or file.suffix == "": - num_dicom_files += 1 - dicom_files_size += file.stat().st_size return { "TotalInputFiles": total_files, "TotalInputFilesSize": total_files_size, - "DicomFiles": num_dicom_files, - "DicomFilesSize": dicom_files_size } @@ -79,13 +74,13 @@ def count_output_files(output_file_path: Union[str, pathlib.Path]): nifti_files_size = 0 total_files = 0 total_files_size = 0 - for file in all_files: - if file.is_file(): + for f in all_files: + if f.is_file(): total_files += 1 - total_files_size += file.stat().st_size - if file.suffix == ".nii" or file.suffix == ".nii.gz": + total_files_size += f.stat().st_size + if str(f).endswith('.nii') or str(f).endswith('.nii.gz'): num_nifti_files += 1 - nifti_files_size += file.stat().st_size + nifti_files_size += f.stat().st_size return { "TotalOutputFiles": total_files, "TotalOutputFilesSize": total_files_size, From 6b53ba4af4c14bb403322e9df2a39ebe0660423f Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 24 Jul 2024 19:13:00 -0400 Subject: [PATCH 03/20] added python functionality --- pypet2bids/pypet2bids/dcm2niix4pet.py | 32 +++++++++++++++-------- pypet2bids/pypet2bids/ecat.py | 26 ++++++++++++++++++ pypet2bids/pypet2bids/helper_functions.py | 10 +++---- pypet2bids/pypet2bids/telemetry.py | 24 ++++++++++++----- 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index 7c43bc6..bb2b0e9 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -42,7 +42,12 @@ metadata_dictionaries, get_metadata_from_spreadsheet, ) - from telemetry import send_telemetry, count_input_files, enable_disable_telemetry, count_output_files + from telemetry import ( + send_telemetry, + count_input_files, + telemetry_enabled, + count_output_files, + ) except ModuleNotFoundError: import pypet2bids.helper_functions as helper_functions import pypet2bids.is_pet as is_pet @@ -55,7 +60,12 @@ metadata_dictionaries, get_metadata_from_spreadsheet, ) - from pypet2bids.telemetry import send_telemetry, count_input_files, enable_disable_telemetry, count_output_files + from pypet2bids.telemetry import ( + send_telemetry, + count_input_files, + telemetry_enabled, + count_output_files, + ) logger = helper_functions.logger("pypet2bids") @@ -460,7 +470,7 @@ def run_dcm2niix(self): image_folder = helper_functions.sanitize_bad_path(self.image_folder) cmd = f"{self.dcm2niix_path} -b y -w 1 -z y {file_format_args} -o {tempdir_pathlike} {image_folder}" convert = subprocess.run(cmd, shell=True, capture_output=True) - self.telemetry_data['dcm2niix'] = { + self.telemetry_data["dcm2niix"] = { "returncode": convert.returncode, } self.telemetry_data.update(count_output_files(self.tempdir_location)) @@ -847,24 +857,24 @@ def convert(self): self.post_dcm2niix() # if telemetry isn't disabled we send a telemetry event to the pypet2bids server - if enable_disable_telemetry: + if telemetry_enabled: # count the number of files self.telemetry_data.update(count_input_files(self.image_folder)) # record if a blood tsv and json file were created - if self.spreadsheet_metadata.get("blood_tsv", {}): + if self.spreadsheet_metadata.get("blood_tsv", {}) != {}: self.telemetry_data["blood_tsv"] = True - if self.spreadsheet_metadata.get("blood_json", {}): - self.telemetry_data["blood_json"] = True + else: + self.telemetry_data["blood_tsv"] = False # record if a metadata spreadsheet was used - if self.metadata_path == "" or self.metadata_path is not None: + if helper_functions.collect_spreadsheets(self.metadata_path): self.telemetry_data["metadata_spreadsheet_used"] = True + else: + self.telemetry_data["metadata_spreadsheet_used"] = False + self.telemetry_data["InputType"] = "DICOM" send_telemetry(self.telemetry_data) - - - def match_dicom_header_to_file(self, destination_path=None): """ Matches a dicom header to a nifti or json file produced by dcm2niix, this is run after dcm2niix converts the diff --git a/pypet2bids/pypet2bids/ecat.py b/pypet2bids/pypet2bids/ecat.py index a900dbd..2b01c37 100644 --- a/pypet2bids/pypet2bids/ecat.py +++ b/pypet2bids/pypet2bids/ecat.py @@ -25,6 +25,7 @@ get_metadata_from_spreadsheet, check_meta_radio_inputs, ) + from telemetry import telemetry_enabled, send_telemetry except ModuleNotFoundError: import pypet2bids.helper_functions as helper_functions import pypet2bids.sidecar as sidecar @@ -35,6 +36,7 @@ get_metadata_from_spreadsheet, check_meta_radio_inputs, ) + from pypet2bids.telemetry import telemetry_enabled, send_telemetry from dateutil import parser @@ -107,6 +109,8 @@ def __init__( self.output_path = None self.metadata_path = metadata_path + self.telemetry_data = {} + # load config file default_json_path = helper_functions.check_pet2bids_config( "DEFAULT_METADATA_JSON" @@ -169,6 +173,12 @@ def __init__( else: self.nifti_file = nifti_file + self.telemetry_data["InputType"] = "ECAT" + str(self.ecat_header["SV_VERSION"]) + self.telemetry_data["TotalInputFiles"] = 1 + self.telemetry_data["TotalInputFilesSize"] = ( + pathlib.Path(self.ecat_file).stat().st_size + ) + # que up metadata path for spreadsheet loading later if self.metadata_path: if ( @@ -198,6 +208,16 @@ def __init__( load_spreadsheet_data["blood_json"] ) + if helper_functions.collect_spreadsheets(self.metadata_path): + self.telemetry_data.update({"metadata_spreadsheet_user": True}) + else: + self.telemetry_data.update({"metadata_spreadsheet_user": False}) + + if self.spreadsheet_metadata.get("blood_tsv", None): + self.telemetry_data.update({"blood_tsv": True}) + else: + self.telemetry_data.update({"blood_tsv": False}) + def make_nifti(self, output_path=None): """ Outputs a nifti from the read in ECAT file. @@ -221,6 +241,9 @@ def make_nifti(self, output_path=None): affine=self.affine, ) + self.telemetry_data["NiftiFiles"] = 1 + self.telemetry_data["NiftiFilesSize"] = pathlib.Path(output).stat().st_size + if "nii.gz" not in output: output = helper_functions.compress(output) @@ -653,3 +676,6 @@ def convert(self): self.prune_sidecar() self.show_sidecar(output_path=self.sidecar_path) self.write_out_blood_files() + + if telemetry_enabled: + send_telemetry(self.telemetry_data) diff --git a/pypet2bids/pypet2bids/helper_functions.py b/pypet2bids/pypet2bids/helper_functions.py index 2a28ec3..8a7d5c3 100644 --- a/pypet2bids/pypet2bids/helper_functions.py +++ b/pypet2bids/pypet2bids/helper_functions.py @@ -959,11 +959,11 @@ def ad_hoc_checks( if items_that_should_be_checked is None: items_that_should_be_checked = {} hardcoded_items = { - 'InjectedRadioactivityUnits': ['MBq', 'mCi'], - 'SpecificRadioactivityUnits': ['Bq/g', 'MBq/ug'], - 'InjectedMassUnits': 'ug', - 'MolarActivityUnits': 'GBq/umolug', - 'MolecularWeightUnits': 'g/mol' + "InjectedRadioactivityUnits": ["MBq", "mCi"], + "SpecificRadioactivityUnits": ["Bq/g", "MBq/ug"], + "InjectedMassUnits": "ug", + "MolarActivityUnits": "GBq/umolug", + "MolecularWeightUnits": "g/mol", } # if none are diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index 746a9b3..2913979 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -1,6 +1,7 @@ import requests import os import pathlib +import subprocess from dotenv import load_dotenv from typing import Union @@ -9,7 +10,7 @@ from pypet2bids.helper_functions import get_version -def enable_disable_telemetry(config_path=None): +def telemetry_enabled(config_path=None): # load dcm2niix file if not config_path: config_file = pathlib.Path.home() / ".pet2bidsconfig" @@ -20,16 +21,25 @@ def enable_disable_telemetry(config_path=None): load_dotenv(dotenv_path=config_file) # check to see if telemetry is disabled - if os.getenv("PET2BIDS_TELEMETRY_ENABLED") == "False".upper(): + if os.getenv("PET2BIDS_TELEMETRY_ENABLED", "").lower() == "false": return False else: return True def send_telemetry(json_data: dict, url: str = "http://52.87.154.236/telemetry/"): - if enable_disable_telemetry(): + if telemetry_enabled(): # update data with version of pet2bids - json_data['pypet2bids_version'] = get_version() + json_data["pypet2bids_version"] = get_version() + # check if it's one of the dev's running this + running_from_cloned_repository = subprocess.run( + ["git", "status"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ) + if running_from_cloned_repository.returncode == 0: + json_data["running_from_cloned_repository"] = True + + json_data["description"] = "pet2bids_python_telemetry" + # Send a POST request to the telemetry server requests.post(url, json=json_data) else: @@ -78,12 +88,12 @@ def count_output_files(output_file_path: Union[str, pathlib.Path]): if f.is_file(): total_files += 1 total_files_size += f.stat().st_size - if str(f).endswith('.nii') or str(f).endswith('.nii.gz'): + if str(f).endswith(".nii") or str(f).endswith(".nii.gz"): num_nifti_files += 1 nifti_files_size += f.stat().st_size return { "TotalOutputFiles": total_files, "TotalOutputFilesSize": total_files_size, "NiftiFiles": num_nifti_files, - "NiftiFilesSize": nifti_files_size - } \ No newline at end of file + "NiftiFilesSize": nifti_files_size, + } From b3bf9af43bcc929229f8816d2df4a5dfa1d00854 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:44:20 -0400 Subject: [PATCH 04/20] matlab is being weird and not finding dcm2niix on path --- matlab/dcm2niix4pet.m | 31 +++++++++-- matlab/telemetry.m | 75 +++++++++++++++++++++++++++ pypet2bids/pypet2bids/dcm2niix4pet.py | 2 + 3 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 matlab/telemetry.m diff --git a/matlab/dcm2niix4pet.m b/matlab/dcm2niix4pet.m index ebb7b9e..2baf951 100644 --- a/matlab/dcm2niix4pet.m +++ b/matlab/dcm2niix4pet.m @@ -50,11 +50,11 @@ function dcm2niix4pet(FolderList,MetaList,varargin) dcm2niixpath = 'D:\MRI\MRIcroGL12win\Resources\dcm2niix.exe'; % for windows machine indicate here, where is dcm2niix if ispc && ~exist(dcm2niixpath,'file') - error('for windows machine please edit the function line 42 and indicate the dcm2niix path') + error('for windows machine please edit the function line 51 and indicate the dcm2niix path') end if ~ispc % overwrite if not windowns (as it should be in the computer path) - dcm2niixpath = 'dcm2niix'; + dcm2niixpath = '/usr/local/bin/dcm2niix'; end % we rely on more recent version of dcm2niix, certain pet fields are unavailable in the sidecar jsons for versions @@ -62,9 +62,19 @@ function dcm2niix4pet(FolderList,MetaList,varargin) minimum_version = 'v1.0.20220720'; minimum_version_date = datetime(minimum_version(6:end), 'InputFormat', 'yyyyMMdd'); -version_cmd = ['dcm2niix', ' -v']; +%version_cmd = ['dcm2niix', ' -v']; % TODO fix this as it's not detecting dcm2niix running matlab on osx as of matlab v2023b. +version_cmd = ['/usr/local/bin/dcm2niix', ' -v']; + [status, version_output_string] = system(version_cmd); -version = regexp(version_output_string, 'v[0-9].[0-9].{8}[0-9]', 'match'); +version = regexp(version_output_string, 'v[0-9].[0-9].{8}[0-9]', 'match'); % TODO this returns an array with two of the same versions + +% initalize telemetry data fror later uploading +telemetry_data = {}; +dcm2niix_data = {}; +dcm2niix_data.version = version; +dcm2niix_data.returncode = 0; +telemetry_data.dcm2niix = dcm2niix_data; +telemetry_data.description = "Matlab_dcm2niix4pet.m" if length(version) >= 1 version_date = version{1}(6:end); @@ -249,10 +259,16 @@ function dcm2niix4pet(FolderList,MetaList,varargin) end out = system(command); + telemetry_data.dcm2niix.returncode = out; + % we still want to send telemetry even if this fails if out ~= 0 + telemetry_data.returncode = 1; + telemetry(telemetry_data, folder); error('%s did not run properly',command) + end - + + % deal with dcm files dcmfiles = dir(fullfile(FolderList{folder},'*.dcm')); if isempty(dcmfiles) % since sometimes they have no ext :-( @@ -318,4 +334,9 @@ function dcm2niix4pet(FolderList,MetaList,varargin) jsonfilename = newmetadata; end updatejsonpetfile(jsonfilename,MetaList,dcminfo); + + % if this all goes well update the telemetry data and send it with a positive return code of 0 + telemetry_data.returncode = 0; + telemetry(telemetry_data, FolderList{folder}) + end diff --git a/matlab/telemetry.m b/matlab/telemetry.m new file mode 100644 index 0000000..dd61758 --- /dev/null +++ b/matlab/telemetry.m @@ -0,0 +1,75 @@ +function telemetry(telemetry_data, input_path, output_path) + arguments + telemetry_data (1,:) struct + input_path (1,:) string = '' + output_path (1,:) string = '' + end + + if telemetry_enabled + % do all the things + + telemetry_data.description = "Matlab"; + + if strcmp(input_path, '') + % do nothing + else + input_file_count = count_input_files(input_path); + telemetry_data.TotalInputFiles = input_file_count.TotalInputFiles; + telemetry_data.TotalInputFileSize = input_file_count.TotalInputFileSize; + end + + url = 'http://52.87.154.236/telemetry/'; + options = weboptions('MediaType', 'application/json'); + response = webwrite(url, telemetry_data, options); + + else + % don't do anything + end +end + +function e = telemetry_enabled() + % checks to see if the telemetry is enabled or disabled + environment = getenv(); + home_dir = environment("HOME"); + loadenv(fullfile(home_dir, '.pet2bidsconfig'), FileType='env'); + % convert string to boolean/logical + disable_telemetry = strcmpi(getenv("PET2BIDS_TELEMETRY_ENABLED"), 'false'); + + if disable_telemetry + e = false; + else + e = true; + end + +end + + +function c = count_input_files(input_path) + % generate a list of all the files in the input directory + % count the number of files in the input directory + % count the total size of the files in the input directory + % return the count and the size + + % if the input path is a file then return 1 and the size of the file + if isfile(input_path) + input_file = dir(input_path); + c.TotalInputFiles = 1; + c.TotalInputFileSize = input_file.bytes; + return + elseif isfolder(input_path) + % get the list of files in the input directory + input_files = dir(input_path); + % count the number of files in the input directory + file_count = length(input_files); + % count the total size of the files in the input directory + total_size = 0; + for i = 1:file_count + total_size = total_size + input_files(i).bytes; + end + c.TotalInputFiles = file_count; + c.TotalInputFileSize = total_size; + return + else + error('Input path is not a file or a directory'); + end +end diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index bb2b0e9..e31bda0 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -873,6 +873,8 @@ def convert(self): self.telemetry_data["InputType"] = "DICOM" + self.telemetry_data["returncode"] = 0 + send_telemetry(self.telemetry_data) def match_dicom_header_to_file(self, destination_path=None): From 8550942db3bfe00641ed033e45c779a3c3b258e0 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:13:57 -0400 Subject: [PATCH 05/20] added telemetry to matlab converters --- matlab/dcm2niix4pet.m | 10 +++++----- matlab/ecat2nii.m | 25 ++++++++++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/matlab/dcm2niix4pet.m b/matlab/dcm2niix4pet.m index 2baf951..08bad7f 100644 --- a/matlab/dcm2niix4pet.m +++ b/matlab/dcm2niix4pet.m @@ -54,7 +54,7 @@ function dcm2niix4pet(FolderList,MetaList,varargin) end if ~ispc % overwrite if not windowns (as it should be in the computer path) - dcm2niixpath = '/usr/local/bin/dcm2niix'; + dcm2niixpath = 'dcm2niix'; end % we rely on more recent version of dcm2niix, certain pet fields are unavailable in the sidecar jsons for versions @@ -62,16 +62,16 @@ function dcm2niix4pet(FolderList,MetaList,varargin) minimum_version = 'v1.0.20220720'; minimum_version_date = datetime(minimum_version(6:end), 'InputFormat', 'yyyyMMdd'); -%version_cmd = ['dcm2niix', ' -v']; % TODO fix this as it's not detecting dcm2niix running matlab on osx as of matlab v2023b. -version_cmd = ['/usr/local/bin/dcm2niix', ' -v']; +version_cmd = ['dcm2niix', ' -v']; [status, version_output_string] = system(version_cmd); -version = regexp(version_output_string, 'v[0-9].[0-9].{8}[0-9]', 'match'); % TODO this returns an array with two of the same versions +version = regexp(version_output_string, 'v[0-9].[0-9].{8}[0-9]', 'match'); + % initalize telemetry data fror later uploading telemetry_data = {}; dcm2niix_data = {}; -dcm2niix_data.version = version; +dcm2niix_data.version = version(1); dcm2niix_data.returncode = 0; telemetry_data.dcm2niix = dcm2niix_data; telemetry_data.description = "Matlab_dcm2niix4pet.m" diff --git a/matlab/ecat2nii.m b/matlab/ecat2nii.m index acfd52f..34222f7 100644 --- a/matlab/ecat2nii.m +++ b/matlab/ecat2nii.m @@ -58,6 +58,10 @@ parts = strsplit(ecat_save_steps_dir, filesep); ecat_save_steps_dir = strjoin([parts(1:end-2), 'ecat_testing', 'steps'], filesep); +%% initialize telemetry variable for reporting +telemetry_data = {}; +telemetry_data.description = "Matlab_ecat2nii"; + %% check inputs % ------------ @@ -131,14 +135,18 @@ %% Read and write data % -------------------- for j=1:length(FileListIn) - + try fprintf('Conversion of file: %s\n',FileListIn{j}); - + % quickly ensure we have the TimeZero - key to all analyzes! info = MetaList{1}; if ~isfield(info,'TimeZero') - error('Metadata TimeZero is missing - set to ScanStart or empty to use the scanning time as injection time') + error_text = 'Metadata TimeZero is missing - set to ScanStart or empty to use the scanning time as injection time'; + telemetry_data.returncode = 1; + telemetry_data.error = error_text; + telemetry(telemetry_data, FileListIn{j}) + error(error_text) end % Read ECAT file headers @@ -180,6 +188,9 @@ end end + % capture ecat version + telemetry_data.InputType = append("ECAT", string(mh.sw_version)); + % save debugging steps 6 and 7 if (ecat_save_steps == '1') first_middle_last_frames_to_text_cell(data,ecat_save_steps_dir, '6_ecat2nii_matlab'); @@ -206,8 +217,6 @@ fclose(fid); end - - % save debugging step 8 - rescale to 16 bits if (ecat_save_steps == '1') first_middle_last_frames_to_text(img_temp,ecat_save_steps_dir, '8_rescale_to_16_ecat2nii_matlab'); @@ -489,8 +498,14 @@ % FileListOut{j} = [filenameout '_pet.nii']; % niftiwrite(img_temp,FileListOut{j},info,'Endian','little','Compressed',false); % end + + telemetry_data.returncode = 0; + telemetry(telemetry_data, FileListIn{j}); catch conversionerr + telemetry_data.returncode = 1; + telemetry_data.error = conversionerr.message; + telemetry(telemetry_data, FileListIn{j}); FileListOut{j} = sprintf('%s failed to convert:%s',FileListIn{j},conversionerr.message, conversionerr.stack.line); end From 272780acb19681be7aae2cda7084d5865a094b80 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:41:52 -0400 Subject: [PATCH 06/20] fixe typo, bump version --- pypet2bids/poetry.lock | 109 +++++++++++++++++----------------- pypet2bids/pypet2bids/ecat.py | 2 +- pypet2bids/pyproject.toml | 2 +- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/pypet2bids/poetry.lock b/pypet2bids/poetry.lock index f0e7fba..c644a2c 100644 --- a/pypet2bids/poetry.lock +++ b/pypet2bids/poetry.lock @@ -24,13 +24,13 @@ files = [ [[package]] name = "babel" -version = "2.14.0" +version = "2.15.0" description = "Internationalization utilities" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, + {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, + {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, ] [package.dependencies] @@ -64,13 +64,13 @@ virtualenv = ["virtualenv (>=20.0.35)"] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.7.4" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -207,13 +207,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -243,22 +243,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.1.0" +version = "8.2.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, - {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, + {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, + {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "importlib-resources" @@ -291,13 +291,13 @@ files = [ [[package]] name = "jinja2" -version = "3.1.3" +version = "3.1.4" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, ] [package.dependencies] @@ -308,13 +308,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "joblib" -version = "1.4.0" +version = "1.4.2" description = "Lightweight pipelining with Python functions" optional = false python-versions = ">=3.8" files = [ - {file = "joblib-1.4.0-py3-none-any.whl", hash = "sha256:42942470d4062537be4d54c83511186da1fc14ba354961a2114da91efa9a4ed7"}, - {file = "joblib-1.4.0.tar.gz", hash = "sha256:1eb0dc091919cd384490de890cb5dfd538410a6d4b3b54eef09fb8c50b409b1c"}, + {file = "joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6"}, + {file = "joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e"}, ] [[package]] @@ -480,13 +480,13 @@ files = [ [[package]] name = "openpyxl" -version = "3.1.2" +version = "3.1.5" description = "A Python library to read/write Excel 2010 xlsx/xlsm files" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, + {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, + {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, ] [package.dependencies] @@ -494,13 +494,13 @@ et-xmlfile = "*" [[package]] name = "packaging" -version = "24.0" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -607,17 +607,16 @@ docs = ["matplotlib", "numpy", "numpydoc", "pillow", "sphinx", "sphinx-copybutto [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] @@ -655,13 +654,13 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" -version = "2024.5" +version = "2024.7" description = "Community maintained hooks for PyInstaller" optional = false python-versions = ">=3.7" files = [ - {file = "pyinstaller_hooks_contrib-2024.5-py2.py3-none-any.whl", hash = "sha256:0852249b7fb1e9394f8f22af2c22fa5294c2c0366157969f98c96df62410c4c6"}, - {file = "pyinstaller_hooks_contrib-2024.5.tar.gz", hash = "sha256:aa5dee25ea7ca317ad46fa16b5afc8dba3b0e43f2847e498930138885efd3cab"}, + {file = "pyinstaller_hooks_contrib-2024.7-py2.py3-none-any.whl", hash = "sha256:8bf0775771fbaf96bcd2f4dfd6f7ae6c1dd1b1efe254c7e50477b3c08e7841d8"}, + {file = "pyinstaller_hooks_contrib-2024.7.tar.gz", hash = "sha256:fd5f37dcf99bece184e40642af88be16a9b89613ecb958a8bd1136634fc9fac5"}, ] [package.dependencies] @@ -768,13 +767,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -827,19 +826,19 @@ test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "sciki [[package]] name = "setuptools" -version = "69.5.1" +version = "72.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, + {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" @@ -1059,13 +1058,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -1092,18 +1091,18 @@ test = ["pytest", "pytest-cov"] [[package]] name = "zipp" -version = "3.18.1" +version = "3.19.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"}, - {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"}, + {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, + {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] [metadata] lock-version = "2.0" diff --git a/pypet2bids/pypet2bids/ecat.py b/pypet2bids/pypet2bids/ecat.py index 2b01c37..efbc2ea 100644 --- a/pypet2bids/pypet2bids/ecat.py +++ b/pypet2bids/pypet2bids/ecat.py @@ -173,7 +173,7 @@ def __init__( else: self.nifti_file = nifti_file - self.telemetry_data["InputType"] = "ECAT" + str(self.ecat_header["SV_VERSION"]) + self.telemetry_data["InputType"] = "ECAT" + str(self.ecat_header["SW_VERSION"]) self.telemetry_data["TotalInputFiles"] = 1 self.telemetry_data["TotalInputFilesSize"] = ( pathlib.Path(self.ecat_file).stat().st_size diff --git a/pypet2bids/pyproject.toml b/pypet2bids/pyproject.toml index bc8e236..95760d0 100644 --- a/pypet2bids/pyproject.toml +++ b/pypet2bids/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pypet2bids" -version = "1.3.12" +version = "1.3.13dev" description = "A python library for converting PET imaging and blood data to BIDS." authors = ["anthony galassi <28850131+bendhouseart@users.noreply.github.com>"] license = "MIT" From 62791ec61475b0722f35f1fac77c5ca193123c33 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 7 Aug 2024 19:40:20 -0400 Subject: [PATCH 07/20] added option to opt out of telemetry --- pypet2bids/pypet2bids/helper_functions.py | 3 +- pypet2bids/pypet2bids/telemetry.py | 90 ++++++++++++++++++++++- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/pypet2bids/pypet2bids/helper_functions.py b/pypet2bids/pypet2bids/helper_functions.py index 8a7d5c3..46afbf5 100644 --- a/pypet2bids/pypet2bids/helper_functions.py +++ b/pypet2bids/pypet2bids/helper_functions.py @@ -15,6 +15,7 @@ | *Copyright OpenNeuroPET team* """ + import os import gzip import re @@ -285,7 +286,7 @@ def decompress(file_like_object, output_path: str = None): return output_path -def load_vars_from_config(path_to_config: str): +def load_vars_from_config(path_to_config: str=pathlib.Path.home() / ".pet2bidsconfig"): """ Loads values from a .env file given a path to said .env file. diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index 2913979..6bdd1c6 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -2,15 +2,65 @@ import os import pathlib import subprocess +import time +import sys +import select from dotenv import load_dotenv from typing import Union -# collect pet2bids version -from pypet2bids.helper_functions import get_version +try: + from helper_functions import get_version, load_vars_from_config, modify_config_file +except ModuleNotFoundError: + # collect pet2bids version + from pypet2bids.helper_functions import get_version, load_vars_from_config, modify_config_file + +pet2bids_config = load_vars_from_config() +telemetry_default_url = pet2bids_config.get("TELEMETRY_URL", "http://52.87.154.236/telemetry/") +telemetry_enabled = pet2bids_config.get("TELEMETRY_ENABLED", True) +telemetry_notify_user = pet2bids_config.get("NOTIFY_USER_OF_TELEMETRY", False) + + +def ask_user_to_opt_out(timeout=10): + """Waits for user input for a specified number of seconds. + + Args: + timeout: Number of seconds to wait for input. + + Returns: + The user input if provided within the timeout, otherwise None. + """ + + print("Do you want to opt out of telemetry? (yes/no): ".format(timeout), end="", flush=True) + sys.stdout.flush() + + ready, _, _ = select.select([sys.stdin], [], [], timeout) + if ready: + response = sys.stdin.readline().rstrip('\n') + if response.lower() == "yes": + return True + else: + return False + + +if telemetry_notify_user is False: + opt_out = ask_user_to_opt_out() + if opt_out: + # update the config file + modify_config_file("TELEMETRY_ENABLED", False) + + modify_config_file("NOTIFY_USER_OF_TELEMETRY", True) def telemetry_enabled(config_path=None): + """ + Check if telemetry is enabled, if it isn't disabled in the .pet2bidsconfig file + it will be considered enabled. One must opt out of tracking usage manually. + :param config_path: The path to the config file + :type config_path: Union[str, pathlib.Path] + :return: Whether telemetry is enabled or not + :rtype: bool + """ # load dcm2niix file if not config_path: config_file = pathlib.Path.home() / ".pet2bidsconfig" @@ -27,7 +77,17 @@ def telemetry_enabled(config_path=None): return True -def send_telemetry(json_data: dict, url: str = "http://52.87.154.236/telemetry/"): +def send_telemetry(json_data: dict, url: str = telemetry_default_url): + """ + Send telemetry data to the telemetry server, by default this will first try + to load the telemetry server url from the config file, if it's not found it will + default to the hardcoded value in this module. This will always send, unless a user + has disabled the pet2bids telemetry in the .pet2bidsconfig file. + :param json_data: The dat to be sent to the telemetry server + :type json_data: dict + :param url: The url of the telemetry server + :type url: str + """ if telemetry_enabled(): # update data with version of pet2bids json_data["pypet2bids_version"] = get_version() @@ -47,7 +107,13 @@ def send_telemetry(json_data: dict, url: str = "http://52.87.154.236/telemetry/" def count_input_files(input_file_path: Union[str, pathlib.Path]): - # Count the number of input files in the input file path + """ + Count the number of input files in the input file path + :param input_file_path: location of the input files + :type input_file_path: Union[str, pathlib.Path] + :return: The number of input files and their size + :rtype: dict + """ if isinstance(input_file_path, str): input_file_path = pathlib.Path(input_file_path) @@ -70,6 +136,20 @@ def count_input_files(input_file_path: Union[str, pathlib.Path]): def count_output_files(output_file_path: Union[str, pathlib.Path]): + """ + Count the number of output files in the output file path. This can be usefull + in determining whether any additional files were created during the conversion process. + However, this is only useful if the conversion process initially takes place from within + a temporary directory. + + # TODO check the last modified date of the files to determine if they were created during + # TODO the conversion process. That should make this usefull in all cases. + :param output_file_path: location of the output files + :type output_file_path: Union[str, pathlib.Path] + :return: The number of output files and their size and spefically the number of nifti files and + their size + :rtype: dict + """ if isinstance(output_file_path, str): output_file_path = pathlib.Path(output_file_path) @@ -97,3 +177,5 @@ def count_output_files(output_file_path: Union[str, pathlib.Path]): "NiftiFiles": num_nifti_files, "NiftiFilesSize": nifti_files_size, } + + From e60aa57db36adf9b361550129e375bab3d23c201 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:13:43 -0400 Subject: [PATCH 08/20] added cli flag and environment variable disabling of telemetry --- matlab/telemetry.m | 9 +++++++- pypet2bids/pypet2bids/dcm2niix4pet.py | 11 ++++++++- pypet2bids/pypet2bids/ecat_cli.py | 11 +++++++++ pypet2bids/pypet2bids/helper_functions.py | 5 +++-- pypet2bids/pypet2bids/telemetry.py | 27 ++++++++++++++++++----- 5 files changed, 53 insertions(+), 10 deletions(-) diff --git a/matlab/telemetry.m b/matlab/telemetry.m index dd61758..baebd18 100644 --- a/matlab/telemetry.m +++ b/matlab/telemetry.m @@ -30,12 +30,19 @@ function telemetry(telemetry_data, input_path, output_path) function e = telemetry_enabled() % checks to see if the telemetry is enabled or disabled environment = getenv(); + % check environment too before loading the config file + if isfield(environment, 'PET2BIDS_TELEMETRY_ENABLED') + disable_telemetry_env = strcmpi(getenv("PET2BIDS_TELEMETRY_ENABLED"), 'false'); + else + disable_telemetry_env = false; + end + home_dir = environment("HOME"); loadenv(fullfile(home_dir, '.pet2bidsconfig'), FileType='env'); % convert string to boolean/logical disable_telemetry = strcmpi(getenv("PET2BIDS_TELEMETRY_ENABLED"), 'false'); - if disable_telemetry + if disable_telemetry | disable_telemetry_env e = false; else e = true; diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index e31bda0..4d6761c 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -1109,7 +1109,14 @@ def cli(): action="version", version=f"{helper_functions.get_version()}", ) - + parser.add_argument( + "--notrack", + action="store_true", + default=False, + help="Opt-out of sending tracking information of this run to the PET2BIDS developers. " + "This information helps to improve PET2BIDS and provides an indicator of real world " + "usage crucial for obtaining funding." + ) return parser @@ -1261,6 +1268,8 @@ def main(): "DEFAULT_METADATA_JSON", cli_args.set_default_metadata_json ) sys.exit(0) + if cli_args.notrack: + environ["PET2BIDS_TRACK"] = "False" elif cli_args.folder: # instantiate class diff --git a/pypet2bids/pypet2bids/ecat_cli.py b/pypet2bids/pypet2bids/ecat_cli.py index b1284f9..fa52584 100644 --- a/pypet2bids/pypet2bids/ecat_cli.py +++ b/pypet2bids/pypet2bids/ecat_cli.py @@ -190,6 +190,14 @@ def cli(): action="version", version=f"{helper_functions.get_version()}", ) + parser.add_argument( + "--notrack", + action="store_true", + default=False, + help="Opt-out of sending tracking information of this run to the PET2BIDS developers. " + "This information helps to improve PET2BIDS and provides an indicator of real world " + "usage crucial for obtaining funding." + ) return parser @@ -262,6 +270,9 @@ def main(): print(example1) sys.exit(0) + if cli_args.notrack: + os.environ["PET2BIDS_TELEMETRY_ENABLED"] = "False" + collect_pixel_data = False if cli_args.convert or cli_args.update: collect_pixel_data = True diff --git a/pypet2bids/pypet2bids/helper_functions.py b/pypet2bids/pypet2bids/helper_functions.py index 46afbf5..e95856a 100644 --- a/pypet2bids/pypet2bids/helper_functions.py +++ b/pypet2bids/pypet2bids/helper_functions.py @@ -15,7 +15,6 @@ | *Copyright OpenNeuroPET team* """ - import os import gzip import re @@ -286,7 +285,9 @@ def decompress(file_like_object, output_path: str = None): return output_path -def load_vars_from_config(path_to_config: str=pathlib.Path.home() / ".pet2bidsconfig"): +def load_vars_from_config( + path_to_config: str = pathlib.Path.home() / ".pet2bidsconfig", +): """ Loads values from a .env file given a path to said .env file. diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index 6bdd1c6..0ef9f19 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -13,11 +13,24 @@ from helper_functions import get_version, load_vars_from_config, modify_config_file except ModuleNotFoundError: # collect pet2bids version - from pypet2bids.helper_functions import get_version, load_vars_from_config, modify_config_file + from pypet2bids.helper_functions import ( + get_version, + load_vars_from_config, + modify_config_file, + ) pet2bids_config = load_vars_from_config() -telemetry_default_url = pet2bids_config.get("TELEMETRY_URL", "http://52.87.154.236/telemetry/") +telemetry_default_url = pet2bids_config.get( + "TELEMETRY_URL", "http://52.87.154.236/telemetry/" +) +# check environment variables as well as the config file +telemetry_enabled_env = os.getenv("PET2BIDS_TELEMETRY_ENABLED", True) telemetry_enabled = pet2bids_config.get("TELEMETRY_ENABLED", True) + +# if telemetry is disabled in the config file or the environment variable disable its use. +if telemetry_enabled_env is False or telemetry_enabled is False: + telemetry_enabled = False + telemetry_notify_user = pet2bids_config.get("NOTIFY_USER_OF_TELEMETRY", False) @@ -31,12 +44,16 @@ def ask_user_to_opt_out(timeout=10): The user input if provided within the timeout, otherwise None. """ - print("Do you want to opt out of telemetry? (yes/no): ".format(timeout), end="", flush=True) + print( + "Do you want to opt out of telemetry? (yes/no): ".format(timeout), + end="", + flush=True, + ) sys.stdout.flush() ready, _, _ = select.select([sys.stdin], [], [], timeout) if ready: - response = sys.stdin.readline().rstrip('\n') + response = sys.stdin.readline().rstrip("\n") if response.lower() == "yes": return True else: @@ -177,5 +194,3 @@ def count_output_files(output_file_path: Union[str, pathlib.Path]): "NiftiFiles": num_nifti_files, "NiftiFilesSize": nifti_files_size, } - - From ff3645d8c7198f8c4e52aacd98e1332a38ceb282 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:14:21 -0400 Subject: [PATCH 09/20] updating matlab to suit passing of --notrack to conversion functions --- matlab/dcm2niix4pet.m | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/matlab/dcm2niix4pet.m b/matlab/dcm2niix4pet.m index 08bad7f..6dc6ac1 100644 --- a/matlab/dcm2niix4pet.m +++ b/matlab/dcm2niix4pet.m @@ -1,4 +1,4 @@ -function dcm2niix4pet(FolderList,MetaList,varargin) +function dcm2niix4pet(FolderList,MetaList,varargin, notrack) % Converts dicom image file to nifti+json calling dcm2niix augmenting the % json file to be BIDS compliant. Note that you are always right when it @@ -12,7 +12,7 @@ function dcm2niix4pet(FolderList,MetaList,varargin) % % :param FolderList: Cell array of char strings with filenames and paths % :param MetaList: Cell array of structures for metadata -% :param options: +% :param varargin: % - *deletedcm* to be 'on' or 'off' % - *o* the output directory or cell arrays of directories % IF the folder is BIDS sub-xx files are renamed automatically @@ -30,6 +30,7 @@ function dcm2niix4pet(FolderList,MetaList,varargin) % - *w* = 2; % write behavior for name conflicts (0,1,2, default 2: 0=skip duplicates, 1=overwrite, 2=add suffix) % - *x* = 'n'; % crop 3D acquisitions (y/n/i, default n, use 'i'gnore to neither crop nor rotate 3D acquistions) % - *z* = 'n'; % gz compress images (y/o/i/n/3, default y) [y=pigz, o=optimal pigz, i=internal:miniz, n=no, 3=no,3D] +% :param notrack: boolean to Opt-out of sending tracking information of this run to the PET2BIDS developers. This information helps to improve PET2BIDS and provides an indicator of real world usage crucial for obtaining funding." % % .. code-block:: % @@ -220,6 +221,7 @@ function dcm2niix4pet(FolderList,MetaList,varargin) elseif strcmpi(varargin{var},'o') outputdir = varargin{var+1}; end + end if isempty(outputdir) @@ -235,6 +237,26 @@ function dcm2niix4pet(FolderList,MetaList,varargin) end end +% check to see if the user has disabled telemetry +no_track_string = lower(string(notrack)); +if strcmp(no_track_string, 'true') + tracking = false; +elseif strcmp(no_track_string, 'false') + tracking = true; +else + try + numeric_notrack = str2num(no_track_string); + tracking = logical(numeric_notrack); + catch + % don't do anything + end +end + +if ~tracking + setenv('PET2BIDS_TELEMETRY_ENABLED', 'false'); +end + + %% convert % ---------- for folder = 1:size(FolderList,1) From 400fec2649b4e951c2dae1a00aa615cddcd7f97b Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 12 Aug 2024 12:05:14 -0400 Subject: [PATCH 10/20] update tracking on matlab to set env to not track if provided via arguments --- matlab/dcm2niix4pet.m | 25 ++++++------------------- matlab/ecat2nii.m | 3 +++ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/matlab/dcm2niix4pet.m b/matlab/dcm2niix4pet.m index 6dc6ac1..e585a10 100644 --- a/matlab/dcm2niix4pet.m +++ b/matlab/dcm2niix4pet.m @@ -1,4 +1,4 @@ -function dcm2niix4pet(FolderList,MetaList,varargin, notrack) +function dcm2niix4pet(FolderList,MetaList,varargin) % Converts dicom image file to nifti+json calling dcm2niix augmenting the % json file to be BIDS compliant. Note that you are always right when it @@ -220,6 +220,11 @@ function dcm2niix4pet(FolderList,MetaList,varargin, notrack) end elseif strcmpi(varargin{var},'o') outputdir = varargin{var+1}; + elseif strcmpi(varargin{var},'notrack') + notrack = varargin{var+1}; + if notrack; setenv('TELEMETRY_ENABLED', 'False'); end + else + error('unknown option %s',varargin{var}) end end @@ -237,24 +242,6 @@ function dcm2niix4pet(FolderList,MetaList,varargin, notrack) end end -% check to see if the user has disabled telemetry -no_track_string = lower(string(notrack)); -if strcmp(no_track_string, 'true') - tracking = false; -elseif strcmp(no_track_string, 'false') - tracking = true; -else - try - numeric_notrack = str2num(no_track_string); - tracking = logical(numeric_notrack); - catch - % don't do anything - end -end - -if ~tracking - setenv('PET2BIDS_TELEMETRY_ENABLED', 'false'); -end %% convert diff --git a/matlab/ecat2nii.m b/matlab/ecat2nii.m index 34222f7..c364010 100644 --- a/matlab/ecat2nii.m +++ b/matlab/ecat2nii.m @@ -114,6 +114,9 @@ gz = varargin{v+1}; elseif strcmpi(varargin{v},'savemat') savemat = varargin{v+1}; + elseif strcmpi(varargin{v},'notrack') + notrack = varargin{v+1}; + if notrack; setenv('TELEMETRY_ENABLED', 'False'); end end end From 5903752ec9a444abd78ab292a1abd34f4a3cb702 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:54:48 -0400 Subject: [PATCH 11/20] finished setting up matlab version --- matlab/dcm2niix4pet.m | 3 +-- matlab/ecat2nii.m | 3 +-- matlab/telemetry.m | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/matlab/dcm2niix4pet.m b/matlab/dcm2niix4pet.m index e585a10..da4399c 100644 --- a/matlab/dcm2niix4pet.m +++ b/matlab/dcm2niix4pet.m @@ -221,8 +221,7 @@ function dcm2niix4pet(FolderList,MetaList,varargin) elseif strcmpi(varargin{var},'o') outputdir = varargin{var+1}; elseif strcmpi(varargin{var},'notrack') - notrack = varargin{var+1}; - if notrack; setenv('TELEMETRY_ENABLED', 'False'); end + setenv('TELEMETRY_ENABLED', 'False') else error('unknown option %s',varargin{var}) end diff --git a/matlab/ecat2nii.m b/matlab/ecat2nii.m index c364010..52b54e4 100644 --- a/matlab/ecat2nii.m +++ b/matlab/ecat2nii.m @@ -115,8 +115,7 @@ elseif strcmpi(varargin{v},'savemat') savemat = varargin{v+1}; elseif strcmpi(varargin{v},'notrack') - notrack = varargin{v+1}; - if notrack; setenv('TELEMETRY_ENABLED', 'False'); end + setenv('TELEMETRY_ENABLED', 'False') end end diff --git a/matlab/telemetry.m b/matlab/telemetry.m index baebd18..06cd1e4 100644 --- a/matlab/telemetry.m +++ b/matlab/telemetry.m @@ -31,8 +31,8 @@ function telemetry(telemetry_data, input_path, output_path) % checks to see if the telemetry is enabled or disabled environment = getenv(); % check environment too before loading the config file - if isfield(environment, 'PET2BIDS_TELEMETRY_ENABLED') - disable_telemetry_env = strcmpi(getenv("PET2BIDS_TELEMETRY_ENABLED"), 'false'); + if isfield(environment, 'TELEMETRY_ENABLED') + disable_telemetry_env = strcmpi(getenv("TELEMETRY_ENABLED"), 'false'); else disable_telemetry_env = false; end @@ -40,7 +40,7 @@ function telemetry(telemetry_data, input_path, output_path) home_dir = environment("HOME"); loadenv(fullfile(home_dir, '.pet2bidsconfig'), FileType='env'); % convert string to boolean/logical - disable_telemetry = strcmpi(getenv("PET2BIDS_TELEMETRY_ENABLED"), 'false'); + disable_telemetry = strcmpi(getenv("TELEMETRY_ENABLED"), 'false'); if disable_telemetry | disable_telemetry_env e = false; From 4b4928a269fca0dd7e5d993f691cf94cf1fe72b9 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:45:50 -0400 Subject: [PATCH 12/20] fix error with loading config --- pypet2bids/pypet2bids/helper_functions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypet2bids/pypet2bids/helper_functions.py b/pypet2bids/pypet2bids/helper_functions.py index e95856a..73ae19f 100644 --- a/pypet2bids/pypet2bids/helper_functions.py +++ b/pypet2bids/pypet2bids/helper_functions.py @@ -297,7 +297,10 @@ def load_vars_from_config( if os.path.isfile(path_to_config): parameters = dotenv.main.dotenv_values(path_to_config) else: - raise FileNotFoundError(path_to_config) + log = logger("pypet2bids") + log.warning(f"Unable to locate {path_to_config}, returning empty dictionary.") + parameters = {} + #raise FileNotFoundError(path_to_config) for parameter, value in parameters.items(): try: From 9bc9e56bf3a250560d4f1c846dd33aeee491e893 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:08:36 -0400 Subject: [PATCH 13/20] automated message was more errofr that it's worth --- pypet2bids/pypet2bids/telemetry.py | 35 ------------------------------ 1 file changed, 35 deletions(-) diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index 0ef9f19..bbc0683 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -34,41 +34,6 @@ telemetry_notify_user = pet2bids_config.get("NOTIFY_USER_OF_TELEMETRY", False) -def ask_user_to_opt_out(timeout=10): - """Waits for user input for a specified number of seconds. - - Args: - timeout: Number of seconds to wait for input. - - Returns: - The user input if provided within the timeout, otherwise None. - """ - - print( - "Do you want to opt out of telemetry? (yes/no): ".format(timeout), - end="", - flush=True, - ) - sys.stdout.flush() - - ready, _, _ = select.select([sys.stdin], [], [], timeout) - if ready: - response = sys.stdin.readline().rstrip("\n") - if response.lower() == "yes": - return True - else: - return False - - -if telemetry_notify_user is False: - opt_out = ask_user_to_opt_out() - if opt_out: - # update the config file - modify_config_file("TELEMETRY_ENABLED", False) - - modify_config_file("NOTIFY_USER_OF_TELEMETRY", True) - - def telemetry_enabled(config_path=None): """ Check if telemetry is enabled, if it isn't disabled in the .pet2bidsconfig file From b360317f3981b01b2f5dbf5748153b40a310e68d Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Wed, 14 Aug 2024 16:34:04 -0400 Subject: [PATCH 14/20] formatted files --- pypet2bids/pypet2bids/dcm2niix4pet.py | 4 ++-- pypet2bids/pypet2bids/ecat_cli.py | 4 ++-- pypet2bids/pypet2bids/helper_functions.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index 4d6761c..7d1bf3b 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -1114,8 +1114,8 @@ def cli(): action="store_true", default=False, help="Opt-out of sending tracking information of this run to the PET2BIDS developers. " - "This information helps to improve PET2BIDS and provides an indicator of real world " - "usage crucial for obtaining funding." + "This information helps to improve PET2BIDS and provides an indicator of real world " + "usage crucial for obtaining funding.", ) return parser diff --git a/pypet2bids/pypet2bids/ecat_cli.py b/pypet2bids/pypet2bids/ecat_cli.py index fa52584..d6a66f2 100644 --- a/pypet2bids/pypet2bids/ecat_cli.py +++ b/pypet2bids/pypet2bids/ecat_cli.py @@ -195,8 +195,8 @@ def cli(): action="store_true", default=False, help="Opt-out of sending tracking information of this run to the PET2BIDS developers. " - "This information helps to improve PET2BIDS and provides an indicator of real world " - "usage crucial for obtaining funding." + "This information helps to improve PET2BIDS and provides an indicator of real world " + "usage crucial for obtaining funding.", ) return parser diff --git a/pypet2bids/pypet2bids/helper_functions.py b/pypet2bids/pypet2bids/helper_functions.py index 73ae19f..420da04 100644 --- a/pypet2bids/pypet2bids/helper_functions.py +++ b/pypet2bids/pypet2bids/helper_functions.py @@ -300,7 +300,7 @@ def load_vars_from_config( log = logger("pypet2bids") log.warning(f"Unable to locate {path_to_config}, returning empty dictionary.") parameters = {} - #raise FileNotFoundError(path_to_config) + # raise FileNotFoundError(path_to_config) for parameter, value in parameters.items(): try: From 878d4acfaed53a7746316d690f56f617fb321c9a Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Thu, 15 Aug 2024 14:33:17 -0400 Subject: [PATCH 15/20] add try catch for .env file check on telemetry.m --- matlab/telemetry.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/matlab/telemetry.m b/matlab/telemetry.m index 06cd1e4..baab2d0 100644 --- a/matlab/telemetry.m +++ b/matlab/telemetry.m @@ -38,8 +38,13 @@ function telemetry(telemetry_data, input_path, output_path) end home_dir = environment("HOME"); - loadenv(fullfile(home_dir, '.pet2bidsconfig'), FileType='env'); - % convert string to boolean/logical + try + loadenv(fullfile(home_dir, '.pet2bidsconfig'), FileType='env'); + % convert string to boolean/logical + catch ME + disable_telemetry = false; + end + disable_telemetry = strcmpi(getenv("TELEMETRY_ENABLED"), 'false'); if disable_telemetry | disable_telemetry_env From 29e32a4cc9dd457231ff366e04c6b60051001966 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:01:01 -0400 Subject: [PATCH 16/20] update poetry lock file --- pypet2bids/poetry.lock | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pypet2bids/poetry.lock b/pypet2bids/poetry.lock index c644a2c..890a072 100644 --- a/pypet2bids/poetry.lock +++ b/pypet2bids/poetry.lock @@ -24,13 +24,13 @@ files = [ [[package]] name = "babel" -version = "2.15.0" +version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" files = [ - {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, - {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, ] [package.dependencies] @@ -262,21 +262,21 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p [[package]] name = "importlib-resources" -version = "6.4.0" +version = "6.4.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, - {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, + {file = "importlib_resources-6.4.2-py3-none-any.whl", hash = "sha256:8bba8c54a8a3afaa1419910845fa26ebd706dc716dd208d9b158b4b6966f5c5c"}, + {file = "importlib_resources-6.4.2.tar.gz", hash = "sha256:6cbfbefc449cc6e2095dd184691b7a12a04f40bc75dd4c55d31c34f174cdf57a"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -654,13 +654,13 @@ hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] [[package]] name = "pyinstaller-hooks-contrib" -version = "2024.7" +version = "2024.8" description = "Community maintained hooks for PyInstaller" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pyinstaller_hooks_contrib-2024.7-py2.py3-none-any.whl", hash = "sha256:8bf0775771fbaf96bcd2f4dfd6f7ae6c1dd1b1efe254c7e50477b3c08e7841d8"}, - {file = "pyinstaller_hooks_contrib-2024.7.tar.gz", hash = "sha256:fd5f37dcf99bece184e40642af88be16a9b89613ecb958a8bd1136634fc9fac5"}, + {file = "pyinstaller_hooks_contrib-2024.8-py3-none-any.whl", hash = "sha256:0057fe9a5c398d3f580e73e58793a1d4a8315ca91c3df01efea1c14ed557825a"}, + {file = "pyinstaller_hooks_contrib-2024.8.tar.gz", hash = "sha256:29b68d878ab739e967055b56a93eb9b58e529d5b054fbab7a2f2bacf80cef3e2"}, ] [package.dependencies] @@ -745,13 +745,13 @@ files = [ [[package]] name = "pywin32-ctypes" -version = "0.2.2" +version = "0.2.3" description = "A (partial) reimplementation of pywin32 using ctypes/cffi" optional = false python-versions = ">=3.6" files = [ - {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, - {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, + {file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"}, + {file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"}, ] [[package]] @@ -826,18 +826,18 @@ test = ["asv", "gmpy2", "mpmath", "pytest", "pytest-cov", "pytest-xdist", "sciki [[package]] name = "setuptools" -version = "72.1.0" +version = "72.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, + {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, ] [package.extras] core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -1091,13 +1091,13 @@ test = ["pytest", "pytest-cov"] [[package]] name = "zipp" -version = "3.19.2" +version = "3.20.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, - {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, + {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, + {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, ] [package.extras] From f312bbd18ba8aa06a76c401362b078359326f973 Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:55:31 -0400 Subject: [PATCH 17/20] added reordering for Isotope Number --- metadata/PET_Radionuclide.mkd | 76 +++++++++++------------ pypet2bids/pypet2bids/helper_functions.py | 34 ++++++++++ pypet2bids/tests/test_helper_functions.py | 15 +++++ 3 files changed, 87 insertions(+), 38 deletions(-) diff --git a/metadata/PET_Radionuclide.mkd b/metadata/PET_Radionuclide.mkd index d0dfb4a..adda3d8 100644 --- a/metadata/PET_Radionuclide.mkd +++ b/metadata/PET_Radionuclide.mkd @@ -2,41 +2,41 @@ Table taken from https://dicom.nema.org/medical/Dicom/2016b/output/chtml/part16/sect_CID_4020.html -| Code Value | Code Meaning | -|:----------:|:---------------:| -| C-105A1 | ^11^Carbon | -| C-107A1 | ^13^Nitrogen | -| C-1018C | ^14^Oxygen | -| C-B1038 | ^15^Oxygen | -| C-111A1 | ^18^Fluorine | -| C-155A1 | ^22^Sodium | -| C-135A4 | ^38^Potassium | -| 126605 | ^43^Scandium | -| 126600 | ^44^Scandium | -| C-166A2 | ^45^Titanium | -| 126601 | ^51^Manganese | -| C-130A1 | ^52^Iron | -| C-149A1 | ^52^Manganese | -| 126607 | ^52m^Manganese | -| C-127A4 | ^60^Copper | -| C-127A1 | ^61^Copper | -| C-127A5 | ^62^Copper | -| C-141A1 | ^62^Zinc | -| C-127A | ^64^Copper | -| C-131A1 | ^66^Gallium | -| C-131A3 | ^68^Gallium | -| C-128A2 | ^68^Germanium | -| 126602 | ^70^Arsenic | -| C-115A2 | ^72^Arsenic | -| C-116A2 | ^73^Selenium | -| C-113A1 | ^75^Bromine | -| C-113A2 | ^76^Bromine | -| C-113A3 | ^77^Bromine | -| C-159A2 | ^82^Rubidium | -| C-162A3 | ^86^Yttrium | -| C-168A4 | ^89^Zirconium | -| 126603 | ^90^Niobium | -| C-162A7 | ^90^Yttrium | -| C-163AA | ^94m^Technetium | -| C-114A5 | ^124^Iodine | -| 126606 | ^152^Terbium | +| Code Value | Code Meaning | Isotope Abbreviation | +|:----------:|:---------------:|:--------------------:| +| C-105A1 | ^11^Carbon | 11C | +| C-107A1 | ^13^Nitrogen | 13N | +| C-1018C | ^14^Oxygen | 14O | +| C-B1038 | ^15^Oxygen | 150 | +| C-111A1 | ^18^Fluorine | 18F | +| C-155A1 | ^22^Sodium | 22Na | +| C-135A4 | ^38^Potassium | 38K | +| 126605 | ^43^Scandium | 43Sc | +| 126600 | ^44^Scandium | 44Sc | +| C-166A2 | ^45^Titanium | 45Ti | +| 126601 | ^51^Manganese | 51Mn | +| C-130A1 | ^52^Iron | 52Fe | +| C-149A1 | ^52^Manganese | 52Mn | +| 126607 | ^52m^Manganese | 52mMn | +| C-127A4 | ^60^Copper | 60Cu | +| C-127A1 | ^61^Copper | 61Cu | +| C-127A5 | ^62^Copper | 62Cu | +| C-141A1 | ^62^Zinc | 62Zn | +| C-127A | ^64^Copper | 64Cu | +| C-131A1 | ^66^Gallium | 66Ga | +| C-131A3 | ^68^Gallium | 68Ga | +| C-128A2 | ^68^Germanium | 68Ge | +| 126602 | ^70^Arsenic | 70As | +| C-115A2 | ^72^Arsenic | 72As | +| C-116A2 | ^73^Selenium | 73Se | +| C-113A1 | ^75^Bromine | 75Br | +| C-113A2 | ^76^Bromine | 76Br | +| C-113A3 | ^77^Bromine | 77Br | +| C-159A2 | ^82^Rubidium | 82Rb | +| C-162A3 | ^86^Yttrium | 86Y | +| C-168A4 | ^89^Zirconium | 89Zr | +| 126603 | ^90^Niobium | 90Nb | +| C-162A7 | ^90^Yttrium | 90Y | +| C-163AA | ^94m^Technetium | 94mTc | +| C-114A5 | ^124^Iodine | 124I | +| 126606 | ^152^Terbium | 152Tb | diff --git a/pypet2bids/pypet2bids/helper_functions.py b/pypet2bids/pypet2bids/helper_functions.py index 2a28ec3..a53e322 100644 --- a/pypet2bids/pypet2bids/helper_functions.py +++ b/pypet2bids/pypet2bids/helper_functions.py @@ -1149,3 +1149,37 @@ def first_middle_last_frames_to_text( delimiter="\t", fmt="%s", ) + +def reorder_isotope(isotope: str) -> str: + """ + Reorders the isotope string to be in the format of "isotope""element name" + :param isotope: isotope string + :type isotope: str + :return: reordered isotope string + :rtype: str + """ + # remove all non-alphanumeric characters from isotope + isotope = re.findall(r"[a-zA-Z0-9]+", isotope) + # combine all elements in isotope into one string + isotope = "".join(isotope) + # collect the isotope number from the isotope string + isotope_num = re.findall(r"\d+", isotope) + # collect the element name from the isotope string + element_name = re.findall(r"[a-zA-Z]+", isotope) + + # capitalize the first letter of the element name if the element name's length is <= 2 + if 1 < len(element_name[0]) <= 2: + e = element_name[0][0].capitalize() + if len(element_name[0]) == 2: + e += element_name[0][1].lower() + elif len(element_name[0]) == 1: + e = element_name[0].capitalize() + + # special case for 52mMn + if "".join(element_name).lower() == "mmn" or "".join(element_name).lower() == "mnm": + e = "mMn" + + # reorder to put the number before the element name + isotope = f"{isotope_num[0]}{e}" + + return isotope diff --git a/pypet2bids/tests/test_helper_functions.py b/pypet2bids/tests/test_helper_functions.py index 52ba136..5a2d39a 100644 --- a/pypet2bids/tests/test_helper_functions.py +++ b/pypet2bids/tests/test_helper_functions.py @@ -343,5 +343,20 @@ def test_collect_pet_spreadsheets(): assert len(pet_spreadsheets) == 3 +def test_reorder_isotope(): + # tests whether the isotope is reordered correctly with the numerical part appearing before the string part + # and if any non-alphanumeric characters are present + isotopes = { + "11C": ["C11", "^11^C", "C-11", "11-C", "11C", "c11", "c-11", "11c"], + "18F": ["F18", "^18^F", "F-18", "18-F", "18F"], + "68Ga": ["Ga68", "^68^Ga", "Ga-68", "68-Ga", "68Ga"], + "52mMn": ["Mn52m", "Mn-52m", "52m-Mn", "52mMn"] + } + + for isotope, isotope_variants in isotopes.items(): + for isotope_variant in isotope_variants: + assert helper_functions.reorder_isotope(isotope_variant) == isotope + + if __name__ == "__main__": unittest.main() From 349ef31fa2560a612e0cead0b874a65220a60bdc Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:06:55 -0400 Subject: [PATCH 18/20] bump version, update url for telemetry --- pypet2bids/pypet2bids/telemetry.py | 9 ++++++--- pypet2bids/pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index bbc0683..2233088 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -21,7 +21,7 @@ pet2bids_config = load_vars_from_config() telemetry_default_url = pet2bids_config.get( - "TELEMETRY_URL", "http://52.87.154.236/telemetry/" + "TELEMETRY_URL", "http://openneuropet.org/pet2bids/" ) # check environment variables as well as the config file telemetry_enabled_env = os.getenv("PET2BIDS_TELEMETRY_ENABLED", True) @@ -82,8 +82,11 @@ def send_telemetry(json_data: dict, url: str = telemetry_default_url): json_data["description"] = "pet2bids_python_telemetry" - # Send a POST request to the telemetry server - requests.post(url, json=json_data) + try: + # Send a POST request to the telemetry server + requests.post(url, json=json_data) + except requests.exceptions.RequestException as e: + pass else: pass diff --git a/pypet2bids/pyproject.toml b/pypet2bids/pyproject.toml index 95760d0..f7117d9 100644 --- a/pypet2bids/pyproject.toml +++ b/pypet2bids/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pypet2bids" -version = "1.3.13dev" +version = "1.3.13" description = "A python library for converting PET imaging and blood data to BIDS." authors = ["anthony galassi <28850131+bendhouseart@users.noreply.github.com>"] license = "MIT" From 684e407faf8accf0dcf1fc1909036e61bbed2ecb Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:50:47 -0500 Subject: [PATCH 19/20] set ModeOfAdministration to lower --- matlab/updatejsonpetfile.m | 5 +++ pypet2bids/poetry.lock | 54 ++++++++++++++++++----- pypet2bids/pypet2bids/dcm2niix4pet.py | 10 +++++ pypet2bids/pypet2bids/ecat.py | 6 +++ pypet2bids/pypet2bids/telemetry.py | 5 ++- pypet2bids/tests/test_helper_functions.py | 2 +- 6 files changed, 68 insertions(+), 14 deletions(-) diff --git a/matlab/updatejsonpetfile.m b/matlab/updatejsonpetfile.m index 3395d86..0aaaf8b 100644 --- a/matlab/updatejsonpetfile.m +++ b/matlab/updatejsonpetfile.m @@ -289,6 +289,11 @@ end filemetadata = update_arrays(filemetadata); + % set ModeOfAdministration to lower case + if isfield(filemetadata,'ModeOfAdministration') + filemetadata.ModeOfAdministration = lower(filemetadata.ModeOfAdministration); + end + % clean-up fn_check = fieldnames(filemetadata); for f=1:size(fn_check,1) diff --git a/pypet2bids/poetry.lock b/pypet2bids/poetry.lock index 9744b2d..ae2937f 100644 --- a/pypet2bids/poetry.lock +++ b/pypet2bids/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.0 and should not be changed by hand. [[package]] name = "alabaster" @@ -261,17 +261,17 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] @@ -891,13 +891,13 @@ type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12 [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -1096,13 +1096,43 @@ files = [ [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] diff --git a/pypet2bids/pypet2bids/dcm2niix4pet.py b/pypet2bids/pypet2bids/dcm2niix4pet.py index 7d1bf3b..991484b 100644 --- a/pypet2bids/pypet2bids/dcm2niix4pet.py +++ b/pypet2bids/pypet2bids/dcm2niix4pet.py @@ -690,6 +690,16 @@ def run_dcm2niix(self): sidecar_json.update(self.spreadsheet_metadata.get("nifti_json", {})) sidecar_json.update(self.additional_arguments) + # set ModeOfAdministration to lower case + if sidecar_json.get("ModeOfAdministration"): + sidecar_json.update( + { + "ModeOfAdministration": sidecar_json.get( + "ModeOfAdministration" + ).lower() + } + ) + # this is mostly for ezBIDS, but it helps us to make better use of the series description that # dcm2niix generates by default for PET imaging collect_these_fields = { diff --git a/pypet2bids/pypet2bids/ecat.py b/pypet2bids/pypet2bids/ecat.py index efbc2ea..a4f7988 100644 --- a/pypet2bids/pypet2bids/ecat.py +++ b/pypet2bids/pypet2bids/ecat.py @@ -453,6 +453,12 @@ def populate_sidecar(self, **kwargs): meta_radio_inputs = check_meta_radio_inputs(self.sidecar_template) self.sidecar_template.update(**meta_radio_inputs) + # set ModeOfAdministration to lower case + if self.sidecar_template.get("ModeOfAdministration", ""): + self.sidecar_template["ModeOfAdministration"] = self.sidecar_template[ + "ModeOfAdministration" + ].lower() + def prune_sidecar(self): """ Eliminate unpopulated fields in sidecar while leaving in mandatory fields even if they are unpopulated. diff --git a/pypet2bids/pypet2bids/telemetry.py b/pypet2bids/pypet2bids/telemetry.py index 6ae54a9..b80d092 100644 --- a/pypet2bids/pypet2bids/telemetry.py +++ b/pypet2bids/pypet2bids/telemetry.py @@ -53,7 +53,10 @@ def telemetry_enabled(config_path=None): load_dotenv(dotenv_path=config_file) # check to see if telemetry is disabled - if os.getenv("PET2BIDS_TELEMETRY_ENABLED", "").lower() == "false" or os.getenv("CI", "false") == "true": + if ( + os.getenv("PET2BIDS_TELEMETRY_ENABLED", "").lower() == "false" + or os.getenv("CI", "false") == "true" + ): return False else: return True diff --git a/pypet2bids/tests/test_helper_functions.py b/pypet2bids/tests/test_helper_functions.py index 5a2d39a..8c9f177 100644 --- a/pypet2bids/tests/test_helper_functions.py +++ b/pypet2bids/tests/test_helper_functions.py @@ -350,7 +350,7 @@ def test_reorder_isotope(): "11C": ["C11", "^11^C", "C-11", "11-C", "11C", "c11", "c-11", "11c"], "18F": ["F18", "^18^F", "F-18", "18-F", "18F"], "68Ga": ["Ga68", "^68^Ga", "Ga-68", "68-Ga", "68Ga"], - "52mMn": ["Mn52m", "Mn-52m", "52m-Mn", "52mMn"] + "52mMn": ["Mn52m", "Mn-52m", "52m-Mn", "52mMn"], } for isotope, isotope_variants in isotopes.items(): From 9d9e9873115dc1c9e83a98c3887e4b7ec290e4af Mon Sep 17 00:00:00 2001 From: Anthony Galassi <28850131+bendhouseart@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:56:06 -0500 Subject: [PATCH 20/20] bump version --- pypet2bids/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypet2bids/pyproject.toml b/pypet2bids/pyproject.toml index 1ddf2cd..ab015b0 100644 --- a/pypet2bids/pyproject.toml +++ b/pypet2bids/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pypet2bids" -version = "1.3.14" +version = "1.3.15" description = "A python library for converting PET imaging and blood data to BIDS." authors = ["anthony galassi <28850131+bendhouseart@users.noreply.github.com>"] license = "MIT"