diff --git a/README.md b/README.md index a71df0c1b..cb8144442 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,4 @@ Please also cite one of the following papers to credit BIDS, depending on which - [EEG-BIDS](https://doi.org/10.1038/s41597-019-0104-8) - [iEEG-BIDS](https://doi.org/10.1038/s41597-019-0105-7) - [NIRS-BIDS](https://doi.org/10.31219/osf.io/7nmcp) +- [Motion-BIDS](https://doi.org/10.1038/s41597-024-03559-8) diff --git a/doc/authors.rst b/doc/authors.rst index da3cf8631..97df068e2 100644 --- a/doc/authors.rst +++ b/doc/authors.rst @@ -59,3 +59,4 @@ .. _waldie11: https://github.com/waldie11 .. _William Turner: https://bootstrapbill.github.io/ .. _Yorguin Mantilla: https://github.com/yjmantilla +.. _Julius Welzel: https://github.com/JuliusWelzel diff --git a/doc/whats_new.rst b/doc/whats_new.rst index 476bf7f31..fd7e7a938 100644 --- a/doc/whats_new.rst +++ b/doc/whats_new.rst @@ -19,11 +19,13 @@ Version 0.18 (unreleased) The following authors contributed for the first time. Thank you so much! 🤩 +* `Julius Welzel`_ * `Alex Lopez Marquez`_ + The following authors had contributed before. Thank you for sticking around! 🤘 -* TBD +* `Stefan Appelhoff`_ Detailed list of changes @@ -33,11 +35,12 @@ Detailed list of changes ^^^^^^^^^^^^^^^ - :func:`mne_bids.write_raw_bids()` has a new parameter `electrodes_tsv_task` which allows adding the `task` entity to the `electrodes.tsv` filepath, by `Alex Lopez Marquez`_ (:gh:`1424`) +- Extended the configuration to recognise `motion` as a valid BIDS datatype by `Julius Welzel`_ (:gh:`1430`) 🧐 API and behavior changes ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- None yet +- `tracksys` accepted as argument in :class:`mne_bids.BIDSPath()` by `Julius Welzel`_ (:gh:`1430`) 🛠 Requirements ^^^^^^^^^^^^^^^ diff --git a/mne_bids/config.py b/mne_bids/config.py index 5888f3f11..5e356bf21 100644 --- a/mne_bids/config.py +++ b/mne_bids/config.py @@ -6,7 +6,7 @@ from mne import io from mne.io.constants import FIFF -BIDS_VERSION = "1.7.0" +BIDS_VERSION = "1.9.0" PYBV_VERSION = "0.7.3" EEGLABIO_VERSION = "0.0.2" @@ -15,17 +15,19 @@ EPHY_ALLOWED_DATATYPES = ["meg", "eeg", "ieeg", "nirs"] -ALLOWED_DATATYPES = EPHY_ALLOWED_DATATYPES + ["anat", "beh"] +ALLOWED_DATATYPES = EPHY_ALLOWED_DATATYPES + ["anat", "beh", "motion"] MEG_CONVERT_FORMATS = ["FIF", "auto"] EEG_CONVERT_FORMATS = ["BrainVision", "auto"] IEEG_CONVERT_FORMATS = ["BrainVision", "auto"] NIRS_CONVERT_FORMATS = ["auto"] +MOTION_CONVERT_FORMATS = ["tsv", "auto"] CONVERT_FORMATS = { "meg": MEG_CONVERT_FORMATS, "eeg": EEG_CONVERT_FORMATS, "ieeg": IEEG_CONVERT_FORMATS, "nirs": NIRS_CONVERT_FORMATS, + "motion": MOTION_CONVERT_FORMATS, } # Orientation of the coordinate system dependent on manufacturer @@ -147,12 +149,17 @@ ".snirf", # SNIRF ] +allowed_extensions_motion = [ + ".tsv", # Tab-separated values +] + # allowed extensions (data formats) in BIDS spec ALLOWED_DATATYPE_EXTENSIONS = { "meg": allowed_extensions_meg, "eeg": allowed_extensions_eeg, "ieeg": allowed_extensions_ieeg, "nirs": allowed_extensions_nirs, + "motion": allowed_extensions_motion, } # allow additional extensions that are not BIDS @@ -190,6 +197,7 @@ "physio", "stim", # behavioral "nirs", + "motion", # motion ] # converts suffix to known path modalities @@ -227,6 +235,7 @@ "description", "suffix", "extension", + "tracking_system", ) ALLOWED_PATH_ENTITIES_SHORT = { "sub": "subject", @@ -239,6 +248,7 @@ "recording": "recording", "split": "split", "desc": "description", + "tracksys": "tracking_system", } # Annotations to never remove during reading or writing @@ -316,6 +326,7 @@ ALLOWED_SPACES["ieeg"] = BIDS_SHARED_COORDINATE_FRAMES + BIDS_IEEG_COORDINATE_FRAMES ALLOWED_SPACES["anat"] = None ALLOWED_SPACES["beh"] = None +ALLOWED_SPACES["motion"] = None # See: https://bids-specification.readthedocs.io/en/latest/appendices/entity-table.html#encephalography-eeg-ieeg-and-meg # noqa: E501 ENTITY_VALUE_TYPE = { @@ -331,6 +342,7 @@ "description": "label", "suffix": "label", "extension": "label", + "tracking_system": "label", } # mapping from supported BIDs coordinate frames -> MNE diff --git a/mne_bids/path.py b/mne_bids/path.py index bcfed1816..f3329880f 100644 --- a/mne_bids/path.py +++ b/mne_bids/path.py @@ -385,6 +385,7 @@ def __init__( space=None, split=None, description=None, + tracking_system=None, root=None, suffix=None, extension=None, @@ -421,6 +422,7 @@ def __init__( space=space, split=split, description=description, + tracking_system=tracking_system, root=root, datatype=datatype, suffix=suffix, @@ -442,6 +444,7 @@ def entities(self): "recording": self.recording, "split": self.split, "description": self.description, + "tracking_system": self.tracking_system, } @property @@ -575,6 +578,15 @@ def description(self) -> str | None: def description(self, value): self.update(description=value) + @property + def tracking_system(self) -> str | None: + """The tracking system entity.""" + return self._tracking_system + + @tracking_system.setter + def tracking_system(self, value): + self.update(tracking_system=value) + @property def suffix(self) -> str | None: """The filename suffix.""" @@ -881,7 +893,9 @@ def fpath(self): if self.suffix is None or self.suffix in ALLOWED_DATATYPES: # now only use valid datatype extension if self.extension is None: - valid_exts = sum(ALLOWED_DATATYPE_EXTENSIONS.values(), []) + valid_exts = ALLOWED_DATATYPE_EXTENSIONS.get( + self.datatype, sum(ALLOWED_DATATYPE_EXTENSIONS.values(), []) + ) else: valid_exts = [self.extension] matching_paths = [ @@ -2325,6 +2339,7 @@ def _filter_fnames( description=None, suffix=None, extension=None, + tracking_system=None, ): """Filter a list of BIDS filenames / paths based on BIDS entity values. @@ -2351,6 +2366,7 @@ def _filter_fnames( description = _ensure_tuple(description) suffix = _ensure_tuple(suffix) extension = _ensure_tuple(extension) + tracking_system = _ensure_tuple(tracking_system) leading_path_str = r".*\/?" # nothing or something ending with a `/` sub_str = r"sub-(" + "|".join(subject) + ")" if subject else r"sub-([^_]+)" @@ -2375,6 +2391,11 @@ def _filter_fnames( ) suffix_str = r"_(" + "|".join(suffix) + ")" if suffix else r"_([^_]+)" ext_str = r"(" + "|".join(extension) + ")$" if extension else r"\.([^_]+)" + tracksys_str = ( + r"tracksys-(" + "|".join(tracking_system) + ")" + if tracking_system + else r"(|tracksys-([^_]+))" + ) regexp = ( leading_path_str @@ -2390,6 +2411,7 @@ def _filter_fnames( + desc_str + suffix_str + ext_str + + tracksys_str ) # Convert to str so we can apply the regexp ... diff --git a/mne_bids/tests/test_path.py b/mne_bids/tests/test_path.py index acd51a956..effbb1644 100644 --- a/mne_bids/tests/test_path.py +++ b/mne_bids/tests/test_path.py @@ -500,6 +500,7 @@ def test_get_entities_from_fname(fname): "recording", "split", "description", + "tracking_system", ] @@ -537,6 +538,7 @@ def test_get_entities_from_fname_errors(fname): "recording", "split", "description", + "tracking_system", ] assert params["subject"] == "01"