From 80c379da975f96bf7df1e181a380750d84c773ed Mon Sep 17 00:00:00 2001 From: Paul McCarthy <pauldmccarthy@gmail.com> Date: Thu, 28 Mar 2019 19:31:56 +0000 Subject: [PATCH] RF,DOC: Class documentation. Minor refactorings. Added comparison/equality operators to Match class so it can be ordered. Protect Match attributes via @property --- fsl/utils/filetree/query.py | 144 +++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 34 deletions(-) diff --git a/fsl/utils/filetree/query.py b/fsl/utils/filetree/query.py index 70e1306f3..18b86b92e 100644 --- a/fsl/utils/filetree/query.py +++ b/fsl/utils/filetree/query.py @@ -30,6 +30,8 @@ from typing import Dict, List, Tuple import numpy as np +from . import FileTree + log = logging.getLogger(__name__) @@ -38,14 +40,55 @@ class FileTreeQuery(object): """The ``FileTreeQuery`` class uses a :class:`.FileTree` to search a directory for files which match a specific query. - + A ``FileTreeQuery`` scans the contents of a directory which is described + by a :class:`.FileTree`, and identifies all file types (a.k.a. _templates_ + or _short names_) that are present, and the values of variables within each + short name that are present. The :meth:`query` method can be used to + retrieve files which match a specific short name, and variable values. + + The :meth:`query` method returns a multi-dimensional ``numpy.array`` + which contains :class:`Match` objects, where each dimension one + represents variable for the short name in question. + + Example usage:: + + >>> from fsl.utils.filetree import FileTree, FileTreeQuery + + >>> tree = FileTree.read('bids_raw', './my_bids_data') + >>> query = FileTreeQuery(tree) + + >>> query.axes('anat_image') + ['acq', 'ext', 'modality', 'participant', 'rec', 'run_index', + 'session'] + + >>> query.variables('anat_image') + {'acq': [None], + 'ext': ['.nii.gz'], + 'modality': ['T1w', 'T2w'], + 'participant': ['01', '02', '03'], + 'rec': [None], + 'run_index': [None, '01', '02', '03'], + 'session': [None]} + + >>> query.query('anat_image', participant='01') + array([[[[[[[Match(./my_bids_data/sub-01/anat/sub-01_T1w.nii.gz)], + [nan], + [nan], + [nan]]]], + + [[[[Match(./my_bids_data/sub-01/anat/sub-01_T2w.nii.gz)], + [nan], + [nan], + [nan]]]]]]], dtype=object) """ def __init__(self, tree): - """Create a ``FileTreeQuery``. + """Create a ``FileTreeQuery``. The contents of the tree directory are + scanned via the :func:`scan` function, which may take some time for + large data sets. - :arg tree: The ``FileTree`` object + :arg tree: The :class:`.FileTree` object """ # Find all files present in the directory @@ -185,7 +228,67 @@ class FileTreeQuery(object): return matcharray[tuple(slc)] -def scan(tree): +class Match(object): + """A ``Match`` object represents a file with a name matching a template in + a ``FileTree``. The :func:`scan` function and :meth:`FileTree.query` + method both return ``Match`` objects. + """ + + + def __init__(self, filename, short_name, variables): + """Create a ``Match`` object. All arguments are added as attributes. + + :arg filename: name of existing file + :arg short_name: template identifier + :arg variables: Dictionary of ``{variable : value}`` mappings + containing all variables present in the file name. + """ + self.__filename = filename + self.__short_name = short_name + self.__variables = dict(variables) + + + @property + def filename(self): + return self.__filename + + + @property + def short_name(self): + return self.__short_name + + + @property + def variables(self): + return dict(self.__variables) + + + def __eq__(self, other): + return (isinstance(other, Match) and + self.filename == other.filename and + self.short_name == other.short_name and + self.variables == other.variables) + + + def __lt__(self, other): + return isinstance(other, Match) and self.filename < other.filename + + + def __le__(self, other): + return isinstance(other, Match) and self.filename <= other.filename + + + def __repr__(self): + """Returns a string representation of this ``Match``. """ + return 'Match({})'.format(self.filename) + + + def __str__(self): + """Returns a string representation of this ``Match``. """ + return repr(self) + + +def scan(tree : FileTree) -> List[Match]: """Scans the directory of the given ``FileTree`` to find all files which match a tree template. @@ -209,7 +312,9 @@ def scan(tree): return matches -def allVariables(tree, matches) -> Tuple[Dict[str, List], Dict[str, List]]: +def allVariables( + tree : FileTree, + matches : List[Match]) -> Tuple[Dict[str, List], Dict[str, List]]: """Identifies the ``FileTree`` variables which are actually represented in files in the directory. @@ -245,32 +350,3 @@ def allVariables(tree, matches) -> Tuple[Dict[str, List], Dict[str, List]]: for sn, vars in allshortnames.items()} return allvars, allshortnames - - -class Match(object): - """A ``Match`` object represents a file with a name matching a template in - a ``FileTree``. - """ - - - def __init__(self, filename, short_name, variables): - """Create a ``Match`` object. All arguments are added as attributes. - - :arg filename: name of existing file - :arg short_name: template identifier - :arg variables: Dictionary of ``{variable : value}`` mappings - containing all variables present in the file name. - """ - self.filename = filename - self.short_name = short_name - self.variables = dict(variables) - - - def __repr__(self): - """Returns a string representation of this ``Match``. """ - return 'Match({})'.format(self.filename) - - - def __str__(self): - """Returns a string representation of this ``Match``. """ - return repr(self) -- GitLab