We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
validator.get_path_values raises KeyError: 'regexp'
What a great tutorial - keeps bearing fruits...  Continuing to #267 I was exploring new features of the validator, and found `get_path_values` which seems want ta path, so echoing `is_file` I pointed it to /sub-02/ses-01/anat/sub-02_ses-01_T2w.json to just get 
```
--------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-71-00eb4b2b179c> in <module>()
----> 1 validator.get_path_values('/sub-02/ses-01/anat/sub-02_ses-01_T2w.json')

/home/yoh/proj/bids/pybids/bids/layout/validation.pyc in get_path_values(self, path)
    164         values = {}
    165 
--> 166         regexps = self.get_regular_expressions('path.json')
    167 
    168         # capture subject

/home/yoh/proj/bids/pybids/bids/layout/validation.pyc in get_regular_expressions(self, filename)
    144             rule = rules[key]
    145 
--> 146             regexp = rule["regexp"]
    147 
    148             if "tokens" in rule:

KeyError: 'regexp'
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bids/layout/validation.py`
Content:
```
1 """Tools for validating BIDS projects."""
2 
3 import re
4 import json
5 from os.path import join, abspath, dirname
6 
7 
8 __all__ = ['BIDSValidator']
9 
10 
11 class BIDSValidator():
12     """An object for BIDS (Brain Imaging Data Structure) verification in a data.
13 
14     The main method of this class is `is_bids()`. You should use it for
15     checking whether a file path compatible with BIDS.
16 
17     Parameters
18     ----------
19     index_associated : bool, default: True
20         Specifies if an associated data should be checked. If it is true then
21         any file paths in directories `code/`, `derivatives/`, `sourcedata/`
22         and `stimuli/` will pass the validation, else they won't.
23 
24     Examples
25     --------
26     >>> from bids.layout import BIDSValidator
27     >>> validator = BIDSValidator()
28     >>> filepaths = ["/sub-01/anat/sub-01_rec-CSD_T1w.nii.gz",
29     >>> "/sub-01/anat/sub-01_acq-23_rec-CSD_T1w.exe", #wrong extension
30     >>> "/participants.tsv"]
31     >>> for filepath in filepaths:
32     >>>     print( validator.is_bids(filepath) )
33     True
34     False
35     True
36     """
37 
38     def __init__(self, index_associated=True):
39         self.rule_dir = join(dirname(abspath(__file__)),'config', 'validator')
40         self.index_associated = index_associated
41 
42     def is_bids(self, path):
43         """Check if a file path appropriate for BIDS.
44 
45         Main method of the validator. uses other class methods for checking
46         different aspects of the file path.
47 
48         Parameters
49         ----------
50             path: string
51                 A path of a file you want to check.
52 
53         Examples
54         --------
55         >>> from bids.layout import BIDSValidator
56         >>> validator = BIDSValidator()
57         >>> validator.is_bids("/sub-01/ses-test/anat/sub-01_ses-test_rec-CSD_run-23_T1w.nii.gz")
58         True
59         >>> validator.is_bids("/sub-01/ses-test/sub-01_run-01_dwi.bvec") # missed session in the filename
60         False
61         """
62         conditions = []
63 
64         conditions.append(self.is_top_level(path))
65         conditions.append(self.is_associated_data(path))
66         conditions.append(self.is_session_level(path))
67         conditions.append(self.is_subject_level(path))
68         conditions.append(self.is_phenotypic(path))
69         conditions.append(self.is_file(path))
70 
71         return (any(conditions))
72 
73     def is_top_level(self, path):
74         """Check if the file has appropriate name for a top-level file."""
75         with open(join(self.rule_dir, 'fixed_top_level_names.json'), 'r') as f:
76             fixed_top_level_json = json.load(f)
77             fixed_top_level_names = fixed_top_level_json['fixed_top_level_names']
78 
79         regexps = self.get_regular_expressions('top_level_rules.json')
80 
81         conditions = [False if re.compile(x).search(path) is None else True
82                       for x in regexps]
83 
84         conditions.append(path in fixed_top_level_names)
85 
86         return (any(conditions))
87 
88     def is_associated_data(self, path):
89         """Check if file is appropriate associated data."""
90         if not self.index_associated:
91             return False
92 
93         regexps = self.get_regular_expressions('associated_data_rules.json')
94 
95         conditions = [(re.compile(x).search(path) is not None)
96                       for x in regexps]
97 
98         return any(conditions)
99 
100     def is_session_level(self, path):
101         """Check if the file has appropriate name for a session level."""
102         regexps = self.get_regular_expressions('session_level_rules.json')
103 
104         conditions = [self.conditional_match(x, path) for x in regexps]
105 
106         return (any(conditions))
107 
108     def is_subject_level(self, path):
109         """Check if the file has appropriate name for a subject level."""
110         regexps = self.get_regular_expressions('subject_level_rules.json')
111 
112         conditions = [(re.compile(x).search(path) is not None)
113                       for x in regexps]
114 
115         return (any(conditions))
116 
117     def is_phenotypic(self, path):
118         """Check if file is phenotypic data."""
119         regexps = self.get_regular_expressions('phenotypic_rules.json')
120 
121         conditions = [(re.compile(x).search(path) is not None)
122                       for x in regexps]
123 
124         return (any(conditions))
125 
126     def is_file(self, path):
127         """Check if file is phenotypic data."""
128         regexps = self.get_regular_expressions('file_level_rules.json')
129 
130         conditions = [(re.compile(x).search(path) is not None)
131                       for x in regexps]
132 
133         return (any(conditions))
134 
135     def get_regular_expressions(self, filename):
136         """Get regular expressions from file."""
137         regexps = []
138 
139         filename = join(self.rule_dir, filename)
140 
141         with open(filename, 'r') as f:
142             rules = json.load(f)
143 
144         for key in list(rules.keys()):
145             rule = rules[key]
146 
147             regexp = rule["regexp"]
148 
149             if "tokens" in rule:
150                 tokens = rule["tokens"]
151 
152                 for token in list(tokens):
153                     regexp = regexp.replace(token, "|".join(tokens[token]))
154 
155             regexps.append(regexp)
156 
157         return regexps
158 
159     def get_path_values(self, path):
160         """Takes a file path and returns values found for the following path
161         keys:
162             sub-
163             ses-
164         """
165         values = {}
166 
167         regexps = self.get_regular_expressions('path.json')
168 
169         # capture subject
170         for paths in ['sub', 'ses']:
171             match = re.compile(regexps[paths]).findall(path)
172             values[paths] = match[1] if match & match[1] else None
173 
174         return values
175 
176     def conditional_match(self, expression, path):
177         match = re.compile(expression).findall(path)
178         match = match[0] if len(match) >= 1 else False
179         # adapted from JS code and JS does not support conditional groups
180         if (match):
181             return ((match[1] == match[2][1:]) | (not match[1]))
182         else:
183             return False
184 
185 def validate_sequences(layout, config):
186     """Checks files in BIDS project match user defined expectations.
187 
188     This method is a wrapper for the check_duplicate_files() and 
189     check_expected_files() methods. Use it to check whether there are
190     files with duplicate content within the BIDS data set and to check
191     the number of data set files against a user customized configuration
192     file. Returns a named tuple of three data frames: duplicates, summary, and problems.
193 
194 
195     Parameters
196     ----------
197         layout: BIDSLayout class
198             A BIDSLayout path of a data set.
199 
200         config: string
201             Path to customized configuration file. Requires `runs` as an input.
202             See the sample config for an example (bids/layout/tests/data/sample_validation_config.json).
203 
204 
205     Examples
206     --------
207     >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')
208     >>> dfs = validate_sequences(layout, 'pybids/bids/layout/tests/data/sample_validation_config.json')
209     >>> dfs.duplicates
210     # Put example output here
211     >>> df.summary
212     # Put example output here
213     >>> df.problems
214     # Put example output here
215     """
216     
217     duplicate_file_df = check_duplicate_files(layout)
218     summary_df, problem_df = check_expected_files(layout, config)
219     output = namedtuple('output', ['duplicates', 'summary', 'problems'])
220     return output(duplicate_file_df, summary_df, problem_df)
221     
222     
223 def check_duplicate_files(layout):
224     """Checks images in BIDS project are not duplicated.
225 
226     Check whether any files have duplicate content within the 
227     BIDS data set. Returns a data frame: duplicate_file_df.
228 
229 
230     Parameters
231     ----------
232         layout: BIDSLayout class
233             A BIDSLayout path of a data set.
234 
235 
236     Examples
237     --------
238     >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')
239     >>> duplicate_file_df = check_duplicate_files(layout)
240     >>> duplicate_file_df
241     # Put example output here
242 
243 
244     Notes
245     ------
246     Returns a data frame in which the first column is the file
247     identifier and the second column is the path to the file.
248     Files with matching identifiers have the same content.
249     """
250     
251     import pandas as pd
252     def md5(fname):
253         hash_md5 = hashlib.md5()
254         with open(fname, "rb") as f:
255             for chunk in iter(lambda: f.read(4096), b""):
256                 hash_md5.update(chunk)
257         return hash_md5.hexdigest()
258     hash_map = {}
259     all_niftis = layout.get(return_type="file", extensions='.nii.gz')
260     for nifti_file in all_niftis:
261         md5sum = md5(nifti_file)
262         if md5sum in hash_map:
263             hash_map[md5sum].append(nifti_file)
264         else:
265             hash_map[md5sum] = [nifti_file]
266     df = pd.DataFrame.from_dict(hash_map, orient='index') 
267     pruned_df = df.stack().reset_index().drop(columns='level_1')
268     out_df = pruned_df.rename(columns={'level_0': 'hash', 0: 'filename'})
269     return out_df
270     
271     
272 def check_expected_files(layout, config):
273     """Checks files in BIDS project match user defined expectations.
274 
275     This method checks the number of data set files against a user customized 
276     configuration file. Returns two data frames: summary_df, problem_df.
277 
278 
279     Parameters
280     ----------
281         layout: BIDSLayout class
282             A BIDSLayout path of a data set.
283 
284         config: string
285             Path to customized configuration file.
286 
287 
288     Examples
289     --------
290     >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')
291     >>> summary_df, problem_df = check_expected_files(layout, 'pybids/bids/layout/tests/data/sample_validation_config.json')
292     >>> summary_df
293     # Put example output here
294     >>> problem_df
295     # Put example output here
296 
297 
298     Notes
299     --------
300 
301     `runs` is a mandatory field in the config file.
302     
303     The configuration file can take any keys that are valid arguments for
304     pybids `layout.get()` Values shoud match those in the BIDS file names. 
305     See the sample config for an example (bids/layout/tests/data/sample_validation_config.json).
306     The more specific keys are provided, the more informative the output will be.
307 
308     """
309 
310     import pandas as pd
311     dictlist = []
312     with open(config) as f:
313         json_data = json.load(f)
314         subjects = layout.get_subjects()
315     for sub in subjects: 
316         for scan_params_d in json_data['sequences']:
317             scan_params = scan_params_d.copy()
318             seq_params = {i: scan_params[i] for i in scan_params if i != 'runs'}
319             actual_runs = layout.get(return_type='obj', subject=sub, extensions='.nii.gz', **seq_params)
320             scan_params['subject'] = sub
321             scan_params['runs_found'] = len(actual_runs)
322             scan_params['problem'] = len(actual_runs) != scan_params['runs']
323             dictlist.append(scan_params)
324     summary_df = pd.DataFrame(dictlist)
325     problem_df = summary_df.loc[summary_df['problem'] == True]
326     return summary_df, problem_df
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bids/layout/validation.py b/bids/layout/validation.py
--- a/bids/layout/validation.py
+++ b/bids/layout/validation.py
@@ -3,6 +3,7 @@
 import re
 import json
 from os.path import join, abspath, dirname
+from collections import namedtuple
 
 
 __all__ = ['BIDSValidator']
@@ -156,23 +157,6 @@
 
         return regexps
 
-    def get_path_values(self, path):
-        """Takes a file path and returns values found for the following path
-        keys:
-            sub-
-            ses-
-        """
-        values = {}
-
-        regexps = self.get_regular_expressions('path.json')
-
-        # capture subject
-        for paths in ['sub', 'ses']:
-            match = re.compile(regexps[paths]).findall(path)
-            values[paths] = match[1] if match & match[1] else None
-
-        return values
-
     def conditional_match(self, expression, path):
         match = re.compile(expression).findall(path)
         match = match[0] if len(match) >= 1 else False
@@ -249,6 +233,7 @@
     """
     
     import pandas as pd
+    import hashlib
     def md5(fname):
         hash_md5 = hashlib.md5()
         with open(fname, "rb") as f:

{"golden_diff": "diff --git a/bids/layout/validation.py b/bids/layout/validation.py\n--- a/bids/layout/validation.py\n+++ b/bids/layout/validation.py\n@@ -3,6 +3,7 @@\n import re\n import json\n from os.path import join, abspath, dirname\n+from collections import namedtuple\n \n \n __all__ = ['BIDSValidator']\n@@ -156,23 +157,6 @@\n \n         return regexps\n \n-    def get_path_values(self, path):\n-        \"\"\"Takes a file path and returns values found for the following path\n-        keys:\n-            sub-\n-            ses-\n-        \"\"\"\n-        values = {}\n-\n-        regexps = self.get_regular_expressions('path.json')\n-\n-        # capture subject\n-        for paths in ['sub', 'ses']:\n-            match = re.compile(regexps[paths]).findall(path)\n-            values[paths] = match[1] if match & match[1] else None\n-\n-        return values\n-\n     def conditional_match(self, expression, path):\n         match = re.compile(expression).findall(path)\n         match = match[0] if len(match) >= 1 else False\n@@ -249,6 +233,7 @@\n     \"\"\"\n     \n     import pandas as pd\n+    import hashlib\n     def md5(fname):\n         hash_md5 = hashlib.md5()\n         with open(fname, \"rb\") as f:\n", "issue": "validator.get_path_values raises KeyError: 'regexp'\nWhat a great tutorial - keeps bearing fruits...  Continuing to #267 I was exploring new features of the validator, and found `get_path_values` which seems want ta path, so echoing `is_file` I pointed it to /sub-02/ses-01/anat/sub-02_ses-01_T2w.json to just get \r\n```\r\n--------------------------------------------------------------------------\r\nKeyError                                  Traceback (most recent call last)\r\n<ipython-input-71-00eb4b2b179c> in <module>()\r\n----> 1 validator.get_path_values('/sub-02/ses-01/anat/sub-02_ses-01_T2w.json')\r\n\r\n/home/yoh/proj/bids/pybids/bids/layout/validation.pyc in get_path_values(self, path)\r\n    164         values = {}\r\n    165 \r\n--> 166         regexps = self.get_regular_expressions('path.json')\r\n    167 \r\n    168         # capture subject\r\n\r\n/home/yoh/proj/bids/pybids/bids/layout/validation.pyc in get_regular_expressions(self, filename)\r\n    144             rule = rules[key]\r\n    145 \r\n--> 146             regexp = rule[\"regexp\"]\r\n    147 \r\n    148             if \"tokens\" in rule:\r\n\r\nKeyError: 'regexp'\r\n```\n", "before_files": [{"content": "\"\"\"Tools for validating BIDS projects.\"\"\"\n\nimport re\nimport json\nfrom os.path import join, abspath, dirname\n\n\n__all__ = ['BIDSValidator']\n\n\nclass BIDSValidator():\n    \"\"\"An object for BIDS (Brain Imaging Data Structure) verification in a data.\n\n    The main method of this class is `is_bids()`. You should use it for\n    checking whether a file path compatible with BIDS.\n\n    Parameters\n    ----------\n    index_associated : bool, default: True\n        Specifies if an associated data should be checked. If it is true then\n        any file paths in directories `code/`, `derivatives/`, `sourcedata/`\n        and `stimuli/` will pass the validation, else they won't.\n\n    Examples\n    --------\n    >>> from bids.layout import BIDSValidator\n    >>> validator = BIDSValidator()\n    >>> filepaths = [\"/sub-01/anat/sub-01_rec-CSD_T1w.nii.gz\",\n    >>> \"/sub-01/anat/sub-01_acq-23_rec-CSD_T1w.exe\", #wrong extension\n    >>> \"/participants.tsv\"]\n    >>> for filepath in filepaths:\n    >>>     print( validator.is_bids(filepath) )\n    True\n    False\n    True\n    \"\"\"\n\n    def __init__(self, index_associated=True):\n        self.rule_dir = join(dirname(abspath(__file__)),'config', 'validator')\n        self.index_associated = index_associated\n\n    def is_bids(self, path):\n        \"\"\"Check if a file path appropriate for BIDS.\n\n        Main method of the validator. uses other class methods for checking\n        different aspects of the file path.\n\n        Parameters\n        ----------\n            path: string\n                A path of a file you want to check.\n\n        Examples\n        --------\n        >>> from bids.layout import BIDSValidator\n        >>> validator = BIDSValidator()\n        >>> validator.is_bids(\"/sub-01/ses-test/anat/sub-01_ses-test_rec-CSD_run-23_T1w.nii.gz\")\n        True\n        >>> validator.is_bids(\"/sub-01/ses-test/sub-01_run-01_dwi.bvec\") # missed session in the filename\n        False\n        \"\"\"\n        conditions = []\n\n        conditions.append(self.is_top_level(path))\n        conditions.append(self.is_associated_data(path))\n        conditions.append(self.is_session_level(path))\n        conditions.append(self.is_subject_level(path))\n        conditions.append(self.is_phenotypic(path))\n        conditions.append(self.is_file(path))\n\n        return (any(conditions))\n\n    def is_top_level(self, path):\n        \"\"\"Check if the file has appropriate name for a top-level file.\"\"\"\n        with open(join(self.rule_dir, 'fixed_top_level_names.json'), 'r') as f:\n            fixed_top_level_json = json.load(f)\n            fixed_top_level_names = fixed_top_level_json['fixed_top_level_names']\n\n        regexps = self.get_regular_expressions('top_level_rules.json')\n\n        conditions = [False if re.compile(x).search(path) is None else True\n                      for x in regexps]\n\n        conditions.append(path in fixed_top_level_names)\n\n        return (any(conditions))\n\n    def is_associated_data(self, path):\n        \"\"\"Check if file is appropriate associated data.\"\"\"\n        if not self.index_associated:\n            return False\n\n        regexps = self.get_regular_expressions('associated_data_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return any(conditions)\n\n    def is_session_level(self, path):\n        \"\"\"Check if the file has appropriate name for a session level.\"\"\"\n        regexps = self.get_regular_expressions('session_level_rules.json')\n\n        conditions = [self.conditional_match(x, path) for x in regexps]\n\n        return (any(conditions))\n\n    def is_subject_level(self, path):\n        \"\"\"Check if the file has appropriate name for a subject level.\"\"\"\n        regexps = self.get_regular_expressions('subject_level_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return (any(conditions))\n\n    def is_phenotypic(self, path):\n        \"\"\"Check if file is phenotypic data.\"\"\"\n        regexps = self.get_regular_expressions('phenotypic_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return (any(conditions))\n\n    def is_file(self, path):\n        \"\"\"Check if file is phenotypic data.\"\"\"\n        regexps = self.get_regular_expressions('file_level_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return (any(conditions))\n\n    def get_regular_expressions(self, filename):\n        \"\"\"Get regular expressions from file.\"\"\"\n        regexps = []\n\n        filename = join(self.rule_dir, filename)\n\n        with open(filename, 'r') as f:\n            rules = json.load(f)\n\n        for key in list(rules.keys()):\n            rule = rules[key]\n\n            regexp = rule[\"regexp\"]\n\n            if \"tokens\" in rule:\n                tokens = rule[\"tokens\"]\n\n                for token in list(tokens):\n                    regexp = regexp.replace(token, \"|\".join(tokens[token]))\n\n            regexps.append(regexp)\n\n        return regexps\n\n    def get_path_values(self, path):\n        \"\"\"Takes a file path and returns values found for the following path\n        keys:\n            sub-\n            ses-\n        \"\"\"\n        values = {}\n\n        regexps = self.get_regular_expressions('path.json')\n\n        # capture subject\n        for paths in ['sub', 'ses']:\n            match = re.compile(regexps[paths]).findall(path)\n            values[paths] = match[1] if match & match[1] else None\n\n        return values\n\n    def conditional_match(self, expression, path):\n        match = re.compile(expression).findall(path)\n        match = match[0] if len(match) >= 1 else False\n        # adapted from JS code and JS does not support conditional groups\n        if (match):\n            return ((match[1] == match[2][1:]) | (not match[1]))\n        else:\n            return False\n\ndef validate_sequences(layout, config):\n    \"\"\"Checks files in BIDS project match user defined expectations.\n\n    This method is a wrapper for the check_duplicate_files() and \n    check_expected_files() methods. Use it to check whether there are\n    files with duplicate content within the BIDS data set and to check\n    the number of data set files against a user customized configuration\n    file. Returns a named tuple of three data frames: duplicates, summary, and problems.\n\n\n    Parameters\n    ----------\n        layout: BIDSLayout class\n            A BIDSLayout path of a data set.\n\n        config: string\n            Path to customized configuration file. Requires `runs` as an input.\n            See the sample config for an example (bids/layout/tests/data/sample_validation_config.json).\n\n\n    Examples\n    --------\n    >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')\n    >>> dfs = validate_sequences(layout, 'pybids/bids/layout/tests/data/sample_validation_config.json')\n    >>> dfs.duplicates\n    # Put example output here\n    >>> df.summary\n    # Put example output here\n    >>> df.problems\n    # Put example output here\n    \"\"\"\n    \n    duplicate_file_df = check_duplicate_files(layout)\n    summary_df, problem_df = check_expected_files(layout, config)\n    output = namedtuple('output', ['duplicates', 'summary', 'problems'])\n    return output(duplicate_file_df, summary_df, problem_df)\n    \n    \ndef check_duplicate_files(layout):\n    \"\"\"Checks images in BIDS project are not duplicated.\n\n    Check whether any files have duplicate content within the \n    BIDS data set. Returns a data frame: duplicate_file_df.\n\n\n    Parameters\n    ----------\n        layout: BIDSLayout class\n            A BIDSLayout path of a data set.\n\n\n    Examples\n    --------\n    >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')\n    >>> duplicate_file_df = check_duplicate_files(layout)\n    >>> duplicate_file_df\n    # Put example output here\n\n\n    Notes\n    ------\n    Returns a data frame in which the first column is the file\n    identifier and the second column is the path to the file.\n    Files with matching identifiers have the same content.\n    \"\"\"\n    \n    import pandas as pd\n    def md5(fname):\n        hash_md5 = hashlib.md5()\n        with open(fname, \"rb\") as f:\n            for chunk in iter(lambda: f.read(4096), b\"\"):\n                hash_md5.update(chunk)\n        return hash_md5.hexdigest()\n    hash_map = {}\n    all_niftis = layout.get(return_type=\"file\", extensions='.nii.gz')\n    for nifti_file in all_niftis:\n        md5sum = md5(nifti_file)\n        if md5sum in hash_map:\n            hash_map[md5sum].append(nifti_file)\n        else:\n            hash_map[md5sum] = [nifti_file]\n    df = pd.DataFrame.from_dict(hash_map, orient='index') \n    pruned_df = df.stack().reset_index().drop(columns='level_1')\n    out_df = pruned_df.rename(columns={'level_0': 'hash', 0: 'filename'})\n    return out_df\n    \n    \ndef check_expected_files(layout, config):\n    \"\"\"Checks files in BIDS project match user defined expectations.\n\n    This method checks the number of data set files against a user customized \n    configuration file. Returns two data frames: summary_df, problem_df.\n\n\n    Parameters\n    ----------\n        layout: BIDSLayout class\n            A BIDSLayout path of a data set.\n\n        config: string\n            Path to customized configuration file.\n\n\n    Examples\n    --------\n    >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')\n    >>> summary_df, problem_df = check_expected_files(layout, 'pybids/bids/layout/tests/data/sample_validation_config.json')\n    >>> summary_df\n    # Put example output here\n    >>> problem_df\n    # Put example output here\n\n\n    Notes\n    --------\n\n    `runs` is a mandatory field in the config file.\n    \n    The configuration file can take any keys that are valid arguments for\n    pybids `layout.get()` Values shoud match those in the BIDS file names. \n    See the sample config for an example (bids/layout/tests/data/sample_validation_config.json).\n    The more specific keys are provided, the more informative the output will be.\n\n    \"\"\"\n\n    import pandas as pd\n    dictlist = []\n    with open(config) as f:\n        json_data = json.load(f)\n        subjects = layout.get_subjects()\n    for sub in subjects: \n        for scan_params_d in json_data['sequences']:\n            scan_params = scan_params_d.copy()\n            seq_params = {i: scan_params[i] for i in scan_params if i != 'runs'}\n            actual_runs = layout.get(return_type='obj', subject=sub, extensions='.nii.gz', **seq_params)\n            scan_params['subject'] = sub\n            scan_params['runs_found'] = len(actual_runs)\n            scan_params['problem'] = len(actual_runs) != scan_params['runs']\n            dictlist.append(scan_params)\n    summary_df = pd.DataFrame(dictlist)\n    problem_df = summary_df.loc[summary_df['problem'] == True]\n    return summary_df, problem_df", "path": "bids/layout/validation.py"}], "after_files": [{"content": "\"\"\"Tools for validating BIDS projects.\"\"\"\n\nimport re\nimport json\nfrom os.path import join, abspath, dirname\nfrom collections import namedtuple\n\n\n__all__ = ['BIDSValidator']\n\n\nclass BIDSValidator():\n    \"\"\"An object for BIDS (Brain Imaging Data Structure) verification in a data.\n\n    The main method of this class is `is_bids()`. You should use it for\n    checking whether a file path compatible with BIDS.\n\n    Parameters\n    ----------\n    index_associated : bool, default: True\n        Specifies if an associated data should be checked. If it is true then\n        any file paths in directories `code/`, `derivatives/`, `sourcedata/`\n        and `stimuli/` will pass the validation, else they won't.\n\n    Examples\n    --------\n    >>> from bids.layout import BIDSValidator\n    >>> validator = BIDSValidator()\n    >>> filepaths = [\"/sub-01/anat/sub-01_rec-CSD_T1w.nii.gz\",\n    >>> \"/sub-01/anat/sub-01_acq-23_rec-CSD_T1w.exe\", #wrong extension\n    >>> \"/participants.tsv\"]\n    >>> for filepath in filepaths:\n    >>>     print( validator.is_bids(filepath) )\n    True\n    False\n    True\n    \"\"\"\n\n    def __init__(self, index_associated=True):\n        self.rule_dir = join(dirname(abspath(__file__)),'config', 'validator')\n        self.index_associated = index_associated\n\n    def is_bids(self, path):\n        \"\"\"Check if a file path appropriate for BIDS.\n\n        Main method of the validator. uses other class methods for checking\n        different aspects of the file path.\n\n        Parameters\n        ----------\n            path: string\n                A path of a file you want to check.\n\n        Examples\n        --------\n        >>> from bids.layout import BIDSValidator\n        >>> validator = BIDSValidator()\n        >>> validator.is_bids(\"/sub-01/ses-test/anat/sub-01_ses-test_rec-CSD_run-23_T1w.nii.gz\")\n        True\n        >>> validator.is_bids(\"/sub-01/ses-test/sub-01_run-01_dwi.bvec\") # missed session in the filename\n        False\n        \"\"\"\n        conditions = []\n\n        conditions.append(self.is_top_level(path))\n        conditions.append(self.is_associated_data(path))\n        conditions.append(self.is_session_level(path))\n        conditions.append(self.is_subject_level(path))\n        conditions.append(self.is_phenotypic(path))\n        conditions.append(self.is_file(path))\n\n        return (any(conditions))\n\n    def is_top_level(self, path):\n        \"\"\"Check if the file has appropriate name for a top-level file.\"\"\"\n        with open(join(self.rule_dir, 'fixed_top_level_names.json'), 'r') as f:\n            fixed_top_level_json = json.load(f)\n            fixed_top_level_names = fixed_top_level_json['fixed_top_level_names']\n\n        regexps = self.get_regular_expressions('top_level_rules.json')\n\n        conditions = [False if re.compile(x).search(path) is None else True\n                      for x in regexps]\n\n        conditions.append(path in fixed_top_level_names)\n\n        return (any(conditions))\n\n    def is_associated_data(self, path):\n        \"\"\"Check if file is appropriate associated data.\"\"\"\n        if not self.index_associated:\n            return False\n\n        regexps = self.get_regular_expressions('associated_data_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return any(conditions)\n\n    def is_session_level(self, path):\n        \"\"\"Check if the file has appropriate name for a session level.\"\"\"\n        regexps = self.get_regular_expressions('session_level_rules.json')\n\n        conditions = [self.conditional_match(x, path) for x in regexps]\n\n        return (any(conditions))\n\n    def is_subject_level(self, path):\n        \"\"\"Check if the file has appropriate name for a subject level.\"\"\"\n        regexps = self.get_regular_expressions('subject_level_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return (any(conditions))\n\n    def is_phenotypic(self, path):\n        \"\"\"Check if file is phenotypic data.\"\"\"\n        regexps = self.get_regular_expressions('phenotypic_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return (any(conditions))\n\n    def is_file(self, path):\n        \"\"\"Check if file is phenotypic data.\"\"\"\n        regexps = self.get_regular_expressions('file_level_rules.json')\n\n        conditions = [(re.compile(x).search(path) is not None)\n                      for x in regexps]\n\n        return (any(conditions))\n\n    def get_regular_expressions(self, filename):\n        \"\"\"Get regular expressions from file.\"\"\"\n        regexps = []\n\n        filename = join(self.rule_dir, filename)\n\n        with open(filename, 'r') as f:\n            rules = json.load(f)\n\n        for key in list(rules.keys()):\n            rule = rules[key]\n\n            regexp = rule[\"regexp\"]\n\n            if \"tokens\" in rule:\n                tokens = rule[\"tokens\"]\n\n                for token in list(tokens):\n                    regexp = regexp.replace(token, \"|\".join(tokens[token]))\n\n            regexps.append(regexp)\n\n        return regexps\n\n    def conditional_match(self, expression, path):\n        match = re.compile(expression).findall(path)\n        match = match[0] if len(match) >= 1 else False\n        # adapted from JS code and JS does not support conditional groups\n        if (match):\n            return ((match[1] == match[2][1:]) | (not match[1]))\n        else:\n            return False\n\ndef validate_sequences(layout, config):\n    \"\"\"Checks files in BIDS project match user defined expectations.\n\n    This method is a wrapper for the check_duplicate_files() and \n    check_expected_files() methods. Use it to check whether there are\n    files with duplicate content within the BIDS data set and to check\n    the number of data set files against a user customized configuration\n    file. Returns a named tuple of three data frames: duplicates, summary, and problems.\n\n\n    Parameters\n    ----------\n        layout: BIDSLayout class\n            A BIDSLayout path of a data set.\n\n        config: string\n            Path to customized configuration file. Requires `runs` as an input.\n            See the sample config for an example (bids/layout/tests/data/sample_validation_config.json).\n\n\n    Examples\n    --------\n    >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')\n    >>> dfs = validate_sequences(layout, 'pybids/bids/layout/tests/data/sample_validation_config.json')\n    >>> dfs.duplicates\n    # Put example output here\n    >>> df.summary\n    # Put example output here\n    >>> df.problems\n    # Put example output here\n    \"\"\"\n    \n    duplicate_file_df = check_duplicate_files(layout)\n    summary_df, problem_df = check_expected_files(layout, config)\n    output = namedtuple('output', ['duplicates', 'summary', 'problems'])\n    return output(duplicate_file_df, summary_df, problem_df)\n    \n    \ndef check_duplicate_files(layout):\n    \"\"\"Checks images in BIDS project are not duplicated.\n\n    Check whether any files have duplicate content within the \n    BIDS data set. Returns a data frame: duplicate_file_df.\n\n\n    Parameters\n    ----------\n        layout: BIDSLayout class\n            A BIDSLayout path of a data set.\n\n\n    Examples\n    --------\n    >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')\n    >>> duplicate_file_df = check_duplicate_files(layout)\n    >>> duplicate_file_df\n    # Put example output here\n\n\n    Notes\n    ------\n    Returns a data frame in which the first column is the file\n    identifier and the second column is the path to the file.\n    Files with matching identifiers have the same content.\n    \"\"\"\n    \n    import pandas as pd\n    import hashlib\n    def md5(fname):\n        hash_md5 = hashlib.md5()\n        with open(fname, \"rb\") as f:\n            for chunk in iter(lambda: f.read(4096), b\"\"):\n                hash_md5.update(chunk)\n        return hash_md5.hexdigest()\n    hash_map = {}\n    all_niftis = layout.get(return_type=\"file\", extensions='.nii.gz')\n    for nifti_file in all_niftis:\n        md5sum = md5(nifti_file)\n        if md5sum in hash_map:\n            hash_map[md5sum].append(nifti_file)\n        else:\n            hash_map[md5sum] = [nifti_file]\n    df = pd.DataFrame.from_dict(hash_map, orient='index') \n    pruned_df = df.stack().reset_index().drop(columns='level_1')\n    out_df = pruned_df.rename(columns={'level_0': 'hash', 0: 'filename'})\n    return out_df\n    \n    \ndef check_expected_files(layout, config):\n    \"\"\"Checks files in BIDS project match user defined expectations.\n\n    This method checks the number of data set files against a user customized \n    configuration file. Returns two data frames: summary_df, problem_df.\n\n\n    Parameters\n    ----------\n        layout: BIDSLayout class\n            A BIDSLayout path of a data set.\n\n        config: string\n            Path to customized configuration file.\n\n\n    Examples\n    --------\n    >>> layout = bids.grabbids.BIDSLayout('/path_to/sample_project_root')\n    >>> summary_df, problem_df = check_expected_files(layout, 'pybids/bids/layout/tests/data/sample_validation_config.json')\n    >>> summary_df\n    # Put example output here\n    >>> problem_df\n    # Put example output here\n\n\n    Notes\n    --------\n\n    `runs` is a mandatory field in the config file.\n    \n    The configuration file can take any keys that are valid arguments for\n    pybids `layout.get()` Values shoud match those in the BIDS file names. \n    See the sample config for an example (bids/layout/tests/data/sample_validation_config.json).\n    The more specific keys are provided, the more informative the output will be.\n\n    \"\"\"\n\n    import pandas as pd\n    dictlist = []\n    with open(config) as f:\n        json_data = json.load(f)\n        subjects = layout.get_subjects()\n    for sub in subjects: \n        for scan_params_d in json_data['sequences']:\n            scan_params = scan_params_d.copy()\n            seq_params = {i: scan_params[i] for i in scan_params if i != 'runs'}\n            actual_runs = layout.get(return_type='obj', subject=sub, extensions='.nii.gz', **seq_params)\n            scan_params['subject'] = sub\n            scan_params['runs_found'] = len(actual_runs)\n            scan_params['problem'] = len(actual_runs) != scan_params['runs']\n            dictlist.append(scan_params)\n    summary_df = pd.DataFrame(dictlist)\n    problem_df = summary_df.loc[summary_df['problem'] == True]\n    return summary_df, problem_df", "path": "bids/layout/validation.py"}]}