Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 24.1.6 into main #871

Merged
merged 34 commits into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
f471c59
update the VERSION for the next bug fix release (#816)
cmadjar Nov 2, 2022
90e7a9a
[dcm2bids] Remove hardcoded dcm2niix binary to use the value stored i…
cmadjar Nov 2, 2022
02333c9
Pull 24.0.3 in 24.1 release (#820)
cmadjar Nov 2, 2022
0614e8c
fix nonetype errors when the visit of a session does not exist so tha…
cmadjar Nov 7, 2022
f47421d
fix some errors when RepetitionTime is not available in JSON file (#825)
cmadjar Nov 8, 2022
f0810cb
Add capability to download file from s3 (#826)
cmadjar Nov 8, 2022
3644f95
Upload to S3: support object name starting with s3://bucket_name/ for…
cmadjar Nov 8, 2022
55c0d8c
fix database class pselect documentation for the return type (#828)
cmadjar Nov 10, 2022
34e8041
map scan type to scan type ID when scan type provided to run_nifti_in…
cmadjar Nov 10, 2022
47c797e
modify permission of script run_push_imaging_files_to_s3_pipeline.py …
cmadjar Nov 10, 2022
02507c5
skip violation if not found on filesystem since it means the scan has…
cmadjar Nov 11, 2022
9c376bb
update VERSION file (#832)
cmadjar Nov 11, 2022
e62ea82
do not push files to S3 when their path in the DB is already an S3 UR…
cmadjar Nov 11, 2022
0d27d49
fix violation files path when checking if the files are on the filesy…
cmadjar Nov 14, 2022
4fd75e2
Merge 24.0 release into 24.1 release (#836)
cmadjar Nov 14, 2022
a826be3
fix check if file already inserted in DB (#845)
cmadjar Dec 5, 2022
c9afaa2
Fix logic of determining file run number when previously inserted fil…
cmadjar Dec 5, 2022
17f6652
update version file (#847)
cmadjar Dec 5, 2022
b224835
Chunk creation subprocess failure check (#848)
regisoc Dec 5, 2022
afe6903
Revert chunk_pb2.py changes (#849)
laemtl Dec 5, 2022
cef8f87
remove prints in nifti_insertion_pipeline.py (#851)
cmadjar Dec 6, 2022
3af8cf6
fix permissoin denied upon deletion of tmp dir (#853)
cmadjar Dec 8, 2022
115283e
update to next bug fix relesae (#854)
cmadjar Dec 8, 2022
e8103e7
fix duplicated protocols error when same scan type returned (#856)
cmadjar Dec 16, 2022
049414e
Add missing exit codes on the python's side (#857)
cmadjar Dec 21, 2022
27f7e63
add ignore case to regex (#859)
cmadjar Dec 23, 2022
bd74369
add download from S3 and reupload if file provided to run_nifti-inser…
cmadjar Jan 4, 2023
a1dc483
fix intended for bug when no acq time available (#861)
cmadjar Jan 6, 2023
bc00fd6
fix bug for intended for when getting the list of files needed Intend…
cmadjar Jan 6, 2023
82c47b8
fix paths when there are not / at the end of the Config (#866)
cmadjar Jan 12, 2023
e77d767
fix NoneType error /opt/loris/bin/mri/python/lib/dcm2bids_imaging_pip…
cmadjar Jan 12, 2023
9f986c8
Properly update `mri_upload` 'Inserting' column when different sectio…
cmadjar Jan 13, 2023
d53b0a7
update version file to 24.1.6 (#870)
cmadjar Jan 18, 2023
4bbdb43
Merge tag 'v24.1.6' into merge_24.1.6_into_main
cmadjar Jan 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24.1.5
24.1.6
2 changes: 1 addition & 1 deletion python/lib/dcm2bids_imaging_pipeline_lib/base_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __init__(self, loris_getopt_obj, script_name):
# ---------------------------------------------------------------------------------------------
# Create tmp dir and log file (their basename being the name of the script run)
# ---------------------------------------------------------------------------------------------
self.tmp_dir = lib.utilities.create_processing_tmp_dir(script_name)
self.tmp_dir = self.loris_getopt_obj.tmp_dir
self.log_obj = Log(
self.db, self.data_dir, script_name, os.path.basename(self.tmp_dir), self.options_dict, self.verbose
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def _run_dicom_archive_validation_pipeline(self):
message = f"run_dicom_archive_validation.py successfully executed for UploadID {self.upload_id} " \
f"and ArchiveLocation {self.tarchive_path}"
self.log_info(message, is_error="N", is_verbose="Y")
# reset mri_upload to Inserting as run_dicom_archive_validation.py will set Inserting=0 after execution
self.imaging_upload_obj.update_mri_upload(upload_id=self.upload_id, fields=('Inserting',), values=('1',))
else:
message = f"run_dicom_archive_validation.py failed validation for UploadID {self.upload_id}" \
f"and ArchiveLocation {self.tarchive_path}. Exit code was {validation_process.returncode}."
Expand Down Expand Up @@ -301,6 +303,8 @@ def _run_nifti_insertion(self, nifti_file_path, json_file_path, bval_file_path=N
message = f"run_nifti_insertion.py successfully executed for file {nifti_file_path}"
self.log_info(message, is_error="N", is_verbose="Y")
self.inserted_file_count += 1
# reset mri_upload to Inserting as run_nifti_insertion.py will set Inserting=0 after execution
self.imaging_upload_obj.update_mri_upload(upload_id=self.upload_id, fields=('Inserting',), values=('1',))
else:
message = f"run_nifti_insertion.py failed for file {nifti_file_path}.\n{stdout}"
print(stdout)
Expand Down Expand Up @@ -346,6 +350,8 @@ def _add_intended_for_to_fieldmap_json_files(self):
"""

fmap_files_dict = self.imaging_obj.determine_intended_for_field_for_fmap_json_files(self.tarchive_id)
if not fmap_files_dict:
return

for key in fmap_files_dict.keys():
sorted_fmap_files_list = fmap_files_dict[key]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import lib.exitcode
import os
import re
import subprocess
import sys

from lib.dcm2bids_imaging_pipeline_lib.base_pipeline import BasePipeline
Expand Down Expand Up @@ -36,6 +37,8 @@ def __init__(self, loris_getopt_obj, script_name):
"""
super().__init__(loris_getopt_obj, script_name)
self.nifti_path = self.options_dict["nifti_path"]["value"]
self.nifti_s3_url = self.options_dict["nifti_path"]["s3_url"] \
if 's3_url' in self.options_dict["nifti_path"].keys() else None
self.nifti_blake2 = blake2b(self.nifti_path.encode('utf-8')).hexdigest()
self.nifti_md5 = hashlib.md5(self.nifti_path.encode()).hexdigest()
self.json_path = self.options_dict["json_path"]["value"]
Expand All @@ -48,6 +51,16 @@ def __init__(self, loris_getopt_obj, script_name):
self.loris_scan_type = self.options_dict["loris_scan_type"]["value"]
self.bypass_extra_checks = self.options_dict["bypass_extra_checks"]["value"]

# ---------------------------------------------------------------------------------------------
# Set 'Inserting' flag to 1 in mri_upload
# ---------------------------------------------------------------------------------------------
self.imaging_upload_obj.update_mri_upload(upload_id=self.upload_id, fields=('Inserting',), values=('1',))

# ---------------------------------------------------------------------------------------------
# Get S3 object from loris_getopt object
# ---------------------------------------------------------------------------------------------
self.s3_obj = self.loris_getopt_obj.s3_obj

# ---------------------------------------------------------------------------------------------
# Check the mri_upload table to see if the DICOM archive has been validated
# ---------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -83,6 +96,8 @@ def __init__(self, loris_getopt_obj, script_name):
self.nifti_path,
self.subject_id_dict["CandMismatchError"]
)
if self.nifti_s3_url: # push candidate errors to S3 if provided file was on S3
self._run_push_to_s3_pipeline()
self.log_error_and_exit(
self.subject_id_dict['CandMismatchError'], lib.exitcode.CANDIDATE_MISMATCH, is_error="Y", is_verbose="N"
)
Expand All @@ -107,6 +122,8 @@ def __init__(self, loris_getopt_obj, script_name):
if not self.scan_type_id:
self._move_to_trashbin()
self._register_protocol_violated_scan()
if self.nifti_s3_url: # push violations to S3 if provided file was on S3
self._run_push_to_s3_pipeline()
message = f"{self.nifti_path}'s acquisition protocol is 'unknown'."
self.log_error_and_exit(message, lib.exitcode.UNKNOWN_PROTOCOL, is_error="Y", is_verbose="N")
else:
Expand All @@ -116,6 +133,8 @@ def __init__(self, loris_getopt_obj, script_name):
if not self.scan_type_id:
self._move_to_trashbin()
self._register_protocol_violated_scan()
if self.nifti_s3_url: # push violations to S3 if provided file was on S3
self._run_push_to_s3_pipeline()
message = f"{self.nifti_path}'s scan type {self.scan_type_name} provided to run_nifti_insertion.py" \
f" is not a valid scan type in the database."
self.log_error_and_exit(message, lib.exitcode.UNKNOWN_PROTOCOL, is_error="Y", is_verbose="N")
Expand All @@ -127,6 +146,8 @@ def __init__(self, loris_getopt_obj, script_name):
if not self.bids_categories_dict:
self._move_to_trashbin()
self._register_protocol_violated_scan()
if self.nifti_s3_url: # push violations to S3 if provided file was on S3
self._run_push_to_s3_pipeline()
message = f"Scan type {self.scan_type_name} does not have BIDS tables set up."
self.log_error_and_exit(message, lib.exitcode.UNKNOWN_PROTOCOL, is_error="Y", is_verbose="N")

Expand All @@ -151,6 +172,8 @@ def __init__(self, loris_getopt_obj, script_name):
self._move_to_trashbin()
self._register_violations_log(self.exclude_violations_list, self.trashbin_nifti_rel_path)
self._register_violations_log(self.warning_violations_list, self.trashbin_nifti_rel_path)
if self.nifti_s3_url: # push violations to S3 if provided file was on S3
self._run_push_to_s3_pipeline()
message = f"{self.nifti_path} violates exclusionary checks listed in mri_protocol_checks. " \
f" List of violations are: {self.exclude_violations_list}"
self.log_error_and_exit(message, lib.exitcode.UNKNOWN_PROTOCOL, is_error="Y", is_verbose="N")
Expand All @@ -167,9 +190,16 @@ def __init__(self, loris_getopt_obj, script_name):
# ---------------------------------------------------------------------------------------------
self.remove_tmp_dir()

# ---------------------------------------------------------------------------------------------
# Push inserted images to S3 if they were downloaded from S3
# ---------------------------------------------------------------------------------------------
if self.nifti_s3_url:
self._run_push_to_s3_pipeline()

# ---------------------------------------------------------------------------------------------
# If we get there, the insertion was complete and successful
# ---------------------------------------------------------------------------------------------
self.imaging_upload_obj.update_mri_upload(upload_id=self.upload_id, fields=('Inserting',), values=('0',))
sys.exit(lib.exitcode.SUCCESS)

def _load_json_sidecar_file(self):
Expand Down Expand Up @@ -658,3 +688,28 @@ def _create_pic_image(self):
pic_rel_path = self.imaging_obj.create_imaging_pic(file_info)

self.imaging_obj.insert_parameter_file(self.file_id, 'check_pic_filename', pic_rel_path)

def _run_push_to_s3_pipeline(self):
"""
Run push to S3 script to upload data to S3. This function is called only when the file path to insert provided
to the script is an S3 URL.
"""

push_to_s3_cmd = [
"run_push_imaging_files_to_s3_pipeline.py",
"-p", self.options_dict["profile"]["value"],
"-u", str(self.upload_id),
]
if self.verbose:
push_to_s3_cmd.append("-v")

s3_process = subprocess.Popen(push_to_s3_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = s3_process.communicate()

if s3_process.returncode == 0:
message = f"run_push_imaging_files_to_s3_pipeline.py successfully executed for Upload ID {self.upload_id}"
self.log_info(message, is_error="N", is_verbose="Y")
else:
message = f"run_push_imaging_files_to_s3_pipeline.py failed for Upload ID {self.upload_id}.\n{stdout}"
print(stdout)
self.log_info(message, is_error="Y", is_verbose="Y")
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import lib.exitcode
import lib.utilities
from lib.aws_s3 import AwsS3
from lib.dcm2bids_imaging_pipeline_lib.base_pipeline import BasePipeline

__license__ = "GPLv3"
Expand Down Expand Up @@ -33,16 +32,14 @@ def __init__(self, loris_getopt_obj, script_name):
self.tarchive_id = self.dicom_archive_obj.tarchive_info_dict["TarchiveID"]

# ---------------------------------------------------------------------------------------------
# Get Bucket information from Config and connect to bucket
# Set 'Inserting' flag to 1 in mri_upload
# ---------------------------------------------------------------------------------------------
s3_endpoint = self.config_db_obj.get_config("AWS_S3_Endpoint")
s3_bucket_name = self.config_db_obj.get_config("AWS_S3_Default_Bucket")
self.s3_obj = AwsS3(
aws_access_key_id=self.config_file.s3["aws_access_key_id"],
aws_secret_access_key=self.config_file.s3["aws_secret_access_key"],
aws_endpoint_url=s3_endpoint if s3_endpoint else self.config_file.s3["aws_s3_endpoint_url"],
bucket_name=s3_bucket_name if s3_bucket_name else self.config_file.s3["aws_s3_bucket_name"]
)
self.imaging_upload_obj.update_mri_upload(upload_id=self.upload_id, fields=('Inserting',), values=('1',))

# ---------------------------------------------------------------------------------------------
# Get S3 object from loris_getopt object
# ---------------------------------------------------------------------------------------------
self.s3_obj = self.loris_getopt_obj.s3_obj

# ---------------------------------------------------------------------------------------------
# Get all the files from files, parameter_file and violation tables
Expand All @@ -69,6 +66,7 @@ def __init__(self, loris_getopt_obj, script_name):
os.remove(full_path)

self._clean_up_empty_folders()
self.imaging_upload_obj.update_mri_upload(upload_id=self.upload_id, fields=('Inserting',), values=('0',))
sys.exit(lib.exitcode.SUCCESS)

def _get_files_to_push_list(self):
Expand Down Expand Up @@ -144,14 +142,11 @@ def _get_list_of_files_from_mri_protocol_violated_scans(self):
"""

entries = self.imaging_obj.mri_prot_viol_scan_db_obj.get_protocol_violations_for_tarchive_id(self.tarchive_id)
print(entries)
for entry in entries:
print(entry['minc_location'])
print(os.path.exists(entry['minc_location']))
if entry['minc_location'].startswith('s3://'):
# skip since file already pushed to S3
continue
if not os.path.exists(self.data_dir + entry['minc_location']):
if not os.path.exists(os.path.join(self.data_dir, entry['minc_location'])):
# violation has been rerun or moved
continue
self.files_to_push_list.append({
Expand Down Expand Up @@ -182,7 +177,7 @@ def _get_list_of_files_from_mri_violations_log(self):
if entry['MincFile'].startswith('s3://'):
# skip since file already pushed to S3
continue
if not os.path.exists(self.data_dir + entry['MincFile']):
if not os.path.exists(os.path.join(self.data_dir, entry['MincFile'])):
# violation has been rerun or moved
continue
self.files_to_push_list.append({
Expand Down Expand Up @@ -266,7 +261,6 @@ def _clean_up_empty_folders(self):
print("Cleaning up empty folders")
cand_id = self.subject_id_dict["CandID"]
bids_cand_id = f"sub-{cand_id}"
print(os.path.join(self.data_dir, "assembly_bids", bids_cand_id))
lib.utilities.remove_empty_folders(os.path.join(self.data_dir, "assembly_bids", bids_cand_id))
lib.utilities.remove_empty_folders(os.path.join(self.data_dir, "pic", cand_id))
lib.utilities.remove_empty_folders(os.path.join(self.data_dir, "trashbin"))
46 changes: 34 additions & 12 deletions python/lib/imaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,9 @@ def look_for_matching_protocols(self, protocols_list, scan_param):
matching_protocols_list = []
for protocol in protocols_list:
if protocol['series_description_regex']:
if re.search(rf"{protocol['series_description_regex']}", scan_param['SeriesDescription']):
if re.search(
rf"{protocol['series_description_regex']}", scan_param['SeriesDescription'], re.IGNORECASE
):
matching_protocols_list.append(protocol['Scan_type'])
elif self.is_scan_protocol_matching_db_protocol(protocol, scan_param):
matching_protocols_list.append(protocol['Scan_type'])
Expand Down Expand Up @@ -793,7 +795,7 @@ def get_violations(self, checks_list, header, severity, scan_param_dict):
True for v in valid_ranges if self.in_range(scan_param, v[0], v[1])]
)) if valid_ranges else True
passes_regex_check = bool(len([
True for r in valid_regexs if re.match(r, scan_param)
True for r in valid_regexs if re.match(r, scan_param, re.IGNORECASE)
])) if valid_regexs else True

if passes_regex_check and passes_range_check:
Expand Down Expand Up @@ -866,9 +868,16 @@ def determine_intended_for_field_for_fmap_json_files(self, tarchive_id):
# get the list of files sorted by acquisition time
sorted_new_files_list = self.get_list_of_files_sorted_by_acq_time(files_list)

if not sorted_new_files_list or not sorted_fmap_files_dict:
# if got empty lists, then there are no files to determine IntendedFor either because acq_time
# was not set or because there are no fieldmap data
return None

for key in sorted_fmap_files_dict.keys():
sorted_fmap_files_list = sorted_fmap_files_dict[key]
for idx, fmap_dict in enumerate(sorted_fmap_files_list):
if not fmap_dict['acq_time']:
continue
fmap_acq_time = fmap_dict['acq_time']
next_fmap_acq_time = sorted_fmap_files_list[idx + 1]['acq_time'] \
if idx + 1 < len(sorted_fmap_files_list) else None
Expand Down Expand Up @@ -926,13 +935,15 @@ def get_list_of_fmap_files_sorted_by_acq_time(self, files_list):
fmap_files_dir_pa = []
fmap_files_no_dir = []
for file_dict in files_list:

bids_info = self.mri_prot_db_obj.get_bids_info_for_scan_type_id(
file_dict['AcquisitionProtocolID']
)
acq_time = self.param_file_db_obj.get_parameter_file_for_file_id_param_type_id(
param_file_result = self.param_file_db_obj.get_parameter_file_for_file_id_param_type_id(
file_dict['FileID'],
self.param_type_db_obj.get_parameter_type_id('acquisition_time')
)['Value']
)
acq_time = param_file_result['Value'] if param_file_result else None
if bids_info['BIDSCategoryName'] == 'fmap' and bids_info['BIDSScanType'] in bids_fmap_suffix_list:
json_file_path = self.param_file_db_obj.get_parameter_file_for_file_id_param_type_id(
file_dict['FileID'],
Expand All @@ -953,11 +964,14 @@ def get_list_of_fmap_files_sorted_by_acq_time(self, files_list):
else:
fmap_files_no_dir.append(file_dict)

fmap_files_dict = {
'dir-AP': sorted(fmap_files_dir_ap, key=lambda x: x['acq_time']),
'dir-PA': sorted(fmap_files_dir_pa, key=lambda x: x['acq_time']),
'no-dir': sorted(fmap_files_no_dir, key=lambda x: x['acq_time']),
}
try:
fmap_files_dict = {
'dir-AP': sorted(fmap_files_dir_ap, key=lambda x: x['acq_time']),
'dir-PA': sorted(fmap_files_dir_pa, key=lambda x: x['acq_time']),
'no-dir': sorted(fmap_files_no_dir, key=lambda x: x['acq_time']),
}
except TypeError:
return None

return fmap_files_dict

Expand Down Expand Up @@ -986,10 +1000,11 @@ def get_list_of_files_sorted_by_acq_time(self, files_list):
bids_info = self.mri_prot_db_obj.get_bids_info_for_scan_type_id(
file_dict['AcquisitionProtocolID']
)
acq_time = self.param_file_db_obj.get_parameter_file_for_file_id_param_type_id(
param_file_result = self.param_file_db_obj.get_parameter_file_for_file_id_param_type_id(
file_dict['FileID'],
self.param_type_db_obj.get_parameter_type_id('acquisition_time')
)['Value']
)
acq_time = param_file_result['Value'] if param_file_result else None
require_fmap = False
if (bids_info['BIDSCategoryName'] == 'dwi' and bids_info['BIDSScanType'] in bids_dwi_suffix_list) \
or (bids_info['BIDSCategoryName'] == 'func' and bids_info['BIDSScanType'] in bids_func_suffix_list)\
Expand All @@ -1012,7 +1027,12 @@ def get_list_of_files_sorted_by_acq_time(self, files_list):
'need_fmap': require_fmap
})

return sorted(new_files_list, key=lambda x: x['acq_time'])
try:
sorted_files_list = sorted(new_files_list, key=lambda x: x['acq_time'])
except TypeError:
return None

return sorted_files_list

def modify_fmap_json_file_to_write_intended_for(self, sorted_fmap_files_list):
"""
Expand All @@ -1023,6 +1043,8 @@ def modify_fmap_json_file_to_write_intended_for(self, sorted_fmap_files_list):
"""

for fmap_dict in sorted_fmap_files_list:
if 'IntendedFor' not in fmap_dict:
continue
json_file_path = os.path.join(self.config_db_obj.get_config('dataDirBasepath'), fmap_dict['json_file_path'])
with open(json_file_path) as json_file:
json_data = json.load(json_file)
Expand Down
Loading