Source code for ffrprep.utils

"""Utility functions for validating BIDS directories and related operations."""

import json
import shutil
import subprocess
import tempfile
import warnings


[docs] def validate_input_dir(exec_env, bids_dir, participant_label): """ Validate BIDS directory and structure via the BIDS-validator. Functionality copied from fmriprep. Parameters ---------- exec_env : str Environment BIDSonym is run in. bids_dir : str Path to BIDS root directory. participant_label: str Label(s) of subject to be checked (without 'sub-'). """ # Configuration for bids-validator validator_config_dict = { "ignore": [ "TSV_EQUAL_ROWS", "TSV_EMPTY_CELL", "TSV_IMPROPER_NA", "VOLUME_COUNT_MISMATCH", "BVAL_MULTIPLE_ROWS", "BVEC_NUMBER_ROWS", "DWI_MISSING_BVAL", "INCONSISTENT_SUBJECTS", "INCONSISTENT_PARAMETERS", "BVEC_ROW_LENGTH", "B_FILE", "PARTICIPANT_ID_COLUMN", "PARTICIPANT_ID_MISMATCH", "TASK_NAME_MUST_DEFINE", "PHENOTYPE_SUBJECTS_MISSING", "STIMULUS_FILE_MISSING", "DWI_MISSING_BVEC", "TSV_IMPROPER_NA", "ACQTIME_FMT", "Participants age 89 or higher", "DATASET_DESCRIPTION_JSON_MISSING", "FILENAME_COLUMN", "WRONG_NEW_LINE", "MISSING_TSV_COLUMN_IEEG_CHANNELS", "MISSING_TSV_COLUMN_IEEG_ELECTRODES", "UNUSED_STIMULUS", "CHANNELS_COLUMN_SFREQ", "CHANNELS_COLUMN_LOWCUT", "CHANNELS_COLUMN_HIGHCUT", "CHANNELS_COLUMN_NOTCH", "CUSTOM_COLUMN_WITHOUT_DESCRIPTION", "ACQTIME_FMT", "SUSPICIOUSLY_LONG_EVENT_DESIGN", "SUSPICIOUSLY_SHORT_EVENT_DESIGN", "MALFORMED_BVEC", "MALFORMED_BVAL", "MISSING_TSV_COLUMN_EEG_ELECTRODES", "MISSING_SESSION", ], "error": ["NO_EGG", "NO_EVENTS_TSV"], "ignoredFiles": ["/dataset_description.json", "/participants.tsv"], } # Limit validation only to data from requested participants if participant_label: # get all participant labels in bids_dir all_subs = set([s.name[4:] for s in bids_dir.glob("sub-*")]) # get selected participant labels selected_subs = set([s[4:] if s.startswith("sub-") else s for s in participant_label]) # check if all selected participants exist in bids_dir bad_labels = selected_subs.difference(all_subs) # raise error if any selected participant does not exist if bad_labels: error_msg = ( "Data for requested participant(s) label(s) not found. " "Could not find data for participant(s): %s. " ) + "Please verify the requested " "participant labels." # Add additional error message depending on execution environment if exec_env == "docker": error_msg += ( " This error can be caused by the input data not being " "accessible inside the docker container. Please make sure " "all volumes are mounted properly (see " "https://docs.docker.com/engine/reference/commandline/run/" "#mount-volume--v---read-only)" ) if exec_env == "singularity": error_msg += ( "accessible inside the singularity container. Please make " "sure all paths are mapped properly (see " "https://www.sylabs.io/guides/3.0/user-guide/" "bind_paths_and_mounts.html)" "guides/3.0/user-guide/bind_paths_and_mounts.html)" ) raise RuntimeError(error_msg % ",".join(bad_labels)) ignored_subs = all_subs.difference(selected_subs) if ignored_subs: for sub in ignored_subs: validator_config_dict["ignoredFiles"].append("/sub-%s/**" % sub) with tempfile.NamedTemporaryFile("w+") as temp: temp.write(json.dumps(validator_config_dict)) temp.flush() # Use the deno-based CLI only (bids-validator-deno). The validator # requires a config file path; pass the temp file we created. if shutil.which("bids-validator-deno") is None: warnings.warn( "bids-validator-deno does not appear to be installed; " "skipping BIDS validation.", stacklevel=2, ) return subprocess.check_call(["bids-validator-deno", str(bids_dir), "-c", temp.name])