Skip to content

Commit 8eeaea6

Browse files
authored
Add a CLI for creating raw bids mri data (#9)
* passing tests * ruff * add cli * add yamls
1 parent 034a3d5 commit 8eeaea6

13 files changed

+131
-444
lines changed

pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dependencies = [
2424
"pandas",
2525
"pybids >= 0.15.6",
2626
"typer",
27-
"yaml",
27+
"pyyaml",
2828
]
2929
dynamic = ["version"]
3030

@@ -63,6 +63,7 @@ Source = "https://github.com/nipreps/simbids"
6363

6464
[project.scripts]
6565
simbids = "simbids.cli.run:main"
66+
simbids-raw-mri = "simbids.cli.raw_mri:main"
6667

6768
[tool.hatch.metadata]
6869
allow-direct-references = true
@@ -83,6 +84,11 @@ exclude = [
8384
"tests/data", # Large test data directory
8485
]
8586

87+
[tool.hatch.build]
88+
artifacts = [
89+
"src/simbids/data/bids_mri/*.yaml",
90+
]
91+
8692
[tool.hatch.envs.default]
8793
dependencies = [
8894
"pytest",

src/simbids/cli/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424

2525
from . import (
2626
parser,
27+
raw_mri,
2728
run,
2829
version,
2930
workflow,
3031
)
3132

3233
__all__ = [
3334
'parser',
35+
'raw_mri',
3436
'run',
3537
'version',
3638
'workflow',

src/simbids/cli/parser.py

Lines changed: 5 additions & 239 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
#
2323
"""Parser."""
2424

25-
import sys
26-
2725
from simbids import config
2826

2927

@@ -39,8 +37,6 @@ def _build_parser(**kwargs):
3937

4038
from packaging.version import Version
4139

42-
from simbids.cli.version import check_latest, is_flagged
43-
4440
# from niworkflows.utils.spaces import OutputReferencesAction
4541

4642
class ToDict(Action):
@@ -114,14 +110,12 @@ def _bids_filter(value, parser):
114110
is_release = not any((currentv.is_devrelease, currentv.is_prerelease, currentv.is_postrelease))
115111

116112
parser = ArgumentParser(
117-
description=(
118-
f'SimBIDS: fMRI POSTprocessing template workflow v{config.environment.version}'
119-
),
113+
description=(f'SimBIDS: Simulated BIDS data and workflows v{config.environment.version}'),
120114
formatter_class=ArgumentDefaultsHelpFormatter,
121115
**kwargs,
122116
)
123117
PathExists = partial(_path_exists, parser=parser)
124-
IsFile = partial(_is_file, parser=parser)
118+
# IsFile = partial(_is_file, parser=parser)
125119
PositiveInt = partial(_min_one, parser=parser)
126120
BIDSFilter = partial(_bids_filter, parser=parser)
127121

@@ -171,12 +165,6 @@ def _bids_filter(value, parser):
171165
'identifier (the sub- prefix can be removed)'
172166
),
173167
)
174-
g_bids.add_argument(
175-
'-t',
176-
'--task-id',
177-
action='store',
178-
help='Select a specific task to be processed',
179-
)
180168
g_bids.add_argument(
181169
'--bids-filter-file',
182170
dest='bids_filters',
@@ -203,16 +191,6 @@ def _bids_filter(value, parser):
203191
'(e.g., `--derivatives smriprep=/path/to/smriprep`).'
204192
),
205193
)
206-
g_bids.add_argument(
207-
'--bids-database-dir',
208-
metavar='PATH',
209-
type=Path,
210-
help=(
211-
'Path to a PyBIDS database folder, for faster indexing '
212-
'(especially useful for large datasets). '
213-
'Will be created if not present.'
214-
),
215-
)
216194

217195
g_perfm = parser.add_argument_group('Options to handle performance')
218196
g_perfm.add_argument(
@@ -241,122 +219,12 @@ def _bids_filter(value, parser):
241219
metavar='MEMORY_MB',
242220
help='Upper bound memory limit for SimBIDS processes',
243221
)
244-
g_perfm.add_argument(
245-
'--low-mem',
246-
action='store_true',
247-
help='Attempt to reduce memory usage (will increase disk usage in working directory)',
248-
)
249-
g_perfm.add_argument(
250-
'--use-plugin',
251-
'--nipype-plugin-file',
252-
action='store',
253-
metavar='FILE',
254-
type=IsFile,
255-
help='Nipype plugin configuration file',
256-
)
257-
g_perfm.add_argument(
258-
'--sloppy',
259-
action='store_true',
260-
default=False,
261-
help='Use low-quality tools for speed - TESTING ONLY',
262-
)
263-
264-
g_subset = parser.add_argument_group('Options for performing only a subset of the workflow')
265-
g_subset.add_argument(
266-
'--boilerplate-only',
267-
'--boilerplate_only',
268-
action='store_true',
269-
default=False,
270-
help='Generate boilerplate only',
271-
)
272-
g_subset.add_argument(
273-
'--reports-only',
274-
action='store_true',
275-
default=False,
276-
help=(
277-
"Only generate reports, don't run workflows. "
278-
'This will only rerun report aggregation, not reportlet generation for specific '
279-
'nodes.'
280-
),
281-
)
282-
283-
g_conf = parser.add_argument_group('Workflow configuration')
284-
g_conf.add_argument(
285-
'--ignore',
286-
required=False,
287-
action='store',
288-
nargs='+',
289-
default=['fieldmaps'],
290-
choices=['fieldmaps', 'slicetiming', 'fmap-jacobian'],
291-
help=(
292-
'Ignore selected aspects of the input dataset to disable corresponding '
293-
'parts of the resampling workflow (a space delimited list)'
294-
),
295-
)
296-
# Disable output spaces until warping works
297-
# g_conf.add_argument(
298-
# '--output-spaces',
299-
# nargs='*',
300-
# action=OutputReferencesAction,
301-
# help="""\
302-
# Standard and non-standard spaces to resample denoised functional images to. \
303-
# Standard spaces may be specified by the form \
304-
# ``<SPACE>[:cohort-<label>][:res-<resolution>][...]``, where ``<SPACE>`` is \
305-
# a keyword designating a spatial reference, and may be followed by optional, \
306-
# colon-separated parameters. \
307-
# Non-standard spaces imply specific orientations and sampling grids. \
308-
# For further details, please check out \
309-
# https://fmriprep.readthedocs.io/en/%s/spaces.html"""
310-
# % (currentv.base_version if is_release else 'latest'),
311-
# )
312-
g_conf.add_argument(
313-
'--dummy-scans',
314-
required=False,
315-
action='store',
316-
default=None,
317-
type=int,
318-
help='Number of nonsteady-state volumes. Overrides automatic detection.',
319-
)
320-
g_conf.add_argument(
321-
'--random-seed',
322-
dest='_random_seed',
323-
action='store',
324-
type=int,
325-
default=None,
326-
help='Initialize the random seed for the workflow',
327-
)
328222

329223
g_outputs = parser.add_argument_group('Options for modulating outputs')
330224
g_outputs.add_argument(
331-
'--md-only-boilerplate',
332-
action='store_true',
333-
default=False,
334-
help='Skip generation of HTML and LaTeX formatted citation with pandoc',
335-
)
336-
g_outputs.add_argument(
337-
'--aggregate-session-reports',
338-
dest='aggr_ses_reports',
339-
action='store',
340-
type=PositiveInt,
341-
default=4,
342-
help=(
343-
"Maximum number of sessions aggregated in one subject's visual report. "
344-
'If exceeded, visual reports are split by session.'
345-
),
346-
)
347-
348-
g_carbon = parser.add_argument_group('Options for carbon usage tracking')
349-
g_carbon.add_argument(
350-
'--track-carbon',
351-
action='store_true',
352-
help='Tracks power draws using CodeCarbon package',
353-
)
354-
g_carbon.add_argument(
355-
'--country-code',
356-
action='store',
357-
default='CAN',
358-
type=str,
359-
help='Country ISO code used by carbon trackers',
225+
'--processing-level',
226+
choices=['participant', 'session'],
227+
help='Processing level to be run, only "participant" or "session" in the case of SimBIDS',
360228
)
361229

362230
g_other = parser.add_argument_group('Other options')
@@ -369,55 +237,19 @@ def _bids_filter(value, parser):
369237
default=0,
370238
help='Increases log verbosity for each occurrence, debug level is -vvv',
371239
)
372-
g_other.add_argument(
373-
'-w',
374-
'--work-dir',
375-
action='store',
376-
type=Path,
377-
default=Path('work').absolute(),
378-
help='Path where intermediate results should be stored',
379-
)
380-
g_other.add_argument(
381-
'--clean-workdir',
382-
action='store_true',
383-
default=False,
384-
help='Clears working directory of contents. Use of this flag is not '
385-
'recommended when running concurrent processes of SimBIDS.',
386-
)
387-
g_other.add_argument(
388-
'--resource-monitor',
389-
action='store_true',
390-
default=False,
391-
help="Enable Nipype's resource monitoring to keep track of memory and CPU usage",
392-
)
393240
g_other.add_argument(
394241
'--config-file',
395242
action='store',
396243
metavar='FILE',
397244
help='Use pre-generated configuration file. Values in file will be overridden '
398245
'by command-line arguments.',
399246
)
400-
g_other.add_argument(
401-
'--write-graph',
402-
action='store_true',
403-
default=False,
404-
help='Write workflow graph.',
405-
)
406247
g_other.add_argument(
407248
'--stop-on-first-crash',
408249
action='store_true',
409250
default=False,
410251
help='Force stopping on first crash, even if a work directory was specified.',
411252
)
412-
g_other.add_argument(
413-
'--notrack',
414-
action='store_true',
415-
default=False,
416-
help='Opt out of sending tracking information of this run to '
417-
'the NiPreps developers. This information helps to '
418-
'improve NiPreps and provides an indicator of real '
419-
'world usage crucial for obtaining funding.',
420-
)
421253
g_other.add_argument(
422254
'--debug',
423255
action='store',
@@ -426,29 +258,6 @@ def _bids_filter(value, parser):
426258
help="Debug mode(s) to enable. 'all' is alias for all available modes.",
427259
)
428260

429-
latest = check_latest()
430-
if latest is not None and currentv < latest:
431-
print(
432-
f"""\
433-
You are using SimBIDS-{currentv},
434-
and a newer version of SimBIDS is available: {latest}.
435-
Please check out our documentation about how and when to upgrade:
436-
https://fmriprep.readthedocs.io/en/latest/faq.html#upgrading""",
437-
file=sys.stderr,
438-
)
439-
440-
_blist = is_flagged()
441-
if _blist[0]:
442-
_reason = _blist[1] or 'unknown'
443-
print(
444-
f"""\
445-
WARNING: Version {config.environment.version} of SimBIDS (current) has been FLAGGED
446-
(reason: {_reason}).
447-
That means some severe flaw was found in it and we strongly
448-
discourage its usage.""",
449-
file=sys.stderr,
450-
)
451-
452261
return parser
453262

454263

@@ -468,58 +277,15 @@ def parse_args(args=None, namespace=None):
468277
config.execution.log_level = int(max(25 - 5 * opts.verbose_count, logging.DEBUG))
469278
config.from_dict(vars(opts), init=['nipype'])
470279

471-
if not config.execution.notrack:
472-
import importlib.util
473-
474-
if importlib.util.find_spec('sentry_sdk') is None:
475-
config.execution.notrack = True
476-
config.loggers.cli.warning('Telemetry disabled because sentry_sdk is not installed.')
477-
else:
478-
config.loggers.cli.info(
479-
'Telemetry system to collect crashes and errors is enabled '
480-
'- thanks for your feedback!. Use option ``--notrack`` to opt out.'
481-
)
482-
483280
# Retrieve logging level
484281
build_log = config.loggers.cli
485282

486-
# Load base plugin_settings from file if --use-plugin
487-
if opts.use_plugin is not None:
488-
import yaml
489-
490-
with open(opts.use_plugin) as f:
491-
plugin_settings = yaml.safe_load(f)
492-
_plugin = plugin_settings.get('plugin')
493-
if _plugin:
494-
config.nipype.plugin = _plugin
495-
config.nipype.plugin_args = plugin_settings.get('plugin_args', {})
496-
config.nipype.nprocs = opts.nprocs or config.nipype.plugin_args.get(
497-
'n_procs', config.nipype.nprocs
498-
)
499-
500-
# Resource management options
501-
# Note that we're making strong assumptions about valid plugin args
502-
# This may need to be revisited if people try to use batch plugins
503-
if 1 < config.nipype.nprocs < config.nipype.omp_nthreads:
504-
build_log.warning(
505-
f'Per-process threads (--omp-nthreads={config.nipype.omp_nthreads}) exceed '
506-
f'total threads (--nthreads/--n_cpus={config.nipype.nprocs})'
507-
)
508-
509283
bids_dir = config.execution.bids_dir
510284
output_dir = config.execution.output_dir
511285
derivatives = config.execution.derivatives
512286
work_dir = config.execution.work_dir
513287
version = config.environment.version
514288

515-
# Wipe out existing work_dir
516-
if opts.clean_workdir and work_dir.exists():
517-
from niworkflows.utils.misc import clean_directory
518-
519-
build_log.info(f'Clearing previous SimBIDS working directory: {work_dir}')
520-
if not clean_directory(work_dir):
521-
build_log.warning(f'Could not clear all contents of working directory: {work_dir}')
522-
523289
# Update the config with an empty dict to trigger initialization of all config
524290
# sections (we used `init=False` above).
525291
# This must be done after cleaning the work directory, or we could delete an

0 commit comments

Comments
 (0)