Skip to content

Commit

Permalink
Interpret 'required' key when present in a reportlets config dictionary.
Browse files Browse the repository at this point in the history
If its true and no files/figures were found then raise an exception.
Report indexer will catch these exceptions and raise a single exception
at the end of its run with all of the collected reportlet exceptions.
  • Loading branch information
rwblair committed May 17, 2024
1 parent 60fd854 commit 58d954b
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 22 deletions.
60 changes: 38 additions & 22 deletions nireports/assembler/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

from nireports.assembler import data
from nireports.assembler.reportlet import Reportlet
from nireports.exceptions import NiReportsException, ReportletException

# Add a new figures spec
try:
Expand Down Expand Up @@ -339,6 +340,7 @@ def index(self, config):
This method also places figures in their final location.
"""
exceptions = []
# Initialize a BIDS layout
_indexer = BIDSLayoutIndexer(
config_filename=data.load("nipreps.json"),
Expand All @@ -361,22 +363,25 @@ def index(self, config):
orderings = [s for s in subrep_cfg.get("ordering", "").strip().split(",") if s]
entities, list_combos = self._process_orderings(orderings, layout.get(**bids_filters))

reportlets = []

if not list_combos: # E.g. this is an anatomical reportlet
reportlets = [
Reportlet(
layout,
config=cfg,
out_dir=out_dir,
bids_filters=bids_filters,
metadata=metadata,
)
for cfg in subrep_cfg["reportlets"]
]
for cfg in subrep_cfg["reportlets"]:
try:
rlet = Reportlet(
layout,
config=cfg,
out_dir=out_dir,
bids_filters=bids_filters,
metadata=metadata,
)
reportlets.append(rlet)
except ReportletException as e:
exceptions.append(e)
list_combos = subrep_cfg.get("nested", False)
else:
# Do not use dictionary for queries, as we need to preserve ordering
# of ordering columns.
reportlets = []
for c in list_combos:
# do not display entities with the value None.
c_filt = [
Expand All @@ -389,20 +394,28 @@ def index(self, config):

for cfg in subrep_cfg["reportlets"]:
cfg["bids"].update({entities[i]: c[i] for i in range(len(c))})
rlet = Reportlet(
layout,
config=cfg,
out_dir=out_dir,
bids_filters=bids_filters,
metadata=metadata,
)
if not rlet.is_empty():
rlet.title = title
title = None
reportlets.append(rlet)
try:
rlet = Reportlet(
layout,
config=cfg,
out_dir=out_dir,
bids_filters=bids_filters,
metadata=metadata,
)
if not rlet.is_empty():
rlet.title = title
title = None
reportlets.append(rlet)
except ReportletException as e:
exceptions.append(e)

Check warning on line 410 in nireports/assembler/report.py

View check run for this annotation

Codecov / codecov/patch

nireports/assembler/report.py#L409-L410

Added lines #L409 - L410 were not covered by tests

# Filter out empty reportlets
reportlets = [r for r in reportlets if not r.is_empty()]

# When support python < 3.11 dropped we can use ExceptionGroups
if exceptions:
raise NiReportsException(('There were errors generating report {self}', *exceptions))

if reportlets:
sub_report = SubReport(
subrep_cfg["name"],
Expand Down Expand Up @@ -444,6 +457,9 @@ def process_plugins(self, config, metadata=None):
)
],
)

def __str__(self):
return f'<Report {self.title}>'

Check warning on line 462 in nireports/assembler/report.py

View check run for this annotation

Codecov / codecov/patch

nireports/assembler/report.py#L462

Added line #L462 was not covered by tests

def generate_report(self):
"""Once the Report has been indexed, the final HTML can be generated"""
Expand Down
5 changes: 5 additions & 0 deletions nireports/assembler/reportlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

from nireports.assembler import data
from nireports.assembler.misc import dict2html, read_crashfile
from nireports.exceptions import RequiredReportletException

SVG_SNIPPET = [
"""\
Expand Down Expand Up @@ -421,6 +422,10 @@ def __init__(self, layout, config=None, out_dir=None, bids_filters=None, metadat
boiler_tabs.append("</ul>")
self.components.append(("\n".join(boiler_tabs + boiler_body), desc_text))

if config.get("required", False) and self.is_empty():
raise RequiredReportletException(config)


def is_empty(self):
"""Determine whether the reportlet has no components."""
return len(self.components) == 0
15 changes: 15 additions & 0 deletions nireports/assembler/tests/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

from nireports.assembler import data
from nireports.assembler.report import Report
from nireports.exceptions import NiReportsException, RequiredReportletException

summary_meta = {
"Summary": {
Expand Down Expand Up @@ -138,6 +139,20 @@ def test_report2(bids_sessions):
subject="01",
)

def test_missing_reportlet(
test_report1,
bids_sessions
):
out_dir = tempfile.mkdtemp()
report = test_report1
settings = yaml.safe_load(data.load.readable("default.yml").read_text())
settings["root"] = str(Path(bids_sessions) / "nireports")
settings["out_dir"] = str(Path(out_dir) / "nireports")
settings["run_uuid"] = "fakeuuid"
settings['sections'][0]['reportlets'][0]['required'] = True
settings['sections'][0]['reportlets'][0]['bids'] = {'datatype': 'fake'}
with pytest.raises(NiReportsException, match='No content found'):
report.index(settings)

@pytest.mark.parametrize(
"orderings,expected_entities,expected_value_combos",
Expand Down
9 changes: 9 additions & 0 deletions nireports/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class NiReportsException(Exception):
pass

class ReportletException(NiReportsException):
pass

class RequiredReportletException(ReportletException):
def __init__(self, config):
self.args = (f'No content found while generated reportlet listed as required with the following config: {config}',)

0 comments on commit 58d954b

Please sign in to comment.