From 8ddf7f8ab0e16080d7b7409b643527ac942a3375 Mon Sep 17 00:00:00 2001
From: Dan Levitas
Date: Tue, 20 Aug 2024 18:39:07 +0000
Subject: [PATCH] [ENH] Add BIDS URI option for IntendedFor mapping
---
handler/convert.js | 6 +++++
handler/ezBIDS_core/ezBIDS_core.py | 43 +++++++++++++++++++-----------
ui/src/Finalize.vue | 1 +
ui/src/SeriesPage.vue | 17 ++++++++++--
ui/src/store/index.ts | 2 ++
5 files changed, 52 insertions(+), 17 deletions(-)
diff --git a/handler/convert.js b/handler/convert.js
index 5a67e853..d01b5243 100755
--- a/handler/convert.js
+++ b/handler/convert.js
@@ -187,6 +187,9 @@ async.forEachOf(info.objects, (o, idx, next_o) => {
}
path += tokens.join("_");
path += "_" + suffix + ".nii.gz"; //TODO - not sure if this is robust enough..
+ if (info.BIDSURI === true) {
+ path = "bids::" + "sub-" + io._entities.subject + "/" + path
+ }
item.sidecar.IntendedFor.push(path);
}
}
@@ -387,6 +390,9 @@ async.forEachOf(info.objects, (o, idx, next_o) => {
}
path += tokens.join("_");
path += "_" + suffix + ".nii.gz"; //TODO - not sure if this is robust enough..
+ if (info.BIDSURI === true) {
+ path = "bids::" + "sub-" + io._entities.subject + "/" + path
+ }
item.sidecar.IntendedFor.push(path);
}
}
diff --git a/handler/ezBIDS_core/ezBIDS_core.py b/handler/ezBIDS_core/ezBIDS_core.py
index ed0f8135..d3576bbf 100755
--- a/handler/ezBIDS_core/ezBIDS_core.py
+++ b/handler/ezBIDS_core/ezBIDS_core.py
@@ -473,7 +473,12 @@ def modify_uploaded_dataset_list(uploaded_img_list):
config_file = ""
exclude_data = False
- config_file_list = natsorted([x for x in os.listdir(DATA_DIR) if x.endswith('ezBIDS_template.json')])
+ config_file_list = []
+ for root, dirs, files in os.walk(DATA_DIR):
+ for file in files:
+ if file.endswith('ezBIDS_template.json'):
+ config_file_list.append(os.path.join(root, file))
+
if len(config_file_list):
# Ideally only one config file uploaded, but if multiple configurations found, select last one (most recent?)
config = True
@@ -1392,7 +1397,7 @@ def determine_sub_ses_IDs(dataset_list, bids_compliant):
List of dictionaries containing pertinent and unique information about
the data, primarily coming from the metadata in the json files.
- subjects_information : list
+ subs_information : list
List of dictionaries containing subject identification info, such as
PatientID, PatientName, PatientBirthDate, and corresponding session
information.
@@ -1402,7 +1407,7 @@ def determine_sub_ses_IDs(dataset_list, bids_compliant):
"""
date_counter = 1
subject_idx_counter = 0
- subjects_information = []
+ subs_information = []
participants_info = {}
# Determine unique subjects from uploaded dataset
for sub in np.unique([x["subject"] for x in dataset_list]):
@@ -1550,9 +1555,9 @@ def determine_sub_ses_IDs(dataset_list, bids_compliant):
"validationErrors": []
}
- subjects_information.append(subject_ids_info)
+ subs_information.append(subject_ids_info)
- return dataset_list, subjects_information, participants_info
+ return dataset_list, subs_information, participants_info
def determine_unique_series(dataset_list, bids_compliant):
@@ -1638,7 +1643,7 @@ def determine_unique_series(dataset_list, bids_compliant):
return dataset_list, dataset_list_unique_series
-def template_configuration(dataset_list_unique_series, subjects_information, config_file):
+def template_configuration(dataset_list_unique_series, subs_information, config_file):
"""
Parameters
----------
@@ -1646,7 +1651,7 @@ def template_configuration(dataset_list_unique_series, subjects_information, con
A modified version of dataset_list, where this list contains only the
dictionaries of acquisitions with a unique series group ID.
- subjects_information : list
+ subs_information : list
List of dictionaries containing subject identification info, such as
PatientID, PatientName, PatientBirthDate, and corresponding session
information.
@@ -1669,13 +1674,16 @@ def template_configuration(dataset_list_unique_series, subjects_information, con
A modified version of dataset_list, where this list contains only the
dictionaries of acquisitions with a unique series group ID.
- subjects_information : list
+ subs_information : list
List of dictionaries containing subject identification info, such as
PatientID, PatientName, PatientBirthDate, and corresponding session
information.
events : dictionary
Information pertaining to the events timing files for func/bold data.
+
+ bids_uri : boolean
+ Specifies whether or not user wants BIDS URI format for IntendedFor metadata mapping
"""
config_data = open(config_file)
@@ -1687,6 +1695,7 @@ def template_configuration(dataset_list_unique_series, subjects_information, con
subjects_sessions_info = config_data["subjects"]
config_dataset_list_unique_series = config_data["series"]
config_dataset_list_objects = config_data["objects"]
+ bids_uri = config_data["BIDSURI"]
# Try to determine subject (and session) mapping from what's in the configuration
match_start_index = None
@@ -1712,7 +1721,7 @@ def template_configuration(dataset_list_unique_series, subjects_information, con
"AcquisitionDateTime"
]
- for sub_info in subjects_information:
+ for sub_info in subs_information:
sub = sub_info["subject"]
for key in ref_patient_info.keys():
if ref_subject_id in ref_patient_info[key]:
@@ -1819,7 +1828,7 @@ def template_configuration(dataset_list_unique_series, subjects_information, con
events["sampleValues"] = {}
return (readme, dataset_description_dic, participants_column_info,
- dataset_list_unique_series, subjects_information, events)
+ dataset_list_unique_series, subs_information, events, bids_uri)
def create_lookup_info():
@@ -3106,15 +3115,15 @@ def check_dwi_b0maps(dataset_list_unique_series):
dataset_list = organize_dataset(dataset_list)
# Determine subject (and session) information
-dataset_list, subjects_information, participants_info = determine_sub_ses_IDs(dataset_list, bids_compliant)
+dataset_list, subs_information, participants_info = determine_sub_ses_IDs(dataset_list, bids_compliant)
# Make a new list containing the dictionaries of only unique dataset acquisitions
dataset_list, dataset_list_unique_series = determine_unique_series(dataset_list, bids_compliant)
# If ezBIDS configuration file detected in upload, use that for datatype, suffix, and entity identifications
if config is True:
- readme, dataset_description_dic, participants_column_info, dataset_list_unique_series, subjects_information, events = \
- template_configuration(dataset_list_unique_series, subjects_information, config_file)
+ readme, dataset_description_dic, participants_column_info, dataset_list_unique_series, subs_information, events, \
+ bids_uri = template_configuration(dataset_list_unique_series, subs_information, config_file)
else:
# README
@@ -3160,6 +3169,9 @@ def check_dwi_b0maps(dataset_list_unique_series):
}
}
+ # BIDS URI
+ bids_uri = False
+
# Generate lookup information directory to help with datatype and suffix identification (and to some degree, entities)
lookup_dic = create_lookup_info()
@@ -3212,12 +3224,13 @@ def check_dwi_b0maps(dataset_list_unique_series):
EZBIDS = {
"readme": readme,
"datasetDescription": dataset_description_dic,
- "subjects": subjects_information,
+ "subjects": subs_information,
"participantsColumn": participants_column_info,
"participantsInfo": participants_info,
"series": ui_series_info_list,
"objects": objects_list,
- "events": events
+ "events": events,
+ "BIDSURI": bids_uri
}
# Write dictionary to ezBIDS_core.json
diff --git a/ui/src/Finalize.vue b/ui/src/Finalize.vue
index 24e2033f..dae1ae24 100755
--- a/ui/src/Finalize.vue
+++ b/ui/src/Finalize.vue
@@ -204,6 +204,7 @@ export default defineComponent({
//things that convert.ts uses
objects: this.ezbids.objects, //most important thing that convert.ts needs
+ BIDSURI: this.ezbids.BIDSURI,
events: this.ezbids.events,
entityMappings, //helps with convert
datasetDescription: this.ezbids.datasetDescription,
diff --git a/ui/src/SeriesPage.vue b/ui/src/SeriesPage.vue
index 852919b3..04ff3cc6 100755
--- a/ui/src/SeriesPage.vue
+++ b/ui/src/SeriesPage.vue
@@ -48,6 +48,9 @@
The information you specify here will be applied to all subjects that uses matching
SeriesDescription. You can also override this information later for each subject.
+
+ Use BIDS URI format for IntendedFor metadata mapping (if applicable)
+
< Please select a series to view/edit
@@ -308,7 +311,7 @@ import ModalityForm from './components/modalityForm.vue';
import { prettyBytes } from './filters';
-import { Series, IObject } from './store';
+import { Series, IObject, IEzbids } from './store';
import { validateEntities, validate_B0FieldIdentifier_B0FieldSource, metadataAlerts } from './libUnsafe';
import anatYaml from '../src/assets/schema/rules/sidecars/anat.yaml';
@@ -378,6 +381,16 @@ export default defineComponent({
this.showInfo[entity] = !this.showInfo[entity];
},
+ BIDSURI($root: IEzbids, b: boolean) {
+ if (b === true) {
+ $root.BIDSURI = true;
+ localStorage.setItem('checkboxState', 'true');
+ } else {
+ $root.BIDSURI = false;
+ localStorage.setItem('checkboxState', 'false');
+ }
+ },
+
validate(s: Series | null) {
if (!s) return;
@@ -389,7 +402,7 @@ export default defineComponent({
validate_B0FieldIdentifier_B0FieldSource(s);
}
- /* Alert users to metadata issues, such as missing required fields or
+ /* Alert users to metadata issues, such as missing required fields or
improperly-formmated metadata field values.
*/
let bidsDatatypeMetadata = {};
diff --git a/ui/src/store/index.ts b/ui/src/store/index.ts
index d8efe742..f353c66a 100755
--- a/ui/src/store/index.ts
+++ b/ui/src/store/index.ts
@@ -314,6 +314,7 @@ const state = {
subjects: [] as Subject[],
series: [] as Series[],
objects: [] as IObject[],
+ BIDSURI: false,
_organized: [] as OrganizedSubject[], //above things are organized into subs/ses/run/object hierarchy for quick access
@@ -509,6 +510,7 @@ const store = createStore({
subjects: [],
series: [],
objects: [],
+ BIDSURI: false,
_organized: [], //above things are organized into subs/ses/run/object hierarchy for quick access