diff --git a/.dockerignore b/.dockerignore index 21a12aa02..c5e8d9867 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,7 +11,7 @@ coverage_html templates **/tests **/bids-examples -lib/*/.git +lib/**/.git # General diff --git a/.github/workflows/tests_octave.yml b/.github/workflows/tests_octave.yml index 93a5ede66..051b0eb76 100644 --- a/.github/workflows/tests_octave.yml +++ b/.github/workflows/tests_octave.yml @@ -71,7 +71,7 @@ jobs: - name: Install python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: '3.11' - name: Clone bidspm uses: actions/checkout@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3bca001d9..9060be88e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,7 +56,7 @@ repos: args: [--config=pyproject.toml] - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.11.0 hooks: - id: pyupgrade args: [--py38-plus] @@ -73,7 +73,7 @@ repos: - id: flynt - repo: https://github.com/asottile/reorder-python-imports - rev: v3.10.0 + rev: v3.11.0 hooks: - id: reorder-python-imports args: [--py38-plus, --add-import, from __future__ import annotations] diff --git a/Makefile b/Makefile index 0184660dc..cd04ea4b9 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ ARG = -nodisplay -nosplash -nodesktop install: npm install -g bids-validator - pip3 install . + pip3 install .[dev] help: ## Show what this Makefile can do @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) @@ -122,7 +122,6 @@ clean_docker: build_image: Dockerfile ## Build stable docker image from the main branch docker build . -f Dockerfile -t cpplab/bidspm:unstable - VERSION=$(cat version.txt | cut -c2-) Dockerfile_matlab: docker run --rm kaczmarj/neurodocker:0.9.1 generate docker \ @@ -138,4 +137,7 @@ Dockerfile_matlab: build_image_matlab: Dockerfile_matlab docker build . -f Dockerfile_matlab -t cpplab/bidspm_matlab:unstable +docker_data: + make -C demos/openneuro data_ds000001 + ################################################################################ diff --git a/demos/MoAE/test_moae.m b/demos/MoAE/test_moae.m index e8427c5d7..e287fbeb6 100644 --- a/demos/MoAE/test_moae.m +++ b/demos/MoAE/test_moae.m @@ -84,13 +84,22 @@ cd(WD); - % with Octave running more n-1 loop in CI is fine + % Octave + % running more n-1 loop in CI is fine % but not running crashes with a segmentation fault % /home/runner/work/_temp/fb8e9d58-fa9f-4f93-8c96-387973f3632e.sh: line 2: % 7487 Segmentation fault (core dumped) octave $OCTFLAGS --eval "run system_tests_facerep;" % % not sure why - if bids.internal.is_octave() + % + + % Windows + % Error using overwriteDir + % C:\Users\runneradmin\AppData\Local\Temp\8f7c44ca98d0\outputs\derivatives\bidspm-stats\ + % sub-01\task-auditory_space-individual_FWHM-6 could not be removed. + % Error in setBatchSubjectLevelGLMSpec (line 73) + % overwriteDir(ffxDir, opt); + if bids.internal.is_octave() || ispc() break end diff --git a/pyproject.toml b/pyproject.toml index 8e2dc7bb7..99a677f7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,6 +69,7 @@ dev = [ "coverage", "pytest", "ruamel.yaml", + 'pandas' ] [project.scripts] diff --git a/tests/Makefile b/tests/Makefile index 3a3b7c656..f52fa6034 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,7 +10,7 @@ clean_local: rm -rf data/derivatives/bidspm-*/jobs create_dummy_dataset: clean_local - sh createDummyDataSet.sh + python create_dummy_dataset.py bids_examples: rm -rf bids-examples/ diff --git a/tests/createDummyDataSet.sh b/tests/createDummyDataSet.sh deleted file mode 100755 index 022b3ac7a..000000000 --- a/tests/createDummyDataSet.sh +++ /dev/null @@ -1,373 +0,0 @@ -#!/bin/bash - -# small bash script to create a dummy BIDS data set - -# defines where the BIDS data set will be created -start_dir=$(pwd) # relative to starting directory -raw_dir=${start_dir}/data/bidspm-raw -preproc_dir=${start_dir}/data/derivatives/bidspm-preproc -stats_dir=${start_dir}/data/derivatives/bidspm-stats -roi_dir=${start_dir}/data/derivatives/bidspm-roi - -subject_list='ctrl01 blind01 01' -session_list='01 02' -roi_list='V1 A1' -hemispheres='L R' - -create_raw_func_vismotion() { - - target_dir=$1 - subject=$2 - ses=$3 - - suffix='_bold' - task_name='vismotion' - - this_dir=${target_dir}/sub-${subject}/ses-${ses}/func - - basename=sub-${subject}_ses-${ses}_task-${task_name} - - mkdir -p "${this_dir}" - - for run in $(seq 1 2); do - filename=${this_dir}/${basename}_run-${run}_part-mag${suffix}.nii - touch "${filename}" - filename=${this_dir}/${basename}_run-${run}_part-phase${suffix}.nii - touch "${filename}" - done - - filename="${this_dir}/${basename}_run-1_events.tsv" - echo "onset\tduration\ttrial_type" >"${filename}" - echo "2\t2\tVisMot" >>"${filename}" - echo "4\t2\tVisStat" >>"${filename}" - - filename="${this_dir}/${basename}_run-2_events.tsv" - echo "onset\tduration\ttrial_type" >"${filename}" - echo "3\t2\tVisStat" >>"${filename}" - echo "6\t2\tVisMot" >>"${filename}" - - touch "${this_dir}/${basename}_acq-1p60mm_run-1${suffix}.nii" - - filename="${this_dir}/${basename}_acq-1p60mm_run-1_events.tsv" - echo "onset\tduration\ttrial_type" >"${filename}" - echo "4\t2\tVisMot" >>"${filename}" - echo "8\t2\tVisStat" >>"${filename}" - - touch "${this_dir}/${basename}_acq-1p60mm_dir-PA_run-1${suffix}.nii" - - filename="${this_dir}/${basename}_acq-1p60mm_run-2_events.tsv" - echo "onset\tduration\ttrial_type" >"${filename}" - echo "4\t2\tVisMot" >>"${filename}" - echo "8\t2\tVisStat" >>"${filename}" - - touch "${this_dir}/${basename}_acq-1p60mm_dir-PA_run-2${suffix}.nii" -} - -create_raw_func_vislocalizer() { - - target_dir=$1 - subject=$2 - ses=$3 - - suffix='_bold' - task_name='vislocalizer' - - this_dir=${target_dir}/sub-${subject}/ses-${ses}/func - - mkdir -p ${this_dir} - - filename=${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag${suffix}.nii - touch "${filename}" - filename=${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-phase${suffix}.nii - touch "${filename}" - - filename=${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_events.tsv - echo "onset\tduration\ttrial_type" >"${filename}" - echo "2\t15\tVisMot" >>"${filename}" - echo "25\t15\tVisStat" >>"${filename}" - -} - -create_raw_func_rest() { - - target_dir=$1 - subject=$2 - ses=$3 - - suffix='_bold' - task_name='rest' - - this_dir=${target_dir}/sub-${subject}/ses-${ses}/func - - mkdir -p "${this_dir}" - - touch "${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}${suffix}.nii" - -} - -create_raw_fmap() { - - target_dir=$1 - subject=$2 - ses=$3 - - this_dir=${target_dir}/sub-${subject}/ses-${ses}/fmap - - mkdir -p "${this_dir}" - - fmap_suffix_list='_phasediff _magnitude1 _magnitude2' - for suffix in ${fmap_suffix_list}; do - touch "${this_dir}/sub-${subject}_ses-${ses}_run-1${suffix}.nii" - touch "${this_dir}/sub-${subject}_ses-${ses}_run-2${suffix}.nii" - done - - EchoTime1=0.006 - EchoTime2=0.00746 - template='{"EchoTime1":%f, "EchoTime2":%f, "IntendedFor":"%s"}' - - suffix='_bold' - - task_name='vislocalizer' - IntendedFor=$(echo ses-${ses}/func/sub-${subject}_ses-${ses}_task-${task_name}_part-mag${suffix}.nii) - json_string=$(printf "$template" "$EchoTime1" "$EchoTime2" "$IntendedFor") - echo "$json_string" >${this_dir}/sub-${subject}_ses-${ses}_run-1_phasediff.json - - task_name='vismotion' - IntendedFor=$(echo ses-${ses}/func/sub-${subject}_ses-${ses}_task-${task_name}_run-1_part-mag${suffix}.nii) - json_string=$(printf "$template" "$EchoTime1" "$EchoTime2" "$IntendedFor") - echo "$json_string" >"${this_dir}/sub-${subject}_ses-${ses}_run-2_phasediff.json" - -} - -create_raw_anat() { - - target_dir=$1 - subject=$2 - - ses='01' - suffix='_T1w' - - this_dir=${target_dir}/sub-${subject}/ses-01/anat - mkdir -p ${this_dir} - - touch "${this_dir}/sub-${subject}_ses-${ses}${suffix}.nii" - - # MP2RAGE - touch "${this_dir}/sub-${subject}_ses-${ses}_UNIT1.nii" - touch "${this_dir}/sub-${subject}_ses-${ses}_inv-1_MP2RAGE.nii" - touch "${this_dir}/sub-${subject}_ses-${ses}_inv-2_MP2RAGE.nii" - - template='{"MagneticFieldStrength":%f, "RepetitionTimePreparation":%f, "InversionTime":%f, "FlipAngle":%f, "FatSat":"%s", "EchoSpacing":%f, "PartialFourierInSlice":%f}' - - FlipAngle=4 - MagneticFieldStrength=7 - RepetitionTimePreparation=4.3 - InversionTime=1 - Fatsat="yes" - EchoSpacing=0.0072 - PartialFourierInSlice=0.75 - - json_string=$(printf "$template" "$MagneticFieldStrength" "$RepetitionTimePreparation" "$InversionTime" "$FlipAngle" "$Fatsat" "$EchoSpacing" "$PartialFourierInSlice") - echo "$json_string" >${this_dir}/sub-${subject}_ses-${ses}_inv-1_MP2RAGE.json - - InversionTime=3.2 - - json_string=$(printf "$template" "$MagneticFieldStrength" "$RepetitionTimePreparation" "$InversionTime" "$FlipAngle" "$Fatsat" "$EchoSpacing" "$PartialFourierInSlice") - echo "$json_string" >${this_dir}/sub-${subject}_ses-${ses}_inv-2_MP2RAGE.json - -} - -# RAW DATASET -for subject in ${subject_list}; do - for ses in ${session_list}; do - create_raw_func_vismotion ${raw_dir} ${subject} ${ses} - create_raw_func_vislocalizer ${raw_dir} ${subject} ${ses} - create_raw_fmap ${raw_dir} ${subject} ${ses} - done - - create_raw_func_rest ${raw_dir} ${subject} '02' - create_raw_anat ${raw_dir} ${subject} -done - -# ROIs -mkdir -p ${roi_dir}/group -for roi in ${roi_list}; do - touch "${roi_dir}/group/${roi}_mask.nii" -done - -for subject in ${subject_list}; do - this_dir=${roi_dir}/sub-${subject}/roi - mkdir -p "${this_dir}" - for roi in ${roi_list}; do - for hemi in ${hemispheres}; do - touch "${this_dir}/sub-${subject}_hemi-${hemi}_space-individual_label-${roi}_mask.nii" - done - done -done - -# DERIVATIVES DATASET -for subject in ${subject_list}; do - - for ses in ${session_list}; do - - # FUNC - create_raw_func_vismotion ${preproc_dir} ${subject} ${ses} - create_raw_func_vislocalizer ${preproc_dir} ${subject} ${ses} - create_raw_fmap ${preproc_dir} ${subject} ${ses} - - this_dir=${preproc_dir}/sub-${subject}/ses-${ses}/func - - ## task: vismotion - suffix='_bold' - task_name='vismotion' - - ### derivatives - desc_label_list='preproc mean smth6' - for acq in $(seq 1 2); do - - acq_entity="" - if [ ${acq} = 2 ]; then - acq_entity="_acq-1p60mm" - fi - - basename=sub-${subject}_ses-${ses}_task-${task_name}${acq_entity} - - for run in $(seq 1 2); do - - filename=${this_dir}/rp_${basename}_run-${run}_part-mag${suffix}.txt - cp data/tsv_files/rp.txt "${filename}" - cp data/tsv_files/rp.tsv ${this_dir}/${basename}_run-${run}_part-mag_desc-confounds_regressors.tsv - - for desc in ${desc_label_list}; do - touch ${this_dir}/${basename}_run-${run}_part-mag_space-individual_desc-${desc}${suffix}.nii - touch ${this_dir}/${basename}_run-${run}_part-mag_space-IXI549Space_desc-${desc}${suffix}.nii - done - touch ${this_dir}/${basename}_run-${run}_part-mag_space-individual_desc-stc${suffix}.nii - - touch ${this_dir}/${basename}_run-${run}_part-mag_space-IXI549Space_label-brain_mask.nii - - done - - if [ ${ses} = '01' ]; then - touch ${this_dir}/${basename}_part-mag_space-individual_desc-mean${suffix}.nii - touch ${this_dir}/${basename}_part-mag_space-IXI549Space_desc-mean${suffix}.nii - - touch ${this_dir}/mean_${basename}_part-mag${suffix}.nii - fi - - done - - ## task: vislocalizer - suffix='_bold' - task_name='vislocalizer' - - ### derivatives - filename=${this_dir}/rp_sub-${subject}_ses-${ses}_task-${task_name}${suffix}.txt - cp data/tsv_files/rp.txt "${filename}" - cp data/tsv_files/rp.tsv ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag_desc-confounds_regressors.tsv - - desc_label_list='preproc smth6' - for desc in ${desc_label_list}; do - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag_space-individual_desc-${desc}${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag_space-IXI549Space_desc-${desc}${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag_space-IXI549Space_label-brain_mask.nii - done - touch ${this_dir}/mean_sub-${subject}_ses-${ses}_task-${task_name}_part-mag${suffix}.nii - - if [ ${ses} = '01' ]; then - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag_space-individual_desc-mean${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_part-mag_space-IXI549Space_desc-mean${suffix}.nii - fi - - done - - ## task: rest - ses='02' - suffix='_bold' - task_name='rest' - - ### raw & derivatives - create_raw_func_rest ${preproc_dir} ${subject} '02' - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_space-individual_desc-preproc${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_space-individual_label-brain_mask.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_space-IXI549Space_desc-preproc${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_task-${task_name}_space-IXI549Space_label-brain_mask.nii - - # ANAT - create_raw_anat ${preproc_dir} ${subject} - - ses='01' - suffix='_T1w' - - ## derivatives - this_dir=${preproc_dir}/sub-${subject}/ses-01/anat - - anat_prefix_list='m c1 c2 c3' - for prefix in ${anat_prefix_list}; do - touch ${this_dir}/${prefix}sub-${subject}_ses-${ses}${suffix}.nii - done - - space='individual' - - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_desc-biascor${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_desc-skullstripped${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_desc-preproc${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_label-brain_mask.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_label-CSF_probseg.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_label-GM_probseg.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_label-WM_probseg.nii - - space='IXI549Space' - - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_res-bold_label-CSF_probseg.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_res-bold_label-GM_probseg.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_res-bold_label-WM_probseg.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_res-hi_desc-preproc${suffix}.nii - touch ${this_dir}/sub-${subject}_ses-${ses}_space-${space}_desc-brain_mask.nii - - # deformation fields - touch ${this_dir}/sub-${subject}_ses-${ses}_from-IXI549Space_to-T1w_mode-image_xfm.nii - - # STATS - task_list='vismotion vislocalizer' - for task in ${task_list}; do - - this_dir=${stats_dir}/sub-${subject}/task-${task}_space-IXI549Space_FWHM-6 - mkdir -p ${this_dir} - - cp data/mat_files/SPM.mat ${this_dir}/SPM.mat - - touch ${this_dir}/mask.nii - - for i in $(seq 1 9); do - touch ${this_dir}/beta_000${i}.nii - done - - for i in $(seq 1 4); do - touch ${this_dir}/spmT_000${i}.nii - touch ${this_dir}/con_000${i}.nii - touch ${this_dir}/s6con_000${i}.nii - done - done - - # different model for model comparison - task_list='vismotion vislocalizer' - for task in ${task_list}; do - this_dir=${stats_dir}/sub-${subject}/task-${task}_space-IXI549Space_FWHM-6_node-globalSignal - mkdir -p ${this_dir} - - cp data/mat_files/SPM.mat ${this_dir}/SPM.mat - - touch ${this_dir}/mask.nii - - for i in $(seq 1 10); do - touch ${this_dir}/beta_000${i}.nii - done - - for i in $(seq 1 2); do - touch ${this_dir}/spmT_000${i}.nii - touch ${this_dir}/con_000${i}.nii - done - done - -done diff --git a/tests/create_dummy_dataset.py b/tests/create_dummy_dataset.py new file mode 100644 index 000000000..6691deb03 --- /dev/null +++ b/tests/create_dummy_dataset.py @@ -0,0 +1,382 @@ +from __future__ import annotations + +import json +import shutil +from pathlib import Path + +import pandas as pd + +ROOT_DIR = Path(__file__).parent.parent +START_DIR = Path(__file__).parent +SUBJECTS = ["ctrl01", "blind01", "01"] +SESSIONS = ["01", "02"] +ROIS = ["V1", "A1"] +HEMISPHERES = ["L", "R"] + + +def touch(path: Path): + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "a"): + pass + + +def main(start_dir, subject_list, session_list, roi_list, hemispheres): + # Create BIDS directory structure + raw_dir = start_dir / "data" / "bidspm-raw" + derivative_dir = start_dir / "data" / "derivatives" + preproc_dir = derivative_dir / "bidspm-preproc" + stats_dir = derivative_dir / "bidspm-stats" + roi_dir = derivative_dir / "bidspm-roi" + + create_raw_dataset(raw_dir, subject_list, session_list) + create_preproc_dataset(preproc_dir, subject_list, session_list) + create_roi_directories(roi_dir, roi_list, subject_list, hemispheres) + + create_stats_dataset(stats_dir, subject_list) + + +def create_raw_dataset(target_dir, subject_list, session_list): + # Create the raw BIDS dataset + for sub in subject_list: + for ses in session_list: + create_raw_func_vismotion(target_dir, sub, ses) + create_raw_func_vislocalizer(target_dir, sub, ses) + create_raw_fmap(target_dir, sub, ses) + + create_raw_func_rest(target_dir, sub, "02") + create_raw_anat(target_dir, sub) + + +def create_raw_func_vismotion(target_dir, sub, ses): + suffix = "bold" + task = "vismotion" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "func" + this_dir.mkdir(parents=True, exist_ok=True) + + basename = f"sub-{sub}_ses-{ses}_task-{task}" + + create_events_tsv( + filename=this_dir / f"{basename}_run-1_events.tsv", + onset=[2, 4], + duration=[2, 2], + trial_type=["VisMot", "VisStat"], + ) + + for run in range(1, 3): + filename = this_dir / f"{basename}_run-{run}_part-mag_{suffix}.nii" + touch(filename) + filename = this_dir / f"{basename}_run-{run}_part-phase_{suffix}.nii" + touch(filename) + + create_events_tsv( + filename=this_dir / f"{basename}_run-2_events.tsv", + onset=[3, 6], + duration=[2, 2], + trial_type=["VisStat", "VisMot"], + ) + + touch(this_dir / f"{basename}_acq-1p60mm_run-1_{suffix}.nii") + + for run in [1, 2]: + create_events_tsv( + filename=this_dir / f"{basename}_acq-1p60mm_run-{run}_events.tsv", + onset=[4, 8], + duration=[2, 2], + trial_type=["VisMot", "VisStat"], + ) + touch(this_dir / f"{basename}_acq-1p60mm_dir-PA_run-{run}_{suffix}.nii") + + +def create_raw_func_vislocalizer(target_dir, sub, ses): + suffix = "bold" + task = "vislocalizer" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "func" + + basename = f"sub-{sub}_ses-{ses}_task-{task}" + touch(this_dir / f"{basename}_part-mag_{suffix}.nii") + touch(this_dir / f"{basename}_part-phase_{suffix}.nii") + + create_events_tsv( + filename=this_dir / f"{basename}_events.tsv", + onset=[2, 25], + duration=[15, 15], + trial_type=["VisMot", "VisStat"], + ) + + +def create_events_tsv(filename, onset, duration, trial_type): + events = { + "onset": onset, + "duration": duration, + "trial_type": trial_type, + } + df = pd.DataFrame(events) + df.to_csv(filename, sep="\t", index=False) + + +def create_raw_func_rest(target_dir, sub, ses): + suffix = "bold" + task = "rest" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "func" + + touch(this_dir / f"sub-{sub}_ses-{ses}_task-{task}_{suffix}.nii") + + +def create_raw_fmap(target_dir, sub, ses): + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "fmap" + + fmap_suffix_list = ["phasediff", "magnitude1", "magnitude2"] + for suffix in fmap_suffix_list: + touch(this_dir / f"sub-{sub}_ses-{ses}_run-1_{suffix}.nii") + touch(this_dir / f"sub-{sub}_ses-{ses}_run-2_{suffix}.nii") + + suffix = "bold" + task = "vislocalizer" + + content = { + "EchoTime1": 0.006, + "EchoTime2": 0.00746, + "IntendedFor": f"ses-{ses}/func/sub-{sub}_ses-{ses}_task-{task}_part-mag_{suffix}.nii", + } + + with open(this_dir / f"sub-{sub}_ses-{ses}_run-1_phasediff.json", "w") as f: + json.dump(content, f, indent=4) + + task = "vismotion" + content[ + "IntendedFor" + ] = f"ses-{ses}/func/sub-{sub}_ses-{ses}_task-{task}_run-1_part-mag_{suffix}.nii" + with open(this_dir / f"sub-{sub}_ses-{ses}_run-2_phasediff.json", "w") as f: + json.dump(content, f, indent=4) + + +def create_raw_anat(target_dir, sub): + ses = "01" + suffix = "T1w" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "anat" + + touch(this_dir / f"sub-{sub}_ses-{ses}_{suffix}.nii") + + # MP2RAGE + touch(this_dir / f"sub-{sub}_ses-{ses}_UNIT1.nii") + touch(this_dir / f"sub-{sub}_ses-{ses}_inv-1_MP2RAGE.nii") + touch(this_dir / f"sub-{sub}_ses-{ses}_inv-2_MP2RAGE.nii") + + content = { + "MagneticFieldStrength": 7, + "RepetitionTimePreparation": 4.3, + "InversionTime": 1, + "FlipAngle": 4, + "FatSat": "yes", + "EchoSpacing": 0.0072, + "PartialFourierInSlice": 0.75, + } + + with open(this_dir / f"sub-{sub}_ses-{ses}_inv-1_MP2RAGE.json", "w") as f: + json.dump(content, f, indent=4) + + content["InversionTime"] = 3.2 + with open(this_dir / f"sub-{sub}_ses-{ses}_inv-2_MP2RAGE.json", "w") as f: + json.dump(content, f, indent=4) + + +def create_preproc_dataset(target_dir, subject_list, session_list): + # Create the derivatives BIDS dataset + create_raw_dataset(target_dir, subject_list, session_list) + + for sub in subject_list: + for ses in session_list: + create_preproc_func_vismotion(target_dir, sub, ses) + create_preproc_func_vislocalizer(target_dir, sub, ses) + create_preproc_func_rest(target_dir, sub, "02") + create_preproc_anat(target_dir, sub) + + +def create_preproc_func_vismotion(target_dir, sub, ses): + suffix = "bold" + task = "vismotion" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "func" + this_dir.mkdir(parents=True, exist_ok=True) + + for acq_entity in ["", "_acq-1p60mm"]: + basename = f"sub-{sub}_ses-{ses}_task-{task}{acq_entity}_part-mag" + + if ses == "01": + touch(this_dir / f"{basename}_space-individual_desc-mean_{suffix}.nii") + touch(this_dir / f"{basename}_space-IXI549Space_desc-mean_{suffix}.nii") + touch(this_dir / f"mean_{basename}_{suffix}.nii") + + for run in range(1, 3): + basename = f"sub-{sub}_ses-{ses}_task-{task}{acq_entity}_run-{run}_part-mag" + + filename = this_dir / f"rp_{basename}_{suffix}.txt" + shutil.copy(ROOT_DIR / "tests" / "data" / "tsv_files" / "rp.txt", filename) + + shutil.copy( + ROOT_DIR / "tests" / "data" / "tsv_files" / "rp.tsv", + this_dir / f"{basename}_desc-confounds_regressors.tsv", + ) + + desc_label_list = ["preproc", "mean", "smth6"] + for desc in desc_label_list: + touch(this_dir / f"{basename}_space-individual_desc-{desc}_{suffix}.nii") + touch(this_dir / f"{basename}_space-IXI549Space_desc-{desc}_{suffix}.nii") + + touch(this_dir / f"{basename}_space-individual_desc-stc_{suffix}.nii") + touch(this_dir / f"{basename}_space-IXI549Space_desc-brain_mask.nii") + + +def create_preproc_func_vislocalizer(target_dir, sub, ses): + suffix = "bold" + task = "vislocalizer" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "func" + this_dir.mkdir(parents=True, exist_ok=True) + + basename = f"sub-{sub}_ses-{ses}_task-{task}" + + filename = this_dir / f"rp_{basename}_{suffix}.txt" + shutil.copy(ROOT_DIR / "tests" / "data" / "tsv_files" / "rp.txt", filename) + shutil.copy( + ROOT_DIR / "tests" / "data" / "tsv_files" / "rp.tsv", + this_dir / f"{basename}_part-mag_desc-confounds_regressors.tsv", + ) + + basename += "_part-mag" + + desc_label_list = ["preproc", "smth6"] + for desc in desc_label_list: + touch(this_dir / f"{basename}_space-individual_desc-{desc}_{suffix}.nii") + touch(this_dir / f"{basename}_space-IXI549Space_desc-{desc}_{suffix}.nii") + touch(this_dir / f"{basename}_space-IXI549Space_desc-brain_mask.nii") + + if ses == "01": + touch(this_dir / f"{basename}_space-individual_desc-mean_{suffix}.nii") + touch(this_dir / f"{basename}_space-IXI549Space_desc-mean_{suffix}.nii") + + touch(this_dir / f"mean_{basename}_{suffix}.nii") + + +def create_preproc_func_rest(target_dir, sub, ses): + task = "rest" + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "func" + + basename = f"sub-{sub}_ses-{ses}_task-{task}" + + for space in ["individual", "IXI549Space"]: + touch(this_dir / f"{basename}_space-{space}_desc-preproc_bold.nii") + touch(this_dir / f"{basename}_space-{space}_desc-brain_mask.nii") + + shutil.copy( + ROOT_DIR / "tests" / "data" / "tsv_files" / "rp.tsv", + this_dir / f"{basename}_part-mag_desc-confounds_regressors.tsv", + ) + + +def create_preproc_anat(target_dir, sub): + ses = "01" + suffix = "T1w" + + this_dir = target_dir / f"sub-{sub}" / f"ses-{ses}" / "anat" + this_dir.mkdir(parents=True, exist_ok=True) + + basename = f"sub-{sub}_ses-{ses}" + + touch(this_dir / f"{basename}_{suffix}.nii") + + anat_prefix_list = ["m", "c1", "c2", "c3"] + for prefix in anat_prefix_list: + touch(this_dir / f"{prefix}{basename}_{suffix}.nii") + + for space in ["individual", "IXI549Space"]: + touch(this_dir / f"{basename}_space-{space}_desc-brain_mask.nii") + for label in ["CSF", "GM", "WM"]: + res_entity = "" + if space == "IXI549Space": + res_entity = "_res-bold" + touch( + this_dir + / f"{basename}_space-{space}{res_entity}_label-{label}_probseg.nii" + ) + + touch(this_dir / f"{basename}_space-individual_desc-biascor_{suffix}.nii") + touch(this_dir / f"{basename}_space-individual_desc-skullstripped_{suffix}.nii") + touch(this_dir / f"{basename}_space-individual_desc-preproc_{suffix}.nii") + + touch(this_dir / f"{basename}_space-IXI549Space_res-hi_desc-preproc_{suffix}.nii") + touch(this_dir / f"{basename}_from-IXI549Space_to-T1w_mode-image_xfm.nii") + + +def create_stats_dataset(stats_dir, subject_list): + task_list = ["vismotion", "vislocalizer"] + + for sub in subject_list: + for task in task_list: + this_dir = stats_dir / f"sub-{sub}" / f"task-{task}_space-IXI549Space_FWHM-6" + this_dir.mkdir(parents=True, exist_ok=True) + + shutil.copy( + ROOT_DIR / "tests" / "data" / "mat_files" / "SPM.mat", + this_dir / "SPM.mat", + ) + + touch(this_dir / "mask.nii") + + for i in range(1, 10): + touch(this_dir / f"beta_000{i}.nii") + + for i in range(1, 5): + touch(this_dir / f"spmT_000{i}.nii") + touch(this_dir / f"con_000{i}.nii") + touch(this_dir / f"s6con_000{i}.nii") + + # Different model for model comparison + for task in task_list: + this_dir = ( + stats_dir + / f"sub-{sub}" + / f"task-{task}_space-IXI549Space_FWHM-6_node-globalSignal" + ) + this_dir.mkdir(parents=True, exist_ok=True) + + shutil.copy( + ROOT_DIR / "tests" / "data" / "mat_files" / "SPM.mat", + this_dir / "SPM.mat", + ) + + touch(this_dir / "mask.nii") + + for i in range(1, 11): + touch(this_dir / f"beta_000{i}.nii") + + for i in range(1, 3): + touch(this_dir / f"spmT_000{i}.nii") + touch(this_dir / f"con_000{i}.nii") + + +def create_roi_directories(roi_dir, roi_list, subject_list, hemispheres): + # Create ROI directories + roi_group_dir = roi_dir / "group" + roi_group_dir.mkdir(parents=True, exist_ok=True) + + for roi in roi_list: + touch(roi_group_dir / f"{roi}_mask.nii") + + for sub in subject_list: + this_dir = roi_dir / f"sub-{sub}" / "roi" + this_dir.mkdir(parents=True, exist_ok=True) + for roi in roi_list: + for hemi in hemispheres: + touch( + this_dir + / f"sub-{sub}_hemi-{hemi}_space-individual_label-{roi}_mask.nii" + ) + + +if __name__ == "__main__": + main( + start_dir=START_DIR, + subject_list=SUBJECTS, + session_list=SESSIONS, + roi_list=ROIS, + hemispheres=HEMISPHERES, + ) diff --git a/tests/tests_QA/test_computeDesignEfficiency.m b/tests/tests_QA/test_computeDesignEfficiency.m index 7407c5c75..09ff0117e 100644 --- a/tests/tests_QA/test_computeDesignEfficiency.m +++ b/tests/tests_QA/test_computeDesignEfficiency.m @@ -13,7 +13,9 @@ function test_computeDesignEfficiency_vislocalizer() close all; - opt = setOptions('vismotion', '01'); + subject = '01'; + + opt = setOptions('vismotion', subject); BIDS = bids.layout(opt.dir.input, ... 'use_schema', false, ... @@ -28,9 +30,11 @@ function test_computeDesignEfficiency_vislocalizer() opt.TR = metadata{1}.RepetitionTime; eventsFile = bids.query(BIDS, 'data', ... - 'sub', opt.subjects, ... + 'sub', subject, ... + 'ses', '01', ... 'task', opt.taskName, ... 'acq', '', ... + 'run', 1, ... 'suffix', 'events'); e = computeDesignEfficiency(eventsFile{1}, opt); @@ -73,7 +77,7 @@ function test_computeDesignEfficiency_vislocalizer() for iBlock = 1:nbBlocks for cdt = 1:numel(conditions) for iTrial = 1:stimPerBlock - trial_type{end + 1} = conditions{cdt}; + trial_type{end + 1} = conditions{cdt}; %#ok<*AGROW> onset(end + 1) = time; duration(end + 1) = stimDuration; time = time + stimDuration + ISI; diff --git a/tests/tests_batches/test_setBatchMeanAnatAndMask.m b/tests/tests_batches/test_setBatchMeanAnatAndMask.m index 2766e4aea..3e0cf8d49 100644 --- a/tests/tests_batches/test_setBatchMeanAnatAndMask.m +++ b/tests/tests_batches/test_setBatchMeanAnatAndMask.m @@ -52,10 +52,10 @@ function test_setBatchMeanAnatAndMask_basic() expectedBatch{2}.spm.util.imcalc = imcalc; - assertEqual(matlabbatch{1}.spm.util.imcalc.input, expectedBatch{1}.spm.util.imcalc.input); - assertEqual(matlabbatch{1}.spm.util.imcalc.output, expectedBatch{1}.spm.util.imcalc.output); assertEqual(matlabbatch{2}.spm.util.imcalc.input, expectedBatch{2}.spm.util.imcalc.input); assertEqual(matlabbatch{2}.spm.util.imcalc.output, expectedBatch{2}.spm.util.imcalc.output); + assertEqual(matlabbatch{1}.spm.util.imcalc.input, expectedBatch{1}.spm.util.imcalc.input); + assertEqual(matlabbatch{1}.spm.util.imcalc.output, expectedBatch{1}.spm.util.imcalc.output); assertEqual(numel(matlabbatch), 6); diff --git a/tests/tests_stats/unit_tests/test_getInclusiveMask.m b/tests/tests_stats/unit_tests/test_getInclusiveMask.m index f8328ceb2..cfab02dc6 100644 --- a/tests/tests_stats/unit_tests/test_getInclusiveMask.m +++ b/tests/tests_stats/unit_tests/test_getInclusiveMask.m @@ -13,14 +13,15 @@ function test_getInclusiveMask_too_many() subLabel = '01'; nodeName = 'run_level'; - opt = setOptions('vismotion', '01'); + opt = setOptions('vismotion', '01', 'useTempDir', true); opt.verbosity = 2; opt.model.bm = BidsModel('file', opt.model.file); - opt.model.bm.Nodes{1}.Model.Options.Mask.label{1} = 'brain'; - opt.model.bm.Nodes{1}.Model.Options.Mask.desc{1} = ''; + % dummy spec to return several images + opt.model.bm.Nodes{1}.Model.Options.Mask.suffix{1} = 'bold'; + opt.model.bm.Nodes{1}.Model.Options.Mask.desc{1} = 'preproc'; opt.space = opt.model.bm.Input.space; diff --git a/tests/tests_unit/IO/test_saveSpmScript.m b/tests/tests_unit/IO/test_saveSpmScript.m index c58674e18..931132bd0 100644 --- a/tests/tests_unit/IO/test_saveSpmScript.m +++ b/tests/tests_unit/IO/test_saveSpmScript.m @@ -28,7 +28,7 @@ function test_saveSpmScript_basic() expectedContent = fscanf(fid1, '%s'); fclose(fid1); if ispc - expectedContent = strrep('/', '\'); + expectedContent = strrep(expectedContent, '/', '\'); end fid2 = fopen(outputFilename, 'r'); diff --git a/tests/utils/createDummyData.m b/tests/utils/createDummyData.m index 89e10150c..0091d50a9 100644 --- a/tests/utils/createDummyData.m +++ b/tests/utils/createDummyData.m @@ -5,7 +5,7 @@ function createDummyData() startDir = pwd; - script = fullfile(getTestDir(), 'createDummyDataSet.sh'); + script = fullfile(getTestDir(), 'create_dummy_dataset.py'); cd(fileparts(script)); @@ -15,7 +15,7 @@ function createDummyData() [status, result] = system('rm -fr data/derivatives/bidspm-stats/group'); [status, result] = system('rm -fr data/derivatives/bidspm-*/jobs'); - system('sh createDummyDataSet.sh'); + system('python create_dummy_dataset.py'); cd(startDir); diff --git a/tests/utils/setOptions.m b/tests/utils/setOptions.m index 61c8b93de..ca6c02fbe 100644 --- a/tests/utils/setOptions.m +++ b/tests/utils/setOptions.m @@ -1,6 +1,9 @@ function opt = setOptions(varargin) % - % opt = setOptions(task, subLabel, 'useRaw', false, 'pipelineType', 'preproc'); + % opt = setOptions(task, subLabel, ... + % 'useRaw', false, ... + % 'useTempDir', false, ... + % 'pipelineType', 'preproc'); % % (C) Copyright 2021 bidspm developers