|
8 | 8 |
|
9 | 9 | #
|
10 | 10 |
|
| 11 | +import os |
11 | 12 | import subprocess
|
12 | 13 |
|
13 | 14 | from chrisapp.base import ChrisApp
|
|
26 | 27 |
|
27 | 28 | Gstr_synopsis = """
|
28 | 29 |
|
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 |
| -
|
35 | 30 | NAME
|
36 | 31 |
|
37 |
| - heudiconv |
| 32 | + pl-heudiconv |
38 | 33 |
|
39 | 34 | SYNOPSIS
|
40 | 35 |
|
|
53 | 48 |
|
54 | 49 | * Bare bones execution
|
55 | 50 |
|
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 \\ |
59 | 54 | /incoming /outgoing
|
60 | 55 |
|
61 | 56 | DESCRIPTION
|
|
89 | 84 |
|
90 | 85 | class Heudiconv(ChrisApp):
|
91 | 86 | """
|
92 |
| - An app to ... |
| 87 | + An app to organize brain imaging data into structured directory layouts. |
93 | 88 | """
|
94 | 89 | PACKAGE = __package__
|
95 | 90 | TITLE = 'A ChRIS plugin for heudiconv'
|
@@ -122,24 +117,279 @@ def define_parameters(self):
|
122 | 117 | Use self.add_argument to specify a new app argument.
|
123 | 118 | """
|
124 | 119 |
|
| 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 | + |
| 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 | + |
125 | 320 | def run(self, options):
|
126 | 321 | """
|
127 | 322 | Define the code to be run by this plugin app.
|
128 | 323 | """
|
129 | 324 |
|
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 | + |
137 | 381 | print(Gstr_title)
|
138 | 382 | print('Version: %s' % self.get_version())
|
139 | 383 |
|
| 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 | + |
140 | 390 | print(f'Command: {" ".join(map(str, cmd))}')
|
141 | 391 |
|
142 |
| - subprocess.run(cmd, check=True) |
| 392 | + subprocess.run(cmd, check=True, env=env) |
143 | 393 |
|
144 | 394 | def show_man_page(self):
|
145 | 395 | """
|
|
0 commit comments