Source code for bids.utils

""" Utility functions. """

import re
import os
from pathlib import Path


[docs] def listify(obj): ''' Wraps all non-list or tuple objects in a list; provides a simple way to accept flexible arguments. ''' return obj if isinstance(obj, (list, tuple, type(None))) else [obj]
[docs] def matches_entities(obj, entities, strict=False): ''' Checks whether an object's entities match the input. ''' if strict and set(obj.entities.keys()) != set(entities.keys()): return False comm_ents = list(set(obj.entities.keys()) & set(entities.keys())) for k in comm_ents: current = obj.entities[k] target = entities[k] if isinstance(target, (list, tuple)): if current not in target: return False elif current != target: return False return True
def natural_sort(l, field=None): ''' based on snippet found at https://stackoverflow.com/a/4836734/2445984 ''' convert = lambda text: int(text) if text.isdigit() else text.lower() def alphanum_key(key): if field is not None: key = getattr(key, field) if not isinstance(key, str): key = str(key) return [convert(c) for c in re.split('([0-9]+)', key)] return sorted(l, key=alphanum_key)
[docs] def convert_JSON(j): """ Recursively convert CamelCase keys to snake_case. From: https://stackoverflow.com/questions/17156078/ converting-identifier-naming-between-camelcase-and- underscores-during-json-seria """ def camel_to_snake(s): a = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))') return a.sub(r'_\1', s).lower() def convertArray(a): newArr = [] for i in a: if isinstance(i, list): newArr.append(convertArray(i)) elif isinstance(i, dict): newArr.append(convert_JSON(i)) else: newArr.append(i) return newArr out = {} for k, value in j.items(): newK = camel_to_snake(k) # Replace transformation uses a dict, so skip lower-casing if isinstance(value, dict) and k != 'Replace': out[newK] = convert_JSON(value) elif isinstance(value, list): out[newK] = convertArray(value) else: out[newK] = value return out
def splitext(path): """splitext for paths with directories that may contain dots. From https://stackoverflow.com/questions/5930036/separating-file-extensions-using-python-os-path-module""" li = [] path_without_extensions = os.path.join(os.path.dirname(path), os.path.basename(path).split(os.extsep)[0]) extensions = os.path.basename(path).split(os.extsep)[1:] li.append(path_without_extensions) # li.append(extensions) if you want extensions in another list inside the list that is returned. li.extend(extensions) return li
[docs] def make_bidsfile(filename): """Create a BIDSFile instance of the appropriate class. """ from .layout import models # Extract all extensions from filename (a.tar.gz -> .tar.gz, not just .gz) ext = ''.join(Path(filename).suffixes) if ext.endswith(('.nii', '.nii.gz', '.gii')): cls = 'BIDSImageFile' elif ext in ['.tsv', '.tsv.gz']: cls = 'BIDSDataFile' elif ext == '.json': cls = 'BIDSJSONFile' else: cls = 'BIDSFile' Cls = getattr(models, cls) return Cls(filename)
def collect_associated_files(layout, files, extra_entities=()): """Collect and group BIDSFiles with multiple files per acquisition. Parameters ---------- layout files : list of BIDSFile extra_entities Returns ------- collected_files : list of list of BIDSFile """ MULTICONTRAST_ENTITIES = ['echo', 'part', 'ch', 'direction'] MULTICONTRAST_SUFFIXES = [ ('bold', 'phase'), ('phase1', 'phase2', 'phasediff', 'magnitude1', 'magnitude2'), ] if len(extra_entities): MULTICONTRAST_ENTITIES += extra_entities collected_files = [] for f in files: if len(collected_files) and any(f in filegroup for filegroup in collected_files): continue ents = f.get_entities() ents = {k: v for k, v in ents.items() if k not in MULTICONTRAST_ENTITIES} # Group files with differing multi-contrast entity values, but same # everything else. all_suffixes = ents['suffix'] for mcs in MULTICONTRAST_SUFFIXES: if ents['suffix'] in mcs: all_suffixes = mcs break ents.pop('suffix') associated_files = layout.get(suffix=all_suffixes, **ents) collected_files.append(associated_files) return collected_files def validate_multiple(val, retval=None): """Any click.Option with the multiple flag will return an empty tuple if not set. This helper method converts empty tuples to a desired return value (default: None). This helper method selects the first item in single-item tuples. """ assert isinstance(val, tuple) if val == tuple(): return retval if len(val) == 1: return val[0] return val