Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 17 additions & 20 deletions heudiconv/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,27 +253,13 @@ def populate_aggregated_jsons(path: str) -> None:
# create a stub onsets file for each one of those
suf = "_bold.json"
assert fpath.endswith(suf)
# specify the name of the '_events.tsv' file:
if "_echo-" in fpath:
# multi-echo sequence: bids (1.1.0) specifies just one '_events.tsv'
# file, common for all echoes. The name will not include _echo-.
# TODO: RF to use re.match for better readability/robustness
# So, find out the echo number:
fpath_split = fpath.split("_echo-", 1) # split fpath using '_echo-'
fpath_split_2 = fpath_split[1].split(
"_", 1
) # split the second part of fpath_split using '_'
echoNo = fpath_split_2[0] # get echo number
if echoNo == "1":
if len(fpath_split_2) != 2:
raise ValueError("Found no trailer after _echo-")
# we modify fpath to exclude '_echo-' + echoNo:
fpath = fpath_split[0] + "_" + fpath_split_2[1]
else:
# for echoNo greater than 1, don't create the events file, so go to
# the next for loop iteration:
continue

# specify the name of the '_events.tsv' file:
parsed_fpath = BIDSFile.parse(op.basename(fpath))
for events_invalid_entity in ['chunk', 'echo', 'part']:
# events.tsv with these entities are not specified
parsed_fpath.drop(events_invalid_entity, missing_ok=True)
fpath = op.join(op.dirname(fpath), str(parsed_fpath))
events_file = remove_suffix(fpath, suf) + "_events.tsv"
# do not touch any existing thing, it may be precious
if not op.lexists(events_file):
Expand Down Expand Up @@ -1090,6 +1076,7 @@ class BIDSFile:
"mt",
"part",
"recording",
"chunk",
]

def __init__(
Expand Down Expand Up @@ -1156,6 +1143,16 @@ def __setitem__(
) -> None: # would puke with some exception if already known
return self.set(entity, value, overwrite=False)

def __contains__(self, entity: object) -> bool:
return entity in self._entities

def drop(self, entity: str, missing_ok: bool=False) -> None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def drop(self, entity: str, missing_ok: bool=False) -> None:
def drop(self, entity: str, missing_ok: bool = False) -> None:

to please linters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, I need to sleep on it but it says

Image

may be you didn't allow contributors to push to you branch and that's the reason @octomike ?

if entity not in self._entities:
if not missing_ok:
raise ValueError(f"{self} does not contain entity {entity!r}")
return
self._entities.pop(entity)

def set(self, entity: str, value: str, overwrite: bool = True) -> None:
if entity not in self._entities:
# just set it; no complains here
Expand Down
37 changes: 36 additions & 1 deletion heudiconv/tests/test_bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from datetime import datetime, timedelta
from glob import glob
import itertools
import logging
import os
import os.path as op
from pathlib import Path
Expand All @@ -31,6 +32,7 @@
get_key_info_for_fmap_assignment,
get_shim_setting,
maybe_na,
populate_aggregated_jsons,
populate_intended_for,
sanitize_label,
select_fmap_from_compatible_groups,
Expand Down Expand Up @@ -1506,7 +1508,7 @@ def test_populate_intended_for(
assert "IntendedFor" not in data.keys()


def test_BIDSFile() -> None:
def test_BIDSFile(caplog: pytest.LogCaptureFixture) -> None:
"""Tests for the BIDSFile class"""

# define entities in the correct order:
Expand Down Expand Up @@ -1574,6 +1576,39 @@ def test_BIDSFile() -> None:
my_bids_file.set("echo", "2")
assert my_bids_file["echo"] == "2"

# Test drop method
my_bids_file.drop("dir")
assert "dir" not in my_bids_file
# dropping an entity which is not set only logs a warning
caplog.set_level(logging.WARNING)
# test previously dropped entity and entirely non-existing
for entity in ['dir', 'not_existing']:
with pytest.raises(ValueError, match=f"does not contain entity {entity!r}"):
my_bids_file.drop(entity)
# implicitly assert that no exception is thrown when using missing_ok
my_bids_file.drop(entity, missing_ok=True)


def test_populate_aggregated_jsons_events(tmp_path: Path) -> None:
"""A single _events.tsv is generated for files differing only in
entities the events are independent of ('chunk', 'echo', and 'part') """
func_path = tmp_path / "sub-01" / "func"
bold_json = {"RepetitionTime": 1.0, "TaskName": "rest"}
create_tree(
str(func_path),
{
f"sub-01_task-rest_{entity}_bold.json": dict(bold_json)
for entity in ["chunk-1", "chunk-2", "echo-1", "echo-2", "part-mag", "part-phase"]
},
)

populate_aggregated_jsons(str(tmp_path))

events_files = sorted(func_path.glob("*_events.tsv"))
assert events_files == [func_path / "sub-01_task-rest_events.tsv"]
# and nothing got written elsewhere in the dataset:
assert sorted(tmp_path.rglob("*_events.tsv")) == events_files


@pytest.mark.skipif(not have_datalad, reason="no datalad")
def test_ME_mag_phase_conversion(
Expand Down
Loading