Skip to content

Commit 9ea19bd

Browse files
committed
Expose most of heudiconv CLI interface
There are a couple of options that I didn't add here since they didn't seem to fit well. Also there are a couple of additional ones for things like git user details when using datalad. There are a couple of known limitations, which I've created issues for in this repo: rh-impact#8 rh-impact#9
1 parent 1d24d3e commit 9ea19bd

File tree

5 files changed

+277
-23
lines changed

5 files changed

+277
-23
lines changed

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ LABEL org.opencontainers.image.authors="grdryn <[email protected]>" \
2727
org.opencontainers.image.description="A ChRIS plugin that..."
2828

2929
ADD https://github.com/rordenlab/dcm2niix/releases/download/v1.0.20220720/dcm2niix_lnx.zip /tmp/dcm2niix_lnx.zip
30-
RUN apt-get -y update && apt-get install -y unzip && unzip /tmp/dcm2niix_lnx.zip -d /usr/local/bin/ && apt-get remove -y unzip
30+
RUN apt-get -y update && \
31+
apt-get install -y unzip git git-annex && \
32+
unzip /tmp/dcm2niix_lnx.zip -d /usr/local/bin/
3133

3234
WORKDIR /usr/local/src
3335

pl_heudiconv/heudiconv.py

Lines changed: 269 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
#
1010

11+
import os
1112
import subprocess
1213

1314
from chrisapp.base import ChrisApp
@@ -26,15 +27,9 @@
2627

2728
Gstr_synopsis = """
2829
29-
(Edit this in-line help for app specifics. At a minimum, the
30-
flags below are supported -- in the case of DS apps, both
31-
positional arguments <inputDir> and <outputDir>; for FS and TS apps
32-
only <outputDir> -- and similarly for <in> <out> directories
33-
where necessary.)
34-
3530
NAME
3631
37-
heudiconv
32+
pl-heudiconv
3833
3934
SYNOPSIS
4035
@@ -53,9 +48,9 @@
5348
5449
* Bare bones execution
5550
56-
docker run --rm -u $(id -u) \
57-
-v $(pwd)/in:/incoming -v $(pwd)/out:/outgoing \
58-
rh-impact/pl-heudiconv pl-heudiconv \
51+
docker run --rm -u $(id -u) \\
52+
-v $(pwd)/in:/incoming -v $(pwd)/out:/outgoing \\
53+
quay.io/rh-impact/pl-heudiconv pl-heudiconv \\
5954
/incoming /outgoing
6055
6156
DESCRIPTION
@@ -89,7 +84,7 @@
8984

9085
class Heudiconv(ChrisApp):
9186
"""
92-
An app to ...
87+
An app to organize brain imaging data into structured directory layouts.
9388
"""
9489
PACKAGE = __package__
9590
TITLE = 'A ChRIS plugin for heudiconv'
@@ -122,24 +117,279 @@ def define_parameters(self):
122117
Use self.add_argument to specify a new app argument.
123118
"""
124119

120+
self.add_argument(
121+
'--inputdir-type',
122+
default='files',
123+
choices=('files', 'dicom_dir_template'),
124+
optional=True,
125+
dest='inputdir_type',
126+
type=str,
127+
help='''For input, heudiconv accepts EITHER a "--files"
128+
argument, or a "--dicom_dir_template" argument. This
129+
option specifies which to use for the positional
130+
"inputdir" argument to this plugin.''')
131+
132+
self.add_argument(
133+
'-f', '--heuristic',
134+
optional=True,
135+
default='reproin',
136+
dest='heuristic',
137+
type=str,
138+
help='''Name of a known heuristic or path to the Python
139+
script containing heuristic.''')
140+
141+
self.add_argument(
142+
'-b', '--bids',
143+
optional=True,
144+
default=False,
145+
dest='bids',
146+
type=bool,
147+
help='''Flag for output into BIDS structure. Can also take
148+
BIDS-specific options by using --bids-options, e.g.,
149+
--bids-options notop.''')
150+
151+
self.add_argument(
152+
'--bids-options',
153+
optional=True,
154+
nargs='+',
155+
default=[],
156+
choices=['notop'],
157+
metavar=('BIDSOPTION1', 'BIDSOPTION2'),
158+
dest='bids_options',
159+
type=str,
160+
help='''The only currently supported bids options is
161+
"notop", which skips creation of top-level BIDS
162+
files. This is useful when running in batch mode to
163+
prevent possible race conditions.''')
164+
165+
self.add_argument(
166+
'--overwrite',
167+
optional=True,
168+
default=False,
169+
dest='overwrite',
170+
type=bool,
171+
help='Overwrite existing converted files.')
172+
173+
self.add_argument(
174+
'--datalad',
175+
optional=True,
176+
default=False,
177+
dest='datalad',
178+
type=bool,
179+
help='''Store the entire collection as DataLad
180+
dataset(s). Small files will be committed directly to git,
181+
while large to annex. New version (6) of annex
182+
repositories will be used in a "thin" mode so it would
183+
look to mortals as just any other regular directory
184+
(i.e. no symlinks to under .git/annex). For now just for
185+
BIDS mode.''')
186+
187+
self.add_argument(
188+
'--minmeta',
189+
optional=True,
190+
default=False,
191+
dest='minmeta',
192+
type=bool,
193+
help='Exclude dcmstack meta information in sidecar jsons.')
194+
195+
self.add_argument(
196+
'--random-seed',
197+
optional=True,
198+
default=[],
199+
dest='random_seed',
200+
type=int,
201+
help='Random seed to initialize RNG.')
202+
203+
self.add_argument(
204+
'-l', '--locator',
205+
optional=True,
206+
default=[],
207+
dest='locator',
208+
type=str,
209+
help='''Study path under outdir. If provided, it overloads
210+
the value provided by the heuristic. If --datalad is
211+
enabled, every directory within locator becomes a
212+
super-dataset thus establishing a hierarchy. Setting to
213+
"unknown" will skip that dataset.''')
214+
215+
self.add_argument(
216+
'-ss', '--ses',
217+
optional=True,
218+
default=[],
219+
dest='session',
220+
type=str,
221+
help='''Session for longitudinal study_sessions. Default is
222+
None.''')
223+
224+
self.add_argument(
225+
'-p', '--with-prov',
226+
optional=True,
227+
default=False,
228+
dest='with_prov',
229+
type=bool,
230+
help='Store additional provenance information.')
231+
232+
self.add_argument(
233+
'--command',
234+
default=[],
235+
choices=(
236+
'heuristics', 'heuristic-info', 'ls', 'populate-templates',
237+
'sanitize-jsons', 'treat-jsons', 'populate-intended-for'),
238+
optional=True,
239+
dest='command',
240+
type=str,
241+
help='''Custom action to be performed on provided files
242+
instead of regular operation.''')
243+
244+
self.add_argument(
245+
'--anon-cmd',
246+
default=[],
247+
optional=True,
248+
dest='anon_cmd',
249+
type=str,
250+
help='Command to run to convert subject IDs used for DICOMs to '
251+
'anonymized IDs. Such command must take a single argument and '
252+
'return a single anonymized ID.')
253+
254+
self.add_argument(
255+
'-s', '--subjects',
256+
dest='subjects',
257+
optional=True,
258+
default=[],
259+
type=str,
260+
nargs='*',
261+
help='''List of subjects - required for dicom template. If
262+
not provided, DICOMS would first be "sorted" and subject
263+
IDs deduced by the heuristic.''')
264+
265+
self.add_argument(
266+
'-g', '--grouping',
267+
default=[],
268+
choices=('studyUID', 'accession_number', 'all', 'custom'),
269+
optional=True,
270+
dest='grouping',
271+
type=str,
272+
help='How to group dicoms (default: by studyUID)')
273+
274+
self.add_argument(
275+
'--dcmconfig',
276+
default=[],
277+
optional=True,
278+
dest='dcmconfig',
279+
type=str,
280+
help='JSON file for additional dcm2niix configuration.')
281+
282+
submission = self.add_argument_group('Conversion submission options')
283+
submission.add_argument(
284+
'-q', '--queue',
285+
choices=("SLURM", None),
286+
default=None,
287+
help='Batch system to submit jobs in parallel.')
288+
289+
submission.add_argument(
290+
'--queue-args',
291+
dest='queue_args',
292+
default=None,
293+
help='''Additional queue arguments passed as a single
294+
string of space-separated Argument=Value pairs.''')
295+
296+
gitopts = self.add_argument_group(
297+
'Git config arguments',
298+
'Used to set user details for git commits when using the datalad option.')
299+
300+
gitopts.add_argument(
301+
'--git-user-name',
302+
default='ChRIS HeuDiConv Plugin',
303+
dest='git_user_name',
304+
type=str,
305+
help='''User name to use for Git commits when --datalad is
306+
specified. It will be set as the value of the
307+
"GIT_AUTHOR_NAME" and "GIT_COMMITTER_NAME" environment
308+
variables. Defaults to "ChRIS HeuDiConv Plugin"''')
309+
310+
gitopts.add_argument(
311+
'--git-user-email',
312+
default='[email protected]',
313+
dest='git_user_email',
314+
type=str,
315+
help='''User email to use for Git commits when --datalad
316+
is specified. It will be set as the value of the
317+
"GIT_AUTHOR_EMAIL" and "GIT_COMMITTER_EMAIL" environment
318+
variables. Defaults to "[email protected]"''')
319+
125320
def run(self, options):
126321
"""
127322
Define the code to be run by this plugin app.
128323
"""
129324

130-
cmd = (
131-
'/usr/local/bin/heudiconv',
132-
'--files', options.inputdir,
133-
'-f', 'reproin',
134-
'-o', options.outputdir,
135-
'--bids'
136-
)
325+
cmd = [
326+
'heudiconv',
327+
'--' + options.inputdir_type, options.inputdir,
328+
'--outdir', options.outputdir,
329+
'--heuristic', options.heuristic
330+
]
331+
332+
if options.bids:
333+
cmd = cmd + ['--bids']
334+
if options.bids_options:
335+
print('bids_options: ')
336+
print(options.bids_options)
337+
cmd = cmd + options.bids_options
338+
339+
if options.overwrite:
340+
cmd = cmd + ['--overwrite']
341+
342+
if options.datalad:
343+
cmd = cmd + ['--datalad']
344+
345+
if options.minmeta:
346+
cmd = cmd + ['--minmeta']
347+
348+
if options.with_prov:
349+
cmd = cmd + ['--with-prov']
350+
351+
if options.random_seed:
352+
cmd = cmd + ['--random-seed', str(options.random_seed)]
353+
354+
if options.command:
355+
cmd = cmd + ['--command', options.command]
356+
357+
if options.grouping:
358+
cmd = cmd + ['--grouping', options.grouping]
359+
360+
if options.locator:
361+
cmd = cmd + ['--locator', options.locator]
362+
363+
if options.session:
364+
cmd = cmd + ['--ses', options.session]
365+
366+
if options.dcmconfig:
367+
cmd = cmd + ['--dcmconfig', options.dcmconfig]
368+
369+
if options.queue:
370+
cmd = cmd + ['--queue', options.queue]
371+
372+
if options.queue_args:
373+
cmd = cmd + ['--queue-args', options.queue_args]
374+
375+
if options.anon_cmd:
376+
cmd = cmd + ['--anon-cmd', options.anon_cmd]
377+
378+
if options.subjects:
379+
cmd = cmd + ['--subjects'] + options.subjects
380+
137381
print(Gstr_title)
138382
print('Version: %s' % self.get_version())
139383

384+
env = os.environ.copy()
385+
env["GIT_AUTHOR_NAME"] = options.git_user_name
386+
env["GIT_AUTHOR_EMAIL"] = options.git_user_email
387+
env["GIT_COMMITTER_NAME"] = options.git_user_name
388+
env["GIT_COMMITTER_EMAIL"] = options.git_user_email
389+
140390
print(f'Command: {" ".join(map(str, cmd))}')
141391

142-
subprocess.run(cmd, check=True)
392+
subprocess.run(cmd, check=True, env=env)
143393

144394
def show_man_page(self):
145395
"""

pl_heudiconv/tests/test_heudiconv.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def test_run(self):
1818
args = []
1919
args.append(self.inputdir)
2020
args.append(self.outputdir)
21+
args.append('--bids')
2122

2223
options = self.app.parse_args(args)
2324
self.assertEqual(options.inputdir, self.inputdir)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
chrisapp~=2.5.3
22
nose~=1.3.7
33
heudiconv~=0.11.3
4+
datalad~=0.17.5

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
setup(
88
name = 'pl-heudiconv',
99
version = '0.1',
10-
description = 'An app to ...',
10+
description = 'An app to organize brain imaging data into structured directory layouts',
1111
long_description = readme,
1212
author = 'grdryn',
1313
author_email = '[email protected]',
14-
url = 'http://wiki',
14+
url = 'https://github.com/rh-impact/pl-heudiconv',
1515
packages = ['pl_heudiconv'],
16-
install_requires = ['chrisapp', 'heudiconv'],
16+
install_requires = ['chrisapp', 'heudiconv', 'datalad'],
1717
test_suite = 'nose.collector',
1818
tests_require = ['nose'],
1919
license = 'MIT',

0 commit comments

Comments
 (0)