From 439524a9a80406f7ee19e9e2663e171be71cdf13 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Wed, 8 Nov 2023 14:11:10 +0100 Subject: [PATCH 1/2] lint --- .pre-commit-config.yaml | 26 ++++ miss_hit.cfg | 20 +++ pipeline_group.m | 28 ++-- pipeline_participant.m | 57 ++++---- spm_BIDS_App.m | 300 ++++++++++++++++++++-------------------- 5 files changed, 242 insertions(+), 189 deletions(-) create mode 100644 miss_hit.cfg diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fd3e29e..4f03f6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,32 @@ repos: - id: check-merge-conflict +- repo: local + + hooks: + - id: mh_style + name: mh_style + entry: mh_style + args: [--process-slx, --fix] + files: ^(.*\.(m|slx))$ + language: python + additional_dependencies: [miss_hit_core] + + - id: mh_metric + name: mh_metric + entry: mh_metric + args: [--ci] + files: ^(.*\.(m|slx))$ + language: python + additional_dependencies: [miss_hit_core] + + - id: mh_lint + name: mh_lint + entry: mh_lint + files: ^(.*\.(m|slx))$ + language: python + additional_dependencies: [miss_hit] + - repo: https://github.com/jumanjihouse/pre-commit-hook-yamlfmt rev: 0.2.3 hooks: diff --git a/miss_hit.cfg b/miss_hit.cfg new file mode 100644 index 0000000..c992751 --- /dev/null +++ b/miss_hit.cfg @@ -0,0 +1,20 @@ +project_root + +octave: true + +# style guide (https://florianschanda.github.io/miss_hit/style_checker.html) +line_length: 120 + +# camelCase +regex_script_name: "[a-zA-Z0-9]+(_[a-zA-Z0-9]*)*" + +suppress_rule: "naming_parameters" +suppress_rule: "copyright_notice" + +tab_width: 4 + +# metrics limit for the code quality (https://florianschanda.github.io/miss_hit/metrics.html) +metric "cnest": limit 4 +metric "file_length": limit 500 +metric "cyc": limit 47 +metric "parameters": limit 5 diff --git a/pipeline_group.m b/pipeline_group.m index 886ac31..dc92824 100644 --- a/pipeline_group.m +++ b/pipeline_group.m @@ -1,16 +1,18 @@ -%========================================================================== +% ========================================================================== % C O N F I G U R A T I O N F I L E : G R O U P -%========================================================================== +% ========================================================================== % Available variables: BIDS and BIDS_App -%========================================================================== -%-Structural mean image -%========================================================================== -a = spm_select('FPListRec',BIDS_App.outdir,'wm.*_T1w.nii'); -if isempty(a), error('Cannot find preprocessed T1-weighted images.'); end +% ========================================================================== +% -Structural mean image +% ========================================================================== +a = spm_select('FPListRec', BIDS_App.outdir, 'wm.*_T1w.nii'); +if isempty(a) + error('Cannot find preprocessed T1-weighted images.'); +end -clear matlabbatch +clear matlabbatch; matlabbatch{1}.spm.util.imcalc.input = cellstr(a); matlabbatch{1}.spm.util.imcalc.output = 'meanT1w.nii'; @@ -19,9 +21,9 @@ matlabbatch{1}.spm.util.imcalc.options.dmtx = 1; matlabbatch{1}.spm.util.imcalc.options.dtype = 4; -[~,prov] = spm_jobman('run',matlabbatch); +[~, prov] = spm_jobman('run', matlabbatch); -%========================================================================== -%-Second Level fMRI -%========================================================================== -%fprintf('Nothing to do at fMRI second level.\n'); +% ========================================================================== +% -Second Level fMRI +% ========================================================================== +% fprintf('Nothing to do at fMRI second level.\n'); diff --git a/pipeline_participant.m b/pipeline_participant.m index 48868dd..66cda6f 100644 --- a/pipeline_participant.m +++ b/pipeline_participant.m @@ -1,12 +1,12 @@ -%========================================================================== +% ========================================================================== % C O N F I G U R A T I O N F I L E : P A R T I C I P A N T -%========================================================================== +% ========================================================================== % Available variables: BIDS and BIDS_App -%========================================================================== -%-fMRI Preprocessing -%========================================================================== +% ========================================================================== +% -fMRI Preprocessing +% ========================================================================== % ask for: slice timing correction (before or after realign) % ask for: fieldmap % ask for: realign and unwarp @@ -18,48 +18,51 @@ vox_func = [3 3 3]; FWHM = [12 12 12]; -f = spm_BIDS(BIDS,'data', 'modality','func', 'type','bold'); -if isempty(f), error('Cannot find BOLD time series.'); end -a = spm_BIDS(BIDS,'data', 'modality','anat', 'type','T1w'); -if isempty(a), error('Cannot find T1-weighted image.'); end +f = spm_BIDS(BIDS, 'data', 'modality', 'func', 'type', 'bold'); +if isempty(f) + error('Cannot find BOLD time series.'); +end +a = spm_BIDS(BIDS, 'data', 'modality', 'anat', 'type', 'T1w'); +if isempty(a) + error('Cannot find T1-weighted image.'); +end -clear matlabbatch +clear matlabbatch; % Realign -%------------------------------------------------------------------ -matlabbatch{1}.spm.spatial.realign.estwrite.data = cellfun(@(x) {{x}},f)'; +% ------------------------------------------------------------------ +matlabbatch{1}.spm.spatial.realign.estwrite.data = cellfun(@(x) {{x}}, f)'; matlabbatch{1}.spm.spatial.realign.estwrite.roptions.which = [0 1]; % Coregister -%------------------------------------------------------------------ -matlabbatch{2}.spm.spatial.coreg.estimate.ref = cellstr(spm_file(f{1},'prefix','mean','number',1)); +% ------------------------------------------------------------------ +matlabbatch{2}.spm.spatial.coreg.estimate.ref = cellstr(spm_file(f{1}, 'prefix', 'mean', 'number', 1)); matlabbatch{2}.spm.spatial.coreg.estimate.source = cellstr(a); % Segment -%------------------------------------------------------------------ +% ------------------------------------------------------------------ matlabbatch{3}.spm.spatial.preproc.channel.vols = cellstr(a); matlabbatch{3}.spm.spatial.preproc.channel.write = [0 1]; matlabbatch{3}.spm.spatial.preproc.warp.write = [0 1]; % Normalise: Write -%------------------------------------------------------------------ -matlabbatch{4}.spm.spatial.normalise.write.subj.def = cellstr(spm_file(a,'prefix','y_','ext','nii')); +% ------------------------------------------------------------------ +matlabbatch{4}.spm.spatial.normalise.write.subj.def = cellstr(spm_file(a, 'prefix', 'y_', 'ext', 'nii')); matlabbatch{4}.spm.spatial.normalise.write.subj.resample = cellstr(f); matlabbatch{4}.spm.spatial.normalise.write.woptions.vox = vox_func; -matlabbatch{5}.spm.spatial.normalise.write.subj.def = cellstr(spm_file(a,'prefix','y_','ext','nii')); -matlabbatch{5}.spm.spatial.normalise.write.subj.resample = cellstr(spm_file(a,'prefix','m','ext','nii')); +matlabbatch{5}.spm.spatial.normalise.write.subj.def = cellstr(spm_file(a, 'prefix', 'y_', 'ext', 'nii')); +matlabbatch{5}.spm.spatial.normalise.write.subj.resample = cellstr(spm_file(a, 'prefix', 'm', 'ext', 'nii')); matlabbatch{5}.spm.spatial.normalise.write.woptions.vox = vox_anat; % Smooth -%------------------------------------------------------------------ -matlabbatch{6}.spm.spatial.smooth.data = cellstr(spm_file(f,'prefix','w')); +% ------------------------------------------------------------------ +matlabbatch{6}.spm.spatial.smooth.data = cellstr(spm_file(f, 'prefix', 'w')); matlabbatch{6}.spm.spatial.smooth.fwhm = FWHM; -[~,prov] = spm_jobman('run',matlabbatch); +[~, prov] = spm_jobman('run', matlabbatch); - -%========================================================================== -%-First Level fMRI -%========================================================================== -%fprintf('Nothing to do at fMRI first level.\n'); +% ========================================================================== +% -First Level fMRI +% ========================================================================== +% fprintf('Nothing to do at fMRI first level.\n'); diff --git a/spm_BIDS_App.m b/spm_BIDS_App.m index f5af1bc..0b6197b 100644 --- a/spm_BIDS_App.m +++ b/spm_BIDS_App.m @@ -11,61 +11,62 @@ % Guillaume Flandin % $Id$ - -%========================================================================== -%-BIDS App structure -%========================================================================== - -BIDS_App = struct(... - 'dir','', ... % BIDS root directory - 'outdir','', ... % output directory - 'level','', ... % first or second level analysis [participant*,group*] - 'participants',{{}}, ... % label of participants to be considered - 'config','',... % configuration script - 'temp',true,... % create local temporary copy of input files - 'validate',true); - -%========================================================================== -%-Input arguments -%========================================================================== - -if numel(inputs) == 0, inputs = {'--help'}; end +% ========================================================================== +% -BIDS App structure +% ========================================================================== + +BIDS_App = struct( ... + 'dir', '', ... % BIDS root directory + 'outdir', '', ... % output directory + 'level', '', ... % first or second level analysis [participant*,group*] + 'participants', {{}}, ... % label of participants to be considered + 'config', '', ... % configuration script + 'temp', true, ... % create local temporary copy of input files + 'validate', true); + +% ========================================================================== +% -Input arguments +% ========================================================================== + +if numel(inputs) == 0 + inputs = {'--help'}; +end if numel(inputs) == 1 switch inputs{1} - case {'-v','--version'} - fprintf('%s BIDS App, %s %s, version %s\n',... - spm('version'), upper(spm_check_version), version, ... - deblank(fileread('/version'))); - case {'-h','--help'} - fprintf([... - 'Usage: bids/spm BIDS_DIR OUTPUT_DIR LEVEL [OPTIONS]\n',... - ' bids/spm [ -h | --help | -v | --version ]\n',... - '\n',... - 'Mandatory inputs:\n',... - ' BIDS_DIR Input directory following the BIDS standard\n',... - ' OUTPUT_DIR Output directory\n',... - ' LEVEL Level of the analysis that will be performed\n',... - ' {participant,group}\n',... - '\n',... - 'Options:\n',... - ' --participant_label PARTICIPANT_LABEL [PARTICIPANT_LABEL ...]\n',... - ' Label(s) of the participant(s) to analyse\n',... - ' --config CONFIG_FILE\n',... - ' Optional configuration M-file describing\n',... - ' the analysis to be performed\n',... - ' --skip-bids-validator\n',... - ' Skip BIDS validation\n',... - ' -h, --help Print usage\n',... - ' -v, --version Print version information and quit\n']); + case {'-v', '--version'} + fprintf('%s BIDS App, %s %s, version %s\n', ... + spm('version'), upper(spm_check_version), version, ... + deblank(fileread('/version'))); + case {'-h', '--help'} + fprintf([ ... + 'Usage: bids/spm BIDS_DIR OUTPUT_DIR LEVEL [OPTIONS]\n', ... + ' bids/spm [ -h | --help | -v | --version ]\n', ... + '\n', ... + 'Mandatory inputs:\n', ... + ' BIDS_DIR Input directory following the BIDS standard\n', ... + ' OUTPUT_DIR Output directory\n', ... + ' LEVEL Level of the analysis that will be performed\n', ... + ' {participant,group}\n', ... + '\n', ... + 'Options:\n', ... + ' --participant_label PARTICIPANT_LABEL [PARTICIPANT_LABEL ...]\n', ... + ' Label(s) of the participant(s) to analyse\n', ... + ' --config CONFIG_FILE\n', ... + ' Optional configuration M-file describing\n', ... + ' the analysis to be performed\n', ... + ' --skip-bids-validator\n', ... + ' Skip BIDS validation\n', ... + ' -h, --help Print usage\n', ... + ' -v, --version Print version information and quit\n']); case {'--gui'} cd(spm('Dir')); waitfor(spm_Welcome); - waitfor(spm_figure('FindWin','Menu')); + waitfor(spm_figure('FindWin', 'Menu')); exit(0); otherwise - fprintf([... - 'bids/spm: ''%s'' is not a valid syntax.\n',... - 'See ''bids/spm --help''.\n'],inputs{1}); + fprintf([ ... + 'bids/spm: ''%s'' is not a valid syntax.\n', ... + 'See ''bids/spm --help''.\n'], inputs{1}); end exit(0); end @@ -90,219 +91,220 @@ case '--skip-bids-validator' BIDS_App.validate = false; i = i + 1; - continue; + continue otherwise - warning('Unknown input argument "%s".',arg); - arg = strtok(arg,'-'); + warning('Unknown input argument "%s".', arg); + arg = strtok(arg, '-'); end j = 1; while true i = i + 1; if i <= numel(inputs) - if inputs{i}(1) == '-', break; end + if inputs{i}(1) == '-' + break + end BIDS_App.(arg){j} = inputs{i}; j = j + 1; else - break; + break end end end -%========================================================================== -%-Validation of input arguments -%========================================================================== +% ========================================================================== +% -Validation of input arguments +% ========================================================================== -%- bids_dir -%-------------------------------------------------------------------------- -if ~exist(BIDS_App.dir,'dir') - error('BIDS directory "%s" does not exist.',BIDS_App.dir); +% - bids_dir +% -------------------------------------------------------------------------- +if ~exist(BIDS_App.dir, 'dir') + error('BIDS directory "%s" does not exist.', BIDS_App.dir); end -%- level [participant/group] & output_dir -%-------------------------------------------------------------------------- -if strncmp('participant',BIDS_App.level,11) - if ~exist(BIDS_App.outdir,'dir') +% - level [participant/group] & output_dir +% -------------------------------------------------------------------------- +if strncmp('participant', BIDS_App.level, 11) + if ~exist(BIDS_App.outdir, 'dir') sts = mkdir(BIDS_App.outdir); if ~sts error('BIDS output directory could not be created.'); end end -elseif strncmp('group',BIDS_App.level,5) - if ~exist(BIDS_App.outdir,'dir') - error('BIDS output directory "%s" does not exist.',BIDS_App.outdir); +elseif strncmp('group', BIDS_App.level, 5) + if ~exist(BIDS_App.outdir, 'dir') + error('BIDS output directory "%s" does not exist.', BIDS_App.outdir); end else - error('Unknown analysis level "%s".',BIDS_App.level); + error('Unknown analysis level "%s".', BIDS_App.level); end -%-Configuration file -%-------------------------------------------------------------------------- +% -Configuration file +% -------------------------------------------------------------------------- if ~isempty(BIDS_App.config) if numel(BIDS_App.config) > 1 error('More than one configuration file provided.'); end BIDS_App.config = char(BIDS_App.config); if isempty(fileparts(BIDS_App.config)) - BIDS_App.config = fullfile(fileparts(mfilename('fullpath')),BIDS_App.config); + BIDS_App.config = fullfile(fileparts(mfilename('fullpath')), BIDS_App.config); end - if isempty(spm_file(BIDS_App.config,'ext')) + if isempty(spm_file(BIDS_App.config, 'ext')) BIDS_App.config = [BIDS_App.config '.m']; end if ~spm_existfile(BIDS_App.config) - error('Cannot find configuration file "%s".',BIDS_App.config); + error('Cannot find configuration file "%s".', BIDS_App.config); end else - BIDS_App.config = fullfile(fileparts(mfilename('fullpath')),... - ['pipeline_' BIDS_App.level '.m']); + BIDS_App.config = fullfile(fileparts(mfilename('fullpath')), ... + ['pipeline_' BIDS_App.level '.m']); if ~spm_existfile(BIDS_App.config) - error('No default configuration file found for "%s" level.',BIDS_App.level); + error('No default configuration file found for "%s" level.', BIDS_App.level); end end -%========================================================================== -%-Parse BIDS directory and validate list of participants -%========================================================================== +% ========================================================================== +% -Parse BIDS directory and validate list of participants +% ========================================================================== -%-Call BIDS Validator -%-------------------------------------------------------------------------- +% -Call BIDS Validator +% -------------------------------------------------------------------------- if BIDS_App.validate [status, result] = system('bids-validator --version'); if ~status [status, result] = system(['bids-validator "' BIDS_App.dir '"']); - if status~=0 - fprintf('%s\n',result); + if status ~= 0 + fprintf('%s\n', result); exit(1); end end end -%-Parse BIDS directory -%-------------------------------------------------------------------------- +% -Parse BIDS directory +% -------------------------------------------------------------------------- BIDS = spm_BIDS(BIDS_App.dir); -%- --participant_label -%-------------------------------------------------------------------------- +% - --participant_label +% -------------------------------------------------------------------------- if isempty(BIDS_App.participants) - BIDS_App.participants = spm_BIDS(BIDS,'subjects'); + BIDS_App.participants = spm_BIDS(BIDS, 'subjects'); else - df = setdiff(BIDS_App.participants,spm_BIDS(BIDS,'subjects')); + df = setdiff(BIDS_App.participants, spm_BIDS(BIDS, 'subjects')); if ~isempty(df) - error('Participant directory "%s" does not exist.',df{1}); + error('Participant directory "%s" does not exist.', df{1}); end end BIDS_App.participants = cellfun(@(s) ['sub-' s], ... - BIDS_App.participants, 'UniformOutput',false); - + BIDS_App.participants, 'UniformOutput', false); -%========================================================================== -%-SPM Initialisation -%========================================================================== +% ========================================================================== +% -SPM Initialisation +% ========================================================================== -spm('defaults','fmri'); +spm('defaults', 'fmri'); spm_jobman('initcfg'); -%========================================================================== -%-(Temporary) Copy of input data and uncompress image files -%========================================================================== +% ========================================================================== +% -(Temporary) Copy of input data and uncompress image files +% ========================================================================== atExit = ''; BIDS_App.tmpdir = BIDS_App.dir; -if strncmp('participant',BIDS_App.level,11) && ~isempty(BIDS_App.participants) +if strncmp('participant', BIDS_App.level, 11) && ~isempty(BIDS_App.participants) if BIDS_App.temp - %-Create temporary directory - %------------------------------------------------------------------ + % -Create temporary directory + % ------------------------------------------------------------------ BIDS_App.tmpdir = BIDS_App.outdir; - %BIDS_App.tmpdir = tempname(BIDS_App.outdir); - %sts = mkdir(BIDS_App.tmpdir); - %if ~sts + % BIDS_App.tmpdir = tempname(BIDS_App.outdir); + % sts = mkdir(BIDS_App.tmpdir); + % if ~sts % error('Output temporary directory could not be created.'); - %end - %atExit = onCleanup(@() rmdir(BIDS_App.tmpdir,'s')); + % end + % atExit = onCleanup(@() rmdir(BIDS_App.tmpdir,'s')); - %-Copy participants' data - %------------------------------------------------------------------ - for s=1:numel(BIDS_App.participants) - %fprintf('Temporary directory: %s\n',... + % -Copy participants' data + % ------------------------------------------------------------------ + for s = 1:numel(BIDS_App.participants) + % fprintf('Temporary directory: %s\n',... % fullfile(BIDS_App.tmpdir,BIDS_App.participants{s})); - sts = copyfile(fullfile(BIDS_App.dir,BIDS_App.participants{s}),... - fullfile(BIDS_App.tmpdir,BIDS_App.participants{s})); + sts = copyfile(fullfile(BIDS_App.dir, BIDS_App.participants{s}), ... + fullfile(BIDS_App.tmpdir, BIDS_App.participants{s})); if ~sts error('Data could not be temporarily copied.'); end end end - %-Uncompress gzipped NIfTI files - %---------------------------------------------------------------------- - for s=1:numel(BIDS_App.participants) - niigz = spm_select('FPListRec',... - fullfile(BIDS_App.tmpdir,BIDS_App.participants{s}),'^.*\.nii\.gz$'); + % -Uncompress gzipped NIfTI files + % ---------------------------------------------------------------------- + for s = 1:numel(BIDS_App.participants) + niigz = spm_select('FPListRec', ... + fullfile(BIDS_App.tmpdir, BIDS_App.participants{s}), '^.*\.nii\.gz$'); if ~isempty(niigz) niigz = cellstr(niigz); - for i=1:numel(niigz) + for i = 1:numel(niigz) gunzip(niigz{i}); delete(niigz{i}); end end end - %-Update BIDS structure to point to new local files - %---------------------------------------------------------------------- - BIDS = spm_changepath(BIDS,BIDS.dir,BIDS_App.tmpdir,false); - BIDS = spm_changepath(BIDS,'.nii.gz','.nii',false); + % -Update BIDS structure to point to new local files + % ---------------------------------------------------------------------- + BIDS = spm_changepath(BIDS, BIDS.dir, BIDS_App.tmpdir, false); + 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); +% -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)' + 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* -%========================================================================== +% ========================================================================== +% -Analysis level: participant* +% ========================================================================== -if strncmp('participant',BIDS_App.level,11) +if strncmp('participant', BIDS_App.level, 11) BIDS_ORIG = BIDS; - for s=1:numel(BIDS_App.participants) + for s = 1:numel(BIDS_App.participants) BIDS = BIDS_ORIG; - idx = ismember({BIDS.subjects.name},BIDS_App.participants{s}); + idx = ismember({BIDS.subjects.name}, BIDS_App.participants{s}); BIDS.subjects = BIDS.subjects(idx); if ~isempty(BIDS.participants) - idx = ismember(BIDS.participants.participant_id,BIDS_App.participants{s}); - for fn=fieldnames(BIDS.participants)' + idx = ismember(BIDS.participants.participant_id, BIDS_App.participants{s}); + for fn = fieldnames(BIDS.participants)' BIDS.participants.(char(fn)) = BIDS.participants.(char(fn))(idx); end end - spm('FnBanner',['BIDS ' upper(BIDS_App.level) ' ' BIDS_App.participants{s}]); - spm('Run',BIDS_App.config); + spm('FnBanner', ['BIDS ' upper(BIDS_App.level) ' ' BIDS_App.participants{s}]); + spm('Run', BIDS_App.config); end % make sure relevant files are stored in BIDS_App.outdir end -%========================================================================== -%-Analysis level: group* -%========================================================================== +% ========================================================================== +% -Analysis level: group* +% ========================================================================== -if strncmp('group',BIDS_App.level,5) +if strncmp('group', BIDS_App.level, 5) - spm('FnBanner',['BIDS ' upper(BIDS_App.level)]); - spm('Run',BIDS_App.config); + spm('FnBanner', ['BIDS ' upper(BIDS_App.level)]); + spm('Run', BIDS_App.config); % make sure relevant files are stored in BIDS_App.outdir end -%========================================================================== -%-Delete temporary files and exit -%========================================================================== -%delete(atExit); -close all force +% ========================================================================== +% -Delete temporary files and exit +% ========================================================================== +% delete(atExit); +close all force; From be2f7fdecc81190866e6ee5fb624912c6475be67 Mon Sep 17 00:00:00 2001 From: Remi Gau Date: Wed, 8 Nov 2023 14:12:46 +0100 Subject: [PATCH 2/2] set line length to 110 --- miss_hit.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/miss_hit.cfg b/miss_hit.cfg index c992751..58e88fa 100644 --- a/miss_hit.cfg +++ b/miss_hit.cfg @@ -3,7 +3,7 @@ project_root octave: true # style guide (https://florianschanda.github.io/miss_hit/style_checker.html) -line_length: 120 +line_length: 110 # camelCase regex_script_name: "[a-zA-Z0-9]+(_[a-zA-Z0-9]*)*"