Skip to content

Commit

Permalink
Expose most of heudiconv CLI interface
Browse files Browse the repository at this point in the history
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:

#8
#9
  • Loading branch information
grdryn committed Sep 18, 2022
1 parent 1d24d3e commit 0c7b743
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 23 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ LABEL org.opencontainers.image.authors="grdryn <[email protected]>" \
org.opencontainers.image.description="A ChRIS plugin that..."

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

WORKDIR /usr/local/src

Expand Down
288 changes: 269 additions & 19 deletions pl_heudiconv/heudiconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# [email protected]
#

import os
import subprocess

from chrisapp.base import ChrisApp
Expand All @@ -26,15 +27,9 @@

Gstr_synopsis = """
(Edit this in-line help for app specifics. At a minimum, the
flags below are supported -- in the case of DS apps, both
positional arguments <inputDir> and <outputDir>; for FS and TS apps
only <outputDir> -- and similarly for <in> <out> directories
where necessary.)
NAME
heudiconv
pl-heudiconv
SYNOPSIS
Expand All @@ -53,9 +48,9 @@
* Bare bones execution
docker run --rm -u $(id -u) \
-v $(pwd)/in:/incoming -v $(pwd)/out:/outgoing \
rh-impact/pl-heudiconv pl-heudiconv \
docker run --rm -u $(id -u) \\
-v $(pwd)/in:/incoming -v $(pwd)/out:/outgoing \\
quay.io/rh-impact/pl-heudiconv pl-heudiconv \\
/incoming /outgoing
DESCRIPTION
Expand Down Expand Up @@ -89,7 +84,7 @@

class Heudiconv(ChrisApp):
"""
An app to ...
An app to organize brain imaging data into structured directory layouts.
"""
PACKAGE = __package__
TITLE = 'A ChRIS plugin for heudiconv'
Expand Down Expand Up @@ -122,24 +117,279 @@ def define_parameters(self):
Use self.add_argument to specify a new app argument.
"""

self.add_argument(
'--inputdir-type',
default='files',
choices=('files', 'dicom_dir_template'),
optional=True,
dest='inputdir_type',
type=str,
help='''For input, heudiconv accepts EITHER a "--files"
argument, or a "--dicom_dir_template" argument. This
option specifies which to use for the positional
"inputdir" argument to this plugin.''')

self.add_argument(
'-f', '--heuristic',
optional=True,
default='reproin',
dest='heuristic',
type=str,
help='''Name of a known heuristic or path to the Python
script containing heuristic.''')

self.add_argument(
'-b', '--bids',
optional=True,
default=False,
dest='bids',
type=bool,
help='''Flag for output into BIDS structure. Can also take
BIDS-specific options by using --bids-options, e.g.,
--bids-options notop.''')

self.add_argument(
'--bids-options',
optional=True,
nargs='+',
default=[],
choices=['notop'],
metavar=('BIDSOPTION1', 'BIDSOPTION2'),
dest='bids_options',
type=str,
help='''The only currently supported bids options is
"notop", which skips creation of top-level BIDS
files. This is useful when running in batch mode to
prevent possible race conditions.''')

self.add_argument(
'--overwrite',
optional=True,
default=False,
dest='overwrite',
type=bool,
help='Overwrite existing converted files.')

self.add_argument(
'--datalad',
optional=True,
default=False,
dest='datalad',
type=bool,
help='''Store the entire collection as DataLad
dataset(s). Small files will be committed directly to git,
while large to annex. New version (6) of annex
repositories will be used in a "thin" mode so it would
look to mortals as just any other regular directory
(i.e. no symlinks to under .git/annex). For now just for
BIDS mode.''')

self.add_argument(
'--minmeta',
optional=True,
default=False,
dest='minmeta',
type=bool,
help='Exclude dcmstack meta information in sidecar jsons.')

self.add_argument(
'--random-seed',
optional=True,
default=[],
dest='random_seed',
type=int,
help='Random seed to initialize RNG.')

self.add_argument(
'-l', '--locator',
optional=True,
default=[],
dest='locator',
type=str,
help='''Study path under outdir. If provided, it overloads
the value provided by the heuristic. If --datalad is
enabled, every directory within locator becomes a
super-dataset thus establishing a hierarchy. Setting to
"unknown" will skip that dataset.''')

self.add_argument(
'-ss', '--ses',
optional=True,
default=[],
dest='session',
type=str,
help='''Session for longitudinal study_sessions. Default is
None.''')

self.add_argument(
'-p', '--with-prov',
optional=True,
default=False,
dest='with_prov',
type=bool,
help='Store additional provenance information.')

self.add_argument(
'--command',
default=[],
choices=(
'heuristics', 'heuristic-info', 'ls', 'populate-templates',
'sanitize-jsons', 'treat-jsons', 'populate-intended-for'),
optional=True,
dest='command',
type=str,
help='''Custom action to be performed on provided files
instead of regular operation.''')

self.add_argument(
'--anon-cmd',
default=[],
optional=True,
dest='anon_cmd',
type=str,
help='Command to run to convert subject IDs used for DICOMs to '
'anonymized IDs. Such command must take a single argument and '
'return a single anonymized ID.')

self.add_argument(
'-s', '--subjects',
dest='subjects',
optional=True,
default=[],
type=str,
nargs='*',
help='''List of subjects - required for dicom template. If
not provided, DICOMS would first be "sorted" and subject
IDs deduced by the heuristic.''')

self.add_argument(
'-g', '--grouping',
default=[],
choices=('studyUID', 'accession_number', 'all', 'custom'),
optional=True,
dest='grouping',
type=str,
help='How to group dicoms (default: by studyUID)')

self.add_argument(
'--dcmconfig',
default=[],
optional=True,
dest='dcmconfig',
type=str,
help='JSON file for additional dcm2niix configuration.')

submission = self.add_argument_group('Conversion submission options')
submission.add_argument(
'-q', '--queue',
choices=("SLURM", None),
default=None,
help='Batch system to submit jobs in parallel.')

submission.add_argument(
'--queue-args',
dest='queue_args',
default=None,
help='''Additional queue arguments passed as a single
string of space-separated Argument=Value pairs.''')

gitopts = self.add_argument_group(
'Git config arguments',
'Used to set user details for git commits when using the datalad option.')

gitopts.add_argument(
'--git-user-name',
default='ChRIS HeuDiConv Plugin',
dest='git_user_name',
type=str,
help='''User name to use for Git commits when --datalad is
specified. It will be set as the value of the
"GIT_AUTHOR_NAME" and "GIT_COMMITTER_NAME" environment
variables. Defaults to "ChRIS HeuDiConv Plugin"''')

gitopts.add_argument(
'--git-user-email',
default='[email protected]',
dest='git_user_email',
type=str,
help='''User email to use for Git commits when --datalad
is specified. It will be set as the value of the
"GIT_AUTHOR_EMAIL" and "GIT_COMMITTER_EMAIL" environment
variables. Defaults to "[email protected]"''')

def run(self, options):
"""
Define the code to be run by this plugin app.
"""

cmd = (
'/usr/local/bin/heudiconv',
'--files', options.inputdir,
'-f', 'reproin',
'-o', options.outputdir,
'--bids'
)
cmd = [
'heudiconv',
'--' + options.inputdir_type, options.inputdir,
'--outdir', options.outputdir,
'--heuristic', options.heuristic
]

if options.bids:
cmd = cmd + ['--bids']
if options.bids_options:
print('bids_options: ')
print(options.bids_options)
cmd = cmd + options.bids_options

if options.overwrite:
cmd = cmd + ['--overwrite']

if options.datalad:
cmd = cmd + ['--datalad']

if options.minmeta:
cmd = cmd + ['--minmeta']

if options.with_prov:
cmd = cmd + ['--with-prov']

if options.random_seed:
cmd = cmd + ['--random-seed', str(options.random_seed)]

if options.command:
cmd = cmd + ['--command', options.command]

if options.grouping:
cmd = cmd + ['--grouping', options.grouping]

if options.locator:
cmd = cmd + ['--locator', options.locator]

if options.session:
cmd = cmd + ['--ses', options.session]

if options.dcmconfig:
cmd = cmd + ['--dcmconfig', options.dcmconfig]

if options.queue:
cmd = cmd + ['--queue', options.queue]

if options.queue_args:
cmd = cmd + ['--queue-args', options.queue_args]

if options.anon_cmd:
cmd = cmd + ['--anon-cmd', options.anon_cmd]

if options.subjects:
cmd = cmd + ['--subjects'] + options.subjects

print(Gstr_title)
print('Version: %s' % self.get_version())

env = os.environ.copy()
env["GIT_AUTHOR_NAME"] = options.git_user_name
env["GIT_AUTHOR_EMAIL"] = options.git_user_email
env["GIT_COMMITTER_NAME"] = options.git_user_name
env["GIT_COMMITTER_EMAIL"] = options.git_user_email

print(f'Command: {" ".join(map(str, cmd))}')

subprocess.run(cmd, check=True)
subprocess.run(cmd, check=True, env=env)

def show_man_page(self):
"""
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
chrisapp~=2.5.3
nose~=1.3.7
heudiconv~=0.11.3
datalad~=0.17.5
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
setup(
name = 'pl-heudiconv',
version = '0.1',
description = 'An app to ...',
description = 'An app to organize brain imaging data into structured directory layouts',
long_description = readme,
author = 'grdryn',
author_email = '[email protected]',
url = 'http://wiki',
url = 'https://github.com/rh-impact/pl-heudiconv',
packages = ['pl_heudiconv'],
install_requires = ['chrisapp', 'heudiconv'],
install_requires = ['chrisapp', 'heudiconv', 'datalad'],
test_suite = 'nose.collector',
tests_require = ['nose'],
license = 'MIT',
Expand Down

0 comments on commit 0c7b743

Please sign in to comment.