Repository: bids-standard/pybids

Issue:
get_associations method of BIDSFile has issue with inheritance
My test case with an event file with a json sidecar in same directory as well as another applicable sidecar at top level has get.associations only returning top level side car instead of both.  Here is the code, I have attached the test BIDS dataset:

 ```
   pennies = BIDSLayout(pennies_dir)
   pennies_events = pennies.get(suffix='events', extension='tsv')
   pennies_dicts = pennies.get(suffix='events', extension='json')
   for file in pennies_events:
        associations = file.get_associations(include_parents=True)
        print(associations)
```
[eeg_match1.zip](https://github.com/bids-standard/pybids/files/5754597/eeg_match1.zip)



Discussion:
Thanks for the report. This indeed looks like a bug in that `get_associations()` isn't returning the complete list of associated files. OTOH, I think the metadata inheritance itself is working correctly, in that the values in the sidecar in the same directory are being applied after the top level value (note that because inheritance doesn't apply in a nested way, the same-dir JSON will *overwrite* some of the values set at the root).

I'll look into this when I have time, but unfortunately it's not going to be a high priority, as `get_associations()` is really just a helper method, and the more important thing is that the metadata be applied and indexed in the right order, which seems to be the case.

Resolving patch:
diff --git a/bids/layout/index.py b/bids/layout/index.py
--- a/bids/layout/index.py
+++ b/bids/layout/index.py
@@ -360,7 +360,7 @@ def create_association_pair(src, dst, kind, kind2=None):
                 continue
 
             # Create DB records for metadata associations
-            js_file = payloads[-1][1]
+            js_file = payloads[0][1]
             create_association_pair(js_file, bf.path, 'Metadata')
 
             # Consolidate metadata by looping over inherited JSON files
diff --git a/bids/layout/models.py b/bids/layout/models.py
--- a/bids/layout/models.py
+++ b/bids/layout/models.py
@@ -261,12 +261,17 @@ def get_associations(self, kind=None, include_parents=False):
         list
             A list of BIDSFile instances.
         """
-        if kind is None:
+        if kind is None and not include_parents:
             return self._associations
+
         session = object_session(self)
         q = (session.query(BIDSFile)
              .join(FileAssociation, BIDSFile.path == FileAssociation.dst)
-             .filter_by(kind=kind, src=self.path))
+             .filter_by(src=self.path))
+
+        if kind is not None:
+            q = q.filter_by(kind=kind)
+
         associations = q.all()
 
         if not include_parents:
@@ -278,7 +283,7 @@ def collect_associations(results, bidsfile):
                 results = collect_associations(results, p)
             return results
 
-        return chain(*[collect_associations([], bf) for bf in associations])
+        return list(chain(*[collect_associations([], bf) for bf in associations]))
 
     def get_metadata(self):
         """Return all metadata associated with the current file. """


Test patch:
diff --git a/bids/layout/tests/test_layout.py b/bids/layout/tests/test_layout.py
--- a/bids/layout/tests/test_layout.py
+++ b/bids/layout/tests/test_layout.py
@@ -582,20 +582,30 @@ def test_indexed_file_associations(layout_7t_trt):
                             acquisition='fullbrain', extension='.nii.gz')[0]
     assocs = img.get_associations()
     assert len(assocs) == 3
-    targets = [
+    targets = {
         os.path.join(layout_7t_trt.root,
                      'sub-01/ses-1/fmap/sub-01_ses-1_run-1_phasediff.nii.gz'),
         os.path.join(
             img.dirname,
             'sub-01_ses-1_task-rest_acq-fullbrain_run-1_physio.tsv.gz'),
-        os.path.join(layout_7t_trt.root, 'task-rest_acq-fullbrain_bold.json')
-    ]
+        os.path.join(
+            img.dirname,
+            'sub-01_ses-1_task-rest_acq-fullbrain_run-1_bold.json'
+        )
+    }
+    assert set([a.path for a in assocs]) == set(targets)
+
+    # Test with parents included
+    targets.add(os.path.join(layout_7t_trt.root, 'task-rest_acq-fullbrain_bold.json'))
+    assocs = img.get_associations(include_parents=True)
+    assert len(assocs) == 4
     assert set([a.path for a in assocs]) == set(targets)
 
-    js = [a for a in assocs if a.path.endswith('json')][0]
-    assert len(js.get_associations()) == 41
+    # Get the root-level JSON and check that its associations are correct
+    js = [a for a in assocs if a.path.endswith('json')][1]
+    assert len(js.get_associations()) == 40
     assert len(js.get_associations('Parent')) == 1
-    assert len(js.get_associations('Metadata')) == 40
+    assert len(js.get_associations('Metadata')) == 39
     assert not js.get_associations('InformedBy')