Skip to content

Commit 5d3d096

Browse files
committed
minor changes to get pipeline running
1 parent 75d86f2 commit 5d3d096

File tree

5 files changed

+231
-39
lines changed

5 files changed

+231
-39
lines changed

dmriprep/interfaces/reports.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,20 @@
55
import os
66
import time
77

8-
from collections import Counter
8+
# from collections import Counter
99
from nipype.interfaces.base import (
1010
traits, TraitedSpec, BaseInterfaceInputSpec,
1111
File, Directory, InputMultiObject, Str, isdefined,
1212
SimpleInterface)
1313
from nipype.interfaces import freesurfer as fs
14-
from niworkflows.utils.bids import BIDS_NAME
14+
# from niworkflows.utils.bids import BIDS_NAME
1515

1616

1717
SUBJECT_TEMPLATE = """\
1818
\t<ul class="elem-desc">
1919
\t\t<li>Subject ID: {subject_id}</li>
2020
\t\t<li>Structural images: {n_t1s:d} T1-weighted {t2w}</li>
2121
\t\t<li>Diffusion Weighted Images: {n_dwi:d}</li>
22-
{tasks}
2322
\t\t<li>Standard output spaces: {std_spaces}</li>
2423
\t\t<li>Non-standard output spaces: {nstd_spaces}</li>
2524
\t\t<li>FreeSurfer reconstruction: {freesurfer_status}</li>
@@ -109,24 +108,24 @@ def _generate_segment(self):
109108
dwi_files = self.inputs.dwi if isdefined(self.inputs.dwi) else []
110109
dwi_files = [s[0] if isinstance(s, list) else s for s in dwi_files]
111110

112-
counts = Counter(BIDS_NAME.search(series).groupdict()['task_id'][5:]
113-
for series in dwi_files)
111+
# counts = Counter(BIDS_NAME.search(series).groupdict()['task_id'][5:]
112+
# for series in dwi_files)
114113

115-
tasks = ''
116-
if counts:
117-
header = '\t\t<ul class="elem-desc">'
118-
footer = '\t\t</ul>'
119-
lines = ['\t\t\t<li>Task: {task_id} ({n_runs:d} run{s})</li>'.format(
120-
task_id=task_id, n_runs=n_runs, s='' if n_runs == 1 else 's')
121-
for task_id, n_runs in sorted(counts.items())]
122-
tasks = '\n'.join([header] + lines + [footer])
114+
# tasks = ''
115+
# if counts:
116+
# header = '\t\t<ul class="elem-desc">'
117+
# footer = '\t\t</ul>'
118+
# lines = ['\t\t\t<li>Task: {task_id} ({n_runs:d} run{s})</li>'.format(
119+
# task_id=task_id, n_runs=n_runs, s='' if n_runs == 1 else 's')
120+
# for task_id, n_runs in sorted(counts.items())]
121+
# tasks = '\n'.join([header] + lines + [footer])
123122

124123
return SUBJECT_TEMPLATE.format(
125124
subject_id=self.inputs.subject_id,
126125
n_t1s=len(self.inputs.t1w),
127126
t2w=t2w_seg,
128127
n_dwi=len(dwi_files),
129-
tasks=tasks,
128+
# tasks=tasks,
130129
std_spaces=', '.join(self.inputs.std_spaces),
131130
nstd_spaces=', '.join(self.inputs.nstd_spaces),
132131
freesurfer_status=freesurfer_status)

dmriprep/utils/bids.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
from bids import BIDSLayout
99

1010

11-
def collect_data(bids_dir, participant_label, task=None, echo=None,
12-
bids_validate=True):
11+
def collect_data(bids_dir, participant_label, bids_validate=True):
1312
"""Replacement for niworkflows' version."""
1413
if isinstance(bids_dir, BIDSLayout):
1514
layout = bids_dir
@@ -19,20 +18,12 @@ def collect_data(bids_dir, participant_label, task=None, echo=None,
1918
queries = {
2019
'fmap': {'datatype': 'fmap'},
2120
'dwi': {'datatype': 'dwi', 'suffix': 'dwi'},
22-
'bold': {'datatype': 'func', 'suffix': 'bold'},
23-
'sbref': {'datatype': 'func', 'suffix': 'sbref'},
2421
'flair': {'datatype': 'anat', 'suffix': 'FLAIR'},
2522
't2w': {'datatype': 'anat', 'suffix': 'T2w'},
2623
't1w': {'datatype': 'anat', 'suffix': 'T1w'},
2724
'roi': {'datatype': 'anat', 'suffix': 'roi'},
2825
}
2926

30-
if task:
31-
queries['bold']['task'] = task
32-
33-
if echo:
34-
queries['bold']['echo'] = echo
35-
3627
subj_data = {
3728
dtype: sorted(layout.get(return_type='file', subject=participant_label,
3829
extension=['nii', 'nii.gz'], **query))
@@ -98,25 +89,20 @@ def validate_input_dir(exec_env, bids_dir, participant_label):
9889
"TSV_EQUAL_ROWS",
9990
"TSV_EMPTY_CELL",
10091
"TSV_IMPROPER_NA",
101-
"VOLUME_COUNT_MISMATCH",
102-
"BVAL_MULTIPLE_ROWS",
103-
"BVEC_NUMBER_ROWS",
104-
"DWI_MISSING_BVAL",
10592
"INCONSISTENT_SUBJECTS",
10693
"INCONSISTENT_PARAMETERS",
107-
"BVEC_ROW_LENGTH",
108-
"B_FILE",
10994
"PARTICIPANT_ID_COLUMN",
11095
"PARTICIPANT_ID_MISMATCH",
11196
"TASK_NAME_MUST_DEFINE",
11297
"PHENOTYPE_SUBJECTS_MISSING",
11398
"STIMULUS_FILE_MISSING",
114-
"DWI_MISSING_BVEC",
99+
"BOLD_NOT_4D",
115100
"EVENTS_TSV_MISSING",
116101
"TSV_IMPROPER_NA",
117102
"ACQTIME_FMT",
118103
"Participants age 89 or higher",
119104
"DATASET_DESCRIPTION_JSON_MISSING",
105+
"TASK_NAME_CONTAIN_ILLEGAL_CHARACTER",
120106
"FILENAME_COLUMN",
121107
"WRONG_NEW_LINE",
122108
"MISSING_TSV_COLUMN_CHANNELS",
@@ -131,8 +117,6 @@ def validate_input_dir(exec_env, bids_dir, participant_label):
131117
"ACQTIME_FMT",
132118
"SUSPICIOUSLY_LONG_EVENT_DESIGN",
133119
"SUSPICIOUSLY_SHORT_EVENT_DESIGN",
134-
"MALFORMED_BVEC",
135-
"MALFORMED_BVAL",
136120
"MISSING_TSV_COLUMN_EEG_ELECTRODES",
137121
"MISSING_SESSION"
138122
],

dmriprep/utils/sentry.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
"""Stripped out routines for Sentry"""
4+
import os
5+
from pathlib import Path
6+
import re
7+
from niworkflows.utils.misc import read_crashfile
8+
import sentry_sdk
9+
10+
CHUNK_SIZE = 16384
11+
# Group common events with pre specified fingerprints
12+
KNOWN_ERRORS = {
13+
'permission-denied': [
14+
"PermissionError: [Errno 13] Permission denied"
15+
],
16+
'memory-error': [
17+
"MemoryError",
18+
"Cannot allocate memory",
19+
"Return code: 134",
20+
],
21+
'reconall-already-running': [
22+
"ERROR: it appears that recon-all is already running"
23+
],
24+
'no-disk-space': [
25+
"[Errno 28] No space left on device",
26+
"[Errno 122] Disk quota exceeded"
27+
],
28+
'segfault': [
29+
"Segmentation Fault",
30+
"Segfault",
31+
"Return code: 139",
32+
],
33+
'potential-race-condition': [
34+
"[Errno 39] Directory not empty",
35+
"_unfinished.json",
36+
],
37+
'keyboard-interrupt': [
38+
"KeyboardInterrupt",
39+
],
40+
}
41+
42+
43+
def start_ping(run_uuid, npart):
44+
with sentry_sdk.configure_scope() as scope:
45+
if run_uuid:
46+
scope.set_tag('run_uuid', run_uuid)
47+
scope.set_tag('npart', npart)
48+
sentry_sdk.add_breadcrumb(message='dMRIPrep started', level='info')
49+
sentry_sdk.capture_message('dMRIPrep started', level='info')
50+
51+
52+
def sentry_setup(opts, exec_env):
53+
from os import cpu_count
54+
import psutil
55+
import hashlib
56+
from ..__about__ import __version__
57+
58+
environment = "prod"
59+
release = __version__
60+
if not __version__:
61+
environment = "dev"
62+
release = "dev"
63+
elif int(os.getenv('DMRIPREP_DEV', '0')) or ('+' in __version__):
64+
environment = "dev"
65+
66+
sentry_sdk.init("https://[email protected]/1137693",
67+
release=release,
68+
environment=environment,
69+
before_send=before_send)
70+
with sentry_sdk.configure_scope() as scope:
71+
scope.set_tag('exec_env', exec_env)
72+
73+
if exec_env == 'dmriprep-docker':
74+
scope.set_tag('docker_version', os.getenv('DOCKER_VERSION_8395080871'))
75+
76+
dset_desc_path = opts.bids_dir / 'dataset_description.json'
77+
if dset_desc_path.exists():
78+
desc_content = dset_desc_path.read_bytes()
79+
scope.set_tag('dset_desc_sha256', hashlib.sha256(desc_content).hexdigest())
80+
81+
free_mem_at_start = round(psutil.virtual_memory().free / 1024**3, 1)
82+
scope.set_tag('free_mem_at_start', free_mem_at_start)
83+
scope.set_tag('cpu_count', cpu_count())
84+
85+
# Memory policy may have a large effect on types of errors experienced
86+
overcommit_memory = Path('/proc/sys/vm/overcommit_memory')
87+
if overcommit_memory.exists():
88+
policy = {'0': 'heuristic',
89+
'1': 'always',
90+
'2': 'never'}.get(overcommit_memory.read_text().strip(), 'unknown')
91+
scope.set_tag('overcommit_memory', policy)
92+
if policy == 'never':
93+
overcommit_kbytes = Path('/proc/sys/vm/overcommit_memory')
94+
kb = overcommit_kbytes.read_text().strip()
95+
if kb != '0':
96+
limit = '{}kB'.format(kb)
97+
else:
98+
overcommit_ratio = Path('/proc/sys/vm/overcommit_ratio')
99+
limit = '{}%'.format(overcommit_ratio.read_text().strip())
100+
scope.set_tag('overcommit_limit', limit)
101+
else:
102+
scope.set_tag('overcommit_limit', 'n/a')
103+
else:
104+
scope.set_tag('overcommit_memory', 'n/a')
105+
scope.set_tag('overcommit_limit', 'n/a')
106+
107+
for k, v in vars(opts).items():
108+
scope.set_tag(k, v)
109+
110+
111+
def process_crashfile(crashfile):
112+
"""Parse the contents of a crashfile and submit sentry messages"""
113+
crash_info = read_crashfile(str(crashfile))
114+
with sentry_sdk.push_scope() as scope:
115+
scope.level = 'fatal'
116+
117+
# Extract node name
118+
node_name = crash_info.pop('node').split('.')[-1]
119+
scope.set_tag("node_name", node_name)
120+
121+
# Massage the traceback, extract the gist
122+
traceback = crash_info.pop('traceback')
123+
# last line is probably most informative summary
124+
gist = traceback.splitlines()[-1]
125+
exception_text_start = 1
126+
for line in traceback.splitlines()[1:]:
127+
if not line[0].isspace():
128+
break
129+
exception_text_start += 1
130+
131+
exception_text = '\n'.join(
132+
traceback.splitlines()[exception_text_start:])
133+
134+
# Extract inputs, if present
135+
inputs = crash_info.pop('inputs', None)
136+
if inputs:
137+
scope.set_extra('inputs', dict(inputs))
138+
139+
# Extract any other possible metadata in the crash file
140+
for k, v in crash_info.items():
141+
strv = list(_chunks(str(v)))
142+
if len(strv) == 1:
143+
scope.set_extra(k, strv[0])
144+
else:
145+
for i, chunk in enumerate(strv):
146+
scope.set_extra('%s_%02d' % (k, i), chunk)
147+
148+
fingerprint = ''
149+
issue_title = '{}: {}'.format(node_name, gist)
150+
for new_fingerprint, error_snippets in KNOWN_ERRORS.items():
151+
for error_snippet in error_snippets:
152+
if error_snippet in traceback:
153+
fingerprint = new_fingerprint
154+
issue_title = new_fingerprint
155+
break
156+
if fingerprint:
157+
break
158+
159+
message = issue_title + '\n\n'
160+
message += exception_text[-(8192 - len(message)):]
161+
if fingerprint:
162+
sentry_sdk.add_breadcrumb(message=fingerprint, level='fatal')
163+
else:
164+
# remove file paths
165+
fingerprint = re.sub(r"(/[^/ ]*)+/?", '', message)
166+
# remove words containing numbers
167+
fingerprint = re.sub(r"([a-zA-Z]*[0-9]+[a-zA-Z]*)+", '', fingerprint)
168+
# adding the return code if it exists
169+
for line in message.splitlines():
170+
if line.startswith("Return code"):
171+
fingerprint += line
172+
break
173+
174+
scope.fingerprint = [fingerprint]
175+
sentry_sdk.capture_message(message, 'fatal')
176+
177+
178+
def before_send(event, hints):
179+
# Filtering log messages about crashed nodes
180+
if 'logentry' in event and 'message' in event['logentry']:
181+
msg = event['logentry']['message']
182+
if msg.startswith("could not run node:"):
183+
return None
184+
if msg.startswith("Saving crash info to "):
185+
return None
186+
if re.match("Node .+ failed to run on host .+", msg):
187+
return None
188+
189+
if 'breadcrumbs' in event and isinstance(event['breadcrumbs'], list):
190+
fingerprints_to_propagate = ['no-disk-space', 'memory-error', 'permission-denied',
191+
'keyboard-interrupt']
192+
for bc in event['breadcrumbs']:
193+
msg = bc.get('message', 'empty-msg')
194+
if msg in fingerprints_to_propagate:
195+
event['fingerprint'] = [msg]
196+
break
197+
198+
return event
199+
200+
201+
def _chunks(string, length=CHUNK_SIZE):
202+
"""
203+
Splits a string into smaller chunks
204+
>>> list(_chunks('some longer string.', length=3))
205+
['som', 'e l', 'ong', 'er ', 'str', 'ing', '.']
206+
"""
207+
return (string[i:i + length]
208+
for i in range(0, len(string), length))

dmriprep/workflows/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def init_single_subject_wf(
314314
# for documentation purposes
315315
subject_data = {
316316
't1w': ['/completely/made/up/path/sub-01_T1w.nii.gz'],
317-
'dwi': ['/completely/made/up/path/sub-01_task-nback_bold.nii.gz']
317+
'dwi': ['/completely/made/up/path/sub-01_dwi.nii.gz']
318318
}
319319
else:
320320
subject_data = collect_data(layout, subject_id)[0]

setup.cfg

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ maintainer_email = [email protected]
77
description = dMRIPrep is a robust and easy-to-use pipeline for preprocessing of diverse dMRI data.
88
long_description = file:README.rst
99
long_description_content_type = text/x-rst; charset=UTF-8
10-
license = 3-clause BSD
10+
license = Apache License, Version 2.0
1111
classifiers =
1212
Development Status :: 3 - Alpha
1313
Intended Audience :: Science/Research
@@ -23,15 +23,16 @@ install_requires =
2323
indexed_gzip >=0.8.8
2424
nibabel >=2.2.1
2525
nilearn !=0.5.0, !=0.5.1
26-
nipype >=1.2.0
27-
niworkflows ~= 0.10.1
26+
nipype >=1.2.2
27+
niworkflows ~= 0.10.3
2828
numpy
2929
pandas
3030
psutil >=5.4
31-
pybids ~= 0.9.2
31+
pybids ~= 0.9.3
3232
pyyaml
3333
scikit-image
34-
smriprep ~= 0.3.0
34+
sdcflows ~= 0.1.0
35+
smriprep ~= 0.3.2
3536
statsmodels
3637
templateflow ~= 0.4.1
3738
test_requires =

0 commit comments

Comments
 (0)