Skip to content

Commit

Permalink
RF: Load report assembler from nireports (#3177)
Browse files Browse the repository at this point in the history
This transition is inspired by how MRIQC defines `generate_reports`
using _nireports_' Report()

mnt: load Report from nireports instead of niworkflows
mnt: adapt function arguments accordingly
mnt: delete run_reports function
mnt: delete Report patch subclass that is no longer necessary
  • Loading branch information
effigies authored Jan 18, 2024
2 parents d85fb53 + 89da33b commit 9568ccb
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 91 deletions.
12 changes: 9 additions & 3 deletions fmriprep/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,17 +219,23 @@ def main():
config.execution.fmriprep_dir,
config.execution.run_uuid,
config=data.load("reports-spec.yml"),
packagename="fmriprep",
)
write_derivative_description(config.execution.bids_dir, config.execution.fmriprep_dir)
write_bidsignore(config.execution.fmriprep_dir)

if failed_reports:
config.loggers.cli.error(
"Report generation was not successful for the following participants : %s.",
", ".join(failed_reports),
)

if sentry_sdk is not None and failed_reports:
sentry_sdk.capture_message(
"Report generation failed for %d subjects" % failed_reports,
"Report generation was not successful for the following participants : %s.",
", ".join(failed_reports),
level="error",
)
sys.exit(int((errno + failed_reports) > 0))
sys.exit(int((errno + len(failed_reports)) > 0))


if __name__ == "__main__":
Expand Down
10 changes: 8 additions & 2 deletions fmriprep/cli/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,19 @@ def build_workflow(config_file, retval):
# Called with reports only
if config.execution.reports_only:
build_log.log(25, "Running --reports-only on participants %s", ", ".join(subject_list))
retval["return_code"] = generate_reports(
failed_reports = generate_reports(
config.execution.participant_label,
config.execution.fmriprep_dir,
config.execution.run_uuid,
config=data.load("reports-spec.yml"),
packagename="fmriprep",
)
if failed_reports:
config.loggers.cli.error(
"Report generation was not successful for the following participants : %s.",
", ".join(failed_reports),
)

retval["return_code"] = len(failed_reports)
return retval

# Build main workflow
Expand Down
114 changes: 28 additions & 86 deletions fmriprep/reports/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,100 +22,42 @@
#
from pathlib import Path

from niworkflows.reports.core import Report as _Report
from nireports.assembler.report import Report

# This patch is intended to permit fMRIPrep 20.2.0 LTS to use the YODA-style
# derivatives directory. Ideally, we will remove this in 20.3.x and use an
# updated niworkflows.


class Report(_Report):
def _load_config(self, config):
from yaml import safe_load as load

settings = load(config.read_text())
self.packagename = self.packagename or settings.get("package", None)

# Removed from here: Appending self.packagename to self.root and self.out_dir
# In this version, pass reportlets_dir and out_dir with fmriprep in the path.

if self.subject_id is not None:
self.root = self.root / f"sub-{self.subject_id}"

if "template_path" in settings:
self.template_path = config.parent / settings["template_path"]

self.index(settings["sections"])


#
# The following are the interface used directly by fMRIPrep
#


def run_reports(
out_dir,
subject_label,
run_uuid,
config=None,
reportlets_dir=None,
packagename=None,
):
"""
Run the reports.
.. testsetup::
>>> copytree_or_skip("data/tests/work", testdir)
>>> (testdir / 'fmriprep').mkdir(parents=True, exist_ok=True)
.. doctest::
>>> run_reports(testdir / 'out', '01', 'madeoutuuid', packagename='fmriprep',
... reportlets_dir=testdir / 'work' / 'reportlets' / 'fmriprep')
0
"""
return Report(
out_dir,
run_uuid,
config=config,
subject_id=subject_label,
packagename=packagename,
reportlets_dir=reportlets_dir,
).generate_report()


def generate_reports(
subject_list, output_dir, run_uuid, config=None, work_dir=None, packagename=None
):
"""Execute run_reports on a list of subjects."""
def generate_reports(subject_list, output_dir, run_uuid, config=None, work_dir=None):
"""Generate reports for a list of subjects."""
reportlets_dir = None
if work_dir is not None:
reportlets_dir = Path(work_dir) / "reportlets"
report_errors = [
run_reports(

errors = []
for subject_label in subject_list:
entities = {}
entities["subject"] = subject_label

robj = Report(
output_dir,
subject_label,
run_uuid,
config=config,
packagename=packagename,
bootstrap_file=config,
reportlets_dir=reportlets_dir,
plugins=None,
plugin_meta=None,
metadata=None,
**entities,
)
for subject_label in subject_list
]

errno = sum(report_errors)
if errno:
import logging
# Count nbr of subject for which report generation failed
try:
robj.generate_report()
except:
import sys
import traceback

logger = logging.getLogger("cli")
error_list = ", ".join(
f"{subid} ({err})" for subid, err in zip(subject_list, report_errors) if err
)
logger.error(
"Preprocessing did not finish successfully. Errors occurred while processing "
"data from participants: %s. Check the HTML reports for details.",
error_list,
)
return errno
errors.append(subject_label)
traceback.print_exception(
*sys.exc_info(),
file=str(Path(output_dir) / "logs" / f"report-{run_uuid}-{subject_label}.err"),
)

return errors

0 comments on commit 9568ccb

Please sign in to comment.