Skip to content

Commit b640c16

Browse files
committed
Merge remote-tracking branch 'nipy/master' into ref/modularize-multifile-renamers
# Conflicts: # heudiconv/convert.py
1 parent 3b29db8 commit b640c16

26 files changed

+807
-427
lines changed

CHANGELOG.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,74 @@ All notable changes to this project will be documented (for humans) in this file
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [0.8.1] - Date
8+
9+
TODO Summary
10+
11+
### Added
12+
### Changed
13+
### Deprecated
14+
### Fixed
15+
### Removed
16+
### Security
17+
18+
19+
## [0.8.0] - 2020-04-15
20+
21+
### Enhancements
22+
23+
- Centralized saving of .json files. Indentation of some files could
24+
change now from previous versions where it could have used `3`
25+
spaces. Now indentation should be consistently `2` for .json files
26+
we produce/modify ([#436][]) (note: dcm2niix uses tabs for indentation)
27+
- ReproIn heuristic: support SBRef and phase data ([#387][])
28+
- Set the "TaskName" field in .json sidecar files for multi-echo data
29+
([#420][])
30+
- Provide an informative exception if command needs heuristic to be
31+
specified ([#437][])
32+
33+
### Refactored
34+
35+
- `embed_nifti` was refactored into `embed_dicom_and_nifti_metadata`
36+
which would no longer create `.nii` file if it does not exist
37+
already ([#432][])
38+
39+
### Fixed
40+
41+
- Skip datalad-based tests if no datalad available ([#430][])
42+
- Search heuristic file path first so we do not pick up a python
43+
module if name conflicts ([#434][])
44+
45+
## [0.7.0] - 2020-03-20
46+
47+
### Removed
48+
49+
- Python 2 support/testing
50+
51+
### Enhancement
52+
53+
- `-g` option obtained two new modes: `all` and `custom`. In case of `all`,
54+
all provided DICOMs will be treated as coming from a single scanning session.
55+
`custom` instructs to use `.grouping` value (could be a DICOM attribute or
56+
a callable)provided by the heuristic ([#359][]).
57+
- Stop before reading pixels data while gathering metadata from DICOMs ([#404][])
58+
- reproin heuristic:
59+
- In addition to original "md5sum of the study_description" `protocols2fix`
60+
could now have (and applied after md5sum matching ones)
61+
1). a regular expression searched in study_description,
62+
2). an empty string as "catch all".
63+
This features could be used to easily provide remapping into reproin
64+
naming (documentation is to come to http://github.com/ReproNim/reproin)
65+
([#425][])
66+
67+
### Fixed
68+
69+
- Use nan, not None for absent echo value in sorting
70+
- reproin heuristic: case seqinfos into a list to be able to modify from
71+
overloaded heuristic ([#419][])
72+
- No spurious errors from the logger upon a warning about `etelemetry`
73+
absence ([#407][])
74+
775
## [0.6.0] - 2019-12-16
876

977
This is largely a bug fix. Metadata and order of `_key-value` fields in BIDS
@@ -271,6 +339,7 @@ TODO Summary
271339
[#348]: https://github.com/nipy/heudiconv/issues/348
272340
[#351]: https://github.com/nipy/heudiconv/issues/351
273341
[#352]: https://github.com/nipy/heudiconv/issues/352
342+
[#359]: https://github.com/nipy/heudiconv/issues/359
274343
[#360]: https://github.com/nipy/heudiconv/issues/360
275344
[#364]: https://github.com/nipy/heudiconv/issues/364
276345
[#369]: https://github.com/nipy/heudiconv/issues/369
@@ -279,4 +348,15 @@ TODO Summary
279348
[#376]: https://github.com/nipy/heudiconv/issues/376
280349
[#379]: https://github.com/nipy/heudiconv/issues/379
281350
[#380]: https://github.com/nipy/heudiconv/issues/380
351+
[#387]: https://github.com/nipy/heudiconv/issues/387
282352
[#390]: https://github.com/nipy/heudiconv/issues/390
353+
[#404]: https://github.com/nipy/heudiconv/issues/404
354+
[#407]: https://github.com/nipy/heudiconv/issues/407
355+
[#419]: https://github.com/nipy/heudiconv/issues/419
356+
[#420]: https://github.com/nipy/heudiconv/issues/420
357+
[#425]: https://github.com/nipy/heudiconv/issues/425
358+
[#430]: https://github.com/nipy/heudiconv/issues/430
359+
[#432]: https://github.com/nipy/heudiconv/issues/432
360+
[#434]: https://github.com/nipy/heudiconv/issues/434
361+
[#436]: https://github.com/nipy/heudiconv/issues/436
362+
[#437]: https://github.com/nipy/heudiconv/issues/437

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
all:
2+
echo 'nothing by default'
3+
4+
prep_release:
5+
# take previous one, and replace with the next one
6+
utils/prep_release

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
# The short X.Y version
2727
version = ''
2828
# The full version, including alpha/beta/rc tags
29-
release = '0.6.0'
29+
release = '0.8.0'
3030

3131

3232
# -- General configuration ---------------------------------------------------

docs/heuristics.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,20 @@ DICOMs where this function returns ``True`` will be filtered out.
6868
Further processing on ``seqinfos`` to deduce/customize subject, session, and locator.
6969

7070
A dictionary of {"locator": locator, "session": session, "subject": subject} is returned.
71+
72+
---------------------------------------------------------------
73+
``grouping`` string or ``grouping(files, dcmfilter, seqinfo)``
74+
---------------------------------------------------------------
75+
76+
Whenever ``--grouping custom`` (``-g custom``) is used, this attribute or callable
77+
will be used to inform how to group the DICOMs into separate groups. From
78+
`original PR#359 <https://github.com/nipy/heudiconv/pull/359>`_::
79+
80+
grouping = 'AcquisitionDate'
81+
82+
or::
83+
84+
def grouping(files, dcmfilter, seqinfo):
85+
seqinfos = collections.OrderedDict()
86+
...
87+
return seqinfos # ordered dict containing seqinfo objects: list of DICOMs

docs/installation.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ If `Docker <https://docs.docker.com/install/>`_ is available on your system, you
2626
can visit `our page on Docker Hub <https://hub.docker.com/r/nipy/heudiconv/tags>`_
2727
to view available releases. To pull the latest release, run::
2828

29-
$ docker pull nipy/heudiconv:0.6.0
29+
$ docker pull nipy/heudiconv:0.8.0
3030

3131

3232
Singularity
@@ -35,4 +35,4 @@ If `Singularity <https://www.sylabs.io/singularity/>`_ is available on your syst
3535
you can use it to pull and convert our Docker images! For example, to pull and
3636
build the latest release, you can run::
3737

38-
$ singularity pull docker://nipy/heudiconv:0.6.0
38+
$ singularity pull docker://nipy/heudiconv:0.8.0

docs/tutorials.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ other users' tutorials covering their experience with ``heudiconv``.
1313

1414
- `Sample Conversion: Coastal Coding 2019 <http://www.repronim.org/coco2019-training/presentations/heudiconv/#1>`_.
1515

16+
- `A joined DataLad and HeuDiConv tutorial for reproducible fMRI studies <http://www.repronim.org/coco2019-training/04-02-reproin/>`_.
17+
18+
- `The ReproIn conversion workflow overview <https://github.com/repronim/reproin#conversion>`_.
19+
20+
- `Slides <https://docs.google.com/presentation/d/14UNWQVY49c9Xc-7sj1FkoILXnt-wYjW404oqT-FtCW8/edit#slide=id.p>`_ and
21+
`recording <https://www.youtube.com/watch?v=j2SKX37-w4c&list=PLs3CA4ShM1DUX0nTMKfoB8Z6kdrZpByLa&index=5&t=0s>`_
22+
of a ReproNim Webinar on ``heudiconv``.
23+
1624
.. caution::
1725
Some of these tutorials may not be up to date with
1826
the latest releases of ``heudiconv``.

docs/usage.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ The second script processes a DICOM directory with ``heudiconv`` using the built
8282
DCMDIR=${DCMDIRS[${SLURM_ARRAY_TASK_ID}]}
8383
echo Submitted directory: ${DCMDIR}
8484
85-
IMG="/singularity-images/heudiconv-0.6.0-dev.sif"
85+
IMG="/singularity-images/heudiconv-0.8.0-dev.sif"
8686
CMD="singularity run -B ${DCMDIR}:/dicoms:ro -B ${OUTDIR}:/output -e ${IMG} --files /dicoms/ -o /output -f reproin -c dcm2niix -b notop --minmeta -l ."
8787
8888
printf "Command:\n${CMD}\n"
@@ -97,7 +97,7 @@ This script creates the top-level bids files (e.g.,
9797
set -eu
9898

9999
OUTDIR=${1}
100-
IMG="/singularity-images/heudiconv-0.6.0-dev.sif"
100+
IMG="/singularity-images/heudiconv-0.8.0-dev.sif"
101101
CMD="singularity run -B ${OUTDIR}:/output -e ${IMG} --files /output -f reproin --command populate-templates"
102102

103103
printf "Command:\n${CMD}\n"

heudiconv/bids.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def populate_aggregated_jsons(path):
171171
act = "Generating"
172172
lgr.debug("%s %s", act, task_file)
173173
fields.update(placeholders)
174-
save_json(task_file, fields, indent=2, sort_keys=True, pretty=True)
174+
save_json(task_file, fields, sort_keys=True, pretty=True)
175175

176176

177177
def tuneup_bids_json_files(json_files):
@@ -193,7 +193,7 @@ def tuneup_bids_json_files(json_files):
193193
# Let's hope no word 'Date' comes within a study name or smth like
194194
# that
195195
raise ValueError("There must be no dates in .json sidecar")
196-
save_json(jsonfile, json_, indent=2)
196+
save_json(jsonfile, json_)
197197

198198
# Load the beast
199199
seqtype = op.basename(op.dirname(jsonfile))
@@ -223,7 +223,7 @@ def tuneup_bids_json_files(json_files):
223223
was_readonly = is_readonly(json_phasediffname)
224224
if was_readonly:
225225
set_readonly(json_phasediffname, False)
226-
save_json(json_phasediffname, json_, indent=2)
226+
save_json(json_phasediffname, json_)
227227
if was_readonly:
228228
set_readonly(json_phasediffname)
229229

@@ -259,8 +259,7 @@ def add_participant_record(studydir, subject, age, sex):
259259
("Description", "(TODO: adjust - by default everyone is in "
260260
"control group)")])),
261261
]),
262-
sort_keys=False,
263-
indent=2)
262+
sort_keys=False)
264263
# Add a new participant
265264
with open(participants_tsv, 'a') as f:
266265
f.write(
@@ -373,8 +372,7 @@ def add_rows_to_scans_keys_file(fn, newrows):
373372
("LongName", "Random string"),
374373
("Description", "md5 hash of UIDs")])),
375374
]),
376-
sort_keys=False,
377-
indent=2)
375+
sort_keys=False)
378376

379377
header = ['filename', 'acq_time', 'operator', 'randstr']
380378
# prepare all the data rows

heudiconv/cli/run.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def process_extra_commands(outdir, args):
6262
for f in args.files:
6363
treat_infofile(f)
6464
elif args.command == 'ls':
65+
ensure_heuristic_arg(args)
6566
heuristic = load_heuristic(args.heuristic)
6667
heuristic_ls = getattr(heuristic, 'ls', None)
6768
for f in args.files:
@@ -78,6 +79,7 @@ def process_extra_commands(outdir, args):
7879
% (str(study_session), len(sequences), suf)
7980
)
8081
elif args.command == 'populate-templates':
82+
ensure_heuristic_arg(args)
8183
heuristic = load_heuristic(args.heuristic)
8284
for f in args.files:
8385
populate_bids_templates(f, getattr(heuristic, 'DEFAULT_FIELDS', {}))
@@ -88,16 +90,21 @@ def process_extra_commands(outdir, args):
8890
for name_desc in get_known_heuristics_with_descriptions().items():
8991
print("- %s: %s" % name_desc)
9092
elif args.command == 'heuristic-info':
91-
from ..utils import get_heuristic_description, get_known_heuristic_names
92-
if not args.heuristic:
93-
raise ValueError("Specify heuristic using -f. Known are: %s"
94-
% ', '.join(get_known_heuristic_names()))
93+
ensure_heuristic_arg(args)
94+
from ..utils import get_heuristic_description
9595
print(get_heuristic_description(args.heuristic, full=True))
9696
else:
9797
raise ValueError("Unknown command %s", args.command)
9898
return
9999

100100

101+
def ensure_heuristic_arg(args):
102+
from ..utils import get_known_heuristic_names
103+
if not args.heuristic:
104+
raise ValueError("Specify heuristic using -f. Known are: %s"
105+
% ', '.join(get_known_heuristic_names()))
106+
107+
101108
def main(argv=None):
102109
parser = get_parser()
103110
args = parser.parse_args(argv)
@@ -124,7 +131,6 @@ def main(argv=None):
124131

125132
if args.debug:
126133
setup_exceptionhook()
127-
128134
process_args(args)
129135

130136

@@ -154,8 +160,7 @@ def get_parser():
154160
'If not provided, DICOMS would first be "sorted" and '
155161
'subject IDs deduced by the heuristic')
156162
parser.add_argument('-c', '--converter',
157-
default='dcm2niix',
158-
choices=('dcm2niix', 'none'),
163+
choices=('dcm2niix', 'none'), default='dcm2niix',
159164
help='tool to use for DICOM conversion. Setting to '
160165
'"none" disables the actual conversion step -- useful'
161166
'for testing heuristics.')
@@ -219,7 +224,7 @@ def get_parser():
219224
help='custom actions to be performed on provided '
220225
'files instead of regular operation.')
221226
parser.add_argument('-g', '--grouping', default='studyUID',
222-
choices=('studyUID', 'accession_number'),
227+
choices=('studyUID', 'accession_number', 'all', 'custom'),
223228
help='How to group dicoms (default: by studyUID)')
224229
parser.add_argument('--minmeta', action='store_true',
225230
help='Exclude dcmstack meta information in sidecar '
@@ -343,7 +348,8 @@ def process_args(args):
343348
seqinfo=seqinfo,
344349
min_meta=args.minmeta,
345350
overwrite=args.overwrite,
346-
dcmconfig=args.dcmconfig,)
351+
dcmconfig=args.dcmconfig,
352+
grouping=args.grouping,)
347353

348354
lgr.info("PROCESSING DONE: {0}".format(
349355
str(dict(subject=sid, outdir=study_outdir, session=session))))

0 commit comments

Comments
 (0)