Skip to content
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

[ENH] generate a single report per subject for each action #231

Merged
merged 6 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ logs
# files
*.nii
*.h5
*.html
*desc-bidsmreye_eyetrack.tsv
pybids_db
tmp.py
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ repos:
rev: v1.11.1
hooks:
- id: mypy
additional_dependencies: [pydantic, numpy]
additional_dependencies: [pydantic, numpy, types-Jinja2]
files: bidsmreye
args: [--config-file, pyproject.toml]

Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from bidsmreye.bidsmreye import bidsmreye
from bidsmreye.defaults import default_log_level, log_levels
from bidsmreye.download import download
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log

Check warning on line 14 in bidsmreye/_cli.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/_cli.py#L14

Added line #L14 was not covered by tests

log = bidsmreye_log(name="bidsmreye")

Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/bids_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
get_bidsname_config,
get_pybids_config,
)
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log
from bidsmreye.methods import methods
from bidsmreye.utils import copy_license, create_dir_if_absent, return_regex

Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/bidsmreye.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from bidsmreye._version import __version__
from bidsmreye.configuration import Config
from bidsmreye.defaults import default_log_level
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log

log = bidsmreye_log(name="bidsmreye")

Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/config/config_bidsname.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"mask": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-eye_mask.p",
"report": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-eye_report.html",
"report": "sub-{subject}/[ses-{session}]/figures/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-eye_report.html",
"no_label": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}][_res-{res}][_den-{den}]_desc-nolabel_bidsmreye.npz",
"confounds_tsv": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}]_desc-bidsmreye_eyetrack.tsv",
"confounds_json": "sub-{subject}/[ses-{session}]/func/sub-{subject}[_ses-{session}]_task-{task}[_acq-{acquisition}][_ce-{ce}][_rec-{rec}][_dir-{dir}][_run-{run}][_space-{space}]_desc-bidsmreye_eyetrack.json",
Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from attrs import asdict, converters, define, field
from bids import BIDSLayout # type: ignore

from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log

log = bidsmreye_log(name="bidsmreye")

Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import bidsmreye
from bidsmreye.defaults import available_models, default_model
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log

log = bidsmreye_log(name="bidsmreye")

Expand Down
2 changes: 1 addition & 1 deletion bidsmreye/generalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
list_subjects,
)
from bidsmreye.configuration import Config
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log
from bidsmreye.quality_control import quality_control_output
from bidsmreye.utils import (
add_sidecar_in_root,
Expand Down
File renamed without changes.
32 changes: 14 additions & 18 deletions bidsmreye/methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import warnings
from pathlib import Path

import chevron

from bidsmreye._version import __version__
from bidsmreye.defaults import available_models, default_model
from bidsmreye.report import TEMPLATES_DIR, return_jinja_env
from bidsmreye.utils import create_dir_for_file


Expand Down Expand Up @@ -44,29 +43,26 @@ def methods(
if not model:
model = default_model()

is_known_models = False
is_known_model = False
is_default_model = False
if model in available_models():
is_known_models = True
is_known_model = True
if model == default_model():
is_default_model = True

if not is_known_models:
if not is_known_model:
warnings.warn(f"{model} is not a known model name.", stacklevel=3)

template_file = str(Path(__file__).parent / "templates" / "CITATION.mustache")
with open(template_file) as template:
output = chevron.render(
template=template,
data={
"version": __version__,
"model": model,
"is_default_model": is_default_model,
"is_known_models": is_known_models,
"qc_only": qc_only,
},
warn=True,
)
env = return_jinja_env(searchpath=TEMPLATES_DIR)
template = env.get_template("CITATION.jinja")

output = template.render(
version=__version__,
model=model,
is_default_model=is_default_model,
is_known_model=is_known_model,
qc_only=qc_only,
)

output_file.write_text(output)

Expand Down
6 changes: 5 additions & 1 deletion bidsmreye/prepare_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
save_sampling_frequency_to_json,
)
from bidsmreye.configuration import Config
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log
from bidsmreye.report import generate_report
from bidsmreye.utils import (
check_if_file_found,
get_deepmreye_filename,
Expand Down Expand Up @@ -213,4 +214,7 @@
)
for subject_label in subjects:
process_subject(cfg, layout_in, layout_out, subject_label)
generate_report(

Check warning on line 217 in bidsmreye/prepare_data.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/prepare_data.py#L217

Added line #L217 was not covered by tests
output_dir=cfg.output_dir, subject_label=subject_label, action="prepare"
)
progress.update(subject_loop, advance=1)
18 changes: 13 additions & 5 deletions bidsmreye/quality_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from __future__ import annotations

import json
import logging
import math
from pathlib import Path

Expand All @@ -20,7 +19,8 @@
list_subjects,
)
from bidsmreye.configuration import Config
from bidsmreye.logging import bidsmreye_log
from bidsmreye.logger import bidsmreye_log
from bidsmreye.report import generate_report
from bidsmreye.utils import (
check_if_file_found,
create_dir_for_file,
Expand Down Expand Up @@ -142,9 +142,7 @@ def perform_quality_control(
add_qc_to_sidecar(confounds, sidecar_name)

fig = visualize_eye_gaze_data(confounds)
fig.update_layout(title=Path(confounds_tsv).name)
if log.isEnabledFor(logging.DEBUG):
fig.show()
fig.update_layout(showlegend=False, height=800)

create_dir_for_file(visualization_html_file)
fig.write_html(visualization_html_file)
Expand Down Expand Up @@ -184,6 +182,11 @@ def quality_control_output(cfg: Config) -> None:
)
for subject_label in subjects:
qc_subject(cfg, layout_out, subject_label)
generate_report(
output_dir=cfg.output_dir,
subject_label=subject_label,
action="generalize",
)
progress.update(subject_loop, advance=1)


Expand All @@ -203,6 +206,11 @@ def quality_control_input(cfg: Config) -> None:
)
for subject_label in subjects:
qc_subject(cfg, layout_in, subject_label, layout_out)
generate_report(
output_dir=cfg.output_dir,
subject_label=subject_label,
action="generalize",
)
progress.update(subject_loop, advance=1)


Expand Down
80 changes: 80 additions & 0 deletions bidsmreye/report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Compile outputs from all tasks, spaces, runs into a single HTML."""

import datetime
from pathlib import Path

from jinja2 import Environment, FileSystemLoader, select_autoescape

from bidsmreye._version import __version__
from bidsmreye.logger import bidsmreye_log

log = bidsmreye_log(name="bidsmreye")

TEMPLATES_DIR = Path(__file__).parent / "templates"


def return_jinja_env(searchpath=None) -> Environment:
if searchpath is None:
searchpath = TEMPLATES_DIR / "report"
return Environment(
loader=FileSystemLoader(searchpath),
autoescape=select_autoescape(),
lstrip_blocks=True,
trim_blocks=True,
)


def generate_report(output_dir: Path, subject_label: str, action: str) -> None:

env = return_jinja_env()
template = env.get_template("base.html")

if action == "prepare":
input_files = sorted(output_dir.glob(f"sub-{subject_label}/**/*report.html"))
elif action == "generalize":
input_files = sorted(output_dir.glob(f"sub-{subject_label}/**/*eyetrack.html"))

files = []
for html_report in input_files:
with open(html_report) as f:
content = f.read()

name: str = html_report.stem
if action == "prepare":
name = name.replace("_desc-eye_report", "_desc-preproc_bold")

Check warning on line 44 in bidsmreye/report.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/report.py#L44

Added line #L44 was not covered by tests

files.append({"name": name, "content": content, "path": html_report})

date = datetime.datetime.now().astimezone().replace(microsecond=0).isoformat()

report = template.render(
action=action,
files=files,
subject_label=subject_label,
date=date,
version=__version__,
)

report_filename = (
output_dir / f"sub-{subject_label}" / f"sub-{subject_label}_{action}.html"
)

with open(report_filename, "w") as f:
f.write(report)

log.info(f"Report saved at: '{report_filename}'.")


if __name__ == "__main__":

cwd = Path("/home/remi/github/cpp-lln-lab/bidsMReye")

Check warning on line 70 in bidsmreye/report.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/report.py#L70

Added line #L70 was not covered by tests

output_dir = cwd / "outputs" / "moae_fmriprep" / "derivatives" / "bidsmreye"
subject = "01"

Check warning on line 73 in bidsmreye/report.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/report.py#L72-L73

Added lines #L72 - L73 were not covered by tests

output_dir = Path("/home/remi/gin/CPP/yin/bidsmreye")
subject_label = "02"

Check warning on line 76 in bidsmreye/report.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/report.py#L75-L76

Added lines #L75 - L76 were not covered by tests

action = "prepare"

Check warning on line 78 in bidsmreye/report.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/report.py#L78

Added line #L78 was not covered by tests

generate_report(output_dir, subject_label, action)

Check warning on line 80 in bidsmreye/report.py

View check run for this annotation

Codecov / codecov/patch

bidsmreye/report.py#L80

Added line #L80 was not covered by tests
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ performed using [*bidsMReye*](https://github.com/cpp-lln-lab/bidsMReye) (version
a BIDS app relying on [deepMReye](https://github.com/DeepMReye/DeepMReye) (@deepmreye)
to decode eye motion from fMRI time series data.

{{^qc_only}}
{% if not qc_only %}
### data extraction

The data of each BOLD runs underwent co-registration conducted
Expand All @@ -28,19 +28,18 @@ across voxels (spatial normalization).

Voxels time series were used as inputs for generalization decoding
using a
{{#is_known_models}}
{% if is_known_model %}
pre-trained model {{ model }} from deepMReye from [OSF](https://osf.io/23t5v).
{{#is_default_model}}
{% if is_default_model %}
This model was trained on the following datasets:
guided fixations (@alexander_open_2017),
smooth pursuit (@nau_real-motion_2018, @polti_rapid_2022, @nau_hexadirectional_2018),
free viewing (@julian_human_2018).
{{/is_default_model}}
{{/is_known_models}}
{{^is_known_models}}
{% endif %}
{% else %}
model trained on calibration data from the current study.
{{/is_known_models}}
{{/qc_only}}
{% endif %}
{% endif %}

### quality control

Expand Down
59 changes: 59 additions & 0 deletions bidsmreye/templates/report/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>bidsmreye report - sub-{{ subject_label }} - {{ action }}</title>

<link rel="icon" type="image/x-icon" href="https://raw.githubusercontent.com/cpp-lln-lab/bidsMReye/main/docs/source/images/bidsMReye_logo.png">

<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous">
</script>

</head>

<body>

<header>{% include "header.html" %}</header>

<main class="container-fluid">

<h2 class="mt-5 pt-5">subject {{ subject_label }}: {{ action }}</h2>

{% for file in files %}
<section id="{{ file.name }}">
<div class="row pt-4 px-5">
<h3 class="pt-3">{{ file.name }}</h3>
<div class="rounded-2 {% if action == 'prepare' %} bg-black {% else %} bg-white {% endif %}">
<div class="m-2 p-2">{{ file.content|safe }}</div>
</div>
</div>
</section>
{% endfor %}

<section id="about">
<h2 class="mt-3">About</h2>
<p>
Report generated
<ul>
<li>on: {{ date }}</li>
<li>with bidsmreye {{ version }}</li>
</ul>
</p>
</section>

</main>

<footer>{% include "footer.html" %}</footer>

</body>
</html>
Loading