From 6e3589c0497bf0b95d382b056546b011aeb88e4e Mon Sep 17 00:00:00 2001
From: Paul McCarthy <pauldmccarthy@gmail.com>
Date: Wed, 16 Jan 2019 08:31:29 +0000
Subject: [PATCH] ENH: New FileTreeQuery class, for searching for files in a
 FileTree

---
 fsl/utils/filetree/query.py | 95 +++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 fsl/utils/filetree/query.py

diff --git a/fsl/utils/filetree/query.py b/fsl/utils/filetree/query.py
new file mode 100644
index 000000000..32b03b9f1
--- /dev/null
+++ b/fsl/utils/filetree/query.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+#
+# query.py -
+#
+# Author: Paul McCarthy <pauldmccarthy@gmail.com>
+# Author: Michiel Cottaar <michiel.cottaar@.ndcn.ox.ac.uk>
+#
+
+
+import logging
+import collections
+from   typing import Dict, Set, List
+
+
+log = logging.getLogger(__name__)
+
+
+class FileTreeQuery(object):
+
+    def __init__(self, tree):
+        """
+        """
+        self.__tree      = tree
+        self.__matches   = self.__tree.scan()
+        self.__variables = Match.allVariables(self.__tree, self.__matches)
+
+
+    def variables(self) -> Dict[str, Set]:
+        """Return a dict of ``{variable : [values]}`` mappings.
+        This dict describes all variables and their possible values in
+        the tree.
+        """
+        return dict(self.__variables)
+
+
+    def query(self, **variables) -> List[str]:
+        """
+        """
+        hits = []
+
+        for m in self.__matches:
+            if all([m.variables.get(n, None) == v
+                    for n, v in variables.items()]):
+                hits.append(m)
+
+        return hits
+
+
+class Match(object):
+    """
+    Filename matching a template in the file tree
+    """
+
+    @staticmethod
+    def allVariables(tree, matches) -> Dict(str, Set):
+        """
+        """
+        allvars = collections.defaultdict(set)
+
+        for m in matches:
+            for var, val in m.variables.items():
+                allvars[var].update(val)
+        return allvars
+
+
+    @staticmethod
+    def scan(tree):
+        """
+        Scans the disk to find any matches
+
+        :return: list of :class:`Match` objects
+        """
+
+        matches = []
+        for template in tree.templates:
+            for filename in tree.get_all(template, glob_vars='all'):
+                variables = tree.extract_variables(template, filename)
+                match = Match(filename, template, variables)
+                matches.append(match)
+        for tree_name, sub_tree in tree.sub_trees:
+            matches.extend(Match.scan(sub_tree))
+        return matches
+
+
+    def __init__(self, filename, short_name, variables):
+        """
+        Defines a new match
+
+        :param filename: name of existing file
+        :param short_name: template identifier
+        :param variables: variable values
+        """
+        self.filename = filename
+        self.short_name = short_name
+        self.variables = dict(variables)
-- 
GitLab