Skip to content

Commit 5ca882a

Browse files
committed
Merge branch 'bf-unique-json-fields' (early part) into rel-v0.5.2
* 'bf-unique-json-fields' (early part): RF: extracted functionality into a dedicated populate_aggregated_jsons Do regenerate top level task-* stab file, retain only our custom fields RF: removed unused imports
2 parents 6b914c9 + ead67ce commit 5ca882a

File tree

3 files changed

+61
-17
lines changed

3 files changed

+61
-17
lines changed

heudiconv/bids.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,37 @@ def populate_bids_templates(path, defaults={}):
6969
"TODO: Provide description for the dataset -- basic details about the "
7070
"study, possibly pointing to pre-registration (if public or embargoed)")
7171

72+
populate_aggregated_jsons(path)
73+
74+
75+
def populate_aggregated_jsons(path):
76+
"""Aggregate across the entire BIDS dataset .json's into top level .json's
77+
78+
Top level .json files would contain only the fields which are
79+
common to all subject[/session]/type/*_modality.json's.
80+
81+
ATM aggregating only for *_task*_bold.json files. Only the task- and
82+
OPTIONAL _acq- field is retained within the aggregated filename. The other
83+
BIDS _key-value pairs are "aggregated over".
84+
85+
Parameters
86+
----------
87+
path: str
88+
Path to the top of the BIDS dataset
89+
"""
7290
# TODO: collect all task- .json files for func files to
7391
tasks = {}
7492
# way too many -- let's just collect all which are the same!
7593
# FIELDS_TO_TRACK = {'RepetitionTime', 'FlipAngle', 'EchoTime',
7694
# 'Manufacturer', 'SliceTiming', ''}
7795
for fpath in find_files('.*_task-.*\_bold\.json', topdir=path,
78-
exclude_vcs=True, exclude="/\.(datalad|heudiconv)/"):
96+
exclude_vcs=True,
97+
exclude="/\.(datalad|heudiconv)/"):
98+
#
99+
# According to BIDS spec I think both _task AND _acq (may be more?
100+
# _rec, _dir, ...?) should be retained?
101+
# TODO: if we are to fix it, then old ones (without _acq) should be
102+
# removed first
79103
task = re.sub('.*_(task-[^_\.]*(_acq-[^_\.]*)?)_.*', r'\1', fpath)
80104
json_ = load_json(fpath)
81105
if task not in tasks:
@@ -94,18 +118,36 @@ def populate_bids_templates(path, defaults={}):
94118
if not op.lexists(events_file):
95119
lgr.debug("Generating %s", events_file)
96120
with open(events_file, 'w') as f:
97-
f.write("onset\tduration\ttrial_type\tresponse_time\tstim_file\tTODO -- fill in rows and add more tab-separated columns if desired")
121+
f.write(
122+
"onset\tduration\ttrial_type\tresponse_time\tstim_file"
123+
"\tTODO -- fill in rows and add more tab-separated "
124+
"columns if desired")
98125
# extract tasks files stubs
99126
for task_acq, fields in tasks.items():
100127
task_file = op.join(path, task_acq + '_bold.json')
101-
# do not touch any existing thing, it may be precious
102-
if not op.lexists(task_file):
103-
lgr.debug("Generating %s", task_file)
104-
fields["TaskName"] = ("TODO: full task name for %s" %
105-
task_acq.split('_')[0].split('-')[1])
106-
fields["CogAtlasID"] = "TODO"
107-
with open(task_file, 'w') as f:
108-
f.write(json_dumps_pretty(fields, indent=2, sort_keys=True))
128+
# Since we are pulling all unique fields we have to possibly
129+
# rewrite this file to guarantee consistency.
130+
# See https://github.com/nipy/heudiconv/issues/277 for a usecase/bug
131+
# when we didn't touch existing one.
132+
# But the fields we enter (TaskName and CogAtlasID) might need need
133+
# to be populated from the file if it already exists
134+
placeholders = {
135+
"TaskName": ("TODO: full task name for %s" %
136+
task_acq.split('_')[0].split('-')[1]),
137+
"CogAtlasID": "TODO",
138+
}
139+
if op.lexists(task_file):
140+
j = load_json(task_file)
141+
# Retain possibly modified placeholder fields
142+
for f in placeholders:
143+
if f in j:
144+
placeholders[f] = j[f]
145+
act = "Regenerating"
146+
else:
147+
act = "Generating"
148+
lgr.debug("%s %s", act, task_file)
149+
fields.update(placeholders)
150+
save_json(task_file, fields, indent=2, sort_keys=True, pretty=True)
109151

110152

111153
def tuneup_bids_json_files(json_files):

heudiconv/parser.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from glob import glob
55
import re
66

7-
from collections import defaultdict, OrderedDict
7+
from collections import defaultdict
88

99
import tarfile
1010
from tempfile import mkdtemp
@@ -13,10 +13,6 @@
1313
from .utils import (
1414
docstring_parameter,
1515
StudySessionInfo,
16-
load_json,
17-
save_json,
18-
create_file_if_missing,
19-
json_dumps_pretty,
2016
)
2117

2218
lgr = logging.getLogger(__name__)

heudiconv/utils.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def assure_no_file_exists(path):
181181
os.unlink(path)
182182

183183

184-
def save_json(filename, data, indent=4):
184+
def save_json(filename, data, indent=4, sort_keys=True, pretty=False):
185185
"""Save data to a json file
186186
187187
Parameters
@@ -190,11 +190,17 @@ def save_json(filename, data, indent=4):
190190
Filename to save data in.
191191
data : dict
192192
Dictionary to save in json file.
193+
indent : int, optional
194+
sort_keys : bool, optional
195+
pretty : bool, optional
193196
194197
"""
195198
assure_no_file_exists(filename)
196199
with open(filename, 'w') as fp:
197-
fp.write(_canonical_dumps(data, sort_keys=True, indent=indent))
200+
fp.write(
201+
(json_dumps_pretty if pretty else _canonical_dumps)(
202+
data, sort_keys=sort_keys, indent=indent)
203+
)
198204

199205

200206
def json_dumps_pretty(j, indent=2, sort_keys=True):

0 commit comments

Comments
 (0)