-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Write out QSIRecon pipeline-wise reports #7
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
3dade06
Add report config files.
tsalo 95cc773
Remove viz submodule.
tsalo f351b0a
Work on nireports.
tsalo 5ecb0df
Add viz utils back in.
tsalo 9e3c58e
More work.
tsalo fb49d18
Merge branch 'main' into fix-reports
tsalo 5ac9c5a
Merge branch 'main' into fix-reports
tsalo 2a69d9e
Merge branch 'main' into fix-reports
tsalo a5c4e4a
Fix import.
tsalo 3efe893
Remove reportlets_dir.
tsalo 59fa7e6
Try to fix things.
tsalo d0b3449
Add qsirecon_suffix info.
tsalo f5be15d
Update core.py
tsalo 29100c8
Update run.py
tsalo f16a6ac
Update expected outputs.
tsalo be041bb
Update run.py
tsalo 6a3155e
Update.
tsalo 75f6b73
Update test_cli.py
tsalo 4762542
Update run.py
tsalo 28d5b9f
Update expected outputs.
tsalo e8ac29f
Update workflow.py
tsalo 1790f19
Update.
tsalo 0a783dc
Fix.
tsalo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -171,39 +171,53 @@ def main(): | |
errno = 0 | ||
finally: | ||
|
||
from ..viz.reports import generate_reports | ||
from ..reports.core import generate_reports | ||
from ..workflows.base import _load_recon_spec | ||
from .workflow import copy_boilerplate | ||
|
||
# Generate reports phase | ||
# session_list = ( | ||
# config.execution.get().get('bids_filters', {}).get('dwi', {}).get('session') | ||
# ) | ||
session_list = config.execution.get().get("bids_filters", {}).get("dwi", {}).get("session") | ||
|
||
failed_reports = generate_reports( | ||
config.execution.participant_label, | ||
# session_list=session_list, | ||
) | ||
write_derivative_description( | ||
config.execution.bids_dir, | ||
config.execution.output_dir, | ||
# dataset_links=config.execution.dataset_links, | ||
) | ||
write_bidsignore(config.execution.output_dir) | ||
|
||
workflow_spec = _load_recon_spec() | ||
# Compile list of output folders | ||
# TODO: Retain QSIRecon pipeline names in the config object | ||
qsirecon_suffixes = [] | ||
for node_spec in workflow_spec["nodes"]: | ||
qsirecon_suffix = node_spec.get("qsirecon_suffix", None) | ||
qsirecon_suffixes += [qsirecon_suffix] if qsirecon_suffix else [] | ||
|
||
qsirecon_suffixes = sorted(list(set(qsirecon_suffixes))) | ||
config.loggers.cli.info(f"QSIRecon pipeline suffixes: {qsirecon_suffixes}") | ||
failed_reports = [] | ||
for qsirecon_suffix in qsirecon_suffixes: | ||
suffix_dir = str(config.execution.output_dir / f"qsirecon-{qsirecon_suffix}") | ||
Comment on lines
+191
to
+200
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a smart approach |
||
|
||
# Copy the boilerplate files | ||
copy_boilerplate(config.execution.output_dir, suffix_dir) | ||
|
||
suffix_failed_reports = generate_reports( | ||
config.execution.participant_label, | ||
suffix_dir, | ||
config.execution.run_uuid, | ||
session_list=session_list, | ||
qsirecon_suffix=qsirecon_suffix, | ||
) | ||
failed_reports += suffix_failed_reports | ||
write_derivative_description( | ||
config.execution.bids_dir, | ||
suffix_dir, | ||
# dataset_links=config.execution.dataset_links, | ||
) | ||
write_bidsignore(suffix_dir) | ||
|
||
if failed_reports: | ||
print(failed_reports) | ||
# msg = ( | ||
# 'Report generation was not successful for the following participants ' | ||
# f': {", ".join(failed_reports)}.' | ||
# ) | ||
# config.loggers.cli.error(msg) | ||
# if sentry_sdk is not None: | ||
# sentry_sdk.capture_message(msg, level='error') | ||
|
||
# If preprocessing and recon are requested in the same call, start the recon workflow now. | ||
if errno > 0: | ||
if config.nipype.stop_on_first_crash: | ||
config.loggers.workflow.critical( | ||
"Errors occurred during preprocessing - Recon will not run." | ||
) | ||
|
||
sys.exit(int((errno + failed_reports) > 0)) | ||
sys.exit(int(errno + len(failed_reports)) > 0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,182 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- | ||
# vi: set ft=python sts=4 ts=4 sw=4 et: | ||
""" Data fetchers module """ | ||
"""QSIRecon data files | ||
|
||
.. autofunction:: load | ||
|
||
.. automethod:: load.readable | ||
|
||
.. automethod:: load.as_path | ||
|
||
.. automethod:: load.cached | ||
|
||
.. autoclass:: Loader | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
import atexit | ||
import os | ||
from contextlib import AbstractContextManager, ExitStack | ||
from functools import cached_property | ||
from pathlib import Path | ||
from types import ModuleType | ||
|
||
try: | ||
from functools import cache | ||
except ImportError: # PY38 | ||
from functools import lru_cache as cache | ||
|
||
try: # Prefer backport to leave consistency to dependency spec | ||
from importlib_resources import as_file, files | ||
except ImportError: | ||
from importlib.resources import as_file, files # type: ignore | ||
|
||
try: # Prefer stdlib so Sphinx can link to authoritative documentation | ||
from importlib.resources.abc import Traversable | ||
except ImportError: | ||
from importlib_resources.abc import Traversable | ||
|
||
__all__ = ["load"] | ||
|
||
|
||
class Loader: | ||
"""A loader for package files relative to a module | ||
|
||
This class wraps :mod:`importlib.resources` to provide a getter | ||
function with an interpreter-lifetime scope. For typical packages | ||
it simply passes through filesystem paths as :class:`~pathlib.Path` | ||
objects. For zipped distributions, it will unpack the files into | ||
a temporary directory that is cleaned up on interpreter exit. | ||
|
||
This loader accepts a fully-qualified module name or a module | ||
object. | ||
|
||
Expected usage:: | ||
|
||
'''Data package | ||
|
||
.. autofunction:: load_data | ||
|
||
.. automethod:: load_data.readable | ||
|
||
.. automethod:: load_data.as_path | ||
|
||
.. automethod:: load_data.cached | ||
''' | ||
|
||
from qsirecon.data import Loader | ||
|
||
load_data = Loader(__package__) | ||
|
||
:class:`~Loader` objects implement the :func:`callable` interface | ||
and generate a docstring, and are intended to be treated and documented | ||
as functions. | ||
|
||
For greater flexibility and improved readability over the ``importlib.resources`` | ||
interface, explicit methods are provided to access resources. | ||
|
||
+---------------+----------------+------------------+ | ||
| On-filesystem | Lifetime | Method | | ||
+---------------+----------------+------------------+ | ||
| `True` | Interpreter | :meth:`cached` | | ||
+---------------+----------------+------------------+ | ||
| `True` | `with` context | :meth:`as_path` | | ||
+---------------+----------------+------------------+ | ||
| `False` | n/a | :meth:`readable` | | ||
+---------------+----------------+------------------+ | ||
|
||
It is also possible to use ``Loader`` directly:: | ||
|
||
from qsirecon.data import Loader | ||
|
||
Loader(other_package).readable('data/resource.ext').read_text() | ||
|
||
with Loader(other_package).as_path('data') as pkgdata: | ||
# Call function that requires full Path implementation | ||
func(pkgdata) | ||
|
||
# contrast to | ||
|
||
from importlib_resources import files, as_file | ||
|
||
files(other_package).joinpath('data/resource.ext').read_text() | ||
|
||
with as_file(files(other_package) / 'data') as pkgdata: | ||
func(pkgdata) | ||
|
||
.. automethod:: readable | ||
|
||
.. automethod:: as_path | ||
|
||
.. automethod:: cached | ||
""" | ||
|
||
def __init__(self, anchor: str | ModuleType): | ||
self._anchor = anchor | ||
self.files = files(anchor) | ||
self.exit_stack = ExitStack() | ||
atexit.register(self.exit_stack.close) | ||
# Allow class to have a different docstring from instances | ||
self.__doc__ = self._doc | ||
|
||
@cached_property | ||
def _doc(self): | ||
"""Construct docstring for instances | ||
|
||
Lists the public top-level paths inside the location, where | ||
non-public means has a `.` or `_` prefix or is a 'tests' | ||
directory. | ||
""" | ||
top_level = sorted( | ||
os.path.relpath(p, self.files) + "/"[: p.is_dir()] | ||
for p in self.files.iterdir() | ||
if p.name[0] not in (".", "_") and p.name != "tests" | ||
) | ||
doclines = [ | ||
f"Load package files relative to ``{self._anchor}``.", | ||
"", | ||
"This package contains the following (top-level) files/directories:", | ||
"", | ||
*(f"* ``{path}``" for path in top_level), | ||
] | ||
|
||
return "\n".join(doclines) | ||
|
||
def readable(self, *segments) -> Traversable: | ||
"""Provide read access to a resource through a Path-like interface. | ||
|
||
This file may or may not exist on the filesystem, and may be | ||
efficiently used for read operations, including directory traversal. | ||
|
||
This result is not cached or copied to the filesystem in cases where | ||
that would be necessary. | ||
""" | ||
return self.files.joinpath(*segments) | ||
|
||
def as_path(self, *segments) -> AbstractContextManager[Path]: | ||
"""Ensure data is available as a :class:`~pathlib.Path`. | ||
|
||
This method generates a context manager that yields a Path when | ||
entered. | ||
|
||
This result is not cached, and any temporary files that are created | ||
are deleted when the context is exited. | ||
""" | ||
return as_file(self.files.joinpath(*segments)) | ||
|
||
@cache # noqa: B019 | ||
def cached(self, *segments) -> Path: | ||
"""Ensure data is available as a :class:`~pathlib.Path`. | ||
|
||
Any temporary files that are created remain available throughout | ||
the duration of the program, and are deleted when Python exits. | ||
|
||
Results are cached so that multiple calls do not unpack the same | ||
data multiple times, but the cache is sensitive to the specific | ||
argument(s) passed. | ||
""" | ||
return self.exit_stack.enter_context(as_file(self.files.joinpath(*segments))) | ||
|
||
__call__ = cached | ||
|
||
|
||
load = Loader(__package__) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning on deleting this entirely.