diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fd3e29e..6a08a3b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -23,7 +23,7 @@ repos: - repo: https://github.com/hadolint/hadolint - rev: v2.12.1-beta + rev: v2.14.0 hooks: - id: hadolint-docker name: Lint Dockerfiles diff --git a/Dockerfile b/Dockerfile index 802be5d..cfd38d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ RUN wget --progress=dot:giga -P /opt https://www.fil.ion.ucl.ac.uk/spm/download/ ${SPM_EXEC} function exit # Configure SPM BIDS App entry point -COPY run.sh spm_BIDS_App.m pipeline_participant.m pipeline_group.m /opt/spm${SPM_VERSION}/ +COPY run.sh spm_BIDS_App.m narrow_participants.m pipeline_participant.m pipeline_group.m /opt/spm${SPM_VERSION}/ RUN chmod +x /opt/spm${SPM_VERSION}/run.sh && \ chmod +x /opt/spm${SPM_VERSION}/spm${SPM_VERSION} && \ chmod +x /opt/spm${SPM_VERSION}/run_spm12.sh diff --git a/narrow_participants.m b/narrow_participants.m new file mode 100644 index 0000000..ddd48d6 --- /dev/null +++ b/narrow_participants.m @@ -0,0 +1,32 @@ +function BIDS = narrow_participants(BIDS, labels) +% NARROW_PARTICIPANTS - restrict BIDS struct to only requested labels +% + +% nothing to do if no labels to change? +if isempty(labels) + return +end + +idx = ismember({BIDS.subjects.name},labels); +BIDS.subjects = BIDS.subjects(idx); + +if isempty(BIDS.participants) + warning('no participants information. missing participants.csv?') +else + if ~ismember(fieldnames(BIDS.participants), 'participant_id') + error('participants.tsv does not contain a participant_id column') + end + idx = ismember(BIDS.participants.participant_id, labels); + for fn=fieldnames(BIDS.participants)' + replace = BIDS.participants.(char(fn)); + % BIDS.participants.meta is 1x1. other fields are 1xN + if(length(replace) < length(idx)) + warning('BIDS participants field "%s" too short: ID idx len %d > number of field values %d; ignored', ... + char(fn), length(idx), length(replace)) + continue + end + BIDS.participants.(char(fn)) = replace(idx); + end +end + +end diff --git a/pipeline_participant.m b/pipeline_participant.m index 48868dd..01ce768 100644 --- a/pipeline_participant.m +++ b/pipeline_participant.m @@ -3,7 +3,7 @@ %========================================================================== % Available variables: BIDS and BIDS_App - +global BIDS; %========================================================================== %-fMRI Preprocessing %========================================================================== diff --git a/spm_BIDS_App.m b/spm_BIDS_App.m index f5af1bc..d83f038 100644 --- a/spm_BIDS_App.m +++ b/spm_BIDS_App.m @@ -201,6 +201,9 @@ spm('defaults','fmri'); spm_jobman('initcfg'); +% if we used --participant_label PARTICIPANT_LABEL [PARTICIPANT_LABEL ...] +BIDS = narrow_participants(BIDS, BIDS_App.participants); + %========================================================================== %-(Temporary) Copy of input data and uncompress image files %========================================================================== @@ -253,17 +256,6 @@ BIDS = spm_changepath(BIDS,'.nii.gz','.nii',false); end -%-Simplify BIDS structure to only contain participants under study -%-------------------------------------------------------------------------- -idx = ismember({BIDS.subjects.name},BIDS_App.participants); -BIDS.subjects = BIDS.subjects(idx); -if ~isempty(BIDS.participants) - idx = ismember(BIDS.participants.participant_id,BIDS_App.participants); - for fn=fieldnames(BIDS.participants)' - BIDS.participants.(char(fn)) = BIDS.participants.(char(fn))(idx); - end -end - %========================================================================== %-Analysis level: participant* %========================================================================== diff --git a/test_narrow_participants.m b/test_narrow_participants.m new file mode 100644 index 0000000..1522768 --- /dev/null +++ b/test_narrow_participants.m @@ -0,0 +1,30 @@ +% +% script based testing for narrow_participants +% participants.json adds a 'meta' field that can be avoided when selecting only some participants +% + +bids_dir = [tempname '_bids_test']; +try + % build minimal BIDS filesystem: participants.json and 1 anat file pair + system(['mkdir -p ' bids_dir '/sub-{a,b}/ses-{1,2}/anat/']); + system([ 'touch ' bids_dir '/sub-{a,b}/ses-{1,2}/anat/T1.{json,nii.gz}']); + system(['echo -e ''participant_id\nsub-a\nsub-a\nsub-b\nsub-b'' > ' bids_dir '/participants.tsv']); + system(['echo ''{ "Name": "test", "BIDSVersion": "1.4.1"}'' > ' bids_dir '/dataset_description.json']); + system(['echo ''{ "age": { "Description": "xyz" } }'' > ' bids_dir '/participants.json']); + + % get what we built -- matches expectations + BIDS = spm_BIDS(bids_dir); + assert(length(BIDS.participants.participant_id) == 4) + + % narrowing works + BIDS_narrow = narrow_participants(BIDS, {'sub-a'}) + assert(length(BIDS_narrow.participants.participant_id) == 2) + + % no change on empty + BIDS_nochange = narrow_participants(BIDS, {}); % TODO: {{}}? + assert(length(BIDS_nochange.participants.participant_id) == 4) + +catch me + rmdir(bids_dir,'s') + rethrow(me) +end