bidsaid 0.20.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bidsaid/__init__.py ADDED
@@ -0,0 +1,27 @@
1
+ """
2
+ Post-hoc BIDS toolkit for NIfTI datasets without original DICOMs.
3
+ -----------------------------------------------------------------
4
+ Documentation can be found at https://bidsaid.readthedocs.io.
5
+
6
+ Submodules
7
+ ----------
8
+ audit -- Contains the ``BIDSAuditor`` class to check for certain file availability
9
+
10
+ bids -- Operations related to initializing and creating BIDS compliant files
11
+
12
+ io -- Generic operations related to loading NIfTI data
13
+
14
+ logging -- Set up a logger using ``RichHandler`` as the default handler if a root or
15
+ module specific handler is not available
16
+
17
+ metadata -- Operations related to extracting or creating metadata information from NIfTI images
18
+
19
+ parsers -- Operations related to standardizing and parsing information logs created by stimulus
20
+ neurobehavioral software such as Presentation and EPrime
21
+
22
+ qc -- Quality control utilities for fMRI data (motion censoring, framewise displacement, etc.)
23
+
24
+ simulate -- Simulate a basic NIfTI image or BIDS dataset for testing purposes
25
+ """
26
+
27
+ __version__ = "0.20.0"
bidsaid/_constants.py ADDED
@@ -0,0 +1,24 @@
1
+ """
2
+ Module for constants.
3
+
4
+ Note: The CLI command for converting edat3 files to text files can be found at the
5
+ following link: https://support.pstnet.com/hc/en-us/articles/360020316014-DATA-Using-E-DataAid-exe-with-Command-Line-Interpreters-25323
6
+ """
7
+
8
+ EDATAAID_PATH = r"C:\Program Files (x86)\PST\E-Prime 3.0\Program\E-DataAid.exe"
9
+
10
+ EDATAAID_CONTROL_FILE = """Inheritance=NULL
11
+ InFile={edat_path}
12
+ OutFile={dst_path}
13
+ ColFlags=0
14
+ ColNames=1
15
+ Comments=0
16
+ BegCommentLine=0
17
+ EndCommentLine=0
18
+ DataSeparator={delimiter}
19
+ VarSeparator={delimiter}
20
+ BegDataLine=0
21
+ EndDataLine=0
22
+ MissingData=nan
23
+ Unicode=0
24
+ """
bidsaid/_decorators.py ADDED
@@ -0,0 +1,97 @@
1
+ """Decorator functions."""
2
+
3
+ import functools, inspect
4
+
5
+ from typing import Any, Callable
6
+
7
+ from ._helpers import iterable_to_str
8
+ from .io import get_nifti_header
9
+
10
+
11
+ def check_all_none(parameter_names: list[str]) -> Callable:
12
+ """
13
+ Checks if specific parameters are assigned ``None``.
14
+
15
+ Parameters
16
+ ----------
17
+ parameter_names : :obj:`list[str]`
18
+ List of parameter names to check.
19
+
20
+ Returns
21
+ -------
22
+ Callable
23
+ Decorator function wrapping target function.
24
+ """
25
+
26
+ def decorator(func: Callable) -> Callable:
27
+ signature = inspect.signature(func)
28
+ if invalid_params := [
29
+ param
30
+ for param in parameter_names
31
+ if param not in signature.parameters.keys()
32
+ ]:
33
+ raise NameError(
34
+ "Error in ``parameter_names`` of decorator. The following "
35
+ f"parameters are not in the signature of '{func.__name__}': "
36
+ f"{iterable_to_str(invalid_params)}."
37
+ )
38
+
39
+ @functools.wraps(func)
40
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
41
+ bound_args = signature.bind(*args, **kwargs)
42
+ bound_args.apply_defaults()
43
+ all_param_values = [bound_args.arguments[name] for name in parameter_names]
44
+ if all(value is None for value in all_param_values):
45
+ raise ValueError(
46
+ "All of the following arguments cannot be None, "
47
+ f"one must be specified: {iterable_to_str(parameter_names)}."
48
+ )
49
+
50
+ return func(*args, **kwargs)
51
+
52
+ return wrapper
53
+
54
+ return decorator
55
+
56
+
57
+ def check_nifti(nifti_param_name: str | None = None) -> Callable:
58
+ """
59
+ Checks if input NIfTI has qform or sform codes set to scanner.
60
+
61
+ Parameters
62
+ ----------
63
+ nifti_param_name : :obj:`list[str]`
64
+ Name of the NIfTI parameter. If None, assumes the
65
+ NIfTI parameter is "nifti_file_or_img".
66
+
67
+ Returns
68
+ -------
69
+ Callable
70
+ Decorator function wrapping target function.
71
+ """
72
+
73
+ def decorator(func: Callable) -> Callable:
74
+ @functools.wraps(func)
75
+ def wrapper(*args, **kwargs) -> Any:
76
+ bound_args = inspect.signature(func).bind(*args, **kwargs)
77
+ bound_args.apply_defaults()
78
+
79
+ param_name = (
80
+ "nifti_file_or_img" if not nifti_param_name else nifti_param_name
81
+ )
82
+ img_val = bound_args.arguments.get(param_name)
83
+ if img_val:
84
+ hdr = get_nifti_header(img_val)
85
+
86
+ # sform takes precedence over qform
87
+ code = "sform_code" if hdr["sform_code"] else "qform_code"
88
+ if hdr[code] != 1:
89
+ raise ValueError(
90
+ f"The {code} is not set to 'scanner' and is not a raw NIfTI image."
91
+ )
92
+
93
+ return func(*args, **kwargs)
94
+
95
+ return wrapper
96
+
97
+ return decorator
bidsaid/_exceptions.py ADDED
@@ -0,0 +1,60 @@
1
+ """Custom exceptions."""
2
+
3
+ from typing import Literal
4
+
5
+
6
+ class SliceAxisError(Exception):
7
+ """
8
+ Incorrect slice axis.
9
+
10
+ Raised when the number of slices does not match "slice_end" plus one.
11
+
12
+ Parameters
13
+ ----------
14
+ slice_axis : :obj:`Literal["x", "y", "z"]`
15
+ The specified slice dimension.
16
+
17
+ n_slices : :obj:`int`
18
+ The number of slices from the specified ``slice_axis``.
19
+
20
+ slice_end : :obj:`int`
21
+ The number of slices specified by "slice_end" in the NIfTI header.
22
+
23
+ message : :obj:`str` or :obj:`None`:
24
+ The error message. If None, a default error message is used.
25
+ """
26
+
27
+ def __init__(
28
+ self,
29
+ slice_axis: Literal["x", "y", "z"],
30
+ n_slices: int,
31
+ slice_end: int,
32
+ message: str | None = None,
33
+ ):
34
+ if not message:
35
+ self.message = (
36
+ "Incorrect slice axis. Number of slices for "
37
+ f"{slice_axis} dimension is {n_slices} but "
38
+ f"'slice_end' in NIfTI header is {slice_end}."
39
+ )
40
+ else:
41
+ self.message = message
42
+
43
+ super().__init__(self.message)
44
+
45
+
46
+ class DataDimensionError(Exception):
47
+ """
48
+ Incorrect data dimensionality.
49
+ """
50
+
51
+ pass
52
+
53
+
54
+ class PathDoesNotExist(Exception):
55
+ """Exception when path does not exist."""
56
+
57
+ def __init__(self, path):
58
+ self.message = f"The following path does not exist: {path}"
59
+
60
+ super().__init__(self.message)
bidsaid/_helpers.py ADDED
@@ -0,0 +1,14 @@
1
+ """Helper functions."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+
7
+ def iterable_to_str(str_list: list[str]) -> None:
8
+ """Converts an iterable containing strings to strings."""
9
+ return ", ".join(["'{a}'".format(a=x) for x in str_list])
10
+
11
+
12
+ def is_path(object: Any) -> bool:
13
+ "Determine if input is a Path or string."
14
+ return isinstance(object, (str, Path))