bids-manager 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 BIDS Manager
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include README.md
2
+ include LICENSE
3
+ recursive-include bids_manager/miscellaneous/images *
@@ -0,0 +1,142 @@
1
+ Metadata-Version: 2.4
2
+ Name: bids-manager
3
+ Version: 0.1.0
4
+ Summary: GUI application to manage BIDS datasets
5
+ Author-email: Karel López Vilaret <karel.mauricio.lopez.vilaret@uni-oldenburg.de>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ANCPLabOldenburg/BIDS-Manager
8
+ Project-URL: Documentation, https://github.com/ANCPLabOldenburg/BIDS-Manager
9
+ Project-URL: Source, https://github.com/ANCPLabOldenburg/BIDS-Manager
10
+ Project-URL: Tracker, https://github.com/ANCPLabOldenburg/BIDS-Manager/issues
11
+ Keywords: BIDS,DICOM,GUI,Neuroimaging
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pydicom==3.0.1
19
+ Requires-Dist: pandas==2.3.1
20
+ Requires-Dist: PyQt5==5.15.11
21
+ Requires-Dist: PyQtWebEngine==5.15.7
22
+ Requires-Dist: heudiconv-ancp
23
+ Requires-Dist: nipype-ancp
24
+ Requires-Dist: dcm2niix==1.0.20250506
25
+ Requires-Dist: nibabel==5.3.2
26
+ Requires-Dist: numpy==2.2.6
27
+ Requires-Dist: psutil==7.0.0
28
+ Requires-Dist: matplotlib==3.10.3
29
+ Requires-Dist: joblib==1.4.2
30
+ Dynamic: license-file
31
+
32
+
33
+ # BIDS Manager
34
+
35
+ **BIDS Manager** is a **PyQt-based** GUI that converts **DICOM** folders into **BIDS**-compliant datasets and allows easy metadata editing.
36
+
37
+ ---
38
+
39
+ ## Requirements
40
+
41
+ | Software | Minimum Version | Notes |
42
+ |----------|-----------------|---------------------------------------------------------|
43
+ | **Python** | 3.10 | Installed automatically if you use the one-click installers |
44
+ | **Git** | — | Must be installed manually (see below) |
45
+
46
+ ### Installing Git
47
+
48
+ | Platform | Command / Action |
49
+ |-------------------|----------------------------------------------------|
50
+ | **Windows** | Download and run the installer: <https://git-scm.com/download/win> |
51
+ | **Ubuntu/Debian** | `sudo apt-get update && sudo apt-get install git` |
52
+
53
+ ---
54
+
55
+ ## Installation
56
+
57
+ You can install BIDS Manager in two ways:
58
+
59
+ ### 1. One-click installers <sup>(recommended)</sup>
60
+
61
+ 1. **Download** the ZIP package:
62
+ **[📦 One-click Installers](https://github.com/ANCPLabOldenburg/BIDS-Manager/raw/main/Installers/Installers.zip
63
+ )**
64
+ 2. **Extract** the ZIP file and run the script for your operating system:
65
+
66
+ | OS | Script | How to Run | Duration |
67
+ |------------------|-------------------------------|------------------------------------|---------|
68
+ | **Windows 10/11**| `install_bids_manager.bat` | Double-click | ≈ 5 min |
69
+ | **Linux** | `install_bids_manager.sh` | `./install_bids_manager.sh` | ≈ 5 min |
70
+
71
+ 3. After the installation finishes, you will find two shortcuts on your desktop:
72
+
73
+ | OS | Launch | Uninstall |
74
+ |-------------|---------------------------|--------------------------------|
75
+ | **Windows** | `run_bidsmanager.bat` | `uninstall_bidsmanager.bat` |
76
+ | **Linux** | **BIDS Manager** (launcher)| `uninstall_bidsmanager.sh` |
77
+
78
+ ---
79
+
80
+ ### 2. Install in a virtual environment (advanced)
81
+
82
+ ```bash
83
+ # 1. Create a virtual environment
84
+ python3 -m venv <env_name>
85
+
86
+ # 2. Activate it
87
+ source <env_name>/bin/activate # On Windows: <env_name>\Scripts\activate
88
+
89
+ # 3. Install BIDS Manager from GitHub
90
+ pip install git+https://github.com/ANCPLabOldenburg/BIDS-Manager.git
91
+ ```
92
+
93
+ The package declares all dependencies including `heudiconv`, so installation
94
+ pulls everything required to run the GUI and helper scripts.
95
+ All core requirements are version pinned in `pyproject.toml` to ensure
96
+ consistent installations.
97
+
98
+ After installation the following commands become available:
99
+
100
+ - `bids-manager` – main GUI combining conversion and editing tools
101
+ - `dicom-inventory` – generate `subject_summary.tsv` from a DICOM directory
102
+ - `build-heuristic` – create a HeuDiConv heuristic from the TSV
103
+ - `run-heudiconv` – run HeuDiConv using the generated heuristic
104
+ - `post-conv-renamer` – rename fieldmap files after conversion
105
+ - `bids-editor` – standalone metadata editor
106
+ - `fill-bids-ignore` – interactively update `.bidsignore`
107
+
108
+ All utilities provide `-h/--help` for details.
109
+
110
+ ### Recent updates
111
+
112
+ - The TSV produced by `dicom-inventory` can now be loaded directly in the GUI and
113
+ its file name customised before generation.
114
+ - The Batch Rename tool previews changes and allows restricting the scope to
115
+ specific subjects.
116
+ - A "Set Intended For" dialog lets you manually edit fieldmap IntendedFor lists
117
+ if the automatic matching needs adjustment.
118
+ - `run-heudiconv` now keeps a copy of `subject_summary.tsv` under `.bids_manager`
119
+ and generates a clean `participants.tsv` using demographics from that file.
120
+ - Re-running `run-heudiconv` on the same dataset now appends new subjects to
121
+ the existing `.bids_manager` records and updates `participants.tsv` instead of
122
+ overwriting them.
123
+ - `dicom-inventory` distinguishes repeated sequences by adding `series_uid` and `rep`
124
+ columns and records `acq_time` for each series in `subject_summary.tsv`.
125
+ - Fieldmap rows for magnitude and phase images are now merged so each acquisition
126
+ appears once with the combined file count, and their `series_uid` values are
127
+ stored as a pipe-separated list so both sequences are converted.
128
+ - `post-conv-renamer` now adds an `IntendedFor` list to each fieldmap JSON so
129
+ fMRI preprocessing tools can automatically match fieldmaps with the relevant
130
+ functional runs.
131
+ - The GUI's Tools menu gained actions to refresh `_scans.tsv` files and edit
132
+ `.bidsignore` entries.
133
+ - The DPI scale dialog now adjusts values in 25% increments and the DPI button
134
+ appears between the CPU and Authorship buttons.
135
+ - On startup the GUI detects the system DPI and applies the matching scale.
136
+ - The scanned data table now provides a "Generate unique IDs" button that
137
+ assigns random 3‑letter/3‑digit identifiers to subjects. If an entry already
138
+ exists for the same study in an existing `.bids_manager/subject_summary.tsv`,
139
+ you are prompted to reuse its identifier.
140
+ - A "Detect repeats" button can recompute repetition numbers based on
141
+ acquisition time when all BIDS and given names are filled.
142
+
@@ -0,0 +1,111 @@
1
+
2
+ # BIDS Manager
3
+
4
+ **BIDS Manager** is a **PyQt-based** GUI that converts **DICOM** folders into **BIDS**-compliant datasets and allows easy metadata editing.
5
+
6
+ ---
7
+
8
+ ## Requirements
9
+
10
+ | Software | Minimum Version | Notes |
11
+ |----------|-----------------|---------------------------------------------------------|
12
+ | **Python** | 3.10 | Installed automatically if you use the one-click installers |
13
+ | **Git** | — | Must be installed manually (see below) |
14
+
15
+ ### Installing Git
16
+
17
+ | Platform | Command / Action |
18
+ |-------------------|----------------------------------------------------|
19
+ | **Windows** | Download and run the installer: <https://git-scm.com/download/win> |
20
+ | **Ubuntu/Debian** | `sudo apt-get update && sudo apt-get install git` |
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ You can install BIDS Manager in two ways:
27
+
28
+ ### 1. One-click installers <sup>(recommended)</sup>
29
+
30
+ 1. **Download** the ZIP package:
31
+ **[📦 One-click Installers](https://github.com/ANCPLabOldenburg/BIDS-Manager/raw/main/Installers/Installers.zip
32
+ )**
33
+ 2. **Extract** the ZIP file and run the script for your operating system:
34
+
35
+ | OS | Script | How to Run | Duration |
36
+ |------------------|-------------------------------|------------------------------------|---------|
37
+ | **Windows 10/11**| `install_bids_manager.bat` | Double-click | ≈ 5 min |
38
+ | **Linux** | `install_bids_manager.sh` | `./install_bids_manager.sh` | ≈ 5 min |
39
+
40
+ 3. After the installation finishes, you will find two shortcuts on your desktop:
41
+
42
+ | OS | Launch | Uninstall |
43
+ |-------------|---------------------------|--------------------------------|
44
+ | **Windows** | `run_bidsmanager.bat` | `uninstall_bidsmanager.bat` |
45
+ | **Linux** | **BIDS Manager** (launcher)| `uninstall_bidsmanager.sh` |
46
+
47
+ ---
48
+
49
+ ### 2. Install in a virtual environment (advanced)
50
+
51
+ ```bash
52
+ # 1. Create a virtual environment
53
+ python3 -m venv <env_name>
54
+
55
+ # 2. Activate it
56
+ source <env_name>/bin/activate # On Windows: <env_name>\Scripts\activate
57
+
58
+ # 3. Install BIDS Manager from GitHub
59
+ pip install git+https://github.com/ANCPLabOldenburg/BIDS-Manager.git
60
+ ```
61
+
62
+ The package declares all dependencies including `heudiconv`, so installation
63
+ pulls everything required to run the GUI and helper scripts.
64
+ All core requirements are version pinned in `pyproject.toml` to ensure
65
+ consistent installations.
66
+
67
+ After installation the following commands become available:
68
+
69
+ - `bids-manager` – main GUI combining conversion and editing tools
70
+ - `dicom-inventory` – generate `subject_summary.tsv` from a DICOM directory
71
+ - `build-heuristic` – create a HeuDiConv heuristic from the TSV
72
+ - `run-heudiconv` – run HeuDiConv using the generated heuristic
73
+ - `post-conv-renamer` – rename fieldmap files after conversion
74
+ - `bids-editor` – standalone metadata editor
75
+ - `fill-bids-ignore` – interactively update `.bidsignore`
76
+
77
+ All utilities provide `-h/--help` for details.
78
+
79
+ ### Recent updates
80
+
81
+ - The TSV produced by `dicom-inventory` can now be loaded directly in the GUI and
82
+ its file name customised before generation.
83
+ - The Batch Rename tool previews changes and allows restricting the scope to
84
+ specific subjects.
85
+ - A "Set Intended For" dialog lets you manually edit fieldmap IntendedFor lists
86
+ if the automatic matching needs adjustment.
87
+ - `run-heudiconv` now keeps a copy of `subject_summary.tsv` under `.bids_manager`
88
+ and generates a clean `participants.tsv` using demographics from that file.
89
+ - Re-running `run-heudiconv` on the same dataset now appends new subjects to
90
+ the existing `.bids_manager` records and updates `participants.tsv` instead of
91
+ overwriting them.
92
+ - `dicom-inventory` distinguishes repeated sequences by adding `series_uid` and `rep`
93
+ columns and records `acq_time` for each series in `subject_summary.tsv`.
94
+ - Fieldmap rows for magnitude and phase images are now merged so each acquisition
95
+ appears once with the combined file count, and their `series_uid` values are
96
+ stored as a pipe-separated list so both sequences are converted.
97
+ - `post-conv-renamer` now adds an `IntendedFor` list to each fieldmap JSON so
98
+ fMRI preprocessing tools can automatically match fieldmaps with the relevant
99
+ functional runs.
100
+ - The GUI's Tools menu gained actions to refresh `_scans.tsv` files and edit
101
+ `.bidsignore` entries.
102
+ - The DPI scale dialog now adjusts values in 25% increments and the DPI button
103
+ appears between the CPU and Authorship buttons.
104
+ - On startup the GUI detects the system DPI and applies the matching scale.
105
+ - The scanned data table now provides a "Generate unique IDs" button that
106
+ assigns random 3‑letter/3‑digit identifiers to subjects. If an entry already
107
+ exists for the same study in an existing `.bids_manager/subject_summary.tsv`,
108
+ you are prompted to reuse its identifier.
109
+ - A "Detect repeats" button can recompute repetition numbers based on
110
+ acquisition time when all BIDS and given names are filled.
111
+
@@ -0,0 +1,11 @@
1
+ """BIDS Manager package."""
2
+
3
+ from importlib import metadata
4
+
5
+ __all__ = ["__version__"]
6
+
7
+ try: # pragma: no cover - version resolution
8
+ __version__ = metadata.version("bids-manager")
9
+ except metadata.PackageNotFoundError: # pragma: no cover
10
+ __version__ = "0.0.0"
11
+
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ build_heuristic_from_tsv.py — **v10**
4
+ ====================================
5
+ Simple heuristic that:
6
+ 1. **Keeps every sequence**, including SBRef.
7
+ 2. **Uses the raw SeriesDescription** (cleaned) as the filename stem – no
8
+ added `rep-*`, task, or echo logic.
9
+ 3. Skips only modalities listed in `SKIP_BY_DEFAULT` (`report`,
10
+ `physio`, `refscan`).
11
+ """
12
+
13
+ from __future__ import annotations
14
+ from pathlib import Path
15
+ from textwrap import dedent
16
+ import pandas as pd
17
+ import re
18
+
19
+ # -----------------------------------------------------------------------------
20
+ # Configuration
21
+ # -----------------------------------------------------------------------------
22
+ SKIP_BY_DEFAULT = {"report", "physio", "refscan"}
23
+
24
+ # -----------------------------------------------------------------------------
25
+ # Helper functions
26
+ # -----------------------------------------------------------------------------
27
+
28
+ def clean(text: str) -> str:
29
+ """Return alphanumerics only (for variable names)."""
30
+ return re.sub(r"[^0-9A-Za-z]+", "", str(text))
31
+
32
+
33
+ def safe_stem(seq: str) -> str:
34
+ """Clean SeriesDescription for use in a filename."""
35
+ return re.sub(r"[^0-9A-Za-z_-]+", "_", seq.strip()).strip("_")
36
+
37
+
38
+ def dedup_parts(*parts: str) -> str:
39
+ """Return underscore-joined *parts* with consecutive repeats removed."""
40
+ tokens: list[str] = []
41
+ for part in parts:
42
+ for t in str(part).split("_"):
43
+ if t and (not tokens or t != tokens[-1]):
44
+ tokens.append(t)
45
+ return "_".join(tokens)
46
+
47
+
48
+ # -----------------------------------------------------------------------------
49
+ # Core writer
50
+ # -----------------------------------------------------------------------------
51
+
52
+ def write_heuristic(df: pd.DataFrame, dst: Path) -> None:
53
+ """Write a HeuDiConv heuristic from ``df`` to ``dst``.
54
+
55
+ Parameters
56
+ ----------
57
+ df : pandas.DataFrame
58
+ Table generated by :mod:`dicom_inventory` describing the DICOM series.
59
+ dst : Path
60
+ Destination ``heuristic_<name>.py`` file.
61
+ """
62
+
63
+ print("Building heuristic (v10)…")
64
+ buf: list[str] = []
65
+
66
+ # 1 ─ header -----------------------------------------------------------
67
+ buf.append(
68
+ dedent(
69
+ '''\
70
+ """AUTO-GENERATED HeuDiConv heuristic (v10)."""
71
+ from typing import Tuple
72
+
73
+ def create_key(template: str,
74
+ outtype: Tuple[str, ...] = ("nii.gz",),
75
+ annotation_classes=None):
76
+ if not template:
77
+ raise ValueError("Template must be non-empty")
78
+ return template, outtype, annotation_classes
79
+ '''
80
+ )
81
+ )
82
+
83
+ # 2 ─ SID_MAP ----------------------------------------------------------
84
+ sid_pairs = {(clean(str(r.source_folder)) or clean(Path(r.source_folder or '.').name), r.BIDS_name) for r in df.itertuples()}
85
+ buf.append("\nSID_MAP = {\n")
86
+ for folder, bids in sorted(sid_pairs):
87
+ buf.append(f" '{folder}': '{bids}',\n")
88
+ buf.append("}\n\n")
89
+
90
+ # 3 ─ template keys ----------------------------------------------------
91
+ # Include series UID (or rep) in the key to handle repeated sequences
92
+ seq2key: dict[tuple[str, str, str, str, str], str] = {}
93
+ key_defs: list[tuple[str, str]] = []
94
+
95
+ rep_counts = (
96
+ df.groupby(["BIDS_name", "session", "sequence"], dropna=False)["sequence"].transform("count")
97
+ )
98
+ rep_index = (
99
+ df.groupby(["BIDS_name", "session", "sequence"], dropna=False).cumcount() + 1
100
+ )
101
+
102
+ key_def_set = set()
103
+ for idx, row in df.iterrows():
104
+ ses_raw = row.get("session", "")
105
+ ses = "" if pd.isna(ses_raw) else str(ses_raw).strip()
106
+ folder = Path(str(row.get("source_folder", "."))).name
107
+ rep_num = rep_index.loc[idx]
108
+ uid_field = str(row.get("series_uid", ""))
109
+ bids = row["BIDS_name"]
110
+ container = row.get("modality_bids", "misc") or "misc"
111
+ stem = safe_stem(row["sequence"])
112
+
113
+ base_parts = [bids, ses, stem]
114
+ if rep_counts.loc[idx] > 1:
115
+ base_parts.append(f"rep-{rep_num}")
116
+ base = dedup_parts(*base_parts)
117
+ path = "/".join(p for p in [bids, ses, container] if p)
118
+ template = f"{path}/{base}"
119
+
120
+ key_parts = [bids, ses, stem]
121
+ if rep_counts.loc[idx] > 1:
122
+ key_parts.append(f"rep-{rep_num}")
123
+ key_var = "key_" + clean("_".join(p for p in key_parts if p))
124
+ if key_var not in key_def_set:
125
+ key_defs.append((key_var, template))
126
+ key_def_set.add(key_var)
127
+
128
+ uid_list = [u for u in uid_field.split("|") if u] or [""]
129
+ for uid in uid_list:
130
+ key_id = (row["sequence"], row["BIDS_name"], ses, folder, uid)
131
+ if key_id in seq2key:
132
+ continue
133
+ seq2key[key_id] = key_var
134
+
135
+ for var, tpl in key_defs:
136
+ buf.append(f"{var} = create_key('{tpl}')\n")
137
+ buf.append("\n")
138
+
139
+ # 4 ─ infotodict() ----------------------------------------------------
140
+ buf.append("def infotodict(seqinfo):\n \"\"\"Return mapping SeriesDescription → key list.\"\"\"\n")
141
+ for var in seq2key.values():
142
+ buf.append(f" {var}_list = []\n")
143
+ buf.append(" info = {\n")
144
+ for var in seq2key.values():
145
+ buf.append(f" {var}: {var}_list,\n")
146
+ buf.append(" }\n\n")
147
+
148
+ buf.append(" for s in seqinfo:\n")
149
+ for (seq, _b, _s, folder, uid), var in seq2key.items():
150
+ seq_esc = seq.replace("'", "\\'")
151
+ fol_esc = folder.replace("'", "\\'")
152
+ uid_esc = str(uid).replace("'", "\\'")
153
+ buf.append(
154
+ f" if s.series_description == '{seq_esc}' and s.dcm_dir_name == '{fol_esc}' and getattr(s, 'series_uid', '') == '{uid_esc}':\n"
155
+ )
156
+ buf.append(f" {var}_list.append(s.series_id)\n")
157
+ buf.append(" return info\n")
158
+
159
+ dst.write_text("".join(buf), encoding="utf-8")
160
+ print("Heuristic written →", dst.resolve())
161
+
162
+
163
+ # -----------------------------------------------------------------------------
164
+ # Driver
165
+ # -----------------------------------------------------------------------------
166
+
167
+ def generate(tsv: Path, out_dir: Path) -> None:
168
+ """Generate heuristic files for each study described in ``tsv``.
169
+
170
+ Parameters
171
+ ----------
172
+ tsv : Path
173
+ Path to ``subject_summary.tsv`` produced by :mod:`dicom_inventory`.
174
+ out_dir : Path
175
+ Directory where the heuristic files will be written.
176
+ """
177
+
178
+ df = pd.read_csv(tsv, sep="\t", keep_default_na=False)
179
+
180
+ # Drop rows with unwanted modalities
181
+ mask = df.modality.isin(SKIP_BY_DEFAULT)
182
+ if mask.any():
183
+ df.loc[mask, "include"] = 0
184
+ print(f"Auto‑skipped {mask.sum()} rows ({', '.join(SKIP_BY_DEFAULT)})")
185
+
186
+ df = df[df.include == 1]
187
+
188
+ out_dir.mkdir(parents=True, exist_ok=True)
189
+
190
+ for study, sub_df in df.groupby("StudyDescription"):
191
+ fname = safe_stem(study or "unknown")
192
+ heur = out_dir / f"heuristic_{fname}.py"
193
+ write_heuristic(sub_df, heur)
194
+ folders = " ".join(sorted({clean(f) or clean(Path(f or '.').name) for f in sub_df.source_folder.unique()}))
195
+ print(dedent(f"""
196
+ heudiconv -d "<RAW_ROOT>/{{subject}}/**/*.*" -s {folders} -f {heur.name} -c dcm2niix -o <BIDS_OUT>/{fname} -b --minmeta --overwrite"""))
197
+
198
+
199
+ def main() -> None:
200
+ """Entry point for the ``build-heuristic`` command line utility."""
201
+
202
+ import argparse
203
+
204
+ parser = argparse.ArgumentParser(description="Generate HeuDiConv heuristic(s) from TSV")
205
+ parser.add_argument("tsv", help="Path to subject_summary.tsv file")
206
+ parser.add_argument("out_dir", help="Directory to write heuristic files")
207
+ args = parser.parse_args()
208
+
209
+ generate(Path(args.tsv), Path(args.out_dir))
210
+
211
+
212
+ if __name__ == "__main__":
213
+ main()
214
+