diff --git a/aslprep/cli/parser.py b/aslprep/cli/parser.py index 86d736f9c..08ea75f05 100644 --- a/aslprep/cli/parser.py +++ b/aslprep/cli/parser.py @@ -544,6 +544,17 @@ def _bids_filter(value, parser): ) g_other = parser.add_argument_group("Other options") + g_other.add_argument( + "--aggregate-session-reports", + dest="aggr_ses_reports", + action="store", + type=PositiveInt, + default=4, + help=( + "Maximum number of sessions aggregated in one subject's visual report. " + "If exceeded, visual reports are split by session." + ), + ) g_other.add_argument("--version", action="version", version=verstr) g_other.add_argument( "-v", diff --git a/aslprep/cli/run.py b/aslprep/cli/run.py index a9d4013c3..877d2b5f5 100644 --- a/aslprep/cli/run.py +++ b/aslprep/cli/run.py @@ -174,27 +174,33 @@ def main(): _copy_any(dseg_tsv, str(config.execution.aslprep_dir / "desc-aparcaseg_dseg.tsv")) errno = 0 finally: - from fmriprep.reports.core import generate_reports + from aslprep.reports.core import generate_reports - from aslprep import data + # Generate reports phase + session_list = ( + config.execution.get().get("bids_filters", {}).get("bold", {}).get("session") + ) # Generate reports phase failed_reports = generate_reports( config.execution.participant_label, config.execution.aslprep_dir, config.execution.run_uuid, - config=data.load("reports-spec.yml"), - packagename="aslprep", + session_list=session_list, ) write_derivative_description(config.execution.bids_dir, config.execution.aslprep_dir) write_bidsignore(config.execution.aslprep_dir) - if sentry_sdk is not None and failed_reports: - sentry_sdk.capture_message( - f"Report generation failed for {failed_reports} subjects", - level="error", + if failed_reports: + msg = ( + "Report generation was not successful for the following participants " + f': {", ".join(failed_reports)}.' ) - sys.exit(int((errno + failed_reports) > 0)) + config.loggers.cli.error(msg) + if sentry_sdk is not None: + sentry_sdk.capture_message(msg, level="error") + + sys.exit(int((errno + len(failed_reports)) > 0)) if __name__ == "__main__": diff --git a/aslprep/cli/workflow.py b/aslprep/cli/workflow.py index 873902782..b2ebfd1ae 100644 --- a/aslprep/cli/workflow.py +++ b/aslprep/cli/workflow.py @@ -12,12 +12,12 @@ def build_workflow(config_file, retval): """Create the Nipype Workflow that supports the whole execution graph.""" - from fmriprep.reports.core import generate_reports from fmriprep.utils.bids import check_pipeline_version from niworkflows.utils.bids import collect_participants from niworkflows.utils.misc import check_valid_fs_license from aslprep import config, data + from aslprep.reports.core import generate_reports from aslprep.utils.misc import check_deps from aslprep.workflows.base import init_aslprep_wf diff --git a/aslprep/config.py b/aslprep/config.py index 7170c7c69..06ba5f277 100644 --- a/aslprep/config.py +++ b/aslprep/config.py @@ -388,6 +388,8 @@ class execution(_Config): """Debug mode(s).""" aslprep_dir = None """Root of ASLPrep BIDS Derivatives dataset.""" + aggr_ses_reports = None + """Maximum number of sessions aggregated in one subject's visual report.""" fs_license_file = _fs_license """An existing file containing a FreeSurfer license.""" fs_subjects_dir = None diff --git a/aslprep/data/reports-spec-anat.yml b/aslprep/data/reports-spec-anat.yml new file mode 100644 index 000000000..8edf66a67 --- /dev/null +++ b/aslprep/data/reports-spec-anat.yml @@ -0,0 +1,47 @@ +package: aslprep +sections: +- name: Summary + reportlets: + - bids: {datatype: figures, desc: summary, suffix: T1w} +- name: Anatomical + reportlets: + - bids: + datatype: figures + desc: conform + extension: [.html] + suffix: T1w + - bids: {datatype: figures, suffix: dseg} + caption: This panel shows the final, preprocessed T1-weighted image, + with contours delineating the detected brain mask and brain tissue segmentations. + subtitle: Brain mask and brain tissue segmentation of the T1w + - bids: {datatype: figures, space: .*, suffix: T1w, regex_search: True} + caption: Spatial normalization of the T1w image to the {space} template. + description: Results of nonlinear alignment of the T1w reference one or more template + space(s). Hover on the panels with the mouse pointer to transition between both + spaces. + static: false + subtitle: Spatial normalization of the anatomical T1w reference + - bids: {datatype: figures, desc: reconall, suffix: T1w} + caption: Surfaces (white and pial) reconstructed with FreeSurfer (recon-all) + overlaid on the participant's T1w template. + subtitle: Surface reconstruction +- name: About + reportlets: + - bids: {datatype: figures, desc: about, suffix: T1w} + - custom: boilerplate + path: '{out_dir}/logs' + bibfile: ['aslprep', 'data/boilerplate.bib'] + caption: | +

We kindly ask to report results preprocessed with this tool using the following boilerplate.

+ + title: Methods + - custom: errors + path: '{out_dir}/sub-{subject}/log/{run_uuid}' + captions: NiReports may have recorded failure conditions. + title: Errors diff --git a/aslprep/data/reports-spec-perf.yml b/aslprep/data/reports-spec-perf.yml new file mode 100644 index 000000000..6c7807ee5 --- /dev/null +++ b/aslprep/data/reports-spec-perf.yml @@ -0,0 +1,212 @@ +package: aslprep +sections: +- name: B0 field mapping + ordering: session,acquisition,run,fmapid + reportlets: + - bids: {datatype: figures, desc: mapped, suffix: fieldmap} + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + along the phase-encoding direction of the image. Some scanners produce a B0 + mapping of the field, using Spiral Echo Imaging (SEI) or postprocessing a "phase-difference" + acquisition. The plot below shows an anatomical "magnitude" reference and the corresponding + fieldmap. + description: Hover over the panels with the mouse pointer to also visualize the intensity of the + field inhomogeneity in Hertz. + static: false + subtitle: "Preprocessed B0 mapping acquisition" + - bids: {datatype: figures, desc: phasediff, suffix: fieldmap} + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + along the phase-encoding direction of the image. A Gradient-Recalled Echo (GRE) scheme was included for the + mapping of the B0 inhomogeneities by subtracting the phase maps obtained at + two subsequent echoes. The plot below shows an anatomical "magnitude" reference and the corresponding + fieldmap. + description: Hover over the panels with the mouse pointer to also visualize the intensity of the + field inhomogeneity in Hertz. + static: false + subtitle: "Preprocessed mapping of phase-difference acquisition" + - bids: {datatype: figures, desc: pepolar, suffix: fieldmap} + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + along the phase-encoding direction of the image. Utilizing two or more images with different + phase-encoding polarities (PEPolar) or directions, it is possible to estimate the inhomogeneity + of the field. The plot below shows a reference EPI (echo-planar imaging) volume generated + using two or more EPI images with varying phase-encoding blips. + description: Hover on the panels with the mouse pointer to also visualize the intensity of the + inhomogeneity of the field in Hertz. + static: false + subtitle: "Preprocessed estimation with varying Phase-Encoding (PE) blips" + - bids: {datatype: figures, desc: anat, suffix: fieldmap} + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + along the phase-encoding direction of the image. Utilizing an anatomically-correct acquisition + (for instance, T1w or T2w), it is possible to estimate the inhomogeneity of the field by means of nonlinear + registration. The plot below shows a reference EPI (echo-planar imaging) volume generated + using two or more EPI images with the same PE encoding, after alignment to the anatomical scan. + description: Hover on the panels with the mouse pointer to also visualize the intensity of the + inhomogeneity of the field in Hertz. + static: false + subtitle: "Preprocessed estimation by nonlinear registration to an anatomical scan (“fieldmap-less”)" + +- name: Arterial Spin Labeling + ordering: session,task,acquisition,ceagent,reconstruction,direction,run,echo + reportlets: + - bids: {datatype: figures, desc: summary, suffix: asl} + - bids: {datatype: figures, desc: validation, suffix: asl} + - bids: {datatype: figures, desc: fmapCoreg, suffix: asl} + caption: | + The estimated fieldmap was aligned to the corresponding EPI reference + with a rigid-registration process of the anatomical reference of the fieldmap, + using antsRegistration. + Overlaid on top of the co-registration results, the final BOLD mask is represented + with a red contour for reference. + static: false + subtitle: Alignment between the anatomical reference of the fieldmap and the target EPI (debug mode) + - bids: {datatype: figures, desc: fieldmap, suffix: asl} + caption: | + Estimated fieldmap, as reconstructed on the target BOLD run space to allow + the assessment of its alignment with the distorted data. + The anatomical reference is the fieldmap's reference moved into the target EPI's grid through + the estimated transformation. + In other words, this plot should be equivalent to that of the + Preprocessed estimation with varying Phase-Encoding (PE) blips shown above in the + fieldmap section. + Therefore, the fieldmap should be positioned relative to the anatomical reference exactly + as it is positioned in the reportlet above. + static: false + subtitle: "Reconstructed B0 map in the corresponding run's space (debug mode)" + - bids: {datatype: figures, desc: sdc, suffix: asl} + caption: | + Results of performing susceptibility distortion correction (SDC) on the EPI + static: false + subtitle: Susceptibility distortion correction + - bids: {datatype: figures, desc: forcedsyn, suffix: asl} + caption: | + The dataset contained some fieldmap information, but the argument --force-syn + was used. The higher-priority SDC method was used. Here, we show the results + of performing SyN-based SDC on the EPI for comparison. + static: false + subtitle: Experimental fieldmap-less susceptibility distortion correction + - bids: {datatype: figures, desc: flirtnobbr, suffix: asl} + caption: | + mri_coreg (FreeSurfer) was used to generate transformations + from EPI space to T1 Space - BBR refinement using FSL flirt rejected. + Note that Nearest Neighbor interpolation is used in the reportlets in order to + highlight potential spin-history and other artifacts, whereas final images are + resampled using Lanczos interpolation. + static: false + subtitle: Alignment of functional and anatomical MRI data (volume based) + - bids: {datatype: figures, desc: coreg, suffix: asl} + caption: | + mri_coreg (FreeSurfer) was used to generate transformations + from EPI space to T1 Space - bbregister refinement rejected. Note + that Nearest Neighbor interpolation is used in the reportlets in order to highlight + potential spin-history and other artifacts, whereas final images are resampled + using Lanczos interpolation. + static: false + subtitle: Alignment of functional and anatomical MRI data (volume based) + - bids: {datatype: figures, desc: flirtbbr, suffix: asl} + caption: | + FSL flirt was used to generate transformations from EPI-space + to T1w-space - The white matter mask calculated with FSL fast (brain + tissue segmentation) was used for BBR. Note that Nearest Neighbor interpolation + is used in the reportlets in order to highlight potential spin-history and other + artifacts, whereas final images are resampled using Lanczos interpolation. + static: false + subtitle: Alignment of functional and anatomical MRI data (surface driven) + - bids: {datatype: figures, desc: bbregister, suffix: asl} + caption: | + bbregister was used to generate transformations from EPI-space + to T1w-space. Note that Nearest Neighbor interpolation is used in the reportlets + in order to highlight potential spin-history and other artifacts, whereas final + images are resampled using Lanczos interpolation. + static: false + subtitle: Alignment of functional and anatomical MRI data (surface driven) + - bids: {datatype: figures, desc: rois, suffix: asl} + caption: | + Brain mask calculated on the BOLD signal (red contour), along with the + regions of interest (ROIs) used for the estimation of physiological and movement + confounding components that can be then used as nuisance regressors in analysis.
+ The anatomical CompCor ROI (magenta contour) is a mask combining + CSF and WM (white-matter), where voxels containing a minimal partial volume + of GM have been removed.
+ The temporal CompCor ROI (blue contour) contains the top 2% most + variable voxels within the brain mask.
+ The brain edge (or crown) ROI (green contour) picks signals + outside but close to the brain, which are decomposed into 24 principal components. + subtitle: Brain mask and (anatomical/temporal) CompCor ROIs + - bids: + datatype: figures + desc: '[at]compcor' + extension: [.html] + suffix: asl + - bids: {datatype: figures, desc: carpetplot, suffix: asl} + caption: | + Summary statistics are plotted, which may reveal trends or artifacts in the ASL data. + DVARS and FD show the standardized DVARS and framewise-displacement measures for each time point. + A carpet plot shows the time series for all voxels within the brain mask. + Voxels are grouped into cortical (blue), and subcortical (orange) gray matter, cerebellum (green) + and white matter and CSF (red), indicated by the color map on the left-hand side. + subtitle: ASL Summary + - bids: {datatype: figures, desc: carpetplot, suffix: cbf} + caption: | + This carpet plot shows the time series for all voxels within the brain mask for CBF. + Voxels are grouped into cortical (blue), and subcortical (orange) gray matter, + cerebellum (green), white matter and CSF (red), indicated by the color map on the left-hand side. + The SCORE index with values greater than zero indicates which volume(s) are removed by SCORE. + subtitle: CBF Summary + - bids: {datatype: figures, desc: cbf, suffix: cbf} + caption: | + The maps plot cerebral blood flow (CBF) for basic CBF. + The unit is mL/100 g/min. + subtitle: CBF + - bids: {datatype: figures, desc: cbfByTissueType, suffix: cbf} + caption: | + The maps plot the distribution of cerebral blood flow (CBF) values for the basic output, + separated by tissue type. + The unit is mL/100 g/min. + subtitle: Mean CBF by Tissue Type + - bids: {datatype: figures, desc: score, suffix: cbf} + caption: | + The maps plot cerebral blood flow (CBF) for SCORE-corrected CBF. + The unit is mL/100 g/min. + subtitle: SCORE CBF + - bids: {datatype: figures, desc: scoreByTissueType, suffix: cbf} + caption: | + The maps plot the distribution of cerebral blood flow (CBF) values for the SCORE output, + separated by tissue type. + The unit is mL/100 g/min. + subtitle: SCORE CBF by Tissue Type + - bids: {datatype: figures, desc: scrub, suffix: cbf} + caption: | + The maps plot cerebral blood flow (CBF) for SCRUB-corrected CBF. + The unit is mL/100 g/min. + subtitle: SCRUB CBF + - bids: {datatype: figures, desc: scrubByTissueType, suffix: cbf} + caption: | + The maps plot the distribution of cerebral blood flow (CBF) values for the SCRUB output, + separated by tissue type. + The unit is mL/100 g/min. + subtitle: SCRUB CBF by Tissue Type + - bids: {datatype: figures, desc: basil, suffix: cbf} + caption: | + The maps plot cerebral blood flow (CBF) for BASIL-estimated CBF. + The unit is mL/100 g/min. + subtitle: BASIL CBF + - bids: {datatype: figures, desc: basilByTissueType, suffix: cbf} + caption: | + The maps plot the distribution of cerebral blood flow (CBF) values for the BASIL output, + separated by tissue type. + The unit is mL/100 g/min. + subtitle: BASIL CBF by Tissue Type + - bids: {datatype: figures, desc: basilGM, suffix: cbf} + caption: | + The maps plot cerebral blood flow (CBF) for gray matter partial volume-corrected CBF. + The unit is mL/100 g/min. + subtitle: BASIL GM-PVC CBF + - bids: {datatype: figures, desc: basilGMByTissueType, suffix: cbf} + caption: | + The maps plot the distribution of cerebral blood flow (CBF) values for the + BASIL gray matter partial volume-corrected output, separated by tissue type. + The unit is mL/100 g/min. + subtitle: BASIL GM-PVC CBF by Tissue Type + +- name: About + reportlets: + - bids: {datatype: figures, desc: about, suffix: T1w} diff --git a/aslprep/data/reports-spec.yml b/aslprep/data/reports-spec.yml index a2491e873..4074d908c 100644 --- a/aslprep/data/reports-spec.yml +++ b/aslprep/data/reports-spec.yml @@ -12,22 +12,18 @@ sections: extension: [.html] suffix: T1w - bids: {datatype: figures, suffix: dseg} - caption: | - This panel shows the template T1-weighted image (if several T1w images - were found), with contours delineating the detected brain mask and brain tissue - segmentations. + caption: This panel shows the final, preprocessed T1-weighted image, + with contours delineating the detected brain mask and brain tissue segmentations. subtitle: Brain mask and brain tissue segmentation of the T1w - bids: {datatype: figures, space: .*, suffix: T1w, regex_search: True} caption: Spatial normalization of the T1w image to the {space} template. - description: | - Results of nonlinear alignment of the T1w reference one or more template + description: Results of nonlinear alignment of the T1w reference one or more template space(s). Hover on the panels with the mouse pointer to transition between both spaces. static: false subtitle: Spatial normalization of the anatomical T1w reference - bids: {datatype: figures, desc: reconall, suffix: T1w} - caption: | - Surfaces (white and pial) reconstructed with FreeSurfer (recon-all) + caption: Surfaces (white and pial) reconstructed with FreeSurfer (recon-all) overlaid on the participant's T1w template. subtitle: Surface reconstruction @@ -35,50 +31,42 @@ sections: ordering: session,acquisition,run,fmapid reportlets: - bids: {datatype: figures, desc: mapped, suffix: fieldmap} - caption: | - Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions along the phase-encoding direction of the image. Some scanners produce a B0 mapping of the field, using Spiral Echo Imaging (SEI) or postprocessing a "phase-difference" acquisition. The plot below shows an anatomical "magnitude" reference and the corresponding fieldmap. - description: | - Hover over the panels with the mouse pointer to also visualize the intensity of the + description: Hover over the panels with the mouse pointer to also visualize the intensity of the field inhomogeneity in Hertz. static: false subtitle: "Preprocessed B0 mapping acquisition" - bids: {datatype: figures, desc: phasediff, suffix: fieldmap} - caption: | - Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions along the phase-encoding direction of the image. A Gradient-Recalled Echo (GRE) scheme was included for the mapping of the B0 inhomogeneities by subtracting the phase maps obtained at two subsequent echoes. The plot below shows an anatomical "magnitude" reference and the corresponding fieldmap. - description: | - Hover over the panels with the mouse pointer to also visualize the intensity of the + description: Hover over the panels with the mouse pointer to also visualize the intensity of the field inhomogeneity in Hertz. static: false subtitle: "Preprocessed mapping of phase-difference acquisition" - bids: {datatype: figures, desc: pepolar, suffix: fieldmap} - caption: | - Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions along the phase-encoding direction of the image. Utilizing two or more images with different phase-encoding polarities (PEPolar) or directions, it is possible to estimate the inhomogeneity of the field. The plot below shows a reference EPI (echo-planar imaging) volume generated using two or more EPI images with varying phase-encoding blips. - description: | - Hover on the panels with the mouse pointer to also visualize the intensity of the + description: Hover on the panels with the mouse pointer to also visualize the intensity of the inhomogeneity of the field in Hertz. static: false subtitle: "Preprocessed estimation with varying Phase-Encoding (PE) blips" - bids: {datatype: figures, desc: anat, suffix: fieldmap} - caption: | - Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions + caption: Inhomogeneities of the B0 field introduce (oftentimes severe) spatial distortions along the phase-encoding direction of the image. Utilizing an anatomically-correct acquisition (for instance, T1w or T2w), it is possible to estimate the inhomogeneity of the field by means of nonlinear registration. The plot below shows a reference EPI (echo-planar imaging) volume generated using two or more EPI images with the same PE encoding, after alignment to the anatomical scan. - description: | - Hover on the panels with the mouse pointer to also visualize the intensity of the + description: Hover on the panels with the mouse pointer to also visualize the intensity of the inhomogeneity of the field in Hertz. static: false subtitle: "Preprocessed estimation by nonlinear registration to an anatomical scan (“fieldmap-less”)" diff --git a/aslprep/reports/__init__.py b/aslprep/reports/__init__.py new file mode 100644 index 000000000..f6a0561b6 --- /dev/null +++ b/aslprep/reports/__init__.py @@ -0,0 +1 @@ +"""Report-generation tools for ASLPrep.""" diff --git a/aslprep/reports/core.py b/aslprep/reports/core.py new file mode 100644 index 000000000..83f0d8abb --- /dev/null +++ b/aslprep/reports/core.py @@ -0,0 +1,150 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +# +# Copyright The NiPreps Developers +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# We support and encourage derived works from this project, please read +# about our expectations at +# +# https://www.nipreps.org/community/licensing/ +# +"""Tools for generating Reports.""" +from pathlib import Path + +from nireports.assembler.report import Report + +from aslprep import config, data + + +def run_reports( + output_dir, + subject_label, + run_uuid, + bootstrap_file=None, + out_filename="report.html", + reportlets_dir=None, + errorname="report.err", + **entities, +): + """Run the reports.""" + robj = Report( + output_dir, + run_uuid, + bootstrap_file=bootstrap_file, + out_filename=out_filename, + reportlets_dir=reportlets_dir, + plugins=None, + plugin_meta=None, + metadata=None, + **entities, + ) + + # Count nbr of subject for which report generation failed + try: + robj.generate_report() + except: # noqa: E722 + import sys + import traceback + + # Store the list of subjects for which report generation failed + traceback.print_exception(*sys.exc_info(), file=str(Path(output_dir) / "logs" / errorname)) + return subject_label + + return None + + +def generate_reports( + subject_list, + output_dir, + run_uuid, + session_list=None, + bootstrap_file=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" + + if isinstance(subject_list, str): + subject_list = [subject_list] + + errors = [] + for subject_label in subject_list: + # The number of sessions is intentionally not based on session_list but + # on the total number of sessions, because I want the final derivatives + # folder to be the same whether sessions were run one at a time or all-together. + n_ses = len(config.execution.layout.get_sessions(subject=subject_label)) + + if bootstrap_file is not None: + # If a config file is precised, we do not override it + html_report = "report.html" + elif n_ses <= config.execution.aggr_ses_reports: + # If there are only a few session for this subject, + # we aggregate them in a single visual report. + bootstrap_file = data.load("reports-spec.yml") + html_report = "report.html" + else: + # Beyond a threshold, we separate the anatomical report from the functional. + bootstrap_file = data.load("reports-spec-anat.yml") + html_report = f'sub-{subject_label.lstrip("sub-")}_anat.html' + + report_error = run_reports( + output_dir, + subject_label, + run_uuid, + bootstrap_file=bootstrap_file, + out_filename=html_report, + reportlets_dir=reportlets_dir, + errorname=f"report-{run_uuid}-{subject_label}.err", + subject=subject_label, + ) + # If the report generation failed, append the subject label for which it failed + if report_error is not None: + errors.append(report_error) + + if n_ses > config.execution.aggr_ses_reports: + # Beyond a certain number of sessions per subject, + # we separate the functional reports per session + if session_list is None: + all_filters = config.execution.bids_filters or {} + filters = all_filters.get("bold", {}) + session_list = config.execution.layout.get_sessions( + subject=subject_label, **filters + ) + + # Drop ses- prefixes + session_list = [ses[4:] if ses.startswith("ses-") else ses for ses in session_list] + + for session_label in session_list: + bootstrap_file = data.load("reports-spec-func.yml") + html_report = f'sub-{subject_label.lstrip("sub-")}_ses-{session_label}_func.html' + + report_error = run_reports( + output_dir, + subject_label, + run_uuid, + bootstrap_file=bootstrap_file, + out_filename=html_report, + reportlets_dir=reportlets_dir, + errorname=f"report-{run_uuid}-{subject_label}-func.err", + subject=subject_label, + session=session_label, + ) + # If the report generation failed, append the subject label for which it failed + if report_error is not None: + errors.append(report_error) + + return errors diff --git a/aslprep/tests/test_cli.py b/aslprep/tests/test_cli.py index 9755f4a98..69f157679 100644 --- a/aslprep/tests/test_cli.py +++ b/aslprep/tests/test_cli.py @@ -3,12 +3,12 @@ import os import pytest -from fmriprep.reports.core import generate_reports from nipype import config as nipype_config from aslprep.cli.parser import parse_args from aslprep.cli.workflow import build_boilerplate, build_workflow from aslprep.data import load as load_data +from aslprep.reports.core import generate_reports from aslprep.tests.utils import ( check_generated_files, download_test_data, diff --git a/aslprep/utils/plotting.py b/aslprep/utils/plotting.py index b24a07a29..d77120029 100644 --- a/aslprep/utils/plotting.py +++ b/aslprep/utils/plotting.py @@ -8,9 +8,8 @@ from matplotlib import gridspec as mgs from nilearn import image, plotting from nilearn._utils.niimg import load_niimg -from niworkflows import NIWORKFLOWS_LOG -from niworkflows.viz.plots import confoundplot, spikesplot -from niworkflows.viz.utils import ( +from nireports.reportlets.nuisance import confoundplot, plot_carpet, spikesplot +from nireports.reportlets.utils import ( compose_view, cuts_from_bbox, extract_svg, @@ -18,6 +17,10 @@ ) from svgutils.transform import SVGFigure +from aslprep import config + +NIWORKFLOWS_LOG = config.loggers.utils + class CBFPlot(object): """Generate the CBF Summary Plot. @@ -173,7 +176,6 @@ def __init__( def plot(self, figure=None): """Generate fMRI plot.""" import seaborn as sns - from niworkflows.viz.plots import plot_carpet as plt_carpet sns.set_style("whitegrid") sns.set_context("paper", font_scale=0.8) @@ -210,7 +212,7 @@ def plot(self, figure=None): raise ValueError(name) grid_id += 1 - plt_carpet( + plot_carpet( self.timeseries, segments=self.segments, subplot=grid[-1], diff --git a/docs/conf.py b/docs/conf.py index 528bd58b8..833df8cd7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -311,7 +311,9 @@ "bids": ("https://bids-standard.github.io/pybids/", None), "nibabel": ("https://nipy.org/nibabel/", None), "nipype": ("https://nipype.readthedocs.io/en/latest/", None), - "niworkflows": ("https://www.nipreps.org/niworkflows/", None), + "fmriprep": ("https://www.nipreps.org/fmriprep/", None), + "nireports": ("https://nireports.readthedocs.io", None), + "niworkflows": ("https://fmriprep.org/", None), "sdcflows": ("https://www.nipreps.org/sdcflows/", None), "smriprep": ("https://www.nipreps.org/smriprep/", None), "templateflow": ("https://www.templateflow.org/python-client", None), diff --git a/pyproject.toml b/pyproject.toml index 31f3afebb..d2c6c6104 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ dependencies = [ "nibabel <= 5.2.1", "nilearn ~= 0.10.3", "nipype >= 1.8.5", + "nireports ~= 23.2.1", "nitransforms >= 21.0.0", "niworkflows ~= 1.10.0", "numpy >= 1.26", @@ -130,17 +131,13 @@ exclude = ''' | _build | build | dist - | aslprep/niworkflows - | aslprep/pybids - | aslprep/sdcflows - | aslprep/smriprep )/ ''' [tool.isort] profile = "black" skip_gitignore = true -skip = ["aslprep/niworkflows", "aslprep/pybids", "aslprep/sdcflows", "aslprep/smriprep"] +skip = [] [tool.flake8] max-line-length = 99 @@ -150,8 +147,6 @@ exclude = [ "aslprep/_warnings.py", "aslprep/config.py", "aslprep/data/", - "aslprep/niworkflows/", - "aslprep/sdcflows/", "aslprep/tests/", ] ignore = ["D107", "E203", "E402", "E722", "W503", "N803", "N806", "N815"]